From 6f76307c6c0a6f8357d52c9feefd1e9722893fe5 Mon Sep 17 00:00:00 2001 From: mjudd Date: Tue, 16 Jun 2009 19:10:48 +0000 Subject: [PATCH] BF [ 1733602 ] - Price List including Tax Error http://sourceforge.net/tracker/?func=detail&aid=1733602&group_id=176962&atid=879332 This relates to order lines and invoice lines when the product or charge includes tax on the price list but the user changes the tax. In this case, we work out the tax component included in the price at the default rate for the tax category and then adjust it for the new tax rate applied. --- base/src/org/compiere/model/MInvoiceLine.java | 96 +++++++++++++++++-- base/src/org/compiere/model/MOrderLine.java | 74 ++++++++++++++ base/src/org/compiere/model/MTaxCategory.java | 31 +++++- 3 files changed, 190 insertions(+), 11 deletions(-) diff --git a/base/src/org/compiere/model/MInvoiceLine.java b/base/src/org/compiere/model/MInvoiceLine.java index 3ff8c32ad1..2b7b87724d 100644 --- a/base/src/org/compiere/model/MInvoiceLine.java +++ b/base/src/org/compiere/model/MInvoiceLine.java @@ -40,6 +40,10 @@ import org.compiere.util.Msg; * @author Teo Sarca, www.arhipac.ro *
  • BF [ 2804142 ] MInvoice.setRMALine should work only for CreditMemo invoices * https://sourceforge.net/tracker/?func=detail&aid=2804142&group_id=176962&atid=879332 + * @author Michael Judd, www.akunagroup.com + *
  • BF [ 1733602 ] Price List including Tax Error - when a user changes the orderline or + * invoice line for a product on a price list that includes tax, the net amount is + * incorrectly calculated. */ public class MInvoiceLine extends X_C_InvoiceLine { @@ -97,7 +101,10 @@ public class MInvoiceLine extends X_C_InvoiceLine /** Static Logger */ private static CLogger s_log = CLogger.getCLogger (MInvoiceLine.class); - + /** Tax */ + private MTax m_tax = null; + + /************************************************************************** * Invoice Line Constructor * @param ctx context @@ -157,7 +164,9 @@ public class MInvoiceLine extends X_C_InvoiceLine private boolean m_IsSOTrx = true; private boolean m_priceSet = false; private MProduct m_product = null; - + /** Charge */ + private MCharge m_charge = null; + /** Cached Name of the line */ private String m_name = null; /** Cached Precision */ @@ -433,7 +442,7 @@ public class MInvoiceLine extends X_C_InvoiceLine /** - * Calculare Tax Amt. + * Calculate Tax Amt. * Assumes Line Net is calculated */ public void setTaxAmt () @@ -461,11 +470,73 @@ public class MInvoiceLine extends X_C_InvoiceLine public void setLineNetAmt () { // Calculations & Rounding - BigDecimal net = getPriceActual().multiply(getQtyInvoiced()); - if (net.scale() > getPrecision()) - net = net.setScale(getPrecision(), BigDecimal.ROUND_HALF_UP); - super.setLineNetAmt (net); + BigDecimal bd = getPriceActual().multiply(getQtyInvoiced()); + + boolean documentLevel = getTax().isDocumentLevel(); + + // juddm: Tax Exempt & Tax Included in Price List & not Document Level - Adjust Line Amount + // http://sourceforge.net/tracker/index.php?func=detail&aid=1733602&group_id=176962&atid=879332 + if (isTaxIncluded() && !documentLevel) { + BigDecimal taxStdAmt = Env.ZERO, taxThisAmt = Env.ZERO; + + MTax invoiceTax = getTax(); + MTax stdTax = null; + + if (getProduct() == null) + { + if (getCharge() != null) // Charge + { + stdTax = new MTax (getCtx(), + ((MTaxCategory) getCharge().getC_TaxCategory()).getDefaultTax().getC_Tax_ID(), + get_TrxName()); + } + + } + else // Product + stdTax = new MTax (getCtx(), + ((MTaxCategory) getProduct().getC_TaxCategory()).getDefaultTax().getC_Tax_ID(), + get_TrxName()); + + if (stdTax != null) + { + + log.fine("stdTax rate is " + stdTax.getRate()); + log.fine("invoiceTax rate is " + invoiceTax.getRate()); + + taxThisAmt = taxThisAmt.add(invoiceTax.calculateTax(bd, isTaxIncluded(), getPrecision())); + taxStdAmt = taxStdAmt.add(stdTax.calculateTax(bd, isTaxIncluded(), getPrecision())); + + bd = bd.subtract(taxStdAmt).add(taxThisAmt); + + log.fine("Price List includes Tax and Tax Changed on Invoice Line: New Tax Amt: " + + taxThisAmt + " Standard Tax Amt: " + taxStdAmt + " Line Net Amt: " + bd); + } + } + + if (bd.scale() > getPrecision()) + bd = bd.setScale(getPrecision(), BigDecimal.ROUND_HALF_UP); + super.setLineNetAmt (bd); } // setLineNetAmt + /** + * Get Charge + * @return product or null + */ + public MCharge getCharge() + { + if (m_charge == null && getC_Charge_ID() != 0) + m_charge = MCharge.get (getCtx(), getC_Charge_ID()); + return m_charge; + } + /** + * Get Tax + * @return tax + */ + protected MTax getTax() + { + if (m_tax == null) + m_tax = MTax.get(getCtx(), getC_Tax_ID()); + return m_tax; + } // getTax /** * Set Qty Invoiced/Entered. @@ -837,9 +908,16 @@ public class MInvoiceLine extends X_C_InvoiceLine if (tax != null) { if (!tax.calculateTaxFromLines()) return false; + // red1 - solving BUGS #[ 1701331 ] , #[ 1786103 ] - if (!tax.save(get_TrxName())) - return false; + if (tax.getTaxAmt().signum() != 0) { + if (!tax.save(get_TrxName())) + return false; + } + else { + if (!tax.is_new() && !tax.delete(false, get_TrxName())) + return false; + } } return true; } diff --git a/base/src/org/compiere/model/MOrderLine.java b/base/src/org/compiere/model/MOrderLine.java index f14a9fb178..e9c7f63c5a 100644 --- a/base/src/org/compiere/model/MOrderLine.java +++ b/base/src/org/compiere/model/MOrderLine.java @@ -45,6 +45,10 @@ import org.compiere.util.Msg; * * @author Teo Sarca, SC ARHIPAC SERVICE SRL *
  • BF [ 2588043 ] Insufficient message ProductNotOnPriceList + * @author Michael Judd, www.akunagroup.com + *
  • BF [ 1733602 ] Price List including Tax Error - when a user changes the orderline or + * invoice line for a product on a price list that includes tax, the net amount is + * incorrectly calculated. */ public class MOrderLine extends X_C_OrderLine { @@ -196,11 +200,16 @@ public class MOrderLine extends X_C_OrderLine private boolean m_IsSOTrx = true; // Product Pricing private MProductPricing m_productPrice = null; + + /** Tax */ + private MTax m_tax = null; /** Cached Currency Precision */ private Integer m_precision = null; /** Product */ private MProduct m_product = null; + /** Charge */ + private MCharge m_charge = null; /** Parent */ private MOrder m_parent = null; @@ -352,11 +361,75 @@ public class MOrderLine extends X_C_OrderLine public void setLineNetAmt () { BigDecimal bd = getPriceActual().multiply(getQtyOrdered()); + + boolean documentLevel = getTax().isDocumentLevel(); + + // juddm: Tax Exempt & Tax Included in Price List & not Document Level - Adjust Line Amount + // http://sourceforge.net/tracker/index.php?func=detail&aid=1733602&group_id=176962&atid=879332 + if (isTaxIncluded() && !documentLevel) { + BigDecimal taxStdAmt = Env.ZERO, taxThisAmt = Env.ZERO; + + MTax orderTax = getTax(); + MTax stdTax = null; + + // get the standard tax + if (getProduct() == null) + { + if (getCharge() != null) // Charge + { + stdTax = new MTax (getCtx(), + ((MTaxCategory) getCharge().getC_TaxCategory()).getDefaultTax().getC_Tax_ID(), + get_TrxName()); + } + + } + else // Product + stdTax = new MTax (getCtx(), + ((MTaxCategory) getProduct().getC_TaxCategory()).getDefaultTax().getC_Tax_ID(), + get_TrxName()); + + if (stdTax != null) + { + log.fine("stdTax rate is " + stdTax.getRate()); + log.fine("orderTax rate is " + orderTax.getRate()); + + taxThisAmt = taxThisAmt.add(orderTax.calculateTax(bd, isTaxIncluded(), getPrecision())); + taxStdAmt = taxStdAmt.add(stdTax.calculateTax(bd, isTaxIncluded(), getPrecision())); + + bd = bd.subtract(taxStdAmt).add(taxThisAmt); + + log.fine("Price List includes Tax and Tax Changed on Order Line: New Tax Amt: " + + taxThisAmt + " Standard Tax Amt: " + taxStdAmt + " Line Net Amt: " + bd); + } + + } + if (bd.scale() > getPrecision()) bd = bd.setScale(getPrecision(), BigDecimal.ROUND_HALF_UP); super.setLineNetAmt (bd); } // setLineNetAmt + /** + * Get Charge + * @return product or null + */ + public MCharge getCharge() + { + if (m_charge == null && getC_Charge_ID() != 0) + m_charge = MCharge.get (getCtx(), getC_Charge_ID()); + return m_charge; + } + /** + * Get Tax + * @return tax + */ + protected MTax getTax() + { + if (m_tax == null) + m_tax = MTax.get(getCtx(), getC_Tax_ID()); + return m_tax; + } // getTax + /** * Get Currency Precision from Currency * @return precision @@ -808,6 +881,7 @@ public class MOrderLine extends X_C_OrderLine if (storages[i].getM_AttributeSetInstance_ID() == getM_AttributeSetInstance_ID()) qty = qty.add(storages[i].getQtyOnHand()); } + if (getQtyOrdered().compareTo(qty) > 0) { log.warning("Qty - Stock=" + qty + ", Ordered=" + getQtyOrdered()); diff --git a/base/src/org/compiere/model/MTaxCategory.java b/base/src/org/compiere/model/MTaxCategory.java index 8e12d959e3..314a9667c9 100644 --- a/base/src/org/compiere/model/MTaxCategory.java +++ b/base/src/org/compiere/model/MTaxCategory.java @@ -18,6 +18,9 @@ package org.compiere.model; import java.sql.ResultSet; import java.util.Properties; +import java.util.List; + +import org.adempiere.exceptions.AdempiereException; /** * Tax Category Model @@ -51,12 +54,36 @@ public class MTaxCategory extends X_C_TaxCategory /** * Load Constructor * @param ctx context - * @param rs resukt set + * @param rs result set * @param trxName trx */ public MTaxCategory (Properties ctx, ResultSet rs, String trxName) { super (ctx, rs, trxName); } // MTaxCategory - + + /** + * getDefaultTax + * Get the default tax id associated with this tax category + * + */ + public MTax getDefaultTax() + { + MTax m_tax = new MTax(getCtx(), 0, get_TrxName()); + + String whereClause = COLUMNNAME_C_TaxCategory_ID+"=? AND "+ COLUMNNAME_IsDefault+"='Y'"; + List list = new Query(getCtx(), MTax.Table_Name, whereClause, get_TrxName()) + .setParameters(new Object[]{getC_TaxCategory_ID()}) + .list(); + if (list.size() == 1) + m_tax = list.get(0); + else { + // Error - should only be one default + throw new AdempiereException("TooManyDefaults"); + } + + + + return m_tax; + } // getDefaultTax } // MTaxCategory