| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| *******************************************************************************/ |
| package org.apache.ofbiz.accounting.invoice; |
| |
| import java.math.BigDecimal; |
| import java.math.MathContext; |
| import java.sql.Timestamp; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.ofbiz.base.util.Debug; |
| import org.apache.ofbiz.base.util.UtilDateTime; |
| import org.apache.ofbiz.base.util.UtilMisc; |
| import org.apache.ofbiz.base.util.UtilNumber; |
| import org.apache.ofbiz.base.util.UtilValidate; |
| import org.apache.ofbiz.entity.Delegator; |
| import org.apache.ofbiz.entity.GenericEntityException; |
| import org.apache.ofbiz.entity.GenericValue; |
| import org.apache.ofbiz.entity.condition.EntityCondition; |
| import org.apache.ofbiz.entity.condition.EntityConditionList; |
| import org.apache.ofbiz.entity.condition.EntityExpr; |
| import org.apache.ofbiz.entity.condition.EntityOperator; |
| import org.apache.ofbiz.entity.util.EntityQuery; |
| import org.apache.ofbiz.entity.util.EntityUtil; |
| import org.apache.ofbiz.entity.util.EntityUtilProperties; |
| |
| /** |
| * InvoiceWorker - Worker methods of invoices |
| */ |
| public final class InvoiceWorker { |
| |
| public static final String module = InvoiceWorker.class.getName(); |
| private static final BigDecimal ZERO = BigDecimal.ZERO; |
| private static final int decimals = UtilNumber.getBigDecimalScale("invoice.decimals"); |
| private static final int rounding = UtilNumber.getBigDecimalRoundingMode("invoice.rounding"); |
| private static final int taxDecimals = UtilNumber.getBigDecimalScale("salestax.calc.decimals"); |
| private static final int taxRounding = UtilNumber.getBigDecimalRoundingMode("salestax.rounding"); |
| |
| private InvoiceWorker () {} |
| |
| /** |
| * Return the total amount of the invoice (including tax) using the the invoiceId as input. |
| * @param delegator the delegator |
| * @param invoiceId the invoice id |
| * @return Return the total amount of the invoice |
| */ |
| public static BigDecimal getInvoiceTotal(Delegator delegator, String invoiceId) { |
| return getInvoiceTotal(delegator, invoiceId, Boolean.TRUE); |
| } |
| |
| /** |
| * Return the total amount of the invoice (including tax) using the the invoiceId as input. |
| * with the ability to specify if the actual currency is required. |
| * @param delegator the delegator |
| * @param invoiceId the invoice Id |
| * @param actualCurrency true: provide the actual currency of the invoice (could be different from the system currency) |
| * false: if required convert the actual currency into the system currency. |
| * @return Return the total amount of the invoice |
| */ |
| public static BigDecimal getInvoiceTotal(Delegator delegator, String invoiceId, Boolean actualCurrency) { |
| if (delegator == null) { |
| throw new IllegalArgumentException("Null delegator is not allowed in this method"); |
| } |
| |
| GenericValue invoice = null; |
| try { |
| invoice = EntityQuery.use(delegator).from("Invoice").where("invoiceId", invoiceId).queryOne(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Problem getting Invoice", module); |
| } |
| |
| if (invoice == null) { |
| throw new IllegalArgumentException("The passed invoiceId [" +invoiceId + "] does not match an existing invoice"); |
| } |
| |
| return getInvoiceTotal(invoice, actualCurrency); |
| } |
| |
| /** |
| * Method to return the total amount of an invoice item i.e. quantity * amount |
| * @param invoiceItem GenericValue object of the invoice item |
| * @return the invoice total as BigDecimal |
| */ |
| public static BigDecimal getInvoiceItemTotal(GenericValue invoiceItem) { |
| BigDecimal quantity = invoiceItem.getBigDecimal("quantity"); |
| if (quantity == null) { |
| quantity = BigDecimal.ONE; |
| } |
| BigDecimal amount = invoiceItem.getBigDecimal("amount"); |
| if (amount == null) { |
| amount = ZERO; |
| } |
| return quantity.multiply(amount).setScale(decimals, rounding); |
| } |
| |
| /** Method to get the taxable invoice item types as a List of invoiceItemTypeIds. These are identified in Enumeration with enumTypeId TAXABLE_INV_ITM_TY. */ |
| public static List<String> getTaxableInvoiceItemTypeIds(Delegator delegator) throws GenericEntityException { |
| List<String> typeIds = new LinkedList<String>(); |
| List<GenericValue> invoiceItemTaxTypes = EntityQuery.use(delegator).from("Enumeration").where("enumTypeId", "TAXABLE_INV_ITM_TY") |
| .cache().queryList(); |
| for (GenericValue invoiceItemTaxType : invoiceItemTaxTypes) { |
| typeIds.add(invoiceItemTaxType.getString("enumId")); |
| } |
| return typeIds; |
| } |
| |
| public static BigDecimal getInvoiceTaxTotal(GenericValue invoice) { |
| BigDecimal taxTotal = ZERO; |
| Map<String, Set<String>> taxAuthPartyAndGeos = InvoiceWorker.getInvoiceTaxAuthPartyAndGeos(invoice); |
| for (Map.Entry<String, Set<String>> taxAuthPartyGeos : taxAuthPartyAndGeos.entrySet()) { |
| String taxAuthPartyId = taxAuthPartyGeos.getKey(); |
| for (String taxAuthGeoId : taxAuthPartyGeos.getValue()) { |
| taxTotal = taxTotal.add(InvoiceWorker.getInvoiceTaxTotalForTaxAuthPartyAndGeo(invoice, taxAuthPartyId, taxAuthGeoId)); |
| } |
| } |
| taxTotal = taxTotal.add(InvoiceWorker.getInvoiceUnattributedTaxTotal(invoice)); |
| return taxTotal; |
| } |
| |
| public static BigDecimal getInvoiceNoTaxTotal(GenericValue invoice) { |
| return getInvoiceTotal(invoice, Boolean.TRUE).subtract(getInvoiceTaxTotal(invoice)); |
| } |
| |
| /** |
| * Method to return the total amount of an invoice |
| * @param invoice GenericValue object of the Invoice |
| * @return the invoice total as BigDecimal |
| */ |
| public static BigDecimal getInvoiceTotal(GenericValue invoice) { |
| return getInvoiceTotal(invoice, Boolean.TRUE); |
| } |
| |
| /** |
| * |
| * Return the total amount of the invoice (including tax) using the the invoice GenericValue as input. |
| * with the ability to specify if the actual currency is required. |
| * @param invoice GenericValue object of the Invoice |
| * @param actualCurrency true: provide the actual currency of the invoice (could be different from the system currency) |
| * false: if required convert the actual currency into the system currency. |
| * @return Return the total amount of the invoice |
| */ |
| public static BigDecimal getInvoiceTotal(GenericValue invoice, Boolean actualCurrency) { |
| BigDecimal invoiceTotal = ZERO; |
| BigDecimal invoiceTaxTotal = ZERO; |
| invoiceTaxTotal = InvoiceWorker.getInvoiceTaxTotal(invoice); |
| |
| List<GenericValue> invoiceItems = null; |
| try { |
| invoiceItems = invoice.getRelated("InvoiceItem", null, null, false); |
| invoiceItems = EntityUtil.filterByAnd( |
| invoiceItems, UtilMisc.toList( |
| EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.NOT_IN, getTaxableInvoiceItemTypeIds(invoice.getDelegator())) |
| )); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting InvoiceItem list", module); |
| } |
| if (invoiceItems != null) { |
| for (GenericValue invoiceItem : invoiceItems) { |
| invoiceTotal = invoiceTotal.add(getInvoiceItemTotal(invoiceItem)).setScale(decimals,rounding); |
| } |
| } |
| invoiceTotal = invoiceTotal.add(invoiceTaxTotal).setScale(decimals, rounding); |
| if (UtilValidate.isNotEmpty(invoiceTotal) && !actualCurrency) { |
| invoiceTotal = invoiceTotal.multiply(getInvoiceCurrencyConversionRate(invoice)).setScale(decimals,rounding); |
| } |
| return invoiceTotal; |
| } |
| |
| /** |
| * Method to obtain the bill to party for an invoice. Note that invoice.partyId is the bill to party. |
| * @param invoice GenericValue object of the Invoice |
| * @return GenericValue object of the Party |
| */ |
| public static GenericValue getBillToParty(GenericValue invoice) { |
| try { |
| GenericValue billToParty = invoice.getRelatedOne("Party", false); |
| if (billToParty != null) { |
| return billToParty; |
| } |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting Party from Invoice", module); |
| } |
| |
| // remaining code is the old method, which we leave here for compatibility purposes |
| List<GenericValue> billToRoles = null; |
| try { |
| billToRoles = invoice.getRelated("InvoiceRole", UtilMisc.toMap("roleTypeId", "BILL_TO_CUSTOMER"), UtilMisc.toList("-datetimePerformed"), false); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting InvoiceRole list", module); |
| } |
| |
| if (billToRoles != null) { |
| GenericValue role = EntityUtil.getFirst(billToRoles); |
| GenericValue party = null; |
| try { |
| party = role.getRelatedOne("Party", false); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting Party from InvoiceRole", module); |
| } |
| if (party != null) |
| return party; |
| } |
| return null; |
| } |
| |
| /** Convenience method to obtain the bill from party for an invoice. Note that invoice.partyIdFrom is the bill from party. */ |
| public static GenericValue getBillFromParty(GenericValue invoice) { |
| try { |
| return invoice.getRelatedOne("FromParty", false); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting FromParty from Invoice", module); |
| } |
| return null; |
| } |
| |
| /** |
| * Method to obtain the send from party for an invoice |
| * @param invoice GenericValue object of the Invoice |
| * @return GenericValue object of the Party |
| */ |
| public static GenericValue getSendFromParty(GenericValue invoice) { |
| GenericValue billFromParty = getBillFromParty(invoice); |
| if (billFromParty != null) { |
| return billFromParty; |
| } |
| |
| // remaining code is the old method, which we leave here for compatibility purposes |
| List<GenericValue> sendFromRoles = null; |
| try { |
| sendFromRoles = invoice.getRelated("InvoiceRole", UtilMisc.toMap("roleTypeId", "BILL_FROM_VENDOR"), UtilMisc.toList("-datetimePerformed"), false); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting InvoiceRole list", module); |
| } |
| |
| if (sendFromRoles != null) { |
| GenericValue role = EntityUtil.getFirst(sendFromRoles); |
| GenericValue party = null; |
| try { |
| party = role.getRelatedOne("Party", false); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting Party from InvoiceRole", module); |
| } |
| if (party != null) |
| return party; |
| } |
| return null; |
| } |
| |
| /** |
| * Method to obtain the billing address for an invoice |
| * @param invoice GenericValue object of the Invoice |
| * @return GenericValue object of the PostalAddress |
| */ |
| public static GenericValue getBillToAddress(GenericValue invoice) { |
| return getInvoiceAddressByType(invoice, "BILLING_LOCATION"); |
| } |
| |
| /** |
| * Method to obtain the sending address for an invoice |
| * @param invoice GenericValue object of the Invoice |
| * @return GenericValue object of the PostalAddress |
| */ |
| public static GenericValue getSendFromAddress(GenericValue invoice) { |
| return getInvoiceAddressByType(invoice, "PAYMENT_LOCATION"); |
| } |
| |
| public static GenericValue getInvoiceAddressByType(GenericValue invoice, String contactMechPurposeTypeId) { |
| return getInvoiceAddressByType(invoice, contactMechPurposeTypeId, true); |
| } |
| |
| public static GenericValue getInvoiceAddressByType(GenericValue invoice, String contactMechPurposeTypeId, boolean fetchPartyAddress) { |
| Delegator delegator = invoice.getDelegator(); |
| List<GenericValue> locations = null; |
| // first try InvoiceContactMech to see if we can find the address needed |
| try { |
| locations = invoice.getRelated("InvoiceContactMech", UtilMisc.toMap("contactMechPurposeTypeId", contactMechPurposeTypeId), null, false); |
| } catch (GenericEntityException e) { |
| Debug.logError("Touble getting InvoiceContactMech entity list", module); |
| } |
| |
| if (UtilValidate.isEmpty(locations) && fetchPartyAddress) { |
| // if no locations found get it from the PartyAndContactMech using the from and to party on the invoice |
| String destinationPartyId = null; |
| Timestamp now = UtilDateTime.nowTimestamp(); |
| if (invoice.getString("invoiceTypeId").equals("SALES_INVOICE")) |
| destinationPartyId = invoice.getString("partyId"); |
| if (invoice.getString("invoiceTypeId").equals("PURCHASE_INVOICE")) |
| destinationPartyId = invoice.getString("partyId"); |
| try { |
| locations = EntityQuery.use(delegator).from("PartyContactWithPurpose") |
| .where("partyId", destinationPartyId, "contactMechPurposeTypeId", contactMechPurposeTypeId).queryList(); |
| locations = EntityUtil.filterByDate(locations, now, "contactFromDate", "contactThruDate", true); |
| locations = EntityUtil.filterByDate(locations, now, "purposeFromDate", "purposeThruDate", true); |
| } catch (GenericEntityException e) { |
| Debug.logError("Trouble getting contact party purpose list", module); |
| } |
| //if still not found get it from the general location |
| if (UtilValidate.isEmpty(locations)) { |
| try { |
| locations = EntityQuery.use(delegator).from("PartyContactWithPurpose") |
| .where("partyId", destinationPartyId, "contactMechPurposeTypeId", "GENERAL_LOCATION").queryList(); |
| locations = EntityUtil.filterByDate(locations, now, "contactFromDate", "contactThruDate", true); |
| locations = EntityUtil.filterByDate(locations, now, "purposeFromDate", "purposeThruDate", true); |
| } catch (GenericEntityException e) { |
| Debug.logError("Trouble getting contact party purpose list", module); |
| } |
| } |
| } |
| |
| // now return the first PostalAddress from the locations |
| GenericValue postalAddress = null; |
| GenericValue contactMech = null; |
| if (UtilValidate.isNotEmpty(locations)) { |
| try { |
| contactMech = locations.get(0).getRelatedOne("ContactMech", false); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting Contact for contactMechId: " + locations.get(0).getString("contactMechId"), module); |
| } |
| |
| if (contactMech != null && contactMech.getString("contactMechTypeId").equals("POSTAL_ADDRESS")) { |
| try { |
| postalAddress = contactMech.getRelatedOne("PostalAddress", false); |
| return postalAddress; |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting PostalAddress for contactMechId: " + contactMech.getString("contactMechId"), module); |
| } |
| } |
| } |
| return contactMech; |
| } |
| |
| /** |
| * Method to return the total amount of an invoice which is not yet applied to a payment |
| * @param delegator the delegator |
| * @param invoiceId the invoice id |
| * @param actualCurrency the currency |
| * @return the invoice total as BigDecimal |
| */ |
| public static BigDecimal getInvoiceNotApplied(Delegator delegator, String invoiceId, Boolean actualCurrency) { |
| return InvoiceWorker.getInvoiceTotal(delegator, invoiceId, actualCurrency).subtract(getInvoiceApplied(delegator, invoiceId, UtilDateTime.nowTimestamp(), actualCurrency)); |
| } |
| public static BigDecimal getInvoiceNotApplied(Delegator delegator, String invoiceId) { |
| return InvoiceWorker.getInvoiceTotal(delegator, invoiceId).subtract(getInvoiceApplied(delegator, invoiceId)); |
| } |
| public static BigDecimal getInvoiceNotApplied(GenericValue invoice) { |
| return InvoiceWorker.getInvoiceTotal(invoice, Boolean.TRUE).subtract(getInvoiceApplied(invoice)); |
| } |
| public static BigDecimal getInvoiceNotApplied(GenericValue invoice, Boolean actualCurrency) { |
| return InvoiceWorker.getInvoiceTotal(invoice, actualCurrency).subtract(getInvoiceApplied(invoice, actualCurrency)); |
| } |
| /** |
| * Returns amount not applied (i.e., still outstanding) of an invoice at an asOfDate, based on Payment.effectiveDate <= asOfDateTime |
| * |
| * @param invoice GenericValue object of the invoice |
| * @param asOfDateTime the date to use |
| * @return Returns amount not applied of the invoice |
| */ |
| public static BigDecimal getInvoiceNotApplied(GenericValue invoice, Timestamp asOfDateTime) { |
| return InvoiceWorker.getInvoiceTotal(invoice, Boolean.TRUE).subtract(getInvoiceApplied(invoice, asOfDateTime)); |
| } |
| |
| |
| /** |
| * Method to return the total amount of an invoice which is applied to a payment |
| * @param delegator the delegator |
| * @param invoiceId the invoice id |
| * @return the invoice total as BigDecimal |
| */ |
| public static BigDecimal getInvoiceApplied(Delegator delegator, String invoiceId) { |
| return getInvoiceApplied(delegator, invoiceId, UtilDateTime.nowTimestamp(), Boolean.TRUE); |
| } |
| |
| /** |
| * Returns amount applied to invoice before an asOfDateTime, based on Payment.effectiveDate <= asOfDateTime |
| * |
| * @param delegator the delegator |
| * @param invoiceId the invoice id |
| * @param asOfDateTime - a Timestamp |
| * @return returns amount applied to invoice before an asOfDateTime |
| */ |
| public static BigDecimal getInvoiceApplied(Delegator delegator, String invoiceId, Timestamp asOfDateTime, Boolean actualCurrency) { |
| if (delegator == null) { |
| throw new IllegalArgumentException("Null delegator is not allowed in this method"); |
| } |
| |
| BigDecimal invoiceApplied = ZERO; |
| List<GenericValue> paymentApplications = null; |
| |
| // lookup payment applications which took place before the asOfDateTime for this invoice |
| EntityConditionList<EntityExpr> dateCondition = EntityCondition.makeCondition(UtilMisc.toList( |
| EntityCondition.makeCondition("effectiveDate", EntityOperator.EQUALS, null), |
| EntityCondition.makeCondition("effectiveDate", EntityOperator.LESS_THAN_EQUAL_TO, asOfDateTime)), EntityOperator.OR); |
| EntityConditionList<EntityCondition> conditions = EntityCondition.makeCondition(UtilMisc.toList( |
| dateCondition, |
| EntityCondition.makeCondition("invoiceId", EntityOperator.EQUALS, invoiceId)), |
| EntityOperator.AND); |
| |
| try { |
| paymentApplications = EntityQuery.use(delegator).from("PaymentAndApplication") |
| .where(conditions).orderBy("effectiveDate").queryList(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting paymentApplicationlist", module); |
| } |
| if (paymentApplications != null) { |
| for (GenericValue paymentApplication : paymentApplications) { |
| invoiceApplied = invoiceApplied.add(paymentApplication.getBigDecimal("amountApplied")).setScale(decimals,rounding); |
| } |
| } |
| if (UtilValidate.isNotEmpty(invoiceApplied) && !actualCurrency) { |
| invoiceApplied = invoiceApplied.multiply(getInvoiceCurrencyConversionRate(delegator, invoiceId)).setScale(decimals,rounding); |
| } |
| return invoiceApplied; |
| } |
| /** |
| * Method to return the total amount of an invoice which is applied to a payment |
| * @param invoice GenericValue object of the invoice |
| * @return the applied total as BigDecimal |
| */ |
| public static BigDecimal getInvoiceApplied(GenericValue invoice) { |
| return getInvoiceApplied(invoice, UtilDateTime.nowTimestamp()); |
| } |
| |
| /** |
| * Return the amount applied to the invoice |
| * @param invoice GenericValue object of the invoice |
| * @param actualCurrency the currency of the invoice |
| * @return returns the amount applied to the invoice |
| */ |
| public static BigDecimal getInvoiceApplied(GenericValue invoice, Boolean actualCurrency) { |
| return getInvoiceApplied(invoice.getDelegator(), invoice.getString("invoiceId"), UtilDateTime.nowTimestamp(), actualCurrency); |
| } |
| public static BigDecimal getInvoiceApplied(GenericValue invoice, Timestamp asOfDateTime) { |
| return getInvoiceApplied(invoice.getDelegator(), invoice.getString("invoiceId"), asOfDateTime, Boolean.TRUE); |
| } |
| /** |
| * Method to return the amount of an invoiceItem which is applied to a payment |
| * @param delegator the delegator |
| * @param invoiceId the invoice id |
| * @param invoiceItemSeqId the invoice item id |
| * @return the invoice total as BigDecimal |
| */ |
| public static BigDecimal getInvoiceItemApplied(Delegator delegator, String invoiceId, String invoiceItemSeqId) { |
| if (delegator == null) { |
| throw new IllegalArgumentException("Null delegator is not allowed in this method"); |
| } |
| |
| GenericValue invoiceItem = null; |
| try { |
| invoiceItem = EntityQuery.use(delegator).from("Invoice").where("invoiceId", invoiceId,"invoiceItemSeqId", invoiceItemSeqId).queryOne(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Problem getting InvoiceItem", module); |
| } |
| |
| if (invoiceItem == null) { |
| throw new IllegalArgumentException("The invoiceId/itemSeqId passed does not match an existing invoiceItem"); |
| } |
| |
| return getInvoiceItemApplied(invoiceItem); |
| } |
| |
| /** |
| * Method to return the total amount of an invoiceItem which is applied to a payment |
| * @param invoiceItem GenericValue object of the invoice item |
| * @return the applied total as BigDecimal |
| */ |
| public static BigDecimal getInvoiceItemApplied(GenericValue invoiceItem) { |
| BigDecimal invoiceItemApplied = ZERO; |
| List<GenericValue> paymentApplications = null; |
| try { |
| paymentApplications = invoiceItem.getRelated("PaymentApplication", null, null, false); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting paymentApplicationlist", module); |
| } |
| if (paymentApplications != null) { |
| for (GenericValue paymentApplication : paymentApplications) { |
| invoiceItemApplied = invoiceItemApplied.add(paymentApplication.getBigDecimal("amountApplied")).setScale(decimals,rounding); |
| } |
| } |
| return invoiceItemApplied; |
| } |
| public static BigDecimal getInvoiceCurrencyConversionRate(GenericValue invoice) { |
| BigDecimal conversionRate = null; |
| Delegator delegator = invoice.getDelegator(); |
| String otherCurrencyUomId = null; |
| // find the organization party currencyUomId which different from the invoice currency |
| try { |
| GenericValue party = EntityQuery.use(delegator).from("PartyAcctgPreference").where("partyId", invoice.get("partyIdFrom")).queryOne(); |
| if (UtilValidate.isEmpty(party) || party.getString("baseCurrencyUomId").equals(invoice.getString("currencyUomId"))) { |
| party = EntityQuery.use(delegator).from("PartyAcctgPreference").where("partyId", invoice.get("partyId")).queryOne(); |
| } |
| if (UtilValidate.isNotEmpty(party) && party.getString("baseCurrencyUomId") != null) { |
| otherCurrencyUomId = party.getString("baseCurrencyUomId"); |
| } else { |
| otherCurrencyUomId = EntityUtilProperties.getPropertyValue("general", "currency.uom.id.default", delegator); |
| } |
| if (otherCurrencyUomId == null) { |
| otherCurrencyUomId = "USD"; // final default |
| } |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting database records....", module); |
| } |
| if (invoice.getString("currencyUomId").equals(otherCurrencyUomId)) { |
| return BigDecimal.ONE; // organization party has the same currency so conversion not required. |
| } |
| |
| try { |
| // check if the invoice is posted and get the conversion from there |
| List<GenericValue> acctgTransEntries = invoice.getRelated("AcctgTrans", null, null, false); |
| if (UtilValidate.isNotEmpty(acctgTransEntries)) { |
| GenericValue acctgTransEntry = (acctgTransEntries.get(0)).getRelated("AcctgTransEntry", null, null, false).get(0); |
| BigDecimal origAmount = acctgTransEntry.getBigDecimal("origAmount"); |
| if (origAmount.compareTo(ZERO) == 1) { |
| conversionRate = acctgTransEntry.getBigDecimal("amount").divide(acctgTransEntry.getBigDecimal("origAmount"), new MathContext(100)).setScale(decimals,rounding); |
| } |
| } |
| // check if a payment is applied and use the currency conversion from there |
| if (UtilValidate.isEmpty(conversionRate)) { |
| List<GenericValue> paymentAppls = invoice.getRelated("PaymentApplication", null, null, false); |
| for (GenericValue paymentAppl : paymentAppls) { |
| GenericValue payment = paymentAppl.getRelatedOne("Payment", false); |
| if (UtilValidate.isNotEmpty(payment.getBigDecimal("actualCurrencyAmount"))) { |
| if (UtilValidate.isEmpty(conversionRate)) { |
| conversionRate = payment.getBigDecimal("amount").divide(payment.getBigDecimal("actualCurrencyAmount"),new MathContext(100)).setScale(decimals,rounding); |
| } else { |
| conversionRate = conversionRate.add(payment.getBigDecimal("amount").divide(payment.getBigDecimal("actualCurrencyAmount"),new MathContext(100))).divide(new BigDecimal("2"),new MathContext(100)).setScale(decimals,rounding); |
| } |
| } |
| } |
| } |
| // use the dated conversion entity |
| if (UtilValidate.isEmpty(conversionRate)) { |
| GenericValue rate = EntityQuery.use(delegator).from("UomConversionDated").where("uomIdTo", invoice.get("currencyUomId"), "uomId", otherCurrencyUomId).filterByDate(invoice.getTimestamp("invoiceDate")).queryFirst(); |
| if (rate != null) { |
| conversionRate = BigDecimal.ONE.divide(rate.getBigDecimal("conversionFactor"), new MathContext(100)).setScale(decimals,rounding); |
| } else { |
| Debug.logError("Could not find conversionrate for invoice: " + invoice.getString("invoiceId"), module); |
| return new BigDecimal("1"); |
| } |
| } |
| |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting database records....", module); |
| } |
| return(conversionRate); |
| } |
| |
| public static BigDecimal getInvoiceCurrencyConversionRate(Delegator delegator, String invoiceId) { |
| if (delegator == null) { |
| throw new IllegalArgumentException("Null delegator is not allowed in this method"); |
| } |
| |
| GenericValue invoice = null; |
| try { |
| invoice = EntityQuery.use(delegator).from("Invoice").where("invoiceId", invoiceId).queryOne(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Problem getting Invoice", module); |
| } |
| |
| if (invoice == null) { |
| throw new IllegalArgumentException("The invoiceId passed does not match an existing invoice"); |
| } |
| |
| return getInvoiceCurrencyConversionRate(invoice); |
| } |
| |
| /** |
| * Return a list of taxes separated by Geo and party and return the tax grand total |
| * @param invoice Generic Value |
| * @return Map: taxByTaxAuthGeoAndPartyList(List) and taxGrandTotal(BigDecimal) |
| */ |
| @Deprecated |
| public static Map<String, Object> getInvoiceTaxByTaxAuthGeoAndParty(GenericValue invoice) { |
| BigDecimal taxGrandTotal = ZERO; |
| List<Map<String, Object>> taxByTaxAuthGeoAndPartyList = new LinkedList<Map<String,Object>>(); |
| List<GenericValue> invoiceItems = null; |
| if (invoice != null) { |
| try { |
| invoiceItems = invoice.getRelated("InvoiceItem", null, null, false); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting InvoiceItem list", module); |
| } |
| if ("SALES_INVOICE".equals(invoice.getString("invoiceTypeId"))) { |
| invoiceItems = EntityUtil.filterByOr( |
| invoiceItems, UtilMisc.toList( |
| EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.EQUALS, "INV_SALES_TAX"), |
| EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.EQUALS, "ITM_SALES_TAX"))); |
| } else if (("PURCHASE_INVOICE".equals(invoice.getString("invoiceTypeId")))) { |
| invoiceItems = EntityUtil.filterByOr( |
| invoiceItems, UtilMisc.toList( |
| EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.EQUALS, "PINV_SALES_TAX"), |
| EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.EQUALS, "PITM_SALES_TAX"))); |
| } else { |
| invoiceItems = null; |
| } |
| if (UtilValidate.isNotEmpty(invoiceItems)) { |
| invoiceItems = EntityUtil.orderBy(invoiceItems, UtilMisc.toList("taxAuthGeoId","taxAuthPartyId")); |
| // get the list of all distinct taxAuthGeoId and taxAuthPartyId. It is for getting the number of taxAuthGeoId and taxAuthPartyId in invoiceItems. |
| List<String> distinctTaxAuthGeoIdList = EntityUtil.getFieldListFromEntityList(invoiceItems, "taxAuthGeoId", true); |
| List<String> distinctTaxAuthPartyIdList = EntityUtil.getFieldListFromEntityList(invoiceItems, "taxAuthPartyId", true); |
| for (String taxAuthGeoId : distinctTaxAuthGeoIdList ) { |
| for (String taxAuthPartyId : distinctTaxAuthPartyIdList) { |
| //get all records for invoices filtered by taxAuthGeoId and taxAurhPartyId |
| List<GenericValue> invoiceItemsByTaxAuthGeoAndPartyIds = EntityUtil.filterByAnd(invoiceItems, UtilMisc.toMap("taxAuthGeoId", taxAuthGeoId, "taxAuthPartyId", taxAuthPartyId)); |
| if (UtilValidate.isNotEmpty(invoiceItemsByTaxAuthGeoAndPartyIds)) { |
| BigDecimal totalAmount = ZERO; |
| //Now for each invoiceItem record get and add amount. |
| for (GenericValue invoiceItem : invoiceItemsByTaxAuthGeoAndPartyIds) { |
| BigDecimal amount = invoiceItem.getBigDecimal("amount"); |
| if (amount == null) { |
| amount = ZERO; |
| } |
| totalAmount = totalAmount.add(amount).setScale(taxDecimals, taxRounding); |
| } |
| totalAmount = totalAmount.setScale(UtilNumber.getBigDecimalScale("salestax.calc.decimals"), UtilNumber.getBigDecimalRoundingMode("salestax.rounding")); |
| taxByTaxAuthGeoAndPartyList.add(UtilMisc.<String, Object>toMap("taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", taxAuthGeoId, "totalAmount", totalAmount)); |
| taxGrandTotal = taxGrandTotal.add(totalAmount); |
| } |
| } |
| } |
| } |
| } |
| Map<String, Object> result = new HashMap<String, Object>(); |
| result.put("taxByTaxAuthGeoAndPartyList", taxByTaxAuthGeoAndPartyList); |
| result.put("taxGrandTotal", taxGrandTotal); |
| return result; |
| } |
| |
| /** |
| * Returns a List of the TaxAuthority Party and Geos for the given Invoice. |
| * @param invoice GenericValue object representing the Invoice |
| * @return A Map containing the each taxAuthPartyId as a key and a Set of taxAuthGeoIds for that taxAuthPartyId as the values. Note this method |
| * will not account for tax lines that do not contain a taxAuthPartyId |
| */ |
| public static Map<String, Set<String>> getInvoiceTaxAuthPartyAndGeos (GenericValue invoice) { |
| Map<String, Set<String>> result = new HashMap<String, Set<String>>(); |
| |
| if (invoice == null) |
| throw new IllegalArgumentException("Invoice cannot be null."); |
| List<GenericValue> invoiceTaxItems = null; |
| try { |
| Delegator delegator = invoice.getDelegator(); |
| invoiceTaxItems = EntityQuery.use(delegator).from("InvoiceItem") |
| .where(EntityCondition.makeCondition("invoiceId", invoice.getString("invoiceId")), |
| EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.IN, getTaxableInvoiceItemTypeIds(delegator)) |
| ).queryList(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting InvoiceItem list", module); |
| return null; |
| } |
| if (invoiceTaxItems != null) { |
| for (GenericValue invoiceItem : invoiceTaxItems) { |
| String taxAuthPartyId = invoiceItem.getString("taxAuthPartyId"); |
| String taxAuthGeoId = invoiceItem.getString("taxAuthGeoId"); |
| if (UtilValidate.isNotEmpty(taxAuthPartyId)) { |
| if (!result.containsKey(taxAuthPartyId)) { |
| Set<String> taxAuthGeos = new HashSet<String>(); |
| taxAuthGeos.add(taxAuthGeoId); |
| result.put(taxAuthPartyId, taxAuthGeos); |
| } else { |
| Set<String> taxAuthGeos = result.get(taxAuthPartyId); |
| taxAuthGeos.add(taxAuthGeoId); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * @param invoice GenericValue object representing the invoice |
| * @param taxAuthPartyId |
| * @param taxAuthGeoId |
| * @return The invoice tax total for a given tax authority and geo location |
| */ |
| public static BigDecimal getInvoiceTaxTotalForTaxAuthPartyAndGeo(GenericValue invoice, String taxAuthPartyId, String taxAuthGeoId) { |
| List<GenericValue> invoiceTaxItems = null; |
| try { |
| Delegator delegator = invoice.getDelegator(); |
| invoiceTaxItems = EntityQuery.use(delegator).from("InvoiceItem") |
| .where(EntityCondition.makeCondition("invoiceId", invoice.getString("invoiceId")), |
| EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.IN, getTaxableInvoiceItemTypeIds(delegator)), |
| EntityCondition.makeCondition("taxAuthPartyId", taxAuthPartyId), |
| EntityCondition.makeCondition("taxAuthGeoId", taxAuthGeoId) |
| ).queryList(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting InvoiceItem list", module); |
| return null; |
| } |
| return getTaxTotalForInvoiceItems(invoiceTaxItems); |
| } |
| |
| /** Returns the invoice tax total for unattributed tax items, that is items which have no taxAuthPartyId value |
| * @param invoice GenericValue object representing the invoice |
| * @return Returns the invoice tax total for unattributed tax items |
| */ |
| public static BigDecimal getInvoiceUnattributedTaxTotal(GenericValue invoice) { |
| List<GenericValue> invoiceTaxItems = null; |
| try { |
| Delegator delegator = invoice.getDelegator(); |
| invoiceTaxItems = EntityQuery.use(delegator).from("InvoiceItem") |
| .where(EntityCondition.makeCondition("invoiceId", invoice.get("invoiceId")), |
| EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.IN, getTaxableInvoiceItemTypeIds(delegator)), |
| EntityCondition.makeCondition("taxAuthPartyId", null) |
| ).queryList(); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Trouble getting InvoiceItem list", module); |
| return null; |
| } |
| return getTaxTotalForInvoiceItems(invoiceTaxItems); |
| } |
| |
| /** Returns the tax total for a given list of tax typed InvoiceItem records |
| * @param taxInvoiceItems |
| * @return |
| */ |
| private static BigDecimal getTaxTotalForInvoiceItems(List<GenericValue> taxInvoiceItems) { |
| if (taxInvoiceItems == null) { |
| return ZERO; |
| } |
| BigDecimal taxTotal = ZERO; |
| for (GenericValue taxInvoiceItem : taxInvoiceItems) { |
| BigDecimal amount = taxInvoiceItem.getBigDecimal("amount"); |
| if (amount == null) { |
| amount = ZERO; |
| } |
| BigDecimal quantity = taxInvoiceItem.getBigDecimal("quantity"); |
| if (quantity == null) { |
| quantity = BigDecimal.ONE; |
| } |
| amount = amount.multiply(quantity); |
| amount = amount.setScale(taxDecimals, taxRounding); |
| taxTotal = taxTotal.add(amount); |
| } |
| return taxTotal.setScale(decimals, rounding); |
| } |
| } |