IDEMPIERE-5173 fix Accounting fact quantity incorrect (#1150)

* IDEMPIERE-5173 fix Accounting fact quantity incorrect
- also add unit tests

* IDEMPIERE-5173 remove c_uom_id check

* IDEMPIERE-5173 Use MMatchInv.isReversal() in preference to MMatch.getReversal_ID() > 0

* IDEMPIERE-5173 Update isReversal() method 
- add new test for is Reversal() when reversing a credit memo
This commit is contained in:
Tony Snook 2022-02-05 18:47:52 +11:00 committed by GitHub
parent 497c93fc63
commit 78b7dc32d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 319 additions and 61 deletions

View File

@ -44,6 +44,7 @@ import org.compiere.model.MInvoice;
import org.compiere.model.MInvoiceLine;
import org.compiere.model.MMatchInv;
import org.compiere.model.MOrderLandedCostAllocation;
import org.compiere.model.MUOM;
import org.compiere.model.ProductCost;
import org.compiere.model.Query;
import org.compiere.model.X_M_Cost;
@ -201,7 +202,7 @@ public class Doc_MatchInv extends Doc
dr.setQty(getQty());
BigDecimal temp = dr.getAcctBalance();
// Set AmtAcctCr/Dr from Receipt (sets also Project)
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (!dr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -250,7 +251,7 @@ public class Doc_MatchInv extends Doc
cr.setAmtSourceCr(BigDecimal.ZERO);
}
temp = cr.getAcctBalance();
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (!cr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -284,7 +285,7 @@ public class Doc_MatchInv extends Doc
invoice.getAD_Client_ID(), invoice.getAD_Org_ID());
cr = fact.createLine (null, expense,
as.getC_Currency_ID(), null, LineNetAmt);
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (!cr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -295,7 +296,8 @@ public class Doc_MatchInv extends Doc
}
else
{
cr.setQty(getQty().multiply(multiplier).negate());
int precision = MUOM.getPrecision(getCtx(), m_invoiceLine.getC_UOM_ID());
cr.setQty(getQty().multiply(multiplier).negate().setScale(precision, RoundingMode.HALF_UP));
}
}
@ -339,20 +341,17 @@ public class Doc_MatchInv extends Doc
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());
if (m_matchInv.isReversal())
{
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);
cr.setQty(getQty().negate());
}
//AZ Goodwill
@ -467,6 +466,7 @@ public class Doc_MatchInv extends Doc
m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as),
as.getC_Currency_ID(), ipv.negate());
updateFactLine(line);
line.setQty(getQty().negate());
line = fact.createLine(null, account, as.getC_Currency_ID(), ipv);
updateFactLine(line);
@ -475,6 +475,7 @@ public class Doc_MatchInv extends Doc
m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as),
as.getC_Currency_ID(), ipv.negate());
updateFactLine(line);
line.setQty(getQty().negate());
line = fact.createLine(null, account, as.getC_Currency_ID(), ipv);
updateFactLine(line);
@ -663,7 +664,7 @@ public class Doc_MatchInv extends Doc
dr.setQty(getQty());
BigDecimal temp = dr.getAcctBalance();
// Set AmtAcctCr/Dr from Receipt (sets also Project)
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (!dr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -713,7 +714,7 @@ public class Doc_MatchInv extends Doc
cr.setAmtSourceCr(BigDecimal.ZERO);
}
temp = cr.getAcctBalance();
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (!cr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -747,7 +748,7 @@ public class Doc_MatchInv extends Doc
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 (m_matchInv.isReversal())
{
if (!cr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -758,7 +759,8 @@ public class Doc_MatchInv extends Doc
}
else
{
cr.setQty(getQty().multiply(multiplier).negate());
int precision = MUOM.getPrecision(getCtx(), m_invoiceLine.getC_UOM_ID());
cr.setQty(getQty().multiply(multiplier).negate().setScale(precision, RoundingMode.HALF_UP));
}
}
@ -803,7 +805,7 @@ public class Doc_MatchInv extends Doc
return null;
}
if (m_matchInv.getReversal_ID() == 0)
if (!m_matchInv.isReversal())
{
cr.setC_Activity_ID(m_invoiceLine.getC_Activity_ID());
cr.setC_Campaign_ID(m_invoiceLine.getC_Campaign_ID());
@ -931,7 +933,7 @@ public class Doc_MatchInv extends Doc
dr.setAmtSourceCr(BigDecimal.ZERO);
}
BigDecimal temp = dr.getAcctBalance();
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (!dr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -965,7 +967,7 @@ public class Doc_MatchInv extends Doc
invoice.getAD_Client_ID(), invoice.getAD_Org_ID());
dr = fact.createLine (null, expense,
as.getC_Currency_ID(), LineNetAmt, null);
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (!dr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -976,10 +978,11 @@ public class Doc_MatchInv extends Doc
}
else
{
dr.setQty(getQty().multiply(multiplier).negate());
int precision = MUOM.getPrecision(getCtx(), m_invoiceLine.getC_UOM_ID());
dr.setQty(getQty().multiply(multiplier).negate().setScale(precision, RoundingMode.HALF_UP));
}
}
if (m_matchInv.getReversal_ID() == 0)
if (!m_matchInv.isReversal())
{
dr.setC_Activity_ID(refInvLine.getC_Activity_ID());
dr.setC_Campaign_ID(refInvLine.getC_Campaign_ID());
@ -1020,7 +1023,7 @@ public class Doc_MatchInv extends Doc
cr.setAmtSourceCr(BigDecimal.ZERO);
}
BigDecimal temp = cr.getAcctBalance();
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (!cr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -1054,7 +1057,7 @@ public class Doc_MatchInv extends Doc
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 (m_matchInv.isReversal())
{
if (!cr.updateReverseLine (MMatchInv.Table_ID, // Amt updated
m_matchInv.getReversal_ID(), 0, BigDecimal.ONE))
@ -1065,7 +1068,8 @@ public class Doc_MatchInv extends Doc
}
else
{
cr.setQty(getQty().multiply(multiplier).negate());
int precision = MUOM.getPrecision(getCtx(), m_invoiceLine.getC_UOM_ID());
cr.setQty(getQty().multiply(multiplier).negate().setScale(precision, RoundingMode.HALF_UP));
}
}
@ -1111,7 +1115,7 @@ public class Doc_MatchInv extends Doc
return null;
}
if (m_matchInv.getReversal_ID() == 0)
if (!m_matchInv.isReversal())
{
cr.setC_Activity_ID(m_invoiceLine.getC_Activity_ID());
cr.setC_Campaign_ID(m_invoiceLine.getC_Campaign_ID());
@ -1213,7 +1217,7 @@ public class Doc_MatchInv extends Doc
MInvoice invoice, BigDecimal matchInvSource, BigDecimal matchInvAccounted,
ArrayList<FactLine> invGainLossFactLines, HashMap<Integer, ArrayList<FactLine>> htFactLineInv)
{
if (m_matchInv.getReversal_ID() > 0 && m_matchInv.get_ID() > m_matchInv.getReversal_ID())
if (m_matchInv.isReversal())
return createReversalInvoiceGainLossRoundingCorrection(as, fact, acct);
BigDecimal invoiceSource = null;
@ -1296,7 +1300,7 @@ public class Doc_MatchInv extends Doc
*/
private String createReversalInvoiceGainLossRoundingCorrection(MAcctSchema as, Fact fact, MAccount acct)
{
if (m_matchInv.getReversal_ID() == 0)
if (!m_matchInv.isReversal())
return null;
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
@ -1339,7 +1343,7 @@ public class Doc_MatchInv extends Doc
ArrayList<FactLine> invGainLossFactLines, ArrayList<MInvoice> invList, ArrayList<MInvoiceLine> invLineList,
HashMap<Integer, ArrayList<FactLine>> htFactLineInv)
{
if (m_matchInv.getReversal_ID() > 0 && m_matchInv.get_ID() > m_matchInv.getReversal_ID())
if (m_matchInv.isReversal())
return null;
HashMap<Integer, ArrayList<FactLine>> htRoundingLineInvLine = new HashMap<Integer, ArrayList<FactLine>>();
@ -1523,7 +1527,7 @@ public class Doc_MatchInv extends Doc
skipMatchInvIdList.add(m_matchInv.get_ID());
for (MMatchInv matchInv : matchInvs)
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
skipMatchInvIdList.add(matchInv.get_ID());
}
@ -1543,9 +1547,9 @@ public class Doc_MatchInv extends Doc
.append(" AND PostingType='A'")
.append(" AND Account_ID=?");
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
sql.append(" AND Record_ID <> ").append(matchInv.get_ID());
sql.append(" AND Record_ID < ").append(m_matchInv.getReversal_ID());
}
@ -1595,9 +1599,9 @@ public class Doc_MatchInv extends Doc
.append(" AND (Account_ID=? OR Account_ID=? OR Account_ID=?)")
.append(" AND Description LIKE 'Invoice%'");
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
sql.append(" AND Record_ID <> ").append(matchInv.get_ID());
sql.append(" AND Record_ID < ").append(m_matchInv.getReversal_ID());
}
@ -1825,7 +1829,7 @@ public class Doc_MatchInv extends Doc
skipMatchInvIdList.add(m_matchInv.get_ID());
for (MMatchInv matchInv : matchInvs)
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
skipMatchInvIdList.add(matchInv.get_ID());
}
@ -1845,9 +1849,9 @@ public class Doc_MatchInv extends Doc
.append(" AND PostingType='A'")
.append(" AND Account_ID=?");
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
sql.append(" AND Record_ID <> ").append(matchInv.get_ID());
sql.append(" AND Record_ID < ").append(m_matchInv.getReversal_ID());
}
@ -1904,9 +1908,9 @@ public class Doc_MatchInv extends Doc
.append(" AND (Account_ID=? OR Account_ID=? OR Account_ID=?)")
.append(" AND Description LIKE 'Invoice Line%'");
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
sql.append(" AND Record_ID <> ").append(matchInv.get_ID());
sql.append(" AND Record_ID < ").append(m_matchInv.getReversal_ID());
}
@ -2029,7 +2033,7 @@ public class Doc_MatchInv extends Doc
MInOut receipt, BigDecimal matchInvSource, BigDecimal matchInvAccounted,
ArrayList<FactLine> mrGainLossFactLines, ArrayList<FactLine> mrFactLines)
{
if (m_matchInv.getReversal_ID() > 0 && m_matchInv.get_ID() > m_matchInv.getReversal_ID())
if (m_matchInv.isReversal())
return createReversalReceiptGainLossRoundingCorrection(as, fact, acct);
BigDecimal receiptSource = null;
@ -2104,7 +2108,7 @@ public class Doc_MatchInv extends Doc
*/
private String createReversalReceiptGainLossRoundingCorrection(MAcctSchema as, Fact fact, MAccount acct)
{
if (m_matchInv.getReversal_ID() == 0)
if (!m_matchInv.isReversal())
return null;
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
@ -2144,7 +2148,7 @@ public class Doc_MatchInv extends Doc
private String createReceiptRoundingCorrection(MAcctSchema as, Fact fact, MAccount acct,
ArrayList<FactLine> mrGainLossFactLines, ArrayList<FactLine> mrFactLines)
{
if (m_matchInv.getReversal_ID() > 0 && m_matchInv.get_ID() > m_matchInv.getReversal_ID())
if (m_matchInv.isReversal())
return null;
ArrayList<FactLine> mrLineRoundingLines = new ArrayList<FactLine>();
@ -2249,7 +2253,7 @@ public class Doc_MatchInv extends Doc
skipMatchInvIdList.add(m_matchInv.get_ID());
for (MMatchInv matchInv : matchInvs)
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
skipMatchInvIdList.add(matchInv.get_ID());
}
@ -2269,9 +2273,9 @@ public class Doc_MatchInv extends Doc
.append(" AND PostingType='A'")
.append(" AND Account_ID=?");
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
sql.append(" AND Record_ID <> ").append(matchInv.get_ID());
sql.append(" AND Record_ID < ").append(m_matchInv.getReversal_ID());
}
@ -2313,9 +2317,9 @@ public class Doc_MatchInv extends Doc
.append(" AND (Account_ID=? OR Account_ID=? OR Account_ID=?)")
.append(" AND Description LIKE 'InOut%'");
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
sql.append(" AND Record_ID <> ").append(matchInv.get_ID());
sql.append(" AND Record_ID < ").append(m_matchInv.getReversal_ID());
}
@ -2484,7 +2488,7 @@ public class Doc_MatchInv extends Doc
skipMatchInvIdList.add(m_matchInv.get_ID());
for (MMatchInv matchInv : matchInvs)
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
skipMatchInvIdList.add(matchInv.get_ID());
}
@ -2504,9 +2508,9 @@ public class Doc_MatchInv extends Doc
.append(" AND PostingType='A'")
.append(" AND Account_ID=?");
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
sql.append(" AND Record_ID <> ").append(matchInv.get_ID());
sql.append(" AND Record_ID < ").append(m_matchInv.getReversal_ID());
}
@ -2546,9 +2550,9 @@ public class Doc_MatchInv extends Doc
.append(" AND (Account_ID=? OR Account_ID=? OR Account_ID=?)")
.append(" AND Description LIKE 'InOut Line%'");
if (m_matchInv.getReversal_ID() > 0)
if (m_matchInv.isReversal())
{
if (matchInv.getReversal_ID() > 0 && matchInv.get_ID() > matchInv.getReversal_ID())
if (matchInv.isReversal())
sql.append(" AND Record_ID <> ").append(matchInv.get_ID());
sql.append(" AND Record_ID < ").append(m_matchInv.getReversal_ID());
}

View File

@ -33,6 +33,7 @@ import org.compiere.model.MCurrency;
import org.compiere.model.MFactAcct;
import org.compiere.model.MMovement;
import org.compiere.model.MRevenueRecognitionPlan;
import org.compiere.model.MUOM;
import org.compiere.model.X_C_AcctSchema_Element;
import org.compiere.model.X_Fact_Acct;
import org.compiere.util.DB;
@ -1206,8 +1207,14 @@ public final class FactLine extends X_Fact_Acct
setC_Tax_ID(fact.getC_Tax_ID());
// Org for cross charge
setAD_Org_ID (fact.getAD_Org_ID());
if (fact.getQty() != null)
setQty(fact.getQty().negate());
if (fact.getQty() != null) {
if (getC_UOM_ID() != 0)
{
int precision = MUOM.getPrecision(getCtx(), getC_UOM_ID());
setQty(fact.getQty().multiply(multiplier).negate().setScale(precision, RoundingMode.HALF_UP));
} else
setQty(fact.getQty().multiply(multiplier).negate().stripTrailingZeros());
}
}
else
log.warning(new StringBuilder("Not Found (try later) ")

View File

@ -407,7 +407,8 @@ public class MMatchInv extends X_M_MatchInv
*/
public boolean isReversal() {
if (getReversal_ID() > 0) {
if (getM_InOutLine().getMovementQty().signum() != getQty().signum())
MMatchInv reversal = new MMatchInv (getCtx(), getReversal_ID(), get_TrxName());
if (reversal.getM_MatchInv_ID() < getM_MatchInv_ID())
return true;
}
return false;

View File

@ -633,10 +633,14 @@ public class MatchInvTest extends AbstractTestCase {
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())
if (fa.getAccount_ID() == acctNIR.getAccount_ID()) {
assertEquals(fa.getAmtAcctDr(), invMatchAmt, "MatchInv incorrect amount posted "+fa.getAmtAcctDr().toPlainString());
else if (fa.getAccount_ID() == acctInvClr.getAccount_ID())
assertEquals(mi.getQty(), fa.getQty(), "Accounting fact quantity incorrect");
}
else if (fa.getAccount_ID() == acctInvClr.getAccount_ID()) {
assertEquals(fa.getAmtAcctCr(), invMatchAmt, "MatchInv incorrect amount posted "+fa.getAmtAcctCr().toPlainString());
assertEquals(mi.getQty().negate(), fa.getQty(), "Accounting fact quantity incorrect");
}
}
}
@ -695,10 +699,12 @@ public class MatchInvTest extends AbstractTestCase {
if (fa.getAccount_ID() == acctInvClr.getAccount_ID() && fa.getQty().compareTo(BigDecimal.ZERO) < 0) {
assertEquals(fa.getAmtAcctCr(), credMatchAmt, "MatchInv incorrect amount posted "+fa.getAmtAcctCr().toPlainString());
amtAcctCrInvClr = amtAcctCrInvClr.add(fa.getAmtAcctCr());
assertEquals(mi.getQty(), fa.getQty(), "Accounting fact quantity incorrect");
}
else if (fa.getAccount_ID() == acctInvClr.getAccount_ID() && fa.getQty().compareTo(BigDecimal.ZERO) > 0) {
assertEquals(fa.getAmtAcctDr(), credMatchAmt, "MatchInv incorrect amount posted "+fa.getAmtAcctDr().toPlainString());
amtAcctDrInvClr = amtAcctDrInvClr.add(fa.getAmtAcctDr());
assertEquals(mi.getQty().negate(), fa.getQty(), "Accounting fact quantity incorrect");
}
}
assertTrue(amtAcctDrInvClr.compareTo(amtAcctCrInvClr) == 0);
@ -994,4 +1000,244 @@ public class MatchInvTest extends AbstractTestCase {
}
}
}
@Test
public void testIsReversalCM() {
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(new BigDecimal("2"));
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(new BigDecimal("2"));
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());
MInvoice creditMemo = new MInvoice(receipt, receipt.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(receiptLine.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());
MMatchInv[] beforeList = MMatchInv.getInvoiceLine(Env.getCtx(), creditMemoLine.get_ID(), getTrxName());
assertEquals(1, beforeList.length);
info = MWorkflow.runDocumentActionWorkflow(creditMemo, DocAction.ACTION_Reverse_Correct);
creditMemo.load(getTrxName());
assertFalse(info.isError());
assertEquals(DocAction.STATUS_Reversed, creditMemo.getDocStatus());
MMatchInv[] afterList = MMatchInv.getInvoiceLine(Env.getCtx(), creditMemoLine.get_ID(), getTrxName());
assertEquals(2, afterList.length);
beforeList[0].load(getTrxName());
assertFalse(beforeList[0].isReversal());
for(MMatchInv mi : afterList) {
if (!mi.equals(beforeList[0])) {
assertTrue(mi.isReversal());
break;
}
}
}
@Test
public void testReversalPosting() {
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);
}
MAcctSchema as = MClient.get(getAD_Client_ID()).getAcctSchema();
BigDecimal invMatchAmt = invoiceLine.getMatchedQty().multiply(invoiceLine.getPriceActual()).setScale(as.getStdPrecision(), RoundingMode.HALF_UP);
invoice.load(getTrxName());
assertTrue(invoice.isPosted());
info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Reverse_Correct);
invoice.load(getTrxName());
assertFalse(info.isError());
assertEquals(DocAction.STATUS_Reversed, invoice.getDocStatus());
MMatchInv[] miList = MMatchInv.getInvoiceLine(Env.getCtx(), invoiceLine.get_ID(), getTrxName());
assertEquals(2, miList.length);
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 + "=" + as.getC_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()) {
if (mi.isReversal())
assertEquals(fa.getAmtAcctCr(), invMatchAmt, "MatchInv incorrect amount posted ");
else
assertEquals(fa.getAmtAcctDr(), invMatchAmt, "MatchInv incorrect amount posted ");
assertEquals(mi.getQty(), fa.getQty(), "Accounting fact quantity incorrect");
}
else if (fa.getAccount_ID() == acctInvClr.getAccount_ID()) {
if (mi.isReversal())
assertEquals(fa.getAmtAcctDr(), invMatchAmt, "MatchInv incorrect amount posted ");
else
assertEquals(fa.getAmtAcctCr(), invMatchAmt, "MatchInv incorrect amount posted ");
assertEquals(mi.getQty().negate(), fa.getQty(), "Accounting fact quantity incorrect");
}
}
}
}
}