From 236729e3f5daa7b568a0a9880ae1e43f83b1c156 Mon Sep 17 00:00:00 2001 From: Elaine Tan <51374241+etantg@users.noreply.github.com> Date: Thu, 10 Sep 2020 20:49:25 +0800 Subject: [PATCH] =?UTF-8?q?IDEMPIERE-4173=20Wrong=20GL=20postings=20for=20?= =?UTF-8?q?matched=20invoice=20in=20RMA/Vendor=20Re=E2=80=A6=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * IDEMPIERE-4173 Wrong GL postings for matched invoice in RMA/Vendor Return/Credit Memo * IDEMPIERE-4173 Wrong GL postings for matched invoice in RMA/Vendor Return/Credit Memo - Added test case --- .../src/org/compiere/acct/Doc_MatchInv.java | 213 ++++++++++- .../org/idempiere/test/base/MatchInvTest.java | 342 ++++++++++++++++++ 2 files changed, 554 insertions(+), 1 deletion(-) create mode 100644 org.idempiere.test/src/org/idempiere/test/base/MatchInvTest.java 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 6816b26021..1c233bca0d 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java @@ -161,7 +161,10 @@ public class Doc_MatchInv extends Doc else return createCreditMemoFacts(as); } - + + if (m_receiptLine.getParent().getC_DocType().getDocBaseType().equals(DOCTYPE_MatShipment)) + return createMatShipmentFacts(as); + // create Fact Header Fact fact = new Fact(this, as, Fact.POST_Actual); setC_Currency_ID (as.getC_Currency_ID()); @@ -582,6 +585,214 @@ public class Doc_MatchInv extends Doc return ""; } + private ArrayList createMatShipmentFacts(MAcctSchema as) { + ArrayList facts = new ArrayList(); + + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + setC_Currency_ID (as.getC_Currency_ID()); + boolean isInterOrg = isInterOrg(as); + + // NotInvoicedReceipt DR + // From Receipt + BigDecimal multiplier = getQty() + .divide(m_receiptLine.getMovementQty(), 12, RoundingMode.HALF_UP); + multiplier = multiplier.negate(); + FactLine dr = fact.createLine (null, + getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as), + as.getC_Currency_ID(), null, Env.ONE); // updated below + if (dr == null) + { + p_Error = "No Product Costs"; + return null; + } + dr.setQty(getQty()); + BigDecimal temp = dr.getAcctBalance(); + // Set AmtAcctCr/Dr from Receipt (sets also Project) + if (m_matchInv.getReversal_ID() > 0) + { + if (!dr.updateReverseLine (MMatchInv.Table_ID, // Amt updated + m_matchInv.getReversal_ID(), 0, BigDecimal.ONE)) + { + p_Error = "Failed to create reversal entry"; + return null; + } + } + else + { + if (!dr.updateReverseLine (MInOut.Table_ID, // Amt updated + m_receiptLine.getM_InOut_ID(), m_receiptLine.getM_InOutLine_ID(), + multiplier)) + { + p_Error = "Mat.Shipment not posted yet"; + return null; + } + } + if (log.isLoggable(Level.FINE)) log.fine("CR - Amt(" + temp + "->" + dr.getAcctBalance() + + ") - " + dr.toString()); + + // InventoryClearing CR + // From Invoice + MAccount expense = m_pc.getAccount(ProductCost.ACCTTYPE_P_InventoryClearing, as); + if (m_pc.isService()) + expense = m_pc.getAccount(ProductCost.ACCTTYPE_P_Expense, as); + BigDecimal LineNetAmt = m_invoiceLine.getLineNetAmt(); + multiplier = getQty() + .divide(m_invoiceLine.getQtyInvoiced(), 12, RoundingMode.HALF_UP); + multiplier = multiplier.negate(); + if (multiplier.compareTo(Env.ONE) != 0) + LineNetAmt = LineNetAmt.multiply(multiplier); + if (m_pc.isService()) + LineNetAmt = dr.getAcctBalance(); // book out exact receipt amt + FactLine cr = null; + if (as.isAccrual()) + { + cr = fact.createLine (null, expense, + as.getC_Currency_ID(), LineNetAmt, null); // updated below + if (cr == null) + { + if (log.isLoggable(Level.FINE)) log.fine("Line Net Amt=0 - M_Product_ID=" + getM_Product_ID() + + ",Qty=" + getQty() + ",InOutQty=" + m_receiptLine.getMovementQty()); + + cr = fact.createLine (null, expense, as.getC_Currency_ID(), Env.ONE, null); + cr.setAmtAcctCr(BigDecimal.ZERO); + cr.setAmtSourceCr(BigDecimal.ZERO); + } + temp = cr.getAcctBalance(); + if (m_matchInv.getReversal_ID() > 0) + { + if (!cr.updateReverseLine (MMatchInv.Table_ID, // Amt updated + m_matchInv.getReversal_ID(), 0, BigDecimal.ONE)) + { + p_Error = "Failed to create reversal entry"; + return null; + } + } + else + { + cr.setQty(getQty().negate()); + + // Set AmtAcctCr/Dr from Invoice (sets also Project) + if (!cr.updateReverseLine (MInvoice.Table_ID, // Amt updated + m_invoiceLine.getC_Invoice_ID(), m_invoiceLine.getC_InvoiceLine_ID(), multiplier)) + { + p_Error = "Invoice not posted yet"; + return null; + } + } + if (log.isLoggable(Level.FINE)) log.fine("DR - Amt(" + temp + "->" + cr.getAcctBalance() + + ") - " + cr.toString()); + } + else // Cash Acct + { + MInvoice invoice = m_invoiceLine.getParent(); + if (as.getC_Currency_ID() != invoice.getC_Currency_ID()) + LineNetAmt = MConversionRate.convert(getCtx(), LineNetAmt, + invoice.getC_Currency_ID(), as.getC_Currency_ID(), + invoice.getDateAcct(), invoice.getC_ConversionType_ID(), + invoice.getAD_Client_ID(), invoice.getAD_Org_ID()); + cr = fact.createLine (null, expense, + as.getC_Currency_ID(), LineNetAmt, null); + if (m_matchInv.getReversal_ID() > 0) + { + if (!cr.updateReverseLine (MMatchInv.Table_ID, // Amt updated + m_matchInv.getReversal_ID(), 0, BigDecimal.ONE)) + { + p_Error = "Failed to create reversal entry"; + return null; + } + } + else + { + cr.setQty(getQty().multiply(multiplier).negate()); + } + } + + // Rounding correction + if (m_receiptLine != null && m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency + { + p_Error = createReceiptGainLoss(as, fact, getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as), m_receiptLine.getParent(), dr.getAmtSourceCr(), dr.getAmtAcctCr()); + if (p_Error != null) + return null; + } + if (m_invoiceLine != null && m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency + { + p_Error = createInvoiceGainLoss(as, fact, expense, m_invoiceLine.getParent(), cr.getAmtSourceDr(), cr.getAmtAcctDr()); + if (p_Error != null) + return null; + } + + if (m_matchInv.getReversal_ID() == 0) + { + cr.setC_Activity_ID(m_invoiceLine.getC_Activity_ID()); + cr.setC_Campaign_ID(m_invoiceLine.getC_Campaign_ID()); + cr.setC_Project_ID(m_invoiceLine.getC_Project_ID()); + cr.setC_ProjectPhase_ID(m_invoiceLine.getC_ProjectPhase_ID()); + cr.setC_ProjectTask_ID(m_invoiceLine.getC_ProjectTask_ID()); + cr.setC_UOM_ID(m_invoiceLine.getC_UOM_ID()); + cr.setUser1_ID(m_invoiceLine.getUser1_ID()); + cr.setUser2_ID(m_invoiceLine.getUser2_ID()); + } + else + { + updateFactLine(cr); + } + + //AZ Goodwill + //Desc: Source Not Balanced problem because Currency is Difference - PO=CNY but AP=USD + //see also Fact.java: checking for isMultiCurrency() + if (dr.getC_Currency_ID() != cr.getC_Currency_ID()) + setIsMultiCurrency(true); + //end AZ + + // Avoid usage of clearing accounts + // If both accounts Not Invoiced Receipts and Inventory Clearing are equal + // then remove the posting + + MAccount acct_db = dr.getAccount(); // not_invoiced_receipts + MAccount acct_cr = cr.getAccount(); // inventory_clearing + + if ((!as.isPostIfClearingEqual()) && acct_db.equals(acct_cr) && (!isInterOrg)) { + + BigDecimal debit = dr.getAmtSourceDr(); + BigDecimal credit = cr.getAmtSourceCr(); + + if (debit.compareTo(credit) == 0) { + fact.remove(dr); + fact.remove(cr); + } + + } + // End Avoid usage of clearing accounts + + + // Invoice Price Variance difference + BigDecimal ipv = cr.getAcctBalance().add(dr.getAcctBalance()).negate(); + processInvoicePriceVariance(as, fact, ipv); + if (log.isLoggable(Level.FINE)) log.fine("IPV=" + ipv + "; Balance=" + fact.getSourceBalance()); + + String error = createMatchInvCostDetail(as); + if (error != null && error.trim().length() > 0) + { + p_Error = error; + return null; + } + // + facts.add(fact); + + /** Commitment release ****/ + if (as.isAccrual() && as.isCreatePOCommitment()) + { + fact = Doc_Order.getCommitmentRelease(as, this, + getQty(), m_invoiceLine.getC_InvoiceLine_ID(), Env.ONE); + if (fact == null) + return null; + facts.add(fact); + } // Commitment + + return facts; + } + public ArrayList createCreditMemoFacts(MAcctSchema as) { ArrayList facts = new ArrayList(); diff --git a/org.idempiere.test/src/org/idempiere/test/base/MatchInvTest.java b/org.idempiere.test/src/org/idempiere/test/base/MatchInvTest.java new file mode 100644 index 0000000000..c86f71da78 --- /dev/null +++ b/org.idempiere.test/src/org/idempiere/test/base/MatchInvTest.java @@ -0,0 +1,342 @@ +/*********************************************************************** + * 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: * + * - Elaine Tan - etantg * + **********************************************************************/ +package org.idempiere.test.base; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.math.BigDecimal; + +import org.compiere.acct.Doc; +import org.compiere.acct.DocManager; +import org.compiere.model.MAccount; +import org.compiere.model.MAcctSchema; +import org.compiere.model.MBPartner; +import org.compiere.model.MClientInfo; +import org.compiere.model.MDocType; +import org.compiere.model.MFactAcct; +import org.compiere.model.MInOut; +import org.compiere.model.MInOutLine; +import org.compiere.model.MInvoice; +import org.compiere.model.MInvoiceLine; +import org.compiere.model.MMatchInv; +import org.compiere.model.MOrder; +import org.compiere.model.MOrderLine; +import org.compiere.model.MProduct; +import org.compiere.model.MRMA; +import org.compiere.model.MRMALine; +import org.compiere.model.MWarehouse; +import org.compiere.model.ProductCost; +import org.compiere.process.DocAction; +import org.compiere.process.DocumentEngine; +import org.compiere.process.ProcessInfo; +import org.compiere.util.Env; +import org.compiere.wf.MWorkflow; +import org.idempiere.test.AbstractTestCase; +import org.junit.jupiter.api.Test; + +/** + * @author Elaine Tan - etantg + */ +public class MatchInvTest extends AbstractTestCase { + + public MatchInvTest() { + } + + @Test + /** + * https://idempiere.atlassian.net/browse/IDEMPIERE-4173 + */ + public void testMatShipmentPosting() { + MBPartner bpartner = MBPartner.get(Env.getCtx(), 114); // Tree Farm Inc. + MProduct product = MProduct.get(Env.getCtx(), 124); // Elm Tree + + MOrder order = new MOrder(Env.getCtx(), 0, getTrxName()); + order.setBPartner(bpartner); + order.setIsSOTrx(false); + order.setC_DocTypeTarget_ID(); + order.setDocStatus(DocAction.STATUS_Drafted); + order.setDocAction(DocAction.ACTION_Complete); + order.saveEx(); + + MOrderLine orderLine = new MOrderLine(order); + orderLine.setLine(10); + orderLine.setProduct(product); + orderLine.setQty(BigDecimal.ONE); + orderLine.saveEx(); + + ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete); + order.load(getTrxName()); + assertFalse(info.isError()); + assertEquals(DocAction.STATUS_Completed, order.getDocStatus()); + + MInOut receipt = new MInOut(order, 122, order.getDateOrdered()); // MM Receipt + receipt.saveEx(); + + MInOutLine receiptLine = new MInOutLine(receipt); + receiptLine.setC_OrderLine_ID(orderLine.get_ID()); + receiptLine.setLine(10); + receiptLine.setProduct(product); + receiptLine.setQty(BigDecimal.ONE); + MWarehouse wh = MWarehouse.get(Env.getCtx(), receipt.getM_Warehouse_ID()); + int M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); + receiptLine.setM_Locator_ID(M_Locator_ID); + receiptLine.saveEx(); + + info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Complete); + receipt.load(getTrxName()); + assertFalse(info.isError()); + assertEquals(DocAction.STATUS_Completed, receipt.getDocStatus()); + + if (!receipt.isPosted()) { + String error = DocumentEngine.postImmediate(Env.getCtx(), receipt.getAD_Client_ID(), MInOut.Table_ID, receipt.get_ID(), false, getTrxName()); + assertTrue(error == null); + } + receipt.load(getTrxName()); + assertTrue(receipt.isPosted()); + + MRMA rma = new MRMA(Env.getCtx(), 0, getTrxName()); + rma.setName(order.getDocumentNo()); + rma.setC_DocType_ID(150); // Vendor Return Material + rma.setM_RMAType_ID(100); // Damaged on Arrival + rma.setM_InOut_ID(receipt.get_ID()); + rma.setIsSOTrx(false); + rma.setSalesRep_ID(100); // SuperUser + rma.saveEx(); + + MRMALine rmaLine = new MRMALine(Env.getCtx(), 0, getTrxName()); + rmaLine.setLine(10); + rmaLine.setM_RMA_ID(rma.get_ID()); + rmaLine.setM_InOutLine_ID(receiptLine.get_ID()); + rmaLine.setQty(BigDecimal.ONE); + rmaLine.saveEx(); + + info = MWorkflow.runDocumentActionWorkflow(rma, DocAction.ACTION_Complete); + rma.load(getTrxName()); + assertFalse(info.isError()); + assertEquals(DocAction.STATUS_Completed, rma.getDocStatus()); + + MInOut delivery = new MInOut(Env.getCtx(), 0, getTrxName()); + delivery.setM_RMA_ID(rma.get_ID()); + delivery.setBPartner(bpartner); + delivery.setIsSOTrx(false); + delivery.setMovementType(MInOut.MOVEMENTTYPE_VendorReturns); + delivery.setC_DocType_ID(151); // MM Vendor Return + delivery.setDocStatus(DocAction.STATUS_Drafted); + delivery.setDocAction(DocAction.ACTION_Complete); + delivery.setM_Warehouse_ID(receipt.getM_Warehouse_ID()); + delivery.saveEx(); + + MInOutLine deliveryLine = new MInOutLine(delivery); + deliveryLine.setM_RMALine_ID(rmaLine.get_ID()); + deliveryLine.setLine(10); + deliveryLine.setProduct(product); + deliveryLine.setQty(BigDecimal.ONE); + deliveryLine.setM_Locator_ID(receiptLine.getM_Locator_ID()); + deliveryLine.saveEx(); + + info = MWorkflow.runDocumentActionWorkflow(delivery, DocAction.ACTION_Complete); + delivery.load(getTrxName()); + assertFalse(info.isError()); + assertEquals(DocAction.STATUS_Completed, delivery.getDocStatus()); + + if (!delivery.isPosted()) { + String error = DocumentEngine.postImmediate(Env.getCtx(), delivery.getAD_Client_ID(), MInOut.Table_ID, delivery.get_ID(), false, getTrxName()); + assertTrue(error == null); + } + delivery.load(getTrxName()); + assertTrue(delivery.isPosted()); + + MInvoice creditMemo = new MInvoice(delivery, delivery.getMovementDate()); + creditMemo.setC_DocTypeTarget_ID(MDocType.DOCBASETYPE_APCreditMemo); + creditMemo.setDocStatus(DocAction.STATUS_Drafted); + creditMemo.setDocAction(DocAction.ACTION_Complete); + creditMemo.saveEx(); + + MInvoiceLine creditMemoLine = new MInvoiceLine(creditMemo); + creditMemoLine.setM_InOutLine_ID(deliveryLine.get_ID()); + creditMemoLine.setLine(10); + creditMemoLine.setProduct(product); + creditMemoLine.setQty(BigDecimal.ONE); + creditMemoLine.saveEx(); + + info = MWorkflow.runDocumentActionWorkflow(creditMemo, DocAction.ACTION_Complete); + creditMemo.load(getTrxName()); + assertFalse(info.isError()); + assertEquals(DocAction.STATUS_Completed, creditMemo.getDocStatus()); + + if (!creditMemo.isPosted()) { + String error = DocumentEngine.postImmediate(Env.getCtx(), creditMemo.getAD_Client_ID(), MInvoice.Table_ID, creditMemo.get_ID(), false, getTrxName()); + assertTrue(error == null); + } + creditMemo.load(getTrxName()); + assertTrue(creditMemo.isPosted()); + + int C_AcctSchema_ID = MClientInfo.get(Env.getCtx()).getC_AcctSchema1_ID(); + MAcctSchema as = MAcctSchema.get(Env.getCtx(), C_AcctSchema_ID); + MMatchInv[] miList = MMatchInv.getInvoiceLine(Env.getCtx(), creditMemoLine.get_ID(), getTrxName()); + for (MMatchInv mi : miList) { + if (!mi.isPosted()) { + String error = DocumentEngine.postImmediate(Env.getCtx(), mi.getAD_Client_ID(), MMatchInv.Table_ID, mi.get_ID(), false, getTrxName()); + assertTrue(error == null); + } + mi.load(getTrxName()); + assertTrue(mi.isPosted()); + + Doc doc = DocManager.getDocument(as, MMatchInv.Table_ID, mi.get_ID(), getTrxName()); + doc.setC_BPartner_ID(mi.getC_InvoiceLine().getC_Invoice().getC_BPartner_ID()); + MAccount acctNIR = doc.getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as); + + ProductCost pc = new ProductCost (Env.getCtx(), mi.getM_Product_ID(), mi.getM_AttributeSetInstance_ID(), getTrxName()); + MAccount acctInvClr = pc.getAccount(ProductCost.ACCTTYPE_P_InventoryClearing, as); + + String whereClause = MFactAcct.COLUMNNAME_AD_Table_ID + "=" + MMatchInv.Table_ID + + " AND " + MFactAcct.COLUMNNAME_Record_ID + "=" + mi.get_ID() + + " AND " + MFactAcct.COLUMNNAME_C_AcctSchema_ID + "=" + C_AcctSchema_ID; + int[] ids = MFactAcct.getAllIDs(MFactAcct.Table_Name, whereClause, getTrxName()); + for (int id : ids) { + MFactAcct fa = new MFactAcct(Env.getCtx(), id, getTrxName()); + if (fa.getAccount_ID() == acctNIR.getAccount_ID()) + assertTrue(fa.getAmtAcctCr().compareTo(Env.ZERO) >= 0); + else if (fa.getAccount_ID() == acctInvClr.getAccount_ID()) + assertTrue(fa.getAmtAcctDr().compareTo(Env.ZERO) >= 0); + } + } + + rollback(); + } + + @Test + /** + * https://idempiere.atlassian.net/browse/IDEMPIERE-4173 + */ + public void testMatReceiptPosting() { + MBPartner bpartner = MBPartner.get(Env.getCtx(), 114); // Tree Farm Inc. + MProduct product = MProduct.get(Env.getCtx(), 124); // Elm Tree + + MOrder order = new MOrder(Env.getCtx(), 0, getTrxName()); + order.setBPartner(bpartner); + order.setIsSOTrx(false); + order.setC_DocTypeTarget_ID(); + order.setDocStatus(DocAction.STATUS_Drafted); + order.setDocAction(DocAction.ACTION_Complete); + order.saveEx(); + + MOrderLine orderLine = new MOrderLine(order); + orderLine.setLine(10); + orderLine.setProduct(product); + orderLine.setQty(BigDecimal.ONE); + orderLine.saveEx(); + + ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete); + order.load(getTrxName()); + assertFalse(info.isError()); + assertEquals(DocAction.STATUS_Completed, order.getDocStatus()); + + MInOut receipt = new MInOut(order, 122, order.getDateOrdered()); // MM Receipt + receipt.saveEx(); + + MInOutLine receiptLine = new MInOutLine(receipt); + receiptLine.setC_OrderLine_ID(orderLine.get_ID()); + receiptLine.setLine(10); + receiptLine.setProduct(product); + receiptLine.setQty(BigDecimal.ONE); + MWarehouse wh = MWarehouse.get(Env.getCtx(), receipt.getM_Warehouse_ID()); + int M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); + receiptLine.setM_Locator_ID(M_Locator_ID); + receiptLine.saveEx(); + + info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Complete); + receipt.load(getTrxName()); + assertFalse(info.isError()); + assertEquals(DocAction.STATUS_Completed, receipt.getDocStatus()); + + if (!receipt.isPosted()) { + String error = DocumentEngine.postImmediate(Env.getCtx(), receipt.getAD_Client_ID(), MInOut.Table_ID, receipt.get_ID(), false, getTrxName()); + assertTrue(error == null); + } + receipt.load(getTrxName()); + assertTrue(receipt.isPosted()); + + MInvoice invoice = new MInvoice(receipt, receipt.getMovementDate()); + invoice.setC_DocTypeTarget_ID(MDocType.DOCBASETYPE_APInvoice); + invoice.setDocStatus(DocAction.STATUS_Drafted); + invoice.setDocAction(DocAction.ACTION_Complete); + invoice.saveEx(); + + MInvoiceLine invoiceLine = new MInvoiceLine(invoice); + invoiceLine.setM_InOutLine_ID(receiptLine.get_ID()); + invoiceLine.setLine(10); + invoiceLine.setProduct(product); + invoiceLine.setQty(BigDecimal.ONE); + invoiceLine.saveEx(); + + info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Complete); + invoice.load(getTrxName()); + assertFalse(info.isError()); + assertEquals(DocAction.STATUS_Completed, invoice.getDocStatus()); + + if (!invoice.isPosted()) { + String error = DocumentEngine.postImmediate(Env.getCtx(), invoice.getAD_Client_ID(), MInvoice.Table_ID, invoice.get_ID(), false, getTrxName()); + assertTrue(error == null); + } + invoice.load(getTrxName()); + assertTrue(invoice.isPosted()); + + int C_AcctSchema_ID = MClientInfo.get(Env.getCtx()).getC_AcctSchema1_ID(); + MAcctSchema as = MAcctSchema.get(Env.getCtx(), C_AcctSchema_ID); + MMatchInv[] miList = MMatchInv.getInvoiceLine(Env.getCtx(), invoiceLine.get_ID(), getTrxName()); + for (MMatchInv mi : miList) { + if (!mi.isPosted()) { + String error = DocumentEngine.postImmediate(Env.getCtx(), mi.getAD_Client_ID(), MMatchInv.Table_ID, mi.get_ID(), false, getTrxName()); + assertTrue(error == null); + } + mi.load(getTrxName()); + assertTrue(mi.isPosted()); + + Doc doc = DocManager.getDocument(as, MMatchInv.Table_ID, mi.get_ID(), getTrxName()); + doc.setC_BPartner_ID(mi.getC_InvoiceLine().getC_Invoice().getC_BPartner_ID()); + MAccount acctNIR = doc.getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as); + + ProductCost pc = new ProductCost (Env.getCtx(), mi.getM_Product_ID(), mi.getM_AttributeSetInstance_ID(), getTrxName()); + MAccount acctInvClr = pc.getAccount(ProductCost.ACCTTYPE_P_InventoryClearing, as); + + String whereClause = MFactAcct.COLUMNNAME_AD_Table_ID + "=" + MMatchInv.Table_ID + + " AND " + MFactAcct.COLUMNNAME_Record_ID + "=" + mi.get_ID() + + " AND " + MFactAcct.COLUMNNAME_C_AcctSchema_ID + "=" + C_AcctSchema_ID; + int[] ids = MFactAcct.getAllIDs(MFactAcct.Table_Name, whereClause, getTrxName()); + for (int id : ids) { + MFactAcct fa = new MFactAcct(Env.getCtx(), id, getTrxName()); + if (fa.getAccount_ID() == acctNIR.getAccount_ID()) + assertTrue(fa.getAmtAcctDr().compareTo(Env.ZERO) >= 0); + else if (fa.getAccount_ID() == acctInvClr.getAccount_ID()) + assertTrue(fa.getAmtAcctCr().compareTo(Env.ZERO) >= 0); + } + } + + rollback(); + } +}