From 7516f85d5544702ee19100665cccc0e2cce3d055 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Wed, 14 Aug 2013 17:37:27 +0800 Subject: [PATCH] IDEMPIERE-1188 Invoice Price Variance for Average PO Costing. --- db/ddlutils/oracle/views/M_CostMovement_V.sql | 5 +- .../postgresql/views/M_CostMovement_V.sql | 5 +- .../oracle/201308140925_IDEMPIERE-1188.sql | 53 +++++++++ .../201308140925_IDEMPIERE-1188.sql | 57 ++++++++++ .../src/org/compiere/acct/Doc_MatchInv.java | 104 +++++++++++++++--- .../org/compiere/model/I_M_CostDetail.java | 15 +++ .../src/org/compiere/model/MCostDetail.java | 70 ++++++++++++ .../org/compiere/model/X_M_CostDetail.java | 30 ++++- 8 files changed, 322 insertions(+), 17 deletions(-) create mode 100644 migration/i1.0c-release/oracle/201308140925_IDEMPIERE-1188.sql create mode 100644 migration/i1.0c-release/postgresql/201308140925_IDEMPIERE-1188.sql diff --git a/db/ddlutils/oracle/views/M_CostMovement_V.sql b/db/ddlutils/oracle/views/M_CostMovement_V.sql index 2adaf9fcab..c4dbbfb607 100644 --- a/db/ddlutils/oracle/views/M_CostMovement_V.sql +++ b/db/ddlutils/oracle/views/M_CostMovement_V.sql @@ -1,5 +1,8 @@ CREATE OR REPLACE VIEW m_costmovement_v AS -SELECT a.ad_client_id, a.ad_org_id, b.c_acctschema_id, a.m_costhistory_id, a.m_costtype_id, a.m_costelement_id, a.m_attributesetinstance_id, b.m_product_id, a.oldqty, a.newqty, a.oldcostprice, a.newcostprice, a.oldcqty, a.newcqty, a.oldcamt, a.newcamt, b.qty, b.amt, b.deltaqty, b.deltaamt, b.c_orderline_id, b.m_inoutline_id, b.c_invoiceline_id, b.m_movementline_id, b.m_inventoryline_id, b.m_productionline_id, b.c_projectissue_id, a.m_costdetail_id, b.description, a.created, a.createdby, a.updated, a.updatedby, a.isactive +SELECT a.ad_client_id, a.ad_org_id, b.c_acctschema_id, a.m_costhistory_id, a.m_costtype_id, a.m_costelement_id, a.m_attributesetinstance_id, + b.m_product_id, a.oldqty, a.newqty, a.oldcostprice, a.newcostprice, a.oldcqty, a.newcqty, a.oldcamt, a.newcamt, b.qty, b.amt, b.deltaqty, + b.deltaamt, b.c_orderline_id, b.m_inoutline_id, b.c_invoiceline_id, b.m_movementline_id, b.m_inventoryline_id, b.m_productionline_id, + b.c_projectissue_id, b.m_matchinv_id, a.m_costdetail_id, b.description, a.created, a.createdby, a.updated, a.updatedby, a.isactive FROM m_costhistory a JOIN m_costdetail b ON a.m_costdetail_id = b.m_costdetail_id ORDER BY a.m_costhistory_id diff --git a/db/ddlutils/postgresql/views/M_CostMovement_V.sql b/db/ddlutils/postgresql/views/M_CostMovement_V.sql index 2adaf9fcab..c4dbbfb607 100644 --- a/db/ddlutils/postgresql/views/M_CostMovement_V.sql +++ b/db/ddlutils/postgresql/views/M_CostMovement_V.sql @@ -1,5 +1,8 @@ CREATE OR REPLACE VIEW m_costmovement_v AS -SELECT a.ad_client_id, a.ad_org_id, b.c_acctschema_id, a.m_costhistory_id, a.m_costtype_id, a.m_costelement_id, a.m_attributesetinstance_id, b.m_product_id, a.oldqty, a.newqty, a.oldcostprice, a.newcostprice, a.oldcqty, a.newcqty, a.oldcamt, a.newcamt, b.qty, b.amt, b.deltaqty, b.deltaamt, b.c_orderline_id, b.m_inoutline_id, b.c_invoiceline_id, b.m_movementline_id, b.m_inventoryline_id, b.m_productionline_id, b.c_projectissue_id, a.m_costdetail_id, b.description, a.created, a.createdby, a.updated, a.updatedby, a.isactive +SELECT a.ad_client_id, a.ad_org_id, b.c_acctschema_id, a.m_costhistory_id, a.m_costtype_id, a.m_costelement_id, a.m_attributesetinstance_id, + b.m_product_id, a.oldqty, a.newqty, a.oldcostprice, a.newcostprice, a.oldcqty, a.newcqty, a.oldcamt, a.newcamt, b.qty, b.amt, b.deltaqty, + b.deltaamt, b.c_orderline_id, b.m_inoutline_id, b.c_invoiceline_id, b.m_movementline_id, b.m_inventoryline_id, b.m_productionline_id, + b.c_projectissue_id, b.m_matchinv_id, a.m_costdetail_id, b.description, a.created, a.createdby, a.updated, a.updatedby, a.isactive FROM m_costhistory a JOIN m_costdetail b ON a.m_costdetail_id = b.m_costdetail_id ORDER BY a.m_costhistory_id diff --git a/migration/i1.0c-release/oracle/201308140925_IDEMPIERE-1188.sql b/migration/i1.0c-release/oracle/201308140925_IDEMPIERE-1188.sql new file mode 100644 index 0000000000..2a95acce29 --- /dev/null +++ b/migration/i1.0c-release/oracle/201308140925_IDEMPIERE-1188.sql @@ -0,0 +1,53 @@ +-- Jul 22, 2013 8:56:39 PM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Column (SeqNoSelection,IsSyncDatabase,Version,AD_Table_ID,AD_Column_ID,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,AD_Reference_ID,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,Updated,CreatedBy,AD_Org_ID,IsActive,Created,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,EntityType,IsEncrypted,AD_Element_ID) VALUES (0,'N',1,808,210662,'N','N','N',0,'N',22,'N',30,'N','N','Y','c5983338-dac3-4173-ac05-81bc1bdb1ce7','N','M_MatchInv_ID','Match Shipment/Receipt to Invoice','Match Invoice','N',TO_DATE('2013-07-22 20:56:37','YYYY-MM-DD HH24:MI:SS'),100,0,'Y',TO_DATE('2013-07-22 20:56:37','YYYY-MM-DD HH24:MI:SS'),100,'N','N',0,'D','N',1689) +; + +-- Jul 22, 2013 8:56:39 PM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy,AD_Column_Trl_UU ) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy,Generate_UUID() FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=210662 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- Jul 22, 2013 8:56:45 PM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +ALTER TABLE M_CostDetail ADD M_MatchInv_ID NUMBER(10) DEFAULT NULL +; + +CREATE OR REPLACE VIEW m_costmovement_v AS +SELECT a.ad_client_id, a.ad_org_id, b.c_acctschema_id, a.m_costhistory_id, a.m_costtype_id, a.m_costelement_id, a.m_attributesetinstance_id, + b.m_product_id, a.oldqty, a.newqty, a.oldcostprice, a.newcostprice, a.oldcqty, a.newcqty, a.oldcamt, a.newcamt, b.qty, b.amt, b.deltaqty, + b.deltaamt, b.c_orderline_id, b.m_inoutline_id, b.c_invoiceline_id, b.m_movementline_id, b.m_inventoryline_id, b.m_productionline_id, + b.c_projectissue_id, b.m_matchinv_id, a.m_costdetail_id, b.description, a.created, a.createdby, a.updated, a.updatedby, a.isactive + FROM m_costhistory a + JOIN m_costdetail b ON a.m_costdetail_id = b.m_costdetail_id + ORDER BY a.m_costhistory_id +; + +-- Jul 23, 2013 7:55:31 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Column (SeqNoSelection,IsSyncDatabase,Version,AD_Table_ID,AD_Column_ID,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,AD_Reference_ID,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,Updated,CreatedBy,AD_Org_ID,IsActive,Created,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,EntityType,IsEncrypted,AD_Element_ID) VALUES (0,'N',1,200002,210663,'N','N','N',0,'N',22,'N',30,'N','N','Y','10f7d192-2c18-497c-bcf3-548f8546fd41','N','M_MatchInv_ID','Match Shipment/Receipt to Invoice','Match Invoice','N',TO_DATE('2013-07-23 07:55:29','YYYY-MM-DD HH24:MI:SS'),100,0,'Y',TO_DATE('2013-07-23 07:55:29','YYYY-MM-DD HH24:MI:SS'),100,'N','N',0,'D','N',1689) +; + +-- Jul 23, 2013 7:55:31 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy,AD_Column_Trl_UU ) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy,Generate_UUID() FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=210663 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- Jul 23, 2013 7:56:36 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Field (NumLines,SortNo,IsEncrypted,AD_Tab_ID,DisplayLength,IsSameLine,IsHeading,AD_Column_ID,SeqNo,IsCentrallyMaintained,AD_Field_ID,IsReadOnly,EntityType,Description,Name,IsDisplayed,IsFieldOnly,AD_Field_UU,UpdatedBy,AD_Org_ID,Created,CreatedBy,Updated,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,ColumnSpan) VALUES (1,0,'N',200000,0,'N','N',210663,270,'Y',202340,'N','D','Match Shipment/Receipt to Invoice','Match Invoice','Y','N','624749f1-6544-45d7-a6b7-cebc6bcdfc93',100,0,TO_DATE('2013-07-23 07:56:34','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2013-07-23 07:56:34','YYYY-MM-DD HH24:MI:SS'),'Y','Y',270,1,'N',0,1) +; + +-- Jul 23, 2013 7:56:36 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Help,Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy,AD_Field_Trl_UU ) SELECT l.AD_Language,t.AD_Field_ID, t.Help,t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy,Generate_UUID() FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=202340 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- Jul 23, 2013 7:57:03 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +UPDATE AD_Field SET SeqNo=270, ColumnSpan=2,Updated=TO_DATE('2013-07-23 07:57:03','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202340 +; + +SELECT register_migration_script('201308140925_IDEMPIERE-1188.sql') FROM dual +; + diff --git a/migration/i1.0c-release/postgresql/201308140925_IDEMPIERE-1188.sql b/migration/i1.0c-release/postgresql/201308140925_IDEMPIERE-1188.sql new file mode 100644 index 0000000000..3ae3dc7902 --- /dev/null +++ b/migration/i1.0c-release/postgresql/201308140925_IDEMPIERE-1188.sql @@ -0,0 +1,57 @@ +-- Jul 22, 2013 8:56:39 PM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Column (SeqNoSelection,IsSyncDatabase,Version,AD_Table_ID,AD_Column_ID,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,AD_Reference_ID,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,Updated,CreatedBy,AD_Org_ID,IsActive,Created,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,EntityType,IsEncrypted,AD_Element_ID) VALUES (0,'N',1,808,210662,'N','N','N',0,'N',22,'N',30,'N','N','Y','c5983338-dac3-4173-ac05-81bc1bdb1ce7','N','M_MatchInv_ID','Match Shipment/Receipt to Invoice','Match Invoice','N',TO_TIMESTAMP('2013-07-22 20:56:37','YYYY-MM-DD HH24:MI:SS'),100,0,'Y',TO_TIMESTAMP('2013-07-22 20:56:37','YYYY-MM-DD HH24:MI:SS'),100,'N','N',0,'D','N',1689) +; + +-- Jul 22, 2013 8:56:39 PM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy,AD_Column_Trl_UU ) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy,Generate_UUID() FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=210662 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- Jul 22, 2013 8:56:45 PM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +ALTER TABLE M_CostDetail ADD COLUMN M_MatchInv_ID NUMERIC(10) DEFAULT NULL +; + +DROP VIEW m_costmovement_v; + +CREATE VIEW m_costmovement_v AS +SELECT a.ad_client_id, a.ad_org_id, b.c_acctschema_id, a.m_costhistory_id, a.m_costtype_id, a.m_costelement_id, a.m_attributesetinstance_id, + b.m_product_id, a.oldqty, a.newqty, a.oldcostprice, a.newcostprice, a.oldcqty, a.newcqty, a.oldcamt, a.newcamt, b.qty, b.amt, b.deltaqty, + b.deltaamt, b.c_orderline_id, b.m_inoutline_id, b.c_invoiceline_id, b.m_movementline_id, b.m_inventoryline_id, b.m_productionline_id, + b.c_projectissue_id, b.m_matchinv_id, a.m_costdetail_id, b.description, a.created, a.createdby, a.updated, a.updatedby, a.isactive + FROM m_costhistory a + JOIN m_costdetail b ON a.m_costdetail_id = b.m_costdetail_id + ORDER BY a.m_costhistory_id +; + + + +-- Jul 23, 2013 7:55:31 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Column (SeqNoSelection,IsSyncDatabase,Version,AD_Table_ID,AD_Column_ID,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,AD_Reference_ID,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,Updated,CreatedBy,AD_Org_ID,IsActive,Created,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,EntityType,IsEncrypted,AD_Element_ID) VALUES (0,'N',1,200002,210663,'N','N','N',0,'N',22,'N',30,'N','N','Y','10f7d192-2c18-497c-bcf3-548f8546fd41','N','M_MatchInv_ID','Match Shipment/Receipt to Invoice','Match Invoice','N',TO_TIMESTAMP('2013-07-23 07:55:29','YYYY-MM-DD HH24:MI:SS'),100,0,'Y',TO_TIMESTAMP('2013-07-23 07:55:29','YYYY-MM-DD HH24:MI:SS'),100,'N','N',0,'D','N',1689) +; + +-- Jul 23, 2013 7:55:31 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy,AD_Column_Trl_UU ) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy,Generate_UUID() FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=210663 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- Jul 23, 2013 7:56:36 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Field (NumLines,SortNo,IsEncrypted,AD_Tab_ID,DisplayLength,IsSameLine,IsHeading,AD_Column_ID,SeqNo,IsCentrallyMaintained,AD_Field_ID,IsReadOnly,EntityType,Description,Name,IsDisplayed,IsFieldOnly,AD_Field_UU,UpdatedBy,AD_Org_ID,Created,CreatedBy,Updated,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,ColumnSpan) VALUES (1,0,'N',200000,0,'N','N',210663,270,'Y',202340,'N','D','Match Shipment/Receipt to Invoice','Match Invoice','Y','N','624749f1-6544-45d7-a6b7-cebc6bcdfc93',100,0,TO_TIMESTAMP('2013-07-23 07:56:34','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2013-07-23 07:56:34','YYYY-MM-DD HH24:MI:SS'),'Y','Y',270,1,'N',0,1) +; + +-- Jul 23, 2013 7:56:36 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Help,Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy,AD_Field_Trl_UU ) SELECT l.AD_Language,t.AD_Field_ID, t.Help,t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy,Generate_UUID() FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=202340 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- Jul 23, 2013 7:57:03 AM MYT +-- IDEMPIERE-1188 Invoice Price Variance for Average PO Costing +UPDATE AD_Field SET SeqNo=270, ColumnSpan=2,Updated=TO_TIMESTAMP('2013-07-23 07:57:03','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202340 +; + +SELECT register_migration_script('201308140925_IDEMPIERE-1188.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java b/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java index ef47927faa..f1bd5733ef 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java @@ -18,9 +18,12 @@ package org.compiere.acct; import java.math.BigDecimal; import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Savepoint; import java.util.ArrayList; import java.util.logging.Level; +import org.adempiere.exceptions.AverageCostingZeroQtyException; import org.compiere.model.MAccount; import org.compiere.model.MAcctSchema; import org.compiere.model.MAcctSchemaElement; @@ -32,7 +35,9 @@ import org.compiere.model.MInvoice; import org.compiere.model.MInvoiceLine; import org.compiere.model.MMatchInv; import org.compiere.model.ProductCost; +import org.compiere.model.X_M_Cost; import org.compiere.util.Env; +import org.compiere.util.Trx; /** * Post MatchInv Documents. @@ -272,20 +277,7 @@ public class Doc_MatchInv extends Doc // Invoice Price Variance difference BigDecimal ipv = cr.getAcctBalance().add(dr.getAcctBalance()).negate(); - if (ipv.signum() != 0) - { - FactLine pv = fact.createLine(null, - m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as), - as.getC_Currency_ID(), ipv); - pv.setC_Activity_ID(m_invoiceLine.getC_Activity_ID()); - pv.setC_Campaign_ID(m_invoiceLine.getC_Campaign_ID()); - pv.setC_Project_ID(m_invoiceLine.getC_Project_ID()); - pv.setC_ProjectPhase_ID(m_invoiceLine.getC_ProjectPhase_ID()); - pv.setC_ProjectTask_ID(m_invoiceLine.getC_ProjectTask_ID()); - pv.setC_UOM_ID(m_invoiceLine.getC_UOM_ID()); - pv.setUser1_ID(m_invoiceLine.getUser1_ID()); - pv.setUser2_ID(m_invoiceLine.getUser2_ID()); - } + processInvoicePriceVariance(as, fact, ipv); if (log.isLoggable(Level.FINE)) log.fine("IPV=" + ipv + "; Balance=" + fact.getSourceBalance()); String error = createMatchInvCostDetail(as); @@ -310,6 +302,90 @@ public class Doc_MatchInv extends Doc return facts; } // createFact + + /** + * @param as + * @param fact + * @param ipv + */ + protected void processInvoicePriceVariance(MAcctSchema as, Fact fact, + BigDecimal ipv) { + if (ipv.signum() == 0) return; + + FactLine pv = fact.createLine(null, + m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as), + as.getC_Currency_ID(), ipv); + pv.setC_Activity_ID(m_invoiceLine.getC_Activity_ID()); + pv.setC_Campaign_ID(m_invoiceLine.getC_Campaign_ID()); + pv.setC_Project_ID(m_invoiceLine.getC_Project_ID()); + pv.setC_ProjectPhase_ID(m_invoiceLine.getC_ProjectPhase_ID()); + pv.setC_ProjectTask_ID(m_invoiceLine.getC_ProjectTask_ID()); + pv.setC_UOM_ID(m_invoiceLine.getC_UOM_ID()); + pv.setUser1_ID(m_invoiceLine.getUser1_ID()); + pv.setUser2_ID(m_invoiceLine.getUser2_ID()); + pv.setM_Product_ID(m_invoiceLine.getM_Product_ID()); + + MMatchInv matchInv = (MMatchInv)getPO(); + Trx trx = Trx.get(getTrxName(), false); + Savepoint savepoint = null; + boolean zeroQty = false; + try { + savepoint = trx.setSavepoint(null); + + if (!MCostDetail.createMatchInvoice(as, m_invoiceLine.getAD_Org_ID(), + m_invoiceLine.getM_Product_ID(), m_invoiceLine.getM_AttributeSetInstance_ID(), + matchInv.getM_MatchInv_ID(), 0, + ipv, BigDecimal.ZERO, "Invoice Price Variance", getTrxName())) { + throw new RuntimeException("Failed to create cost detail record."); + } + } catch (SQLException e) { + throw new RuntimeException(e.getLocalizedMessage(), e); + } catch (AverageCostingZeroQtyException e) { + zeroQty = true; + try { + trx.rollback(savepoint); + savepoint = null; + } catch (SQLException e1) { + throw new RuntimeException(e1.getLocalizedMessage(), e1); + } + } finally { + if (savepoint != null) { + try { + trx.releaseSavepoint(savepoint); + } catch (SQLException e) {} + } + } + + String costingMethod = m_pc.getProduct().getCostingMethod(as); + if (X_M_Cost.COSTINGMETHOD_AveragePO.equals(costingMethod)) { + MAccount account = zeroQty ? m_pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, as) : m_pc.getAccount(ProductCost.ACCTTYPE_P_Asset, as); + + FactLine line = fact.createLine(null, + m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as), + as.getC_Currency_ID(), ipv.negate()); + line.setC_Activity_ID(m_invoiceLine.getC_Activity_ID()); + line.setC_Campaign_ID(m_invoiceLine.getC_Campaign_ID()); + line.setC_Project_ID(m_invoiceLine.getC_Project_ID()); + line.setC_ProjectPhase_ID(m_invoiceLine.getC_ProjectPhase_ID()); + line.setC_ProjectTask_ID(m_invoiceLine.getC_ProjectTask_ID()); + line.setC_UOM_ID(m_invoiceLine.getC_UOM_ID()); + line.setUser1_ID(m_invoiceLine.getUser1_ID()); + line.setUser2_ID(m_invoiceLine.getUser2_ID()); + line.setM_Product_ID(m_invoiceLine.getM_Product_ID()); + + line = fact.createLine(null, account, as.getC_Currency_ID(), ipv); + line.setC_Activity_ID(m_invoiceLine.getC_Activity_ID()); + line.setC_Campaign_ID(m_invoiceLine.getC_Campaign_ID()); + line.setC_Project_ID(m_invoiceLine.getC_Project_ID()); + line.setC_ProjectPhase_ID(m_invoiceLine.getC_ProjectPhase_ID()); + line.setC_ProjectTask_ID(m_invoiceLine.getC_ProjectTask_ID()); + line.setC_UOM_ID(m_invoiceLine.getC_UOM_ID()); + line.setUser1_ID(m_invoiceLine.getUser1_ID()); + line.setUser2_ID(m_invoiceLine.getUser2_ID()); + line.setM_Product_ID(m_invoiceLine.getM_Product_ID()); + } + } + /** Verify if the posting involves two or more organizations @return true if there are more than one org involved on the posting */ diff --git a/org.adempiere.base/src/org/compiere/model/I_M_CostDetail.java b/org.adempiere.base/src/org/compiere/model/I_M_CostDetail.java index 910dfcbab6..abc5ffa28a 100644 --- a/org.adempiere.base/src/org/compiere/model/I_M_CostDetail.java +++ b/org.adempiere.base/src/org/compiere/model/I_M_CostDetail.java @@ -350,6 +350,21 @@ public interface I_M_CostDetail public org.compiere.model.I_M_InventoryLine getM_InventoryLine() throws RuntimeException; + /** Column name M_MatchInv_ID */ + public static final String COLUMNNAME_M_MatchInv_ID = "M_MatchInv_ID"; + + /** Set Match Invoice. + * Match Shipment/Receipt to Invoice + */ + public void setM_MatchInv_ID (int M_MatchInv_ID); + + /** Get Match Invoice. + * Match Shipment/Receipt to Invoice + */ + public int getM_MatchInv_ID(); + + public org.compiere.model.I_M_MatchInv getM_MatchInv() throws RuntimeException; + /** Column name M_MovementLine_ID */ public static final String COLUMNNAME_M_MovementLine_ID = "M_MovementLine_ID"; diff --git a/org.adempiere.base/src/org/compiere/model/MCostDetail.java b/org.adempiere.base/src/org/compiere/model/MCostDetail.java index d87d1e79af..673d58b1e7 100644 --- a/org.adempiere.base/src/org/compiere/model/MCostDetail.java +++ b/org.adempiere.base/src/org/compiere/model/MCostDetail.java @@ -464,6 +464,69 @@ public class MCostDetail extends X_M_CostDetail return ok; } // createProduction + /** + * @param as + * @param AD_Org_ID + * @param M_Product_ID + * @param M_AttributeSetInstance_ID + * @param M_MatchInv_ID + * @param M_CostElement_ID + * @param Amt + * @param Qty + * @param Description + * @param trxName + * @return true if no error + */ + public static boolean createMatchInvoice (MAcctSchema as, int AD_Org_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + int M_MatchInv_ID, int M_CostElement_ID, + BigDecimal Amt, BigDecimal Qty, + String Description, String trxName) + { + // Delete Unprocessed zero Differences + StringBuilder sql = new StringBuilder("DELETE M_CostDetail ") + .append("WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0") + .append(" AND M_MatchInv_ID=").append(M_MatchInv_ID) + .append(" AND C_AcctSchema_ID =").append(as.getC_AcctSchema_ID()) + .append(" AND M_AttributeSetInstance_ID=").append(M_AttributeSetInstance_ID) + .append(" AND Coalesce(M_CostElement_ID,0)=").append(M_CostElement_ID); + + int no = DB.executeUpdate(sql.toString(), trxName); + if (no != 0) + if (s_log.isLoggable(Level.CONFIG)) s_log.config("Deleted #" + no); + MCostDetail cd = get (as.getCtx(), "M_MatchInv_ID=? AND Coalesce(M_CostElement_ID,0)="+M_CostElement_ID, + M_MatchInv_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); + // + if (cd == null) // createNew + { + cd = new MCostDetail (as, AD_Org_ID, + M_Product_ID, M_AttributeSetInstance_ID, + M_CostElement_ID, + Amt, Qty, Description, trxName); + cd.setM_MatchInv_ID(M_MatchInv_ID); + } + else + { + cd.setDeltaAmt(Amt.subtract(cd.getAmt())); + cd.setDeltaQty(Qty.subtract(cd.getQty())); + if (cd.isDelta()) + { + cd.setProcessed(false); + cd.setAmt(Amt); + cd.setQty(Qty); + } + else + return true; // nothing to do + } + boolean ok = cd.save(); + if (ok && !cd.isProcessed()) + { + ok = cd.process(); + } + if (s_log.isLoggable(Level.CONFIG)) s_log.config("(" + ok + ") " + cd); + return ok; + } // createMatchInvoice + /************************************************************************** * Get Cost Detail * @param ctx context @@ -1184,6 +1247,13 @@ public class MCostDetail extends X_M_CostDetail log.warning("QtyAdjust - " + ce + " - " + cost); } + else if (getM_MatchInv_ID() > 0) + { + if (ce.isAveragePO()) + { + cost.setWeightedAverage(amt, qty); + } + } else // unknown or no id { log.warning("Unknown Type: " + toString()); diff --git a/org.adempiere.base/src/org/compiere/model/X_M_CostDetail.java b/org.adempiere.base/src/org/compiere/model/X_M_CostDetail.java index 2358e6921e..0452400dd5 100644 --- a/org.adempiere.base/src/org/compiere/model/X_M_CostDetail.java +++ b/org.adempiere.base/src/org/compiere/model/X_M_CostDetail.java @@ -31,7 +31,7 @@ public class X_M_CostDetail extends PO implements I_M_CostDetail, I_Persistent /** * */ - private static final long serialVersionUID = 20130626L; + private static final long serialVersionUID = 20130722L; /** Standard Constructor */ public X_M_CostDetail (Properties ctx, int M_CostDetail_ID, String trxName) @@ -520,6 +520,34 @@ public class X_M_CostDetail extends PO implements I_M_CostDetail, I_Persistent return ii.intValue(); } + public org.compiere.model.I_M_MatchInv getM_MatchInv() throws RuntimeException + { + return (org.compiere.model.I_M_MatchInv)MTable.get(getCtx(), org.compiere.model.I_M_MatchInv.Table_Name) + .getPO(getM_MatchInv_ID(), get_TrxName()); } + + /** Set Match Invoice. + @param M_MatchInv_ID + Match Shipment/Receipt to Invoice + */ + public void setM_MatchInv_ID (int M_MatchInv_ID) + { + if (M_MatchInv_ID < 1) + set_ValueNoCheck (COLUMNNAME_M_MatchInv_ID, null); + else + set_ValueNoCheck (COLUMNNAME_M_MatchInv_ID, Integer.valueOf(M_MatchInv_ID)); + } + + /** Get Match Invoice. + @return Match Shipment/Receipt to Invoice + */ + public int getM_MatchInv_ID () + { + Integer ii = (Integer)get_Value(COLUMNNAME_M_MatchInv_ID); + if (ii == null) + return 0; + return ii.intValue(); + } + public org.compiere.model.I_M_MovementLine getM_MovementLine() throws RuntimeException { return (org.compiere.model.I_M_MovementLine)MTable.get(getCtx(), org.compiere.model.I_M_MovementLine.Table_Name)