Author: lektran
Date: Wed Jan 20 05:53:22 2010
New Revision: 901070
URL: http://svn.apache.org/viewvc?rev=901070&view=rev
Log:
Rewrote the invoice tax calculation methods, now takes into account tax invoice
items that do not have a taxAuthPartyId set and generally makes things a little
more comprehendible. Resolves OFBIZ-3318 reported by Willem Janssen.
Modified:
ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/ledger/GeneralLedgerServices.xml
ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceWorker.java
Modified:
ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/ledger/GeneralLedgerServices.xml
URL:
http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/ledger/GeneralLedgerServices.xml?rev=901070&r1=901069&r2=901070&view=diff
==============================================================================
---
ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/ledger/GeneralLedgerServices.xml
(original)
+++
ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/ledger/GeneralLedgerServices.xml
Wed Jan 20 05:53:22 2010
@@ -1790,25 +1790,41 @@
<set field="acctgTransEntries[]" from-field="debitEntry"
type="Object"/>
</iterate>
<!-- debit entry for SALES_TAX-->
- <call-class-method method-name="getInvoiceTaxByTaxAuthGeoAndParty"
class-name="org.ofbiz.accounting.invoice.InvoiceWorker"
- ret-field="invoiceTaxByTaxAuthGeoAndPartyResult">
+ <call-class-method method-name="getInvoiceTaxAuthPartyAndGeos"
class-name="org.ofbiz.accounting.invoice.InvoiceWorker"
+ ret-field="taxAuthPartyAndGeos">
<field field="invoice" type="org.ofbiz.entity.GenericValue"/>
</call-class-method>
- <set field="taxByTaxAuthGeoAndPartyList"
from-field="invoiceTaxByTaxAuthGeoAndPartyResult.taxByTaxAuthGeoAndPartyList"/>
- <set field="invoiceTaxTotal"
from-field="invoiceTaxByTaxAuthGeoAndPartyResult.taxGrandTotal"/>
- <iterate list="taxByTaxAuthGeoAndPartyList"
entry="taxByTaxAuthGeoAndParty">
- <clear-field field="creditEntry"/>
- <make-value entity-name="AcctgTransEntry"
value-field="creditEntry"/>
- <set field="creditEntry.debitCreditFlag" value="D"/>
- <set field="creditEntry.organizationPartyId"
from-field="invoice.partyIdFrom"/>
- <set field="creditEntry.origAmount"
from-field="taxByTaxAuthGeoAndParty.totalAmount"/>
- <set field="creditEntry.origCurrencyUomId"
from-field="invoice.currencyUomId"/>
- <if-not-empty field="taxByTaxAuthGeoAndParty.taxAuthPartyId">
- <set field="creditEntry.partyId"
from-field="taxByTaxAuthGeoAndParty.taxAuthPartyId"/>
- <set field="creditEntry.roleTypeId" value="TAX_AUTHORITY"/>
- </if-not-empty>
- <set field="acctgTransEntries[]" from-field="creditEntry"
type="Object"/>
- </iterate>
+ <iterate-map key="taxAuthPartyId" value="taxAuthGeoIds"
map="taxAuthPartyAndGeos">
+ <iterate entry="taxAuthGeoId" list="taxAuthGeoIds">
+ <clear-field field="debitEntry"/>
+ <make-value entity-name="AcctgTransEntry"
value-field="debitEntry"/>
+ <set field="debitEntry.debitCreditFlag" value="D"/>
+ <set field="debitEntry.organizationPartyId"
from-field="invoice.partyIdFrom"/>
+ <call-class-method
method-name="getInvoiceTaxTotalForTaxAuthPartyAndGeo"
class-name="org.ofbiz.accounting.invoice.InvoiceWorker"
+ ret-field="taxAmount">
+ <field field="invoice" type="GenericValue"/>
+ <field field="taxAuthPartyId" type="String"/>
+ <field field="taxAuthGeoId" type="String"/>
+ </call-class-method>
+ <set field="debitEntry.origAmount" from-field="taxAmount"/>
+ <set field="debitEntry.origCurrencyUomId"
from-field="invoice.currencyUomId"/>
+ <set field="debitEntry.partyId"
from-field="taxAuthPartyId"/>
+ <set field="debitEntry.roleTypeId" value="TAX_AUTHORITY"/>
+ <set field="acctgTransEntries[]" from-field="debitEntry"
type="Object"/>
+ </iterate>
+ </iterate-map>
+ <!-- Another entry for tax not attributed to a taxAuthPartyId -->
+ <clear-field field="debitEntry"/>
+ <make-value entity-name="AcctgTransEntry"
value-field="debitEntry"/>
+ <set field="debitEntry.debitCreditFlag" value="D"/>
+ <set field="debitEntry.organizationPartyId"
from-field="invoice.partyIdFrom"/>
+ <call-class-method method-name="getInvoiceUnattributedTaxTotal"
class-name="org.ofbiz.accounting.invoice.InvoiceWorker"
+ ret-field="taxAmount">
+ <field field="invoice" type="GenericValue"/>
+ </call-class-method>
+ <set field="debitEntry.origAmount" from-field="taxAmount"/>
+ <set field="debitEntry.origCurrencyUomId"
from-field="invoice.currencyUomId"/>
+ <set field="acctgTransEntries[]" from-field="debitEntry"
type="Object"/>
<!-- Credit -->
<make-value entity-name="AcctgTransEntry"
value-field="creditEntry"/>
@@ -1889,25 +1905,41 @@
</iterate>
<!-- credit entry for SALES_TAX-->
- <call-class-method method-name="getInvoiceTaxByTaxAuthGeoAndParty"
class-name="org.ofbiz.accounting.invoice.InvoiceWorker"
- ret-field="invoiceTaxByTaxAuthGeoAndPartyResult">
+ <call-class-method method-name="getInvoiceTaxAuthPartyAndGeos"
class-name="org.ofbiz.accounting.invoice.InvoiceWorker"
+ ret-field="taxAuthPartyAndGeos">
<field field="invoice" type="org.ofbiz.entity.GenericValue"/>
</call-class-method>
- <set field="taxByTaxAuthGeoAndPartyList"
from-field="invoiceTaxByTaxAuthGeoAndPartyResult.taxByTaxAuthGeoAndPartyList"/>
- <set field="invoiceTaxTotal"
from-field="invoiceTaxByTaxAuthGeoAndPartyResult.taxGrandTotal"/>
- <iterate list="taxByTaxAuthGeoAndPartyList"
entry="taxByTaxAuthGeoAndParty">
- <clear-field field="creditEntry"/>
- <make-value entity-name="AcctgTransEntry"
value-field="creditEntry"/>
- <set field="creditEntry.debitCreditFlag" value="C"/>
- <set field="creditEntry.organizationPartyId"
from-field="invoice.partyIdFrom"/>
- <set field="creditEntry.origAmount"
from-field="taxByTaxAuthGeoAndParty.totalAmount"/>
- <set field="creditEntry.origCurrencyUomId"
from-field="invoice.currencyUomId"/>
- <if-not-empty field="taxByTaxAuthGeoAndParty.taxAuthPartyId">
- <set field="creditEntry.partyId"
from-field="taxByTaxAuthGeoAndParty.taxAuthPartyId"/>
+ <iterate-map key="taxAuthPartyId" value="taxAuthGeoIds"
map="taxAuthPartyAndGeos">
+ <iterate entry="taxAuthGeoId" list="taxAuthGeoIds">
+ <clear-field field="creditEntry"/>
+ <make-value entity-name="AcctgTransEntry"
value-field="creditEntry"/>
+ <set field="creditEntry.debitCreditFlag" value="C"/>
+ <set field="creditEntry.organizationPartyId"
from-field="invoice.partyIdFrom"/>
+ <call-class-method
method-name="getInvoiceTaxTotalForTaxAuthPartyAndGeo"
class-name="org.ofbiz.accounting.invoice.InvoiceWorker"
+ ret-field="taxAmount">
+ <field field="invoice" type="GenericValue"/>
+ <field field="taxAuthPartyId" type="String"/>
+ <field field="taxAuthGeoId" type="String"/>
+ </call-class-method>
+ <set field="creditEntry.origAmount"
from-field="taxAmount"/>
+ <set field="creditEntry.origCurrencyUomId"
from-field="invoice.currencyUomId"/>
+ <set field="creditEntry.partyId"
from-field="taxAuthPartyId"/>
<set field="creditEntry.roleTypeId" value="TAX_AUTHORITY"/>
- </if-not-empty>
- <set field="acctgTransEntries[]" from-field="creditEntry"
type="Object"/>
- </iterate>
+ <set field="acctgTransEntries[]" from-field="creditEntry"
type="Object"/>
+ </iterate>
+ </iterate-map>
+ <!-- Another entry for tax not attributed to a taxAuthPartyId -->
+ <clear-field field="creditEntry"/>
+ <make-value entity-name="AcctgTransEntry"
value-field="creditEntry"/>
+ <set field="creditEntry.debitCreditFlag" value="C"/>
+ <set field="creditEntry.organizationPartyId"
from-field="invoice.partyIdFrom"/>
+ <call-class-method method-name="getInvoiceUnattributedTaxTotal"
class-name="org.ofbiz.accounting.invoice.InvoiceWorker"
+ ret-field="taxAmount">
+ <field field="invoice" type="GenericValue"/>
+ </call-class-method>
+ <set field="creditEntry.origAmount" from-field="taxAmount"/>
+ <set field="creditEntry.origCurrencyUomId"
from-field="invoice.currencyUomId"/>
+ <set field="acctgTransEntries[]" from-field="creditEntry"
type="Object"/>
<!-- Debit -->
<make-value entity-name="AcctgTransEntry"
value-field="debitEntry"/>
Modified:
ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceWorker.java
URL:
http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceWorker.java?rev=901070&r1=901069&r2=901070&view=diff
==============================================================================
---
ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceWorker.java
(original)
+++
ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceWorker.java
Wed Jan 20 05:53:22 2010
@@ -23,9 +23,11 @@
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javolution.util.FastList;
import javolution.util.FastMap;
+import javolution.util.FastSet;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.UtilDateTime;
@@ -102,7 +104,11 @@
if (quantity == null) {
quantity = BigDecimal.ONE;
}
- return
quantity.multiply(invoiceItem.getBigDecimal("amount")).setScale(decimals,
rounding);
+ 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. */
@@ -116,35 +122,16 @@
}
public static BigDecimal getInvoiceTaxTotal(GenericValue invoice) {
- BigDecimal invoiceTaxTotal = ZERO;
- BigDecimal ONE = BigDecimal.ONE;
-
- if (invoice == null)
- throw new IllegalArgumentException("The invoiceId passed does not
match an existing invoice");
- List<GenericValue> invoiceTaxItems = null;
- try {
- Delegator delegator = invoice.getDelegator();
- EntityConditionList<EntityExpr> condition =
EntityCondition.makeCondition(UtilMisc.toList(
- EntityCondition.makeCondition("invoiceId",
invoice.getString("invoiceId")),
- EntityCondition.makeCondition("invoiceItemTypeId",
EntityOperator.IN, getTaxableInvoiceItemTypeIds(delegator))),
- EntityOperator.AND);
- invoiceTaxItems = delegator.findList("InvoiceItem", condition,
null, null, null, false);
- } catch (GenericEntityException e) {
- Debug.logError(e, "Trouble getting InvoiceItem list", module);
- }
- if (invoiceTaxItems != null) {
- for (GenericValue invoiceItem : invoiceTaxItems) {
- BigDecimal amount = invoiceItem.getBigDecimal("amount");
- BigDecimal quantity = invoiceItem.getBigDecimal("quantity");
- if (amount == null)
- amount = ZERO;
- if (quantity == null)
- quantity = ONE;
- invoiceTaxTotal =
invoiceTaxTotal.add(amount.multiply(quantity)).setScale(decimals + 1, rounding);
+ 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));
}
}
- return invoiceTaxTotal.setScale(decimals, rounding);
-
+ taxTotal =
taxTotal.add(InvoiceWorker.getInvoiceUnattributedTaxTotal(invoice));
+ return taxTotal;
}
public static BigDecimal getInvoiceNoTaxTotal(GenericValue invoice) {
@@ -172,35 +159,21 @@
public static BigDecimal getInvoiceTotal(GenericValue invoice, Boolean
actualCurrency) {
BigDecimal invoiceTotal = ZERO;
BigDecimal invoiceTaxTotal = ZERO;
- Map<String, Object> invoiceTaxByTaxAuthGeoAndPartyResult =
getInvoiceTaxByTaxAuthGeoAndParty(invoice);
- invoiceTaxTotal = (BigDecimal)
invoiceTaxByTaxAuthGeoAndPartyResult.get("taxGrandTotal");
+ invoiceTaxTotal = InvoiceWorker.getInvoiceTaxTotal(invoice);
List<GenericValue> invoiceItems = null;
try {
invoiceItems = invoice.getRelated("InvoiceItem");
- if ("SALES_INVOICE".equals(invoice.getString("invoiceTypeId"))) {
- invoiceItems = EntityUtil.filterByAnd(
- invoiceItems, UtilMisc.toList(
-
EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.NOT_EQUAL,
"INV_SALES_TAX"),
-
EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.NOT_EQUAL,
"ITM_SALES_TAX")));
- } else if
(("PURCHASE_INVOICE".equals(invoice.getString("invoiceTypeId")))) {
- invoiceItems = EntityUtil.filterByAnd(
- invoiceItems, UtilMisc.toList(
-
EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.NOT_EQUAL,
"PINV_SALES_TAX"),
-
EntityCondition.makeCondition("invoiceItemTypeId", EntityOperator.NOT_EQUAL,
"PITM_SALES_TAX")));
- }
+ 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) {
- BigDecimal amount = invoiceItem.getBigDecimal("amount");
- BigDecimal quantity = invoiceItem.getBigDecimal("quantity");
- if (amount == null)
- amount = ZERO;
- if (quantity == null)
- quantity = BigDecimal.ONE;
- invoiceTotal = invoiceTotal.add(
amount.multiply(quantity)).setScale(decimals,rounding);
+ invoiceTotal =
invoiceTotal.add(getInvoiceItemTotal(invoiceItem)).setScale(decimals,rounding);
}
}
invoiceTotal = invoiceTotal.add(invoiceTaxTotal).setScale(decimals,
rounding);
@@ -596,6 +569,7 @@
* @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 =
FastList.newInstance();
@@ -651,4 +625,116 @@
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 = FastMap.newInstance();
+
+ if (invoice == null)
+ throw new IllegalArgumentException("Invoice cannot be null.");
+ List<GenericValue> invoiceTaxItems = null;
+ try {
+ Delegator delegator = invoice.getDelegator();
+ EntityConditionList<EntityExpr> condition =
EntityCondition.makeCondition(UtilMisc.toList(
+ EntityCondition.makeCondition("invoiceId",
invoice.getString("invoiceId")),
+ EntityCondition.makeCondition("invoiceItemTypeId",
EntityOperator.IN, getTaxableInvoiceItemTypeIds(delegator))),
+ EntityOperator.AND);
+ invoiceTaxItems = delegator.findList("InvoiceItem", condition,
null, null, null, false);
+ } 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 = FastSet.newInstance();
+ 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();
+ EntityConditionList<EntityExpr> condition =
EntityCondition.makeCondition(UtilMisc.toList(
+ EntityCondition.makeCondition("invoiceId",
invoice.getString("invoiceId")),
+ EntityCondition.makeCondition("invoiceItemTypeId",
EntityOperator.IN, getTaxableInvoiceItemTypeIds(delegator)),
+ EntityCondition.makeCondition("taxAuthPartyId",
taxAuthPartyId),
+ EntityCondition.makeCondition("taxAuthGeoId",
taxAuthGeoId)),
+ EntityOperator.AND);
+ invoiceTaxItems = delegator.findList("InvoiceItem", condition,
null, null, null, false);
+ } 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
+ */
+ public static BigDecimal getInvoiceUnattributedTaxTotal(GenericValue
invoice) {
+ List<GenericValue> invoiceTaxItems = null;
+ try {
+ Delegator delegator = invoice.getDelegator();
+ EntityConditionList<EntityExpr> condition =
EntityCondition.makeCondition(UtilMisc.toList(
+ EntityCondition.makeCondition("invoiceId",
invoice.getString("invoiceId")),
+ EntityCondition.makeCondition("invoiceItemTypeId",
EntityOperator.IN, getTaxableInvoiceItemTypeIds(delegator)),
+ EntityCondition.makeCondition("taxAuthPartyId", null)),
+ EntityOperator.AND);
+ invoiceTaxItems = delegator.findList("InvoiceItem", condition,
null, null, null, false);
+ } 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);
+ }
}