From 3090f2384ba31097ebc8d7eb69453c81ac833b0e Mon Sep 17 00:00:00 2001 From: hengsin Date: Wed, 23 Mar 2022 22:00:08 +0800 Subject: [PATCH] IDEMPIERE-5056 Order and Invoice: Tax lookup interface (#1253) * IDEMPIERE-5056 Order and Invoice: Tax lookup interface * IDEMPIERE-5056 Order and Invoice: Tax lookup interface - add default value for MSysConfig.getValue call. --- .../oracle/202203171454_IDEMPIERE-5056.sql | 14 ++ .../202203171454_IDEMPIERE-5056.sql | 11 ++ .../src/org/adempiere/model/CalloutRMA.java | 13 +- .../org/compiere/model/CalloutInvoice.java | 29 +++- .../compiere/model/CalloutInvoiceBatch.java | 37 ++++- .../src/org/compiere/model/CalloutOrder.java | 5 +- .../org.adempiere.base.DefaultTaxLookup.xml | 7 + .../src/org/adempiere/base/Core.java | 15 ++ .../org/adempiere/base/DefaultTaxLookup.java | 64 ++++++++ .../src/org/adempiere/base/ITaxLookup.java | 78 ++++++++++ .../src/org/compiere/model/MInvoiceLine.java | 12 +- .../src/org/compiere/model/MOrderLine.java | 4 +- .../src/org/compiere/model/MRMALine.java | 12 +- .../src/org/compiere/model/MSysConfig.java | 1 + .../src/org/compiere/model/Tax.java | 141 +++++++++++++++++- .../org/idempiere/test/model/MTaxTest.java | 29 ++++ 16 files changed, 444 insertions(+), 28 deletions(-) create mode 100644 migration/iD10/oracle/202203171454_IDEMPIERE-5056.sql create mode 100644 migration/iD10/postgresql/202203171454_IDEMPIERE-5056.sql create mode 100644 org.adempiere.base/OSGI-INF/org.adempiere.base.DefaultTaxLookup.xml create mode 100644 org.adempiere.base/src/org/adempiere/base/DefaultTaxLookup.java create mode 100644 org.adempiere.base/src/org/adempiere/base/ITaxLookup.java diff --git a/migration/iD10/oracle/202203171454_IDEMPIERE-5056.sql b/migration/iD10/oracle/202203171454_IDEMPIERE-5056.sql new file mode 100644 index 0000000000..21d97ba733 --- /dev/null +++ b/migration/iD10/oracle/202203171454_IDEMPIERE-5056.sql @@ -0,0 +1,14 @@ +-- IDEMPIERE-5056 Order and Invoice: Tax lookup interface +SELECT register_migration_script('202203171454_IDEMPIERE-5056.sql') FROM dual; + +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- Mar 17, 2022, 2:54:31 PM MYT +INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200198,0,0,TO_TIMESTAMP('2022-03-17 14:54:30','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2022-03-17 14:54:30','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','TAX_LOOKUP_SERVICE','50001','D','S','1161c587-6449-448a-9cb9-2fb600cf2b35') +; + +-- Mar 17, 2022, 2:56:49 PM MYT +UPDATE AD_SysConfig SET Value='org.adempiere.base.DefaultTaxLookup', Description='OSGi component name of tax lookup service', ConfigurationLevel='C',Updated=TO_TIMESTAMP('2022-03-17 14:56:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_SysConfig_ID=200198 +; + diff --git a/migration/iD10/postgresql/202203171454_IDEMPIERE-5056.sql b/migration/iD10/postgresql/202203171454_IDEMPIERE-5056.sql new file mode 100644 index 0000000000..55ee7d6dc4 --- /dev/null +++ b/migration/iD10/postgresql/202203171454_IDEMPIERE-5056.sql @@ -0,0 +1,11 @@ +-- IDEMPIERE-5056 Order and Invoice: Tax lookup interface +SELECT register_migration_script('202203171454_IDEMPIERE-5056.sql') FROM dual; + +-- Mar 17, 2022, 2:54:31 PM MYT +INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200198,0,0,TO_TIMESTAMP('2022-03-17 14:54:30','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2022-03-17 14:54:30','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','TAX_LOOKUP_SERVICE','50001','D','S','1161c587-6449-448a-9cb9-2fb600cf2b35') +; + +-- Mar 17, 2022, 2:56:49 PM MYT +UPDATE AD_SysConfig SET Value='org.adempiere.base.DefaultTaxLookup', Description='OSGi component name of tax lookup service', ConfigurationLevel='C',Updated=TO_TIMESTAMP('2022-03-17 14:56:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_SysConfig_ID=200198 +; + diff --git a/org.adempiere.base.callout/src/org/adempiere/model/CalloutRMA.java b/org.adempiere.base.callout/src/org/adempiere/model/CalloutRMA.java index 5a07e38bec..4eab58ce9a 100644 --- a/org.adempiere.base.callout/src/org/adempiere/model/CalloutRMA.java +++ b/org.adempiere.base.callout/src/org/adempiere/model/CalloutRMA.java @@ -36,7 +36,6 @@ import org.compiere.model.MOrderLine; import org.compiere.model.MRMA; import org.compiere.model.MRMALine; import org.compiere.model.Query; -import org.compiere.model.Tax; import org.compiere.util.DB; import org.compiere.util.Env; @@ -160,11 +159,15 @@ public class CalloutRMA extends CalloutEngine { pp.setPriceDate(invoice.getDateInvoiced()); precision = invoice.getPrecision(); - taxId = Tax.get(ctx, M_Product_ID, 0, + String deliveryViaRule = null; + if (invoice.getC_Order_ID() > 0) { + deliveryViaRule = new MOrder(ctx, invoice.getC_Order_ID(), null).getDeliveryViaRule(); + } + taxId = Core.getTaxLookup().get(ctx, M_Product_ID, 0, invoice.getDateInvoiced(), invoice.getDateInvoiced(), AD_Org_ID, rma.getShipment().getM_Warehouse_ID(), invoice.getC_BPartner_Location_ID(), // should be bill to - invoice.getC_BPartner_Location_ID(), rma.isSOTrx(), null); + invoice.getC_BPartner_Location_ID(), rma.isSOTrx(), deliveryViaRule, null); } else { @@ -175,11 +178,11 @@ public class CalloutRMA extends CalloutEngine { pp.setPriceDate(order.getDateOrdered()); precision = order.getPrecision(); - taxId = Tax.get(ctx, M_Product_ID, 0, + taxId = Core.getTaxLookup().get(ctx, M_Product_ID, 0, order.getDateOrdered(), order.getDateOrdered(), AD_Org_ID, order.getM_Warehouse_ID(), order.getC_BPartner_Location_ID(), // should be bill to - order.getC_BPartner_Location_ID(), rma.isSOTrx(), null); + order.getC_BPartner_Location_ID(), rma.isSOTrx(), order.getDeliveryViaRule(), null); } else return "No Invoice/Order found the Shipment/Receipt associated"; diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoice.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoice.java index 1c3e0fc1ed..8d10cc3c67 100644 --- a/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoice.java +++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoice.java @@ -488,9 +488,10 @@ public class CalloutInvoice extends CalloutEngine if (log.isLoggable(Level.FINE)) log.fine("Warehouse=" + M_Warehouse_ID); // - int C_Tax_ID = Tax.get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate, + String deliveryViaRule = getLineDeliveryViaRule(ctx, WindowNo, mTab); + int C_Tax_ID = Core.getTaxLookup().get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID, - Env.getContext(ctx, WindowNo, "IsSOTrx").equals("Y"), null); + Env.getContext(ctx, WindowNo, "IsSOTrx").equals("Y"), deliveryViaRule, null); if (log.isLoggable(Level.INFO)) log.info("Tax ID=" + C_Tax_ID); // if (C_Tax_ID == 0) @@ -501,7 +502,29 @@ public class CalloutInvoice extends CalloutEngine return amt (ctx, WindowNo, mTab, mField, value); } // tax - + private String getLineDeliveryViaRule(Properties ctx, int windowNo, GridTab mTab) { + if (mTab.getValue("C_OrderLine_ID") != null) { + int C_OrderLine_ID = (Integer) mTab.getValue("C_OrderLine_ID"); + if (C_OrderLine_ID > 0) { + MOrderLine orderLine = new MOrderLine(ctx, C_OrderLine_ID, null); + return orderLine.getParent().getDeliveryViaRule(); + } + } + if (mTab.getValue("M_InOutLine_ID") != null) { + int M_InOutLine_ID = (Integer) mTab.getValue("M_InOutLine_ID"); + if (M_InOutLine_ID > 0) { + MInOutLine ioLine = new MInOutLine(ctx, M_InOutLine_ID, null); + return ioLine.getParent().getDeliveryViaRule(); + } + } + int C_Order_ID = Env.getContextAsInt(ctx, windowNo, "C_Order_ID", true); + if (C_Order_ID > 0) { + MOrder order = new MOrder(ctx, C_Order_ID, null); + return order.getDeliveryViaRule(); + } + return null; + } + /** * Invoice - Amount. * - called from QtyInvoiced, PriceActual diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoiceBatch.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoiceBatch.java index fa9da07da2..cee2fe3d5c 100644 --- a/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoiceBatch.java +++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoiceBatch.java @@ -25,6 +25,7 @@ import java.sql.Timestamp; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.base.Core; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.DisplayType; @@ -317,9 +318,10 @@ public class CalloutInvoiceBatch extends CalloutEngine if (log.isLoggable(Level.FINE)) log.fine("Warehouse=" + M_Warehouse_ID); // - int C_Tax_ID = Tax.get(ctx, 0, C_Charge_ID, billDate, shipDate, + String deliveryViaRule = getLineDeliveryViaRule(ctx, WindowNo, mTab); + int C_Tax_ID = Core.getTaxLookup().get(ctx, 0, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, C_BPartner_Location_ID, C_BPartner_Location_ID, - Env.getContext(ctx, WindowNo, "IsSOTrx").equals("Y"), null); + Env.getContext(ctx, WindowNo, "IsSOTrx").equals("Y"), deliveryViaRule, null); if (log.isLoggable(Level.INFO)) log.info("Tax ID=" + C_Tax_ID); // if (C_Tax_ID == 0) @@ -330,7 +332,36 @@ public class CalloutInvoiceBatch extends CalloutEngine return amt (ctx, WindowNo, mTab, mField, value); } // tax - + private String getLineDeliveryViaRule(Properties ctx, int windowNo, GridTab mTab) { + if (mTab.getValue("C_InvoiceLine_ID") != null) { + int C_InvoiceLine_ID = (Integer) mTab.getValue("C_InvoiceLine_ID"); + if (C_InvoiceLine_ID > 0) { + MInvoiceLine invoiceLine = new MInvoiceLine(ctx, C_InvoiceLine_ID, null); + int C_OrderLine_ID = invoiceLine.getC_OrderLine_ID(); + if (C_OrderLine_ID > 0) { + MOrderLine orderLine = new MOrderLine(ctx, C_OrderLine_ID, null); + return orderLine.getParent().getDeliveryViaRule(); + } + int M_InOutLine_ID = invoiceLine.getM_InOutLine_ID(); + if (M_InOutLine_ID > 0) { + MInOutLine ioLine = new MInOutLine(ctx, M_InOutLine_ID, null); + return ioLine.getParent().getDeliveryViaRule(); + } + } + } + if (mTab.getValue("C_Invoice_ID") != null) { + int C_Invoice_ID = (Integer) mTab.getValue("C_Invoice_ID"); + if (C_Invoice_ID > 0) { + MInvoice invoice = new MInvoice(ctx, C_Invoice_ID, null); + I_C_Order order = invoice.getC_Order(); + if (order != null) { + return order.getDeliveryViaRule(); + } + } + } + return null; + } + /** * Invoice - Amount. * - called from QtyEntered, PriceEntered diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutOrder.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutOrder.java index 6706d944c8..1854523f3f 100644 --- a/org.adempiere.base.callout/src/org/compiere/model/CalloutOrder.java +++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutOrder.java @@ -981,9 +981,10 @@ public class CalloutOrder extends CalloutEngine if (log.isLoggable(Level.FINE)) log.fine("Bill BP_Location=" + billC_BPartner_Location_ID); // - int C_Tax_ID = Tax.get (ctx, M_Product_ID, C_Charge_ID, billDate, shipDate, + String deliveryViaRule = Env.getContext(ctx, WindowNo, I_C_Order.COLUMNNAME_DeliveryViaRule, true); + int C_Tax_ID = Core.getTaxLookup().get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID, - "Y".equals(Env.getContext(ctx, WindowNo, "IsSOTrx")), null); + "Y".equals(Env.getContext(ctx, WindowNo, "IsSOTrx")), deliveryViaRule, null); if (log.isLoggable(Level.INFO)) log.info("Tax ID=" + C_Tax_ID); // if (C_Tax_ID == 0) diff --git a/org.adempiere.base/OSGI-INF/org.adempiere.base.DefaultTaxLookup.xml b/org.adempiere.base/OSGI-INF/org.adempiere.base.DefaultTaxLookup.xml new file mode 100644 index 0000000000..12a9126f70 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.adempiere.base.DefaultTaxLookup.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/src/org/adempiere/base/Core.java b/org.adempiere.base/src/org/adempiere/base/Core.java index 8f7233ce0f..fe9ab7ca84 100644 --- a/org.adempiere.base/src/org/adempiere/base/Core.java +++ b/org.adempiere.base/src/org/adempiere/base/Core.java @@ -45,6 +45,7 @@ import org.compiere.model.MAddressValidation; import org.compiere.model.MAuthorizationAccount; import org.compiere.model.MBankAccountProcessor; import org.compiere.model.MPaymentProcessor; +import org.compiere.model.MSysConfig; import org.compiere.model.MTaxProvider; import org.compiere.model.ModelValidator; import org.compiere.model.PaymentInterface; @@ -1061,4 +1062,18 @@ public class Core { return DefaultReservationTracerFactory.getInstance(); } + + /** + * Get tax lookup service + * @return ITaxLookup service + */ + public static ITaxLookup getTaxLookup() { + String service = MSysConfig.getValue(MSysConfig.TAX_LOOKUP_SERVICE, DefaultTaxLookup.class.getName(), Env.getAD_Client_ID(Env.getCtx())); + IServiceHolder serviceHolder = Service.locator().locate(ITaxLookup.class, service, null); + if (serviceHolder != null) + return serviceHolder.getService(); + + //fall back, should not reach here + return new DefaultTaxLookup(); + } } diff --git a/org.adempiere.base/src/org/adempiere/base/DefaultTaxLookup.java b/org.adempiere.base/src/org/adempiere/base/DefaultTaxLookup.java new file mode 100644 index 0000000000..3f3c376244 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/base/DefaultTaxLookup.java @@ -0,0 +1,64 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.base; + +import java.sql.Timestamp; +import java.util.Properties; + +import org.compiere.model.Tax; +import org.osgi.service.component.annotations.Component; + +/** + * @author hengsin + * + */ +@Component(immediate = true, service = {ITaxLookup.class}) +public class DefaultTaxLookup implements ITaxLookup { + + /** + * default constructor + */ + public DefaultTaxLookup() { + } + + /* (non-Javadoc) + * @see org.adempiere.base.ITaxLookup#get(java.util.Properties, int, int, java.sql.Timestamp, java.sql.Timestamp, int, int, int, int, boolean, java.lang.String) + */ + @Override + public int get(Properties ctx, int M_Product_ID, int C_Charge_ID, Timestamp billDate, Timestamp shipDate, + int AD_Org_ID, int M_Warehouse_ID, int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID, + boolean IsSOTrx, String deliveryViaRule, String trxName) { + return Tax.get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID, + IsSOTrx, deliveryViaRule, trxName); + } + + @Override + public int get(Properties ctx, int C_TaxCategory_ID, boolean IsSOTrx, Timestamp shipDate, int shipFromC_Location_ID, + int shipToC_Location_ID, Timestamp billDate, int billFromC_Location_ID, int billToC_Location_ID, + String trxName) { + return Tax.get(ctx, C_TaxCategory_ID, IsSOTrx, shipDate, shipFromC_Location_ID, shipToC_Location_ID, billDate, billFromC_Location_ID, billToC_Location_ID, trxName); + } + +} diff --git a/org.adempiere.base/src/org/adempiere/base/ITaxLookup.java b/org.adempiere.base/src/org/adempiere/base/ITaxLookup.java new file mode 100644 index 0000000000..147adb623f --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/base/ITaxLookup.java @@ -0,0 +1,78 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.base; + +import java.sql.Timestamp; +import java.util.Properties; + +/** + * Interface to lookup C_Tax record id + * @author hengsin + * + */ +public interface ITaxLookup { + + /** + * Find C_Tax_ID by Product/Charge + Warehouse Location + BPartner Location + DeliveryViaRule + * @param ctx + * @param M_Product_ID + * @param C_Charge_ID + * @param billDate Billing Date + * @param shipDate Shipment Date + * @param AD_Org_ID + * @param M_Warehouse_ID + * @param billC_BPartner_Location_ID Bill to location + * @param shipC_BPartner_Location_ID Ship to location + * @param IsSOTrx + * @param deliveryViaRule Order/Invoice's Delivery Via Rule + * @param trxName + * @return C_Tax_ID + */ + public int get (Properties ctx, int M_Product_ID, int C_Charge_ID, + Timestamp billDate, Timestamp shipDate, + int AD_Org_ID, int M_Warehouse_ID, + int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID, + boolean IsSOTrx, String deliveryViaRule, String trxName); + + /** + * Find C_Tax_ID + * @param ctx + * @param C_TaxCategory_ID + * @param IsSOTrx + * @param shipDate Shipment Date + * @param shipFromC_Location_ID Shipping from (not use in default lookup implementation) + * @param shipToC_Location_ID Shipping to (not use in default lookup implementation) + * @param billDate Billing Date + * @param billFromC_Location_ID Billing from (Tax Location from) + * @param billToC_Location_ID Billing to (Tax Location to) + * @param deliveryRule Order/Invoice's Delivery Via Rule + * @param trxName + * @return C_Tax_ID + */ + public int get (Properties ctx, + int C_TaxCategory_ID, boolean IsSOTrx, + Timestamp shipDate, int shipFromC_Location_ID, int shipToC_Location_ID, + Timestamp billDate, int billFromC_Location_ID, int billToC_Location_ID, String trxName); +} diff --git a/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java b/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java index a4b5789be3..ad77033d84 100644 --- a/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java +++ b/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java @@ -477,10 +477,18 @@ public class MInvoiceLine extends X_C_InvoiceLine // int M_Warehouse_ID = Env.getContextAsInt(getCtx(), Env.M_WAREHOUSE_ID); // - int C_Tax_ID = Tax.get(getCtx(), getM_Product_ID(), getC_Charge_ID() , m_DateInvoiced, m_DateInvoiced, + String deliveryViaRule = null; + if (getC_OrderLine_ID() > 0) { + deliveryViaRule = new MOrderLine(getCtx(), getC_OrderLine_ID(), get_TrxName()).getParent().getDeliveryViaRule(); + } else if (getM_InOutLine_ID() > 0) { + deliveryViaRule = new MInOutLine(getCtx(), getM_InOutLine_ID(), get_TrxName()).getParent().getDeliveryViaRule(); + } else if (getParent().getC_Order_ID() > 0) { + deliveryViaRule = new MOrder(getCtx(), getParent().getC_Order_ID(), get_TrxName()).getDeliveryViaRule(); + } + int C_Tax_ID = Core.getTaxLookup().get(getCtx(), getM_Product_ID(), getC_Charge_ID() , m_DateInvoiced, m_DateInvoiced, getAD_Org_ID(), M_Warehouse_ID, m_C_BPartner_Location_ID, // should be bill to - m_C_BPartner_Location_ID, m_IsSOTrx, get_TrxName()); + m_C_BPartner_Location_ID, m_IsSOTrx, deliveryViaRule, get_TrxName()); if (C_Tax_ID == 0) { log.log(Level.SEVERE, "No Tax found"); diff --git a/org.adempiere.base/src/org/compiere/model/MOrderLine.java b/org.adempiere.base/src/org/compiere/model/MOrderLine.java index cf51ea9e3c..49dadf7554 100644 --- a/org.adempiere.base/src/org/compiere/model/MOrderLine.java +++ b/org.adempiere.base/src/org/compiere/model/MOrderLine.java @@ -329,10 +329,10 @@ public class MOrderLine extends X_C_OrderLine */ public boolean setTax() { - int ii = Tax.get(getCtx(), getM_Product_ID(), getC_Charge_ID(), getDateOrdered(), getDateOrdered(), + int ii = Core.getTaxLookup().get(getCtx(), getM_Product_ID(), getC_Charge_ID(), getDateOrdered(), getDateOrdered(), getAD_Org_ID(), getM_Warehouse_ID(), getC_BPartner_Location_ID(), // should be bill to - getC_BPartner_Location_ID(), m_IsSOTrx, get_TrxName()); + getC_BPartner_Location_ID(), m_IsSOTrx, getParent().getDeliveryViaRule(), get_TrxName()); if (ii == 0) { log.log(Level.SEVERE, "No Tax found"); diff --git a/org.adempiere.base/src/org/compiere/model/MRMALine.java b/org.adempiere.base/src/org/compiere/model/MRMALine.java index dcae4be72b..993c70c379 100644 --- a/org.adempiere.base/src/org/compiere/model/MRMALine.java +++ b/org.adempiere.base/src/org/compiere/model/MRMALine.java @@ -162,10 +162,14 @@ public class MRMALine extends X_M_RMALine pp.setPriceDate(invoice.getDateInvoiced()); precision = invoice.getPrecision(); - taxId = Tax.get(getCtx(), getM_Product_ID(), getC_Charge_ID(), invoice.getDateInvoiced(), invoice.getDateInvoiced(), + String deliveryViaRule = null; + if (invoice.getC_Order_ID() > 0) { + deliveryViaRule = new MOrder(getCtx(), invoice.getC_Order_ID(), get_TrxName()).getDeliveryViaRule(); + } + taxId = Core.getTaxLookup().get(getCtx(), getM_Product_ID(), getC_Charge_ID(), invoice.getDateInvoiced(), invoice.getDateInvoiced(), getAD_Org_ID(), getParent().getShipment().getM_Warehouse_ID(), invoice.getC_BPartner_Location_ID(), // should be bill to - invoice.getC_BPartner_Location_ID(), getParent().isSOTrx(), get_TrxName()); + invoice.getC_BPartner_Location_ID(), getParent().isSOTrx(), deliveryViaRule, get_TrxName()); } else { @@ -176,10 +180,10 @@ public class MRMALine extends X_M_RMALine pp.setPriceDate(order.getDateOrdered()); precision = order.getPrecision(); - taxId = Tax.get(getCtx(), getM_Product_ID(), getC_Charge_ID(), order.getDateOrdered(), order.getDateOrdered(), + taxId = Core.getTaxLookup().get(getCtx(), getM_Product_ID(), getC_Charge_ID(), order.getDateOrdered(), order.getDateOrdered(), getAD_Org_ID(), order.getM_Warehouse_ID(), order.getC_BPartner_Location_ID(), // should be bill to - order.getC_BPartner_Location_ID(), getParent().isSOTrx(), get_TrxName()); + order.getC_BPartner_Location_ID(), getParent().isSOTrx(), order.getDeliveryViaRule(), get_TrxName()); } else throw new IllegalStateException("No Invoice/Order found the Shipment/Receipt associated"); diff --git a/org.adempiere.base/src/org/compiere/model/MSysConfig.java b/org.adempiere.base/src/org/compiere/model/MSysConfig.java index 77d12a7580..d23aec6e74 100644 --- a/org.adempiere.base/src/org/compiere/model/MSysConfig.java +++ b/org.adempiere.base/src/org/compiere/model/MSysConfig.java @@ -161,6 +161,7 @@ public class MSysConfig extends X_AD_SysConfig public static final String SYSTEM_IN_MAINTENANCE_MODE = "SYSTEM_IN_MAINTENANCE_MODE"; public static final String SYSTEM_INSERT_CHANGELOG = "SYSTEM_INSERT_CHANGELOG"; public static final String SYSTEM_NATIVE_SEQUENCE = "SYSTEM_NATIVE_SEQUENCE"; + public static final String TAX_LOOKUP_SERVICE="TAX_LOOKUP_SERVICE"; public static final String TOP_MARGIN_PIXELS_FOR_HEADER = "TOP_MARGIN_PIXELS_FOR_HEADER"; public static final String TRACE_ALL_TRX_CONNECTION_GET = "TRACE_ALL_TRX_CONNECTION_GET"; public static final String TWOPACK_COMMIT_DDL = "2PACK_COMMIT_DDL"; diff --git a/org.adempiere.base/src/org/compiere/model/Tax.java b/org.adempiere.base/src/org/compiere/model/Tax.java index baec802718..d5b7b5f947 100644 --- a/org.adempiere.base/src/org/compiere/model/Tax.java +++ b/org.adempiere.base/src/org/compiere/model/Tax.java @@ -23,6 +23,7 @@ import java.sql.Timestamp; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.base.Core; import org.adempiere.exceptions.DBException; import org.adempiere.exceptions.TaxCriteriaNotFoundException; import org.adempiere.exceptions.TaxForChangeNotFoundException; @@ -93,6 +94,7 @@ public class Tax * @param billC_BPartner_Location_ID invoice location * @param shipC_BPartner_Location_ID ship location (ignored) * @param IsSOTrx is a sales trx + * @param trxName * @return C_Tax_ID * @throws TaxCriteriaNotFoundException if a criteria was not found */ @@ -101,13 +103,50 @@ public class Tax int AD_Org_ID, int M_Warehouse_ID, int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID, boolean IsSOTrx, String trxName) + { + return get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, + billC_BPartner_Location_ID, shipC_BPartner_Location_ID, IsSOTrx, null, trxName); + } + + /************************************************************************** + * Get Tax ID - converts parameters to call Get Tax. + *
{@code
+	 *		M_Product_ID/C_Charge_ID	->	C_TaxCategory_ID
+	 *		billDate, shipDate			->	billDate, shipDate
+	 *		AD_Org_ID					->	billFromC_Location_ID
+	 *		M_Warehouse_ID				->	shipFromC_Location_ID
+	 *		billC_BPartner_Location_ID  ->	billToC_Location_ID
+	 *		shipC_BPartner_Location_ID 	->	shipToC_Location_ID
+	 *
+	 *  if IsSOTrx is false, bill and ship are reversed
+	 *  }
+ * @param ctx context + * @param M_Product_ID product + * @param C_Charge_ID product + * @param billDate invoice date + * @param shipDate ship date (ignored) + * @param AD_Org_ID org + * @param M_Warehouse_ID warehouse (ignored) + * @param billC_BPartner_Location_ID invoice location + * @param shipC_BPartner_Location_ID ship location (ignored) + * @param IsSOTrx is a sales trx + * @param deliveryViaRule if Delivery Via Rule is PickUp, use Warehouse Location instead of Billing Location as Tax Location to + * @param trxName + * @return C_Tax_ID + * @throws TaxCriteriaNotFoundException if a criteria was not found + */ + public static int get (Properties ctx, int M_Product_ID, int C_Charge_ID, + Timestamp billDate, Timestamp shipDate, + int AD_Org_ID, int M_Warehouse_ID, + int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID, + boolean IsSOTrx, String deliveryViaRule, String trxName) { if (M_Product_ID != 0) return getProduct (ctx, M_Product_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, - billC_BPartner_Location_ID, shipC_BPartner_Location_ID, IsSOTrx, trxName); + billC_BPartner_Location_ID, shipC_BPartner_Location_ID, IsSOTrx, deliveryViaRule, trxName); else if (C_Charge_ID != 0) return getCharge (ctx, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, - billC_BPartner_Location_ID, shipC_BPartner_Location_ID, IsSOTrx, trxName); + billC_BPartner_Location_ID, shipC_BPartner_Location_ID, IsSOTrx, deliveryViaRule, trxName); else return getExemptTax (ctx, AD_Org_ID, trxName); } // get @@ -134,6 +173,42 @@ public class Tax return getCharge(ctx, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID, IsSOTrx, null); } + /** + * Get Tax ID - converts parameters to call Get Tax. + *
{@code
+	 *		C_Charge_ID					->	C_TaxCategory_ID
+	 *		billDate					->	billDate
+	 *		shipDate					->	shipDate (ignored)
+	 *		AD_Org_ID					->	billFromC_Location_ID
+	 *		M_Warehouse_ID				->	shipFromC_Location_ID (ignored)
+	 *		billC_BPartner_Location_ID  ->	billToC_Location_ID
+	 *		shipC_BPartner_Location_ID 	->	shipToC_Location_ID (ignored)
+	 *
+	 *  if IsSOTrx is false, bill and ship are reversed
+	 *  }
+ * @param ctx context + * @param C_Charge_ID product + * @param billDate invoice date + * @param shipDate ship date (ignored) + * @param AD_Org_ID org + * @param M_Warehouse_ID warehouse (ignored) + * @param billC_BPartner_Location_ID invoice location + * @param shipC_BPartner_Location_ID ship location (ignored) + * @param IsSOTrx is a sales trx + * @param trxName + * @return C_Tax_ID + * @throws TaxForChangeNotFoundException if criteria not found for given change + * @throws TaxCriteriaNotFoundException if a criteria was not found + */ + public static int getCharge (Properties ctx, int C_Charge_ID, + Timestamp billDate, Timestamp shipDate, + int AD_Org_ID, int M_Warehouse_ID, + int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID, + boolean IsSOTrx, String trxName) + { + return getCharge(ctx, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, + billC_BPartner_Location_ID, shipC_BPartner_Location_ID, IsSOTrx, null, trxName); + } /** * Get Tax ID - converts parameters to call Get Tax. @@ -157,6 +232,8 @@ public class Tax * @param billC_BPartner_Location_ID invoice location * @param shipC_BPartner_Location_ID ship location (ignored) * @param IsSOTrx is a sales trx + * @param deliveryViaRule if Delivery Via Rule is PickUp, use Warehouse Location instead of Billing Location as Tax Location to + * @param trxName * @return C_Tax_ID * @throws TaxForChangeNotFoundException if criteria not found for given change * @throws TaxCriteriaNotFoundException if a criteria was not found @@ -165,13 +242,14 @@ public class Tax Timestamp billDate, Timestamp shipDate, int AD_Org_ID, int M_Warehouse_ID, int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID, - boolean IsSOTrx, String trxName) + boolean IsSOTrx, String deliveryViaRule, String trxName) { int C_TaxCategory_ID = 0; int shipFromC_Location_ID = 0; int shipToC_Location_ID = 0; int billFromC_Location_ID = 0; int billToC_Location_ID = 0; + int warehouseC_Location_ID = 0; String IsTaxExempt = null; String IsSOTaxExempt = null; String IsPOTaxExempt = null; @@ -208,6 +286,7 @@ public class Tax IsTaxExempt = IsSOTrx ? IsSOTaxExempt : IsPOTaxExempt; shipFromC_Location_ID = rs.getInt (6); shipToC_Location_ID = rs.getInt (7); + warehouseC_Location_ID = rs.getInt(6); found = true; } DB.close(rs, pstmt); @@ -243,13 +322,17 @@ public class Tax shipFromC_Location_ID = shipToC_Location_ID; shipToC_Location_ID = temp; } + else if (X_C_Order.DELIVERYVIARULE_Pickup.equals(deliveryViaRule)) + { + billToC_Location_ID = warehouseC_Location_ID; + } // if (log.isLoggable(Level.FINE)) log.fine("getCharge - C_TaxCategory_ID=" + C_TaxCategory_ID + ", billFromC_Location_ID=" + billFromC_Location_ID + ", billToC_Location_ID=" + billToC_Location_ID + ", shipFromC_Location_ID=" + shipFromC_Location_ID + ", shipToC_Location_ID=" + shipToC_Location_ID); - return get (ctx, C_TaxCategory_ID, IsSOTrx, + return Core.getTaxLookup().get (ctx, C_TaxCategory_ID, IsSOTrx, shipDate, shipFromC_Location_ID, shipToC_Location_ID, billDate, billFromC_Location_ID, billToC_Location_ID, trxName); } // getCharge @@ -299,6 +382,7 @@ public class Tax * @param billC_BPartner_Location_ID invoice location * @param shipC_BPartner_Location_ID ship location (ignored) * @param IsSOTrx is a sales trx + * @param trxName * @return C_Tax_ID * If error it returns 0 and sets error log (TaxCriteriaNotFound) */ @@ -307,6 +391,43 @@ public class Tax int AD_Org_ID, int M_Warehouse_ID, int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID, boolean IsSOTrx, String trxName) + { + return getProduct(ctx, M_Product_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, + billC_BPartner_Location_ID, shipC_BPartner_Location_ID, IsSOTrx, null, trxName); + } + + /** + * Get Tax ID - converts parameters to call Get Tax. + *
{@code
+	 *		M_Product_ID				->	C_TaxCategory_ID
+	 *		billDate					->	billDate
+	 *		shipDate					->	shipDate (ignored)
+	 *		AD_Org_ID					->	billFromC_Location_ID
+	 *		M_Warehouse_ID				->	shipFromC_Location_ID (ignored)
+	 *		billC_BPartner_Location_ID  ->	billToC_Location_ID
+	 *		shipC_BPartner_Location_ID 	->	shipToC_Location_ID (ignored)
+	 *
+	 *  if IsSOTrx is false, bill and ship are reversed
+	 *  }
+ * @param ctx context + * @param M_Product_ID product + * @param billDate invoice date + * @param shipDate ship date (ignored) + * @param AD_Org_ID org + * @param M_Warehouse_ID warehouse (ignored) + * @param billC_BPartner_Location_ID invoice location + * @param shipC_BPartner_Location_ID ship location (ignored) + * @param IsSOTrx is a sales trx + * @param deliveryViaRule if Delivery Via Rule is PickUp, use Warehouse Location instead of Billing Location as Tax Location to + * @param trxName + * @return C_Tax_ID + * If error it returns 0 and sets error log (TaxCriteriaNotFound) + */ + public static int getProduct (Properties ctx, int M_Product_ID, + Timestamp billDate, Timestamp shipDate, + int AD_Org_ID, int M_Warehouse_ID, + int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID, + boolean IsSOTrx, String deliveryViaRule, String trxName) { String variable = ""; int C_TaxCategory_ID = 0; @@ -314,6 +435,7 @@ public class Tax int shipToC_Location_ID = 0; int billFromC_Location_ID = 0; int billToC_Location_ID = 0; + int warehouseC_Location_ID = 0; String IsTaxExempt = null; String IsSOTaxExempt = null; String IsPOTaxExempt = null; @@ -351,6 +473,7 @@ public class Tax IsTaxExempt = IsSOTrx ? IsSOTaxExempt : IsPOTaxExempt; shipFromC_Location_ID = rs.getInt(6); shipToC_Location_ID = rs.getInt(7); + warehouseC_Location_ID = rs.getInt(6); found = true; } DB.close(rs, pstmt); @@ -371,12 +494,16 @@ public class Tax shipFromC_Location_ID = shipToC_Location_ID; shipToC_Location_ID = temp; } + else if (X_C_Order.DELIVERYVIARULE_Pickup.equals(deliveryViaRule)) + { + billToC_Location_ID = warehouseC_Location_ID; + } if (log.isLoggable(Level.FINE)) log.fine("getProduct - C_TaxCategory_ID=" + C_TaxCategory_ID + ", billFromC_Location_ID=" + billFromC_Location_ID + ", billToC_Location_ID=" + billToC_Location_ID + ", shipFromC_Location_ID=" + shipFromC_Location_ID + ", shipToC_Location_ID=" + shipToC_Location_ID); - return get(ctx, C_TaxCategory_ID, IsSOTrx, + return Core.getTaxLookup().get(ctx, C_TaxCategory_ID, IsSOTrx, shipDate, shipFromC_Location_ID, shipToC_Location_ID, billDate, billFromC_Location_ID, billToC_Location_ID, trxName); } @@ -528,8 +655,8 @@ public class Tax * @param shipFromC_Location_ID ship from (ignored) * @param shipToC_Location_ID ship to (ignored) * @param billDate invoice date - * @param billFromC_Location_ID invoice from - * @param billToC_Location_ID invoice to + * @param billFromC_Location_ID invoice from (Tax Location from) + * @param billToC_Location_ID invoice to (Tax Location to) * @param trxName Transaction * @return C_Tax_ID * @throws TaxNotFoundException if no tax found for given criteria diff --git a/org.idempiere.test/src/org/idempiere/test/model/MTaxTest.java b/org.idempiere.test/src/org/idempiere/test/model/MTaxTest.java index 80a31763c2..620e11372f 100644 --- a/org.idempiere.test/src/org/idempiere/test/model/MTaxTest.java +++ b/org.idempiere.test/src/org/idempiere/test/model/MTaxTest.java @@ -25,8 +25,12 @@ package org.idempiere.test.model; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.adempiere.base.Core; +import org.compiere.model.MBPartner; import org.compiere.model.MTax; +import org.compiere.model.Tax; import org.compiere.util.Env; import org.compiere.util.TimeUtil; import org.idempiere.test.AbstractTestCase; @@ -42,6 +46,9 @@ public class MTaxTest extends AbstractTestCase { private static final int STANDARD_TAX_ID = 104; private static final int STANDARD_TAX_CATEGORY_ID=107; + private final static int BP_JOE_BLOCK = 118; + private static final int PRODUCT_MULCH = 137; + public MTaxTest() { } @@ -60,4 +67,26 @@ public class MTaxTest extends AbstractTestCase { tax.saveEx(); assertEquals(0, tax.getParent_Tax_ID(), "Unexpected parent tax id"); } + + @Test + public void testTaxLookup() { + int taxExemptId = Tax.getExemptTax(Env.getCtx(), getAD_Org_ID(), getTrxName()); + assertTrue(taxExemptId>0, "Fail to get tax exempt Id"); + + MBPartner bp = new MBPartner(Env.getCtx(), BP_JOE_BLOCK, getTrxName()); + bp.setIsTaxExempt(true); + bp.saveEx(); + + int id = Core.getTaxLookup().get(Env.getCtx(), PRODUCT_MULCH, 0, getLoginDate(), getLoginDate(), getAD_Org_ID(), getM_Warehouse_ID(), + bp.getPrimaryC_BPartner_Location_ID(), bp.getPrimaryC_BPartner_Location_ID(), true, null, getTrxName()); + assertEquals(taxExemptId, id, "Unexpected tax id"); + + bp.setIsTaxExempt(false); + bp.saveEx(); + + id = Core.getTaxLookup().get(Env.getCtx(), PRODUCT_MULCH, 0, getLoginDate(), getLoginDate(), getAD_Org_ID(), getM_Warehouse_ID(), + bp.getPrimaryC_BPartner_Location_ID(), bp.getPrimaryC_BPartner_Location_ID(), true, null, getTrxName()); + assertTrue(id != taxExemptId, "Unexpected tax id: " + id); + assertEquals(STANDARD_TAX_ID, id, "Unexpected tax id"); + } }