package org.bigk.invoices.services;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.bigk.invoices.exceptions.ServiceException;
import org.bigk.invoices.model.Invoice;
import org.bigk.invoices.model.InvoiceFilter;
import org.bigk.invoices.model.InvoicePosition;
import org.bigk.invoices.model.InvoicePositionSummary;
import org.bigk.invoices.model.InvoicePurchaser;
import org.bigk.invoices.model.Invoice_;
import org.bigk.invoices.model.PaymentKind;
import org.bigk.invoices.model.Purchaser;
import org.bigk.invoices.model.PurchaserRole;
import org.bigk.invoices.model.Tax;
import org.bigk.invoices.services.taxesconfig.TaxConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pl.bigk.utils.lexengines.LexEngine;
import pl.bigk.utils.lexengines.LexEnginePL;
@Service("invoicesService")
@Transactional
public class InvoicesServiceImpl implements InvoicesService {
private static final Logger logger = LoggerFactory.getLogger(InvoicesServiceImpl.class);
private static final String ALL_INVOICES_QUERY =
"SELECT i FROM Invoice i ORDER BY p.year ASC, p.number ASC";
@PersistenceContext
protected EntityManager em;
@Autowired
protected PaymentKindsService paymentKindsService;
@Autowired
protected TaxesService taxesService;
@Autowired
protected PurchasersRolesService purchasersRolesService;
@Autowired
protected HRInvoiceNumberService hrInvoiceNumberService;
@Autowired
protected RecalcTotalOfIPSService recalcTotalOfIPSService;
@Autowired
private TaxesConfigService taxesConfigService;
@Resource(name="invoiceTemplate")
protected Invoice invoiceTemplate;
@Override
public List<Invoice> listAllItems() throws ServiceException {
logger.debug("listAllItems() - start");
TypedQuery<Invoice> query = em.createQuery(ALL_INVOICES_QUERY, Invoice.class);
List<Invoice> list = query.getResultList();
if (list != null) {
for (Invoice invoice : list) {
recalculateInvoice(invoice);
hrInvoiceNumberService.updateHRInvoiceNumber(invoice);
}
}
logger.debug("listAllItems() - end - read [{}] element(s)", CollectionUtils.size(list));
return list;
}
@Override
public List<Invoice> listItems4Page(InvoiceFilter filter) throws ServiceException {
List<Invoice> list = readListForFilter(filter);
updateFilterStats(filter);
if (list != null) {
for (Invoice invoice : list) {
recalculateInvoice(invoice);
hrInvoiceNumberService.updateHRInvoiceNumber(invoice);
}
}
return list;
}
private List<Invoice> readListForFilter(InvoiceFilter filter) {
logger.debug("listItems4Page(filter=[{}]) - start", filter);
CriteriaBuilder critBuilder = em.getCriteriaBuilder();
CriteriaQuery<Invoice> critQuery = critBuilder.createQuery(Invoice.class);
// select-clause
Root<Invoice> root = critQuery.from(Invoice.class);
critQuery.select(root);
// where-clause
Predicate wherePredicate = whereClauseForFilter(critBuilder, critQuery, root, filter);
if (wherePredicate != null) {
critQuery.where(wherePredicate);
}
// order-by-clause
critQuery.orderBy(Arrays.asList(
critBuilder.desc(root.get(Invoice_.year)),
critBuilder.desc(root.get(Invoice_.number))));
TypedQuery<Invoice> query = em.createQuery(critQuery);
query.setFirstResult(filter.calculateFirstResultIndex());
query.setMaxResults(filter.getPaginatedPageSize());
List<Invoice> list = query.getResultList();
logger.debug("listItems4Page() - end - read [{}] element(s)", CollectionUtils.size(list));
return list;
}
private void updateFilterStats(InvoiceFilter filter) {
logger.debug("countItems4Page(filter=[{}]) - start", filter);
CriteriaBuilder critBuilder = em.getCriteriaBuilder();
CriteriaQuery<Number> critQuery = critBuilder.createQuery(Number.class);
// select-clause
Root<Invoice> root = critQuery.from(Invoice.class);
critQuery.select(critBuilder.count(root));
// where-clause
Predicate wherePredicate = whereClauseForFilter(critBuilder, critQuery, root, filter);
if (wherePredicate != null) {
critQuery.where(wherePredicate);
}
Number count = em.createQuery(critQuery).getSingleResult();
filter.setCalculatedTotalRecordsAndPages(count.intValue());
logger.debug("countItems4Page() - items counted: [{}]", count);
}
private Predicate whereClauseForFilter(CriteriaBuilder critBuilder,
CriteriaQuery<?> critQuery, Root<Invoice> root,
InvoiceFilter filter) {
if (filter == null) {
return null;
}
List<Predicate> where = new ArrayList<Predicate>();
if (filter.getId() != null) {
where.add(critBuilder.equal(root.get(Invoice_.id), filter.getId()));
}
if (StringUtils.isNotEmpty(filter.getYear())) {
where.add(critBuilder.equal(root.get(Invoice_.year), filter.getYear()));
}
if (filter.getNumber() != null) {
where.add(critBuilder.equal(root.get(Invoice_.number), filter.getNumber()));
}
if (StringUtils.isNotEmpty(filter.getPurchaserName())) {
where.add(critBuilder.like(
root.<Purchaser>get("invoicePurchasers").<String>get("name"), "%" + filter.getPurchaserName() + "%"));
}
if (where.size() > 0) {
return critBuilder.and(where.toArray(new Predicate[0]));
} else {
return null;
}
}
@Override
public Invoice getInvoice(Long id) throws ServiceException {
logger.debug("getInvoice(id=[{}]) - start", + id);
Invoice invoice = em.find(Invoice.class, id);
recalculateInvoice(invoice);
hrInvoiceNumberService.updateHRInvoiceNumber(invoice);
logger.debug("getInvoice(Long) - end - return value=[{}]", invoice);
return invoice;
}
public Invoice prepareNewInvoice() throws ServiceException {
logger.debug("prepareNewInvoice() - start");
Invoice invoice = null;
if (this.invoiceTemplate != null) {
try {
invoice = invoiceTemplate.clone();
} catch (CloneNotSupportedException e) {
logger.warn("prepareNewInvoice()", e);
}
}
if (invoice == null) {
invoice = new Invoice();
}
invoice.setInvoicePositions(new ArrayList<InvoicePosition>());
invoice.setInvoicePurchasers(new ArrayList<InvoicePurchaser>());
Calendar nowDay = Calendar.getInstance();
nowDay.set(Calendar.HOUR, 0);
nowDay.set(Calendar.MINUTE, 0);
nowDay.set(Calendar.SECOND, 0);
nowDay.set(Calendar.MILLISECOND, 0);
Long curYear = new Long(nowDay.get(Calendar.YEAR));
invoice.setYear(curYear.toString());
int maxInvoiceNumber = this.getMaxInvoiceNumber(curYear.toString());
maxInvoiceNumber++;
Long number = new Long(maxInvoiceNumber);
invoice.setNumber(number);
invoice.setDocumentDate(nowDay.getTime());
invoice.setSoldDate(nowDay.getTime());
PaymentKind pk = null;
if (invoice.getPaymentKindId() != null) {
pk = paymentKindsService.getPaymentKind(invoice.getPaymentKindId());
}
Date paymentDate = null;
if (pk != null) {
invoice.setPaymentKind(pk);
paymentDate = paymentKindsService.calculatePaymentDate(nowDay.getTime(), pk.getId());
invoice.setPaymentDate(paymentDate);
}
recalculateInvoice(invoice);
hrInvoiceNumberService.updateHRInvoiceNumber(invoice);
if (logger.isDebugEnabled()) {
logger.debug("prepareNewInvoice() - end - return value=" + invoice);
}
return invoice;
}
protected int getMaxInvoiceNumber(String year) throws ServiceException {
logger.debug("getMaxInvoiceNumber(year=[{}]) - start", year);
CriteriaBuilder critBuilder = em.getCriteriaBuilder();
CriteriaQuery<Invoice> critQuery = critBuilder.createQuery(Invoice.class);
// select-clause
Root<Invoice> root = critQuery.from(Invoice.class);
critQuery.select(root)
.where(critBuilder.equal(root.get(Invoice_.year), year))
.orderBy(critBuilder.desc(root.get(Invoice_.number)));
TypedQuery<Invoice> query = em.createQuery(critQuery).setMaxResults(1);
Invoice invoice = null;
try {
invoice = query.getSingleResult();
} catch (NoResultException ex) {
logger.info("getMaxInvoiceNumber() - no result found - no invoices in this year?");
}
int maxNumber = 0;
if (invoice != null) {
maxNumber = invoice.getNumber().intValue();
}
logger.debug("getMaxInvoiceNumber() - end - return value=[{}]", maxNumber);
return maxNumber;
}
public void saveInvoice(Invoice invoice) throws ServiceException {
logger.debug("saveInvoice(Invoice invoice=[{}]) - start", invoice);
List<InvoicePosition> positions = invoice.getInvoicePositions();
if (positions != null) {
for (InvoicePosition ip : positions) {
ip.setInvoice(invoice);
}
}
List<InvoicePurchaser> purchasers = invoice.getInvoicePurchasers();
if (purchasers != null) {
for (InvoicePurchaser ip : purchasers) {
ip.setInvoice(invoice);
}
}
em.merge(invoice);
logger.debug("saveInvoice(Invoice) - end");
}
@Override
public void updateInvoice(Invoice invoice) throws ServiceException {
throw new UnsupportedOperationException("Ups... This method must be implemented!");
}
@Override
public void addInvoicePosition(Invoice invoice, InvoicePosition invoicePosition)
throws ServiceException {
if (logger.isDebugEnabled()) {
logger.debug("addInvoicePosition(Invoice invoice=" + invoice
+ ", InvoicePosition invoicePosition=" + invoicePosition
+ ") - start");
}
// read and set Tax object
if (invoicePosition.getTaxId() != null) {
Tax tax = taxesService.getTax(invoicePosition.getTaxId());
invoicePosition.setTax(tax);
}
// set parent of this position
invoicePosition.setInvoice(invoice);
// add positions to parent's list
List<InvoicePosition> invoicePositions = invoice.getInvoicePositions();
if (invoicePositions == null) {
invoicePositions = new ArrayList<InvoicePosition>();
invoice.setInvoicePositions(invoicePositions);
}
invoicePositions.add(invoicePosition);
this.recalculateInvoice(invoice);
hrInvoiceNumberService.updateHRInvoiceNumber(invoice);
if (logger.isDebugEnabled()) {
logger.debug("addInvoicePosition(Invoice, InvoicePosition) - end");
}
}
public void updateInvoicePosition(Invoice invoice,
InvoicePosition invoicePosition, Long idx)
throws ServiceException {
if (logger.isDebugEnabled()) {
logger.debug("updateInvoicePosition(Invoice invoice=" + invoice
+ ", InvoicePosition invoicePosition=" + invoicePosition
+ ", Long idx=" + idx + ") - start");
}
// read and set Tax object
if (invoicePosition.getTaxId() != null) {
Tax tax = taxesService.getTax(invoicePosition.getTaxId());
invoicePosition.setTax(tax);
}
// set parent of this position
invoicePosition.setInvoice(invoice);
// add positions to parent's list
List<InvoicePosition> invoicePositions = invoice.getInvoicePositions();
if (invoicePositions == null) {
invoicePositions = new ArrayList<InvoicePosition>();
invoice.setInvoicePositions(invoicePositions);
}
invoicePositions.set(idx.intValue(), invoicePosition);
this.recalculateInvoice(invoice);
hrInvoiceNumberService.updateHRInvoiceNumber(invoice);
if (logger.isDebugEnabled()) {
logger.debug("updateInvoicePosition(Invoice, InvoicePosition, Long) - end");
}
}
@Override
public InvoicePosition getInvoicePosition(Invoice i, Long idx)
throws ServiceException {
InvoicePosition ip = null;
if (i != null
&& i.getInvoicePositions() != null
&& i.getInvoicePositions().size() > idx) {
ip = i.getInvoicePositions().get(idx.intValue());
}
return ip;
}
@Override
public void addInvoicePurchaser(Invoice invoice, InvoicePurchaser invoicePurchaser)
throws ServiceException {
if (logger.isDebugEnabled()) {
logger.debug("addInvoicePurchaser(Invoice invoice=" + invoice
+ ", InvoicePurchaser invoicePurchaser=" + invoicePurchaser
+ ") - start");
}
// read and set PurchaserRole object
if (invoicePurchaser.getPurchaserRoleId() != null) {
PurchaserRole role = purchasersRolesService.getPurchaserRole(invoicePurchaser.getPurchaserRoleId());
invoicePurchaser.setPurchaserRole(role);
}
// set parent of this purchaser
invoicePurchaser.setInvoice(invoice);
// add purchaser to parent's list
List<InvoicePurchaser> invoicePurchasers = invoice.getInvoicePurchasers();
if (invoicePurchasers == null) {
invoicePurchasers = new ArrayList<InvoicePurchaser>();
invoice.setInvoicePurchasers(invoicePurchasers);
}
invoicePurchasers.add(invoicePurchaser);
if (logger.isDebugEnabled()) {
logger.debug("addInvoicePurchaser(Invoice, InvoicePurchaser) - end");
}
}
public void updateInvoicePurchaser(Invoice invoice,
InvoicePurchaser invoicePurchaser, Long idx)
throws ServiceException {
if (logger.isDebugEnabled()) {
logger.debug("updateInvoicePurchaser(Invoice invoice=" + invoice
+ ", InvoicePurchaser invoicePurchaser=" + invoicePurchaser
+ ", Long idx=" + idx + ") - start");
}
// read and set PurchaserRole object
if (invoicePurchaser.getPurchaserRoleId() != null) {
PurchaserRole role = purchasersRolesService.getPurchaserRole(invoicePurchaser.getPurchaserRoleId());
invoicePurchaser.setPurchaserRole(role);
}
// set parent of this purchaser
invoicePurchaser.setInvoice(invoice);
// set purchaser at parent's list
List<InvoicePurchaser> invoicePurchasers = invoice.getInvoicePurchasers();
if (invoicePurchasers == null) {
invoicePurchasers = new ArrayList<InvoicePurchaser>();
invoice.setInvoicePurchasers(invoicePurchasers);
}
invoicePurchasers.set(idx.intValue(), invoicePurchaser);
if (logger.isDebugEnabled()) {
logger.debug("updateInvoicePurchaser(Invoice, InvoicePurchaser, Long) - end");
}
}
@Override
public InvoicePurchaser getInvoicePurchaser(Invoice i, Long idx)
throws ServiceException {
InvoicePurchaser ip = null;
if (i != null
&& i.getInvoicePurchasers() != null
&& i.getInvoicePurchasers().size() > idx) {
ip = i.getInvoicePurchasers().get(idx.intValue());
}
return ip;
}
protected void recalculateInvoice(Invoice invoice) {
if (logger.isDebugEnabled()) {
logger.debug("recalculateInvoice(Invoice invoice=" + invoice + ") - start");
}
if (invoice == null)
return;
// create Map with summaries
Map<Double, InvoicePositionSummary> calculatedSummaries =
new HashMap<Double, InvoicePositionSummary>(5);
// create List with keys of summaries
List<Double> calculatedSummariesKeys = new ArrayList<Double>(4);
TaxConfig tc = taxesConfigService.getTaxConfigByDate(invoice.getSoldDate());
if (tc != null) {
List<Double> availTaxes = tc.getAvailTaxes();
for (Double at : availTaxes) {
calculatedSummariesKeys.add(at);
calculatedSummaries.put(at, new InvoicePositionSummary(at));
}
}
calculatedSummariesKeys.add(NumberUtils.DOUBLE_ZERO);
invoice.setCalculatedSummariesKeys(calculatedSummariesKeys);
calculatedSummaries.put(NumberUtils.DOUBLE_ZERO,
new InvoicePositionSummary(NumberUtils.DOUBLE_ZERO));
// position with 'total' aggregates
InvoicePositionSummary ip1 = new InvoicePositionSummary(NumberUtils.DOUBLE_ONE);
calculatedSummaries.put(NumberUtils.DOUBLE_ONE, ip1);
invoice.setCalculatedSummaries(calculatedSummaries);
if (invoice.getInvoicePositions() != null) {
for (InvoicePosition pos : invoice.getInvoicePositions()) {
if (logger.isDebugEnabled()) {
logger.debug("recalculateInvoice(Invoice) - iterate through invoicePositions - pos=" + pos);
}
Double percentTax = pos.getTax().getValue();
if (logger.isDebugEnabled()) {
logger.debug("recalculateInvoice(Invoice) - iterate through invoicePositions - percentTax=" + percentTax);
}
InvoicePositionSummary posFromMap =
(InvoicePositionSummary) calculatedSummaries.get(percentTax);
if (logger.isDebugEnabled()) {
logger.debug("recalculateInvoice(Invoice) - iterate through invoicePositions - before add - posFromMap=" + posFromMap);
logger.debug("recalculateInvoice(Invoice) - iterate through invoicePositions - before add - ip1=" + ip1);
}
if (posFromMap != null) {
posFromMap.addValueNetto(pos.getValueNetto());
posFromMap.addSum(pos.getSum());
posFromMap.addTotal(pos.getTotal());
}
ip1.addValueNetto(pos.getValueNetto());
ip1.addSum(pos.getSum());
ip1.addTotal(pos.getTotal());
if (logger.isDebugEnabled()) {
logger.debug("recalculateInvoice(Invoice) - iterate through invoicePositions - after add - posFromMap=" + posFromMap);
logger.debug("recalculateInvoice(Invoice) - iterate through invoicePositions - after add - ip1=" + ip1);
}
}
// recalculate InvoicePositionSummary when dedicated service is set
if (recalcTotalOfIPSService != null) {
recalcTotalOfIPSService.recalculateTotal(ip1);
}
// get text for ip1::total
BigDecimal total = ip1.getTotal();
LexEngine le = new LexEnginePL();
long integerPart = total.longValue();
long digitsPart = total.multiply(new BigDecimal(100d)).remainder(new BigDecimal(100d)).longValue();
invoice.setHrTotalIntegerPart(le.getTextForNumber(integerPart));
invoice.setHrTotalDigitsPart(le.getTextForNumber(digitsPart));
if (logger.isDebugEnabled()) {
logger.debug("recalculateInvoice(Invoice) - set - hrTotalIntegerPart=" + invoice.getHrTotalIntegerPart());
logger.debug("recalculateInvoice(Invoice) - set - hrTotalDigitsPart=" + invoice.getHrTotalDigitsPart());
}
}
if (logger.isDebugEnabled()) {
logger.debug("recalculateInvoice(Invoice) - end");
}
}
public void deleteInvoice(Invoice invoice) throws ServiceException {
logger.debug("deleteInvoice(invoice=[{}]) - start", invoice);
em.remove(em.merge(invoice));
logger.debug("deleteInvoice() - end");
}
}