IDEMPIERE-4263 Wrong matched invoice GL posting (currency balancing) in alternate schema for vendor credit memo if > 1 credit memo line (#250)
* IDEMPIERE-4263 Wrong matched invoice GL posting (currency balancing) in alternate schema for vendor credit memo if > 1 credit memo line
This commit is contained in:
parent
599e13dda3
commit
79d6c1e903
|
@ -23,6 +23,7 @@ import java.sql.SQLException;
|
||||||
import java.sql.Savepoint;
|
import java.sql.Savepoint;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -75,6 +76,9 @@ public class Doc_MatchInv extends Doc
|
||||||
super(as, MMatchInv.class, rs, DOCTYPE_MatMatchInv, trxName);
|
super(as, MMatchInv.class, rs, DOCTYPE_MatMatchInv, trxName);
|
||||||
} // Doc_MatchInv
|
} // Doc_MatchInv
|
||||||
|
|
||||||
|
/** Tolerance G&L */
|
||||||
|
private static final BigDecimal TOLERANCE = BigDecimal.valueOf(0.02);
|
||||||
|
|
||||||
/** Invoice Line */
|
/** Invoice Line */
|
||||||
private MInvoiceLine m_invoiceLine = null;
|
private MInvoiceLine m_invoiceLine = null;
|
||||||
/** Material Receipt */
|
/** Material Receipt */
|
||||||
|
@ -142,6 +146,13 @@ public class Doc_MatchInv extends Doc
|
||||||
public ArrayList<Fact> createFacts (MAcctSchema as)
|
public ArrayList<Fact> createFacts (MAcctSchema as)
|
||||||
{
|
{
|
||||||
ArrayList<Fact> facts = new ArrayList<Fact>();
|
ArrayList<Fact> facts = new ArrayList<Fact>();
|
||||||
|
// invoice gain/loss accounting fact line list
|
||||||
|
ArrayList<FactLine> invGainLossFactLines = new ArrayList<FactLine>();
|
||||||
|
// invoice list
|
||||||
|
ArrayList<MInvoice> invList = new ArrayList<MInvoice>();
|
||||||
|
// C_Invoice_ID and the current M_MatchInv inventory clearing/expense accounting fact lines
|
||||||
|
HashMap<Integer, ArrayList<FactLine>> htFactLineInv = new HashMap<Integer, ArrayList<FactLine>>();
|
||||||
|
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
if (getM_Product_ID() == 0 // no Product
|
if (getM_Product_ID() == 0 // no Product
|
||||||
|| getQty().signum() == 0
|
|| getQty().signum() == 0
|
||||||
|
@ -292,16 +303,32 @@ public class Doc_MatchInv extends Doc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rounding correction
|
// gain/loss + rounding adjustment
|
||||||
if (m_receiptLine != null && m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency
|
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.getAmtSourceDr(), dr.getAmtAcctDr());
|
p_Error = createReceiptGainLoss(as, fact, getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as), m_receiptLine.getParent(), dr.getAmtSourceDr(), dr.getAmtAcctDr());
|
||||||
if (p_Error != null)
|
if (p_Error != null)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// gain/loss
|
||||||
if (m_invoiceLine != null && m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency
|
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.getAmtSourceCr(), cr.getAmtAcctCr());
|
MInvoice invoice = m_invoiceLine.getParent();
|
||||||
|
if (!invList.contains(invoice))
|
||||||
|
invList.add(invoice);
|
||||||
|
ArrayList<FactLine> factLineList = htFactLineInv.get(invoice.get_ID());
|
||||||
|
if (factLineList == null)
|
||||||
|
factLineList = new ArrayList<FactLine>();
|
||||||
|
factLineList.add(cr);
|
||||||
|
htFactLineInv.put(invoice.get_ID(), factLineList);
|
||||||
|
p_Error = createInvoiceGainLoss(as, fact, expense, invoice, cr.getAmtSourceCr(), cr.getAmtAcctCr(), invGainLossFactLines, htFactLineInv);
|
||||||
|
if (p_Error != null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// rounding adjustment
|
||||||
|
if (!htFactLineInv.isEmpty())
|
||||||
|
{
|
||||||
|
p_Error = createInvoiceRoundingCorrection(as, fact, expense, invGainLossFactLines, invList, htFactLineInv);
|
||||||
if (p_Error != null)
|
if (p_Error != null)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -587,6 +614,12 @@ public class Doc_MatchInv extends Doc
|
||||||
|
|
||||||
private ArrayList<Fact> createMatShipmentFacts(MAcctSchema as) {
|
private ArrayList<Fact> createMatShipmentFacts(MAcctSchema as) {
|
||||||
ArrayList<Fact> facts = new ArrayList<Fact>();
|
ArrayList<Fact> facts = new ArrayList<Fact>();
|
||||||
|
// invoice gain/loss accounting fact line list
|
||||||
|
ArrayList<FactLine> invGainLossFactLines = new ArrayList<FactLine>();
|
||||||
|
// invoice list
|
||||||
|
ArrayList<MInvoice> invList = new ArrayList<MInvoice>();
|
||||||
|
// C_Invoice_ID and the current M_MatchInv inventory clearing/expense accounting fact lines
|
||||||
|
HashMap<Integer, ArrayList<FactLine>> htFactLineInv = new HashMap<Integer, ArrayList<FactLine>>();
|
||||||
|
|
||||||
// create Fact Header
|
// create Fact Header
|
||||||
Fact fact = new Fact(this, as, Fact.POST_Actual);
|
Fact fact = new Fact(this, as, Fact.POST_Actual);
|
||||||
|
@ -708,16 +741,33 @@ public class Doc_MatchInv extends Doc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rounding correction
|
// gain/loss + rounding adjustment
|
||||||
if (m_receiptLine != null && m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency
|
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());
|
p_Error = createReceiptGainLoss(as, fact, getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as), m_receiptLine.getParent(), dr.getAmtSourceCr(), dr.getAmtAcctCr());
|
||||||
if (p_Error != null)
|
if (p_Error != null)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gain/loss
|
||||||
if (m_invoiceLine != null && m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency
|
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());
|
MInvoice invoice = m_invoiceLine.getParent();
|
||||||
|
if (!invList.contains(invoice))
|
||||||
|
invList.add(invoice);
|
||||||
|
ArrayList<FactLine> factLineList = htFactLineInv.get(invoice.get_ID());
|
||||||
|
if (factLineList == null)
|
||||||
|
factLineList = new ArrayList<FactLine>();
|
||||||
|
factLineList.add(cr);
|
||||||
|
htFactLineInv.put(invoice.get_ID(), factLineList);
|
||||||
|
p_Error = createInvoiceGainLoss(as, fact, expense, invoice, cr.getAmtSourceDr(), cr.getAmtAcctDr(), invGainLossFactLines, htFactLineInv);
|
||||||
|
if (p_Error != null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// rounding adjustment
|
||||||
|
if (!htFactLineInv.isEmpty())
|
||||||
|
{
|
||||||
|
p_Error = createInvoiceRoundingCorrection(as, fact, expense, invGainLossFactLines, invList, htFactLineInv);
|
||||||
if (p_Error != null)
|
if (p_Error != null)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -795,6 +845,12 @@ public class Doc_MatchInv extends Doc
|
||||||
|
|
||||||
public ArrayList<Fact> createCreditMemoFacts(MAcctSchema as) {
|
public ArrayList<Fact> createCreditMemoFacts(MAcctSchema as) {
|
||||||
ArrayList<Fact> facts = new ArrayList<Fact>();
|
ArrayList<Fact> facts = new ArrayList<Fact>();
|
||||||
|
// invoice gain/loss accounting fact line list
|
||||||
|
ArrayList<FactLine> invGainLossFactLines = new ArrayList<FactLine>();
|
||||||
|
// invoice list
|
||||||
|
ArrayList<MInvoice> invList = new ArrayList<MInvoice>();
|
||||||
|
// C_Invoice_ID and the current M_MatchInv inventory clearing/expense accounting fact lines
|
||||||
|
HashMap<Integer, ArrayList<FactLine>> htFactLineInv = new HashMap<Integer, ArrayList<FactLine>>();
|
||||||
|
|
||||||
// create Fact Header
|
// create Fact Header
|
||||||
Fact fact = new Fact(this, as, Fact.POST_Actual);
|
Fact fact = new Fact(this, as, Fact.POST_Actual);
|
||||||
|
@ -975,16 +1031,40 @@ public class Doc_MatchInv extends Doc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rounding correction
|
// gain / loss
|
||||||
if (refInvLine != null && refInvLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency
|
if (refInvLine != null && refInvLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency
|
||||||
{
|
{
|
||||||
p_Error = createInvoiceGainLoss(as, fact, expense, refInvLine.getParent(), dr.getAmtSourceCr(), dr.getAmtAcctCr());
|
MInvoice invoice = refInvLine.getParent();
|
||||||
|
if (!invList.contains(invoice))
|
||||||
|
invList.add(invoice);
|
||||||
|
ArrayList<FactLine> factLineList = htFactLineInv.get(invoice.get_ID());
|
||||||
|
if (factLineList == null)
|
||||||
|
factLineList = new ArrayList<FactLine>();
|
||||||
|
factLineList.add(dr);
|
||||||
|
htFactLineInv.put(invoice.get_ID(), factLineList);
|
||||||
|
p_Error = createInvoiceGainLoss(as, fact, expense, invoice, dr.getAmtSourceCr(), dr.getAmtAcctCr(), invGainLossFactLines, htFactLineInv);
|
||||||
if (p_Error != null)
|
if (p_Error != null)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (m_invoiceLine != null && m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency
|
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());
|
MInvoice invoice = m_invoiceLine.getParent();
|
||||||
|
if (!invList.contains(invoice))
|
||||||
|
invList.add(invoice);
|
||||||
|
ArrayList<FactLine> factLineList = htFactLineInv.get(invoice.get_ID());
|
||||||
|
if (factLineList == null)
|
||||||
|
factLineList = new ArrayList<FactLine>();
|
||||||
|
factLineList.add(cr);
|
||||||
|
htFactLineInv.put(invoice.get_ID(), factLineList);
|
||||||
|
p_Error = createInvoiceGainLoss(as, fact, expense, invoice, cr.getAmtSourceDr(), cr.getAmtAcctDr(), invGainLossFactLines, htFactLineInv);
|
||||||
|
if (p_Error != null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rounding adjustment
|
||||||
|
if (!htFactLineInv.isEmpty())
|
||||||
|
{
|
||||||
|
p_Error = createInvoiceRoundingCorrection(as, fact, expense, invGainLossFactLines, invList, htFactLineInv);
|
||||||
if (p_Error != null)
|
if (p_Error != null)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1024,7 +1104,7 @@ public class Doc_MatchInv extends Doc
|
||||||
BigDecimal debit = dr.getAmtSourceDr();
|
BigDecimal debit = dr.getAmtSourceDr();
|
||||||
BigDecimal credit = cr.getAmtSourceCr();
|
BigDecimal credit = cr.getAmtSourceCr();
|
||||||
|
|
||||||
if (debit.compareTo(credit) == 0) {
|
if (debit.compareTo(credit) == 0 && (cr.getAcctBalance().add(dr.getAcctBalance())).compareTo(Env.ZERO) == 0) {
|
||||||
fact.remove(dr);
|
fact.remove(dr);
|
||||||
fact.remove(cr);
|
fact.remove(cr);
|
||||||
}
|
}
|
||||||
|
@ -1076,7 +1156,8 @@ public class Doc_MatchInv extends Doc
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createInvoiceGainLoss(MAcctSchema as, Fact fact, MAccount acct,
|
private String createInvoiceGainLoss(MAcctSchema as, Fact fact, MAccount acct,
|
||||||
MInvoice invoice, BigDecimal matchInvSource, BigDecimal matchInvAccounted)
|
MInvoice invoice, BigDecimal matchInvSource, BigDecimal matchInvAccounted,
|
||||||
|
ArrayList<FactLine> invGainLossFactLines, HashMap<Integer, ArrayList<FactLine>> htFactLineInv)
|
||||||
{
|
{
|
||||||
BigDecimal invoiceSource = null;
|
BigDecimal invoiceSource = null;
|
||||||
BigDecimal invoiceAccounted = null;
|
BigDecimal invoiceAccounted = null;
|
||||||
|
@ -1105,126 +1186,46 @@ public class Doc_MatchInv extends Doc
|
||||||
if (invoiceSource == null || invoiceAccounted == null)
|
if (invoiceSource == null || invoiceAccounted == null)
|
||||||
return null;
|
return null;
|
||||||
//
|
//
|
||||||
|
|
||||||
if (m_matchInv.getReversal_ID() == 0)
|
|
||||||
{
|
|
||||||
String matchInvLineSql = "SELECT M_MatchInv_ID FROM M_MatchInv "
|
|
||||||
+ "WHERE C_InvoiceLine_ID IN (SELECT C_InvoiceLine_ID FROM C_InvoiceLine WHERE C_Invoice_ID=?) "
|
|
||||||
+ "AND COALESCE(Reversal_ID,0)=0";
|
|
||||||
List<List<Object>> list = DB.getSQLArrayObjectsEx(getTrxName(), matchInvLineSql, invoice.get_ID());
|
|
||||||
StringBuilder s = new StringBuilder();
|
|
||||||
|
|
||||||
if (list == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
for (int index=0; index < list.size(); index++)
|
|
||||||
{
|
|
||||||
List<Object> l = list.get(index);
|
|
||||||
s.append(l.get(0));
|
|
||||||
if (index != list.size()-1)
|
|
||||||
s.append(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
sql = new StringBuilder()
|
|
||||||
.append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)")
|
|
||||||
.append(" FROM Fact_Acct ")
|
|
||||||
.append("WHERE AD_Table_ID=? AND Record_ID IN (").append(s).append(")")
|
|
||||||
.append(" AND Record_ID <> ?")
|
|
||||||
.append(" AND C_AcctSchema_ID=?")
|
|
||||||
.append(" AND Account_ID=?")
|
|
||||||
.append(" AND PostingType='A'");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sql = new StringBuilder()
|
|
||||||
.append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)")
|
|
||||||
.append(" FROM Fact_Acct ")
|
|
||||||
.append("WHERE AD_Table_ID=? AND Record_ID IN (").append(m_matchInv.getReversal_ID()).append(")")
|
|
||||||
.append(" AND Record_ID <> ?")
|
|
||||||
.append(" AND C_AcctSchema_ID=?")
|
|
||||||
.append(" AND Account_ID=?")
|
|
||||||
.append(" AND PostingType='A'");
|
|
||||||
}
|
|
||||||
|
|
||||||
BigDecimal acctDifference = null; // gain is negative
|
|
||||||
// For Match Invoice
|
|
||||||
valuesInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(),
|
|
||||||
MMatchInv.Table_ID, get_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID());
|
|
||||||
if (valuesInv != null)
|
|
||||||
{
|
|
||||||
BigDecimal totalAmtSourceDr = (BigDecimal) valuesInv.get(0);
|
|
||||||
if (totalAmtSourceDr == null)
|
|
||||||
totalAmtSourceDr = Env.ZERO;
|
|
||||||
BigDecimal totalAmtAcctDr = (BigDecimal) valuesInv.get(1);
|
|
||||||
if (totalAmtAcctDr == null)
|
|
||||||
totalAmtAcctDr = Env.ZERO;
|
|
||||||
BigDecimal totalAmtSourceCr = (BigDecimal) valuesInv.get(2);
|
|
||||||
if (totalAmtSourceCr == null)
|
|
||||||
totalAmtSourceCr = Env.ZERO;
|
|
||||||
BigDecimal totalAmtAcctCr = (BigDecimal) valuesInv.get(3);
|
|
||||||
if (totalAmtAcctCr == null)
|
|
||||||
totalAmtAcctCr = Env.ZERO;
|
|
||||||
|
|
||||||
if (m_matchInv.getReversal_ID() == 0)
|
|
||||||
{
|
|
||||||
if (totalAmtSourceDr.signum() == 0 && totalAmtAcctDr.signum() == 0)
|
|
||||||
{
|
|
||||||
matchInvSource = matchInvSource.add(totalAmtSourceCr);
|
|
||||||
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr);
|
|
||||||
}
|
|
||||||
else if (totalAmtSourceCr.signum() == 0 && totalAmtAcctCr.signum() == 0)
|
|
||||||
{
|
|
||||||
matchInvSource = matchInvSource.add(totalAmtSourceDr);
|
|
||||||
matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr);
|
|
||||||
}
|
|
||||||
else if (totalAmtAcctDr.compareTo(totalAmtAcctCr) > 0)
|
|
||||||
{
|
|
||||||
matchInvSource = matchInvSource.add(totalAmtSourceDr);
|
|
||||||
matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr).subtract(totalAmtAcctCr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
matchInvSource = matchInvSource.add(totalAmtSourceCr);
|
|
||||||
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr).subtract(totalAmtAcctDr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (totalAmtAcctDr.compareTo(totalAmtAcctCr) > 0)
|
|
||||||
{
|
|
||||||
matchInvSource = matchInvSource.add(totalAmtSourceDr);
|
|
||||||
matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr);
|
|
||||||
acctDifference = totalAmtAcctCr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
matchInvSource = matchInvSource.add(totalAmtSourceCr);
|
|
||||||
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr);
|
|
||||||
acctDifference = totalAmtAcctDr.negate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder description = new StringBuilder("Invoice=(").append(invoice.getC_Currency_ID()).append(")").append(invoiceSource).append("/").append(invoiceAccounted)
|
StringBuilder description = new StringBuilder("Invoice=(").append(invoice.getC_Currency_ID()).append(")").append(invoiceSource).append("/").append(invoiceAccounted)
|
||||||
.append(" - MatchInv=(").append(getC_Currency_ID()).append(")").append(matchInvSource).append("/").append(matchInvAccounted);
|
.append(" - MatchInv=(").append(getC_Currency_ID()).append(")").append(matchInvSource).append("/").append(matchInvAccounted);
|
||||||
if (log.isLoggable(Level.FINE)) log.fine(description.toString());
|
if (log.isLoggable(Level.FINE)) log.fine(description.toString());
|
||||||
//
|
//
|
||||||
|
|
||||||
// Full Payment in currency
|
BigDecimal acctDifference = null;
|
||||||
if (acctDifference == null && matchInvSource.compareTo(invoiceSource) == 0)
|
// Full MR in currency
|
||||||
|
if (matchInvSource.compareTo(invoiceSource) == 0)
|
||||||
{
|
{
|
||||||
acctDifference = matchInvAccounted.subtract(invoiceAccounted.abs()); // gain is negative
|
acctDifference = matchInvAccounted.abs().subtract(invoiceAccounted.abs()); // gain is negative
|
||||||
StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference);
|
StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference);
|
||||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||||
description.append(" - ").append(d2);
|
description.append(" - ").append(d2);
|
||||||
}
|
}
|
||||||
|
else // partial or MC
|
||||||
|
{
|
||||||
|
BigDecimal matchInvAccounted0 = MConversionRate.convert(getCtx(),
|
||||||
|
matchInvSource, invoice.getC_Currency_ID(),
|
||||||
|
as.getC_Currency_ID(), invoice.getDateAcct(),
|
||||||
|
invoice.getC_ConversionType_ID(), invoice.getAD_Client_ID(), invoice.getAD_Org_ID());
|
||||||
|
acctDifference = matchInvAccounted0.abs().subtract(matchInvAccounted.abs());
|
||||||
|
// ignore Tolerance
|
||||||
|
if (acctDifference.abs().compareTo(TOLERANCE) < 0)
|
||||||
|
acctDifference = Env.ZERO;
|
||||||
|
// Round
|
||||||
|
int precision = as.getStdPrecision();
|
||||||
|
if (acctDifference.scale() > precision)
|
||||||
|
acctDifference = acctDifference.setScale(precision, RoundingMode.HALF_UP);
|
||||||
|
StringBuilder d2 = new StringBuilder("(partial) = ").append(acctDifference);
|
||||||
|
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||||
|
description.append(" - ").append(d2);
|
||||||
|
}
|
||||||
|
|
||||||
if (acctDifference == null || acctDifference.signum() == 0)
|
if (acctDifference.signum() == 0)
|
||||||
{
|
{
|
||||||
log.fine("No Difference");
|
log.fine("No Difference");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
|
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
|
||||||
MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct());
|
MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct());
|
||||||
//
|
//
|
||||||
|
@ -1233,32 +1234,409 @@ public class Doc_MatchInv extends Doc
|
||||||
FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference.negate());
|
FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference.negate());
|
||||||
fl.setDescription(description.toString());
|
fl.setDescription(description.toString());
|
||||||
updateFactLine(fl);
|
updateFactLine(fl);
|
||||||
|
ArrayList<FactLine> factLineList = htFactLineInv.get(invoice.get_ID());
|
||||||
if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID()) {
|
if (factLineList == null)
|
||||||
fl = fact.createLine (null, as.getCurrencyBalancing_Acct(), as.getC_Currency_ID(), acctDifference);
|
factLineList = new ArrayList<FactLine>();
|
||||||
} else {
|
factLineList.add(fl);
|
||||||
fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference);
|
htFactLineInv.put(invoice.get_ID(), factLineList);
|
||||||
}
|
|
||||||
|
fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference);
|
||||||
fl.setDescription(description.toString());
|
fl.setDescription(description.toString());
|
||||||
updateFactLine(fl);
|
updateFactLine(fl);
|
||||||
|
invGainLossFactLines.add(fl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference);
|
FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference);
|
||||||
fl.setDescription(description.toString());
|
fl.setDescription(description.toString());
|
||||||
updateFactLine(fl);
|
updateFactLine(fl);
|
||||||
|
ArrayList<FactLine> factLineList = htFactLineInv.get(invoice.get_ID());
|
||||||
|
if (factLineList == null)
|
||||||
|
factLineList = new ArrayList<FactLine>();
|
||||||
|
factLineList.add(fl);
|
||||||
|
htFactLineInv.put(invoice.get_ID(), factLineList);
|
||||||
|
|
||||||
if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID()) {
|
fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference.negate());
|
||||||
fl = fact.createLine (null, as.getCurrencyBalancing_Acct(), as.getC_Currency_ID(), acctDifference.negate());
|
|
||||||
} else {
|
|
||||||
fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference.negate());
|
|
||||||
}
|
|
||||||
fl.setDescription(description.toString());
|
fl.setDescription(description.toString());
|
||||||
updateFactLine(fl);
|
updateFactLine(fl);
|
||||||
|
invGainLossFactLines.add(fl);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} // createInvoiceGainLoss
|
} // createInvoiceGainLoss
|
||||||
|
|
||||||
|
private String createInvoiceRoundingCorrection(MAcctSchema as, Fact fact, MAccount acct,
|
||||||
|
ArrayList<FactLine> invGainLossFactLines, ArrayList<MInvoice> invList, HashMap<Integer, ArrayList<FactLine>> htFactLineInv)
|
||||||
|
{
|
||||||
|
// C_Invoice_ID and the total source amount from C_Invoice accounting fact lines
|
||||||
|
HashMap<Integer, BigDecimal> htInvSource = new HashMap<Integer, BigDecimal>();
|
||||||
|
// C_Invoice_ID and the total accounted amount from C_Invoice accounting fact lines
|
||||||
|
HashMap<Integer, BigDecimal> htInvAccounted = new HashMap<Integer, BigDecimal>();
|
||||||
|
for (MInvoice invoice : invList)
|
||||||
|
{
|
||||||
|
StringBuilder sql = new StringBuilder()
|
||||||
|
.append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)")
|
||||||
|
.append(" FROM Fact_Acct ")
|
||||||
|
.append("WHERE AD_Table_ID=? AND Record_ID=?")
|
||||||
|
.append(" AND C_AcctSchema_ID=?")
|
||||||
|
.append(" AND Account_ID=?")
|
||||||
|
.append(" AND PostingType='A'");
|
||||||
|
|
||||||
|
// For Invoice
|
||||||
|
List<Object> valuesInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(),
|
||||||
|
MInvoice.Table_ID, invoice.getC_Invoice_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID());
|
||||||
|
if (valuesInv != null) {
|
||||||
|
BigDecimal invoiceSource = (BigDecimal) valuesInv.get(0); // AmtSourceDr
|
||||||
|
BigDecimal invoiceAccounted = (BigDecimal) valuesInv.get(1); // AmtAcctDr
|
||||||
|
if (invoiceSource.signum() == 0 && invoiceAccounted.signum() == 0) {
|
||||||
|
invoiceSource = (BigDecimal) valuesInv.get(2); // AmtSourceCr
|
||||||
|
invoiceAccounted = (BigDecimal) valuesInv.get(3); // AmtAcctCr
|
||||||
|
}
|
||||||
|
htInvSource.put(invoice.getC_Invoice_ID(), invoiceSource);
|
||||||
|
htInvAccounted.put(invoice.getC_Invoice_ID(), invoiceAccounted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
|
||||||
|
MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct());
|
||||||
|
|
||||||
|
// C_Invoice_ID and the total source DR amount from the current M_MatchInv accounting fact lines
|
||||||
|
HashMap<Integer, BigDecimal> htTotalAmtSourceDr = new HashMap<Integer, BigDecimal>();
|
||||||
|
// C_Invoice_ID and the total accounted DR amount from the current M_MatchInv accounting fact lines
|
||||||
|
HashMap<Integer, BigDecimal> htTotalAmtAcctDr = new HashMap<Integer, BigDecimal>();
|
||||||
|
// C_Invoice_ID and the total source CR amount from the current M_MatchInv accounting fact lines
|
||||||
|
HashMap<Integer, BigDecimal> htTotalAmtSourceCr = new HashMap<Integer, BigDecimal>();
|
||||||
|
// C_Invoice_ID and the total accounted CR amount from the current M_MatchInv accounting fact lines
|
||||||
|
HashMap<Integer, BigDecimal> htTotalAmtAcctCr = new HashMap<Integer, BigDecimal>();
|
||||||
|
for (Integer C_Invoice_ID : htFactLineInv.keySet())
|
||||||
|
{
|
||||||
|
ArrayList<FactLine> factLineList = htFactLineInv.get(C_Invoice_ID);
|
||||||
|
for (FactLine factLine : factLineList)
|
||||||
|
{
|
||||||
|
if (factLine.getAccount_ID() == acct.getAccount_ID())
|
||||||
|
{
|
||||||
|
BigDecimal totalAmtSourceDr = htTotalAmtSourceDr.get(C_Invoice_ID);
|
||||||
|
if (totalAmtSourceDr == null)
|
||||||
|
totalAmtSourceDr = Env.ZERO;
|
||||||
|
BigDecimal totalAmtAcctDr = htTotalAmtAcctDr.get(C_Invoice_ID);
|
||||||
|
if (totalAmtAcctDr == null)
|
||||||
|
totalAmtAcctDr = Env.ZERO;
|
||||||
|
BigDecimal totalAmtSourceCr = htTotalAmtSourceCr.get(C_Invoice_ID);
|
||||||
|
if (totalAmtSourceCr == null)
|
||||||
|
totalAmtSourceCr = Env.ZERO;
|
||||||
|
BigDecimal totalAmtAcctCr = htTotalAmtAcctCr.get(C_Invoice_ID);
|
||||||
|
if (totalAmtAcctCr == null)
|
||||||
|
totalAmtAcctCr = Env.ZERO;
|
||||||
|
|
||||||
|
totalAmtSourceDr = totalAmtSourceDr.add(factLine.getAmtSourceDr());
|
||||||
|
totalAmtAcctDr = totalAmtAcctDr.add(factLine.getAmtAcctDr());
|
||||||
|
totalAmtSourceCr = totalAmtSourceCr.add(factLine.getAmtSourceCr());
|
||||||
|
totalAmtAcctCr = totalAmtAcctCr.add(factLine.getAmtAcctCr());
|
||||||
|
|
||||||
|
htTotalAmtSourceDr.put(C_Invoice_ID, totalAmtSourceDr);
|
||||||
|
htTotalAmtAcctDr.put(C_Invoice_ID, totalAmtAcctDr);
|
||||||
|
htTotalAmtSourceCr.put(C_Invoice_ID, totalAmtSourceCr);
|
||||||
|
htTotalAmtAcctCr.put(C_Invoice_ID, totalAmtAcctCr);
|
||||||
|
}
|
||||||
|
else if (factLine.getAccount_ID() == gain.getAccount_ID() || factLine.getAccount_ID() == loss.getAccount_ID())
|
||||||
|
{
|
||||||
|
if (!invGainLossFactLines.contains(factLine))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BigDecimal totalAmtSourceDr = htTotalAmtSourceDr.get(C_Invoice_ID);
|
||||||
|
if (totalAmtSourceDr == null)
|
||||||
|
totalAmtSourceDr = Env.ZERO;
|
||||||
|
BigDecimal totalAmtSourceCr = htTotalAmtSourceCr.get(C_Invoice_ID);
|
||||||
|
if (totalAmtSourceCr == null)
|
||||||
|
totalAmtSourceCr = Env.ZERO;
|
||||||
|
|
||||||
|
totalAmtSourceDr = totalAmtSourceDr.subtract(factLine.getAmtSourceCr());
|
||||||
|
totalAmtSourceCr = totalAmtSourceCr.subtract(factLine.getAmtSourceDr());
|
||||||
|
|
||||||
|
htTotalAmtSourceDr.put(C_Invoice_ID, totalAmtSourceDr);
|
||||||
|
htTotalAmtSourceCr.put(C_Invoice_ID, totalAmtSourceCr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// C_Invoice_ID and the total source amount from M_MatchInv accounting fact lines
|
||||||
|
HashMap<Integer, BigDecimal> htMatchInvSource = new HashMap<Integer, BigDecimal>();
|
||||||
|
// C_Invoice_ID and the total accounted amount from M_MatchInv accounting fact lines
|
||||||
|
HashMap<Integer, BigDecimal> htMatchInvAccounted = new HashMap<Integer, BigDecimal>();
|
||||||
|
// C_Invoice_ID and the total source amount from M_MatchInv accounting fact lines
|
||||||
|
HashMap<Integer, BigDecimal> htMatchInvAcctDiff = new HashMap<Integer, BigDecimal>();
|
||||||
|
for (MInvoice invoice : invList)
|
||||||
|
{
|
||||||
|
BigDecimal matchInvSource = Env.ZERO;
|
||||||
|
BigDecimal matchInvAccounted = Env.ZERO;
|
||||||
|
|
||||||
|
BigDecimal totalAmtSourceDr = htTotalAmtSourceDr.get(invoice.getC_Invoice_ID());
|
||||||
|
if (totalAmtSourceDr == null)
|
||||||
|
totalAmtSourceDr = Env.ZERO;
|
||||||
|
BigDecimal totalAmtAcctDr = htTotalAmtAcctDr.get(invoice.getC_Invoice_ID());
|
||||||
|
if (totalAmtAcctDr == null)
|
||||||
|
totalAmtAcctDr = Env.ZERO;
|
||||||
|
BigDecimal totalAmtSourceCr = htTotalAmtSourceCr.get(invoice.getC_Invoice_ID());
|
||||||
|
if (totalAmtSourceCr == null)
|
||||||
|
totalAmtSourceCr = Env.ZERO;
|
||||||
|
BigDecimal totalAmtAcctCr = htTotalAmtAcctCr.get(invoice.getC_Invoice_ID());
|
||||||
|
if (totalAmtAcctCr == null)
|
||||||
|
totalAmtAcctCr = Env.ZERO;
|
||||||
|
|
||||||
|
if (totalAmtSourceDr.signum() == 0 && totalAmtAcctDr.signum() == 0)
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceCr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr);
|
||||||
|
}
|
||||||
|
else if (totalAmtSourceCr.signum() == 0 && totalAmtAcctCr.signum() == 0)
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceDr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (totalAmtAcctDr.compareTo(totalAmtAcctCr) > 0)
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceDr).subtract(totalAmtSourceCr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr).subtract(totalAmtAcctCr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceCr).subtract(totalAmtSourceDr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr).subtract(totalAmtAcctDr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_matchInv.getReversal_ID() == 0)
|
||||||
|
{
|
||||||
|
MMatchInv[] matchInvs = MMatchInv.getInvoice(getCtx(), invoice.get_ID(), getTrxName());
|
||||||
|
|
||||||
|
ArrayList<Integer> skipMatchInvIdList = new ArrayList<Integer>();
|
||||||
|
skipMatchInvIdList.add(m_matchInv.get_ID());
|
||||||
|
for (MMatchInv matchInv : matchInvs)
|
||||||
|
{
|
||||||
|
if (matchInv.getReversal_ID() > 0)
|
||||||
|
skipMatchInvIdList.add(matchInv.get_ID());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MMatchInv matchInv : matchInvs)
|
||||||
|
{
|
||||||
|
if (matchInv.get_ID() == m_matchInv.get_ID())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (skipMatchInvIdList.contains(matchInv.get_ID()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
StringBuilder sql = new StringBuilder()
|
||||||
|
.append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)")
|
||||||
|
.append(" FROM Fact_Acct ")
|
||||||
|
.append("WHERE AD_Table_ID=? AND (Record_ID=? OR Record_ID=?)") // match inv
|
||||||
|
.append(" AND C_AcctSchema_ID=?")
|
||||||
|
.append(" AND PostingType='A'")
|
||||||
|
.append(" AND Account_ID=?");
|
||||||
|
|
||||||
|
if (matchInv.getRef_MatchInv_ID() > 0)
|
||||||
|
{
|
||||||
|
if (invoice.isCreditMemo() && matchInv.getQty().compareTo(BigDecimal.ZERO) < 0)
|
||||||
|
sql.append(" AND Qty > 0");
|
||||||
|
else
|
||||||
|
sql.append(" AND Qty < 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Match Inv
|
||||||
|
List<Object> valuesMatchInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(),
|
||||||
|
MMatchInv.Table_ID, matchInv.get_ID(), matchInv.getRef_MatchInv_ID() > 0 ? matchInv.getRef_MatchInv_ID() : -1, as.getC_AcctSchema_ID(), acct.getAccount_ID());
|
||||||
|
if (valuesMatchInv != null) {
|
||||||
|
totalAmtSourceDr = (BigDecimal) valuesMatchInv.get(0);
|
||||||
|
if (totalAmtSourceDr == null)
|
||||||
|
totalAmtSourceDr = Env.ZERO;
|
||||||
|
totalAmtAcctDr = (BigDecimal) valuesMatchInv.get(1);
|
||||||
|
if (totalAmtAcctDr == null)
|
||||||
|
totalAmtAcctDr = Env.ZERO;
|
||||||
|
totalAmtSourceCr = (BigDecimal) valuesMatchInv.get(2);
|
||||||
|
if (totalAmtSourceCr == null)
|
||||||
|
totalAmtSourceCr = Env.ZERO;
|
||||||
|
totalAmtAcctCr = (BigDecimal) valuesMatchInv.get(3);
|
||||||
|
if (totalAmtAcctCr == null)
|
||||||
|
totalAmtAcctCr = Env.ZERO;
|
||||||
|
|
||||||
|
if (totalAmtSourceDr.signum() == 0 && totalAmtAcctDr.signum() == 0)
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceCr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr);
|
||||||
|
}
|
||||||
|
else if (totalAmtSourceCr.signum() == 0 && totalAmtAcctCr.signum() == 0)
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceDr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (totalAmtAcctDr.compareTo(totalAmtAcctCr) > 0)
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceDr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceCr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = new StringBuilder()
|
||||||
|
.append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)")
|
||||||
|
.append(" FROM Fact_Acct ")
|
||||||
|
.append("WHERE AD_Table_ID=? AND (Record_ID=? OR Record_ID=?)") // match inv
|
||||||
|
.append(" AND C_AcctSchema_ID=?")
|
||||||
|
.append(" AND PostingType='A'")
|
||||||
|
.append(" AND (Account_ID=? OR Account_ID=? OR Account_ID=?)")
|
||||||
|
.append(" AND Description LIKE 'Invoice%'");
|
||||||
|
|
||||||
|
// For Match Inv
|
||||||
|
valuesMatchInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(),
|
||||||
|
MMatchInv.Table_ID, matchInv.get_ID(), matchInv.getRef_MatchInv_ID() > 0 ? matchInv.getRef_MatchInv_ID() : -1, as.getC_AcctSchema_ID(),
|
||||||
|
gain.getAccount_ID(), loss.getAccount_ID(), as.getCurrencyBalancing_Acct().getAccount_ID());
|
||||||
|
if (valuesMatchInv != null) {
|
||||||
|
totalAmtSourceDr = (BigDecimal) valuesMatchInv.get(0);
|
||||||
|
if (totalAmtSourceDr == null)
|
||||||
|
totalAmtSourceDr = Env.ZERO;
|
||||||
|
totalAmtAcctDr = (BigDecimal) valuesMatchInv.get(1);
|
||||||
|
if (totalAmtAcctDr == null)
|
||||||
|
totalAmtAcctDr = Env.ZERO;
|
||||||
|
totalAmtSourceCr = (BigDecimal) valuesMatchInv.get(2);
|
||||||
|
if (totalAmtSourceCr == null)
|
||||||
|
totalAmtSourceCr = Env.ZERO;
|
||||||
|
totalAmtAcctCr = (BigDecimal) valuesMatchInv.get(3);
|
||||||
|
if (totalAmtAcctCr == null)
|
||||||
|
totalAmtAcctCr = Env.ZERO;
|
||||||
|
|
||||||
|
matchInvAccounted = matchInvAccounted.subtract(totalAmtAcctDr).subtract(totalAmtAcctCr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
htMatchInvSource.put(invoice.getC_Invoice_ID(), matchInvSource);
|
||||||
|
htMatchInvAccounted.put(invoice.getC_Invoice_ID(), matchInvAccounted);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BigDecimal acctDifference = Env.ZERO;
|
||||||
|
StringBuilder sql = new StringBuilder()
|
||||||
|
.append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)")
|
||||||
|
.append(" FROM Fact_Acct ")
|
||||||
|
.append("WHERE AD_Table_ID=? AND Record_ID IN (").append(m_matchInv.getReversal_ID()).append(")")
|
||||||
|
.append(" AND Record_ID <> ?")
|
||||||
|
.append(" AND C_AcctSchema_ID=?")
|
||||||
|
.append(" AND PostingType='A'")
|
||||||
|
.append(" AND Account_ID=?");
|
||||||
|
|
||||||
|
// For Match Inv
|
||||||
|
List<Object> valuesMatchInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(),
|
||||||
|
MMatchInv.Table_ID, get_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID());
|
||||||
|
if (valuesMatchInv != null) {
|
||||||
|
totalAmtSourceDr = (BigDecimal) valuesMatchInv.get(0);
|
||||||
|
if (totalAmtSourceDr == null)
|
||||||
|
totalAmtSourceDr = Env.ZERO;
|
||||||
|
totalAmtAcctDr = (BigDecimal) valuesMatchInv.get(1);
|
||||||
|
if (totalAmtAcctDr == null)
|
||||||
|
totalAmtAcctDr = Env.ZERO;
|
||||||
|
totalAmtSourceCr = (BigDecimal) valuesMatchInv.get(2);
|
||||||
|
if (totalAmtSourceCr == null)
|
||||||
|
totalAmtSourceCr = Env.ZERO;
|
||||||
|
totalAmtAcctCr = (BigDecimal) valuesMatchInv.get(3);
|
||||||
|
if (totalAmtAcctCr == null)
|
||||||
|
totalAmtAcctCr = Env.ZERO;
|
||||||
|
|
||||||
|
if (totalAmtAcctDr.compareTo(totalAmtAcctCr) > 0)
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceDr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr);
|
||||||
|
acctDifference = totalAmtAcctCr.negate();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matchInvSource = matchInvSource.add(totalAmtSourceCr);
|
||||||
|
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr);
|
||||||
|
acctDifference = totalAmtAcctDr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
htMatchInvSource.put(invoice.getC_Invoice_ID(), matchInvSource);
|
||||||
|
htMatchInvAccounted.put(invoice.getC_Invoice_ID(), matchInvAccounted);
|
||||||
|
htMatchInvAcctDiff.put(invoice.getC_Invoice_ID(), acctDifference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MInvoice invoice : invList)
|
||||||
|
{
|
||||||
|
BigDecimal invSource = htInvSource.get(invoice.getC_Invoice_ID());
|
||||||
|
if (invSource == null)
|
||||||
|
invSource = Env.ZERO;
|
||||||
|
BigDecimal invAccounted = htInvAccounted.get(invoice.getC_Invoice_ID());
|
||||||
|
if (invAccounted == null)
|
||||||
|
invAccounted = Env.ZERO;
|
||||||
|
BigDecimal matchInvSource = htMatchInvSource.get(invoice.getC_Invoice_ID());
|
||||||
|
if (matchInvSource == null)
|
||||||
|
matchInvSource = Env.ZERO;
|
||||||
|
BigDecimal matchInvAccounted = htMatchInvAccounted.get(invoice.getC_Invoice_ID());
|
||||||
|
if (matchInvAccounted == null)
|
||||||
|
matchInvAccounted = Env.ZERO;
|
||||||
|
BigDecimal acctDifference = htMatchInvAcctDiff.get(invoice.getC_Invoice_ID());
|
||||||
|
|
||||||
|
StringBuilder description = new StringBuilder("Invoice=(").append(getC_Currency_ID()).append(")").append(invSource).append("/").append(invAccounted)
|
||||||
|
.append(" - Match Invoice=(").append(getC_Currency_ID()).append(")").append(matchInvSource).append("/").append(matchInvAccounted);
|
||||||
|
if (log.isLoggable(Level.FINE)) log.fine(description.toString());
|
||||||
|
if (acctDifference == null && matchInvSource.abs().compareTo(invSource.abs()) == 0)
|
||||||
|
{
|
||||||
|
acctDifference = invAccounted.abs().subtract(matchInvAccounted.abs()); // gain is negative
|
||||||
|
StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference);
|
||||||
|
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||||
|
description.append(" - ").append(d2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acctDifference == null || acctDifference.signum() == 0)
|
||||||
|
{
|
||||||
|
log.fine("No Difference");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acctDifference.abs().compareTo(TOLERANCE) > 0)
|
||||||
|
{
|
||||||
|
log.fine("acctDifference="+acctDifference);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (invoice.isSOTrx())
|
||||||
|
{
|
||||||
|
FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference);
|
||||||
|
fl.setDescription(description.toString());
|
||||||
|
updateFactLine(fl);
|
||||||
|
|
||||||
|
if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID())
|
||||||
|
fl = fact.createLine (null, as.getCurrencyBalancing_Acct(), as.getC_Currency_ID(), acctDifference.negate());
|
||||||
|
else
|
||||||
|
fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference.negate());
|
||||||
|
fl.setDescription(description.toString());
|
||||||
|
updateFactLine(fl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference.negate());
|
||||||
|
fl.setDescription(description.toString());
|
||||||
|
updateFactLine(fl);
|
||||||
|
|
||||||
|
if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID())
|
||||||
|
fl = fact.createLine (null, as.getCurrencyBalancing_Acct(), as.getC_Currency_ID(), acctDifference);
|
||||||
|
else
|
||||||
|
fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference);
|
||||||
|
fl.setDescription(description.toString());
|
||||||
|
updateFactLine(fl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private String createReceiptGainLoss(MAcctSchema as, Fact fact, MAccount acct,
|
private String createReceiptGainLoss(MAcctSchema as, Fact fact, MAccount acct,
|
||||||
MInOut receipt, BigDecimal matchInvSource, BigDecimal matchInvAccounted)
|
MInOut receipt, BigDecimal matchInvSource, BigDecimal matchInvAccounted)
|
||||||
{
|
{
|
||||||
|
|
|
@ -339,4 +339,185 @@ public class MatchInvTest extends AbstractTestCase {
|
||||||
|
|
||||||
rollback();
|
rollback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
/**
|
||||||
|
* Test the matched invoice posting for credit memo
|
||||||
|
* PO Qty=10 > IV Qty=10 > MR Qty=9 > CM Qty=1
|
||||||
|
*/
|
||||||
|
public void testCreditMemoPosting() {
|
||||||
|
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.TEN);
|
||||||
|
orderLine.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
|
||||||
|
order.load(getTrxName());
|
||||||
|
assertFalse(info.isError());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
|
||||||
|
|
||||||
|
MInvoice invoice = new MInvoice(Env.getCtx(), 0, getTrxName());
|
||||||
|
invoice.setOrder(order);
|
||||||
|
invoice.setDateAcct(order.getDateOrdered());
|
||||||
|
invoice.setSalesRep_ID(order.getSalesRep_ID());
|
||||||
|
invoice.setC_BPartner_ID(order.getBill_BPartner_ID());
|
||||||
|
invoice.setC_BPartner_Location_ID(order.getBill_Location_ID());
|
||||||
|
invoice.setAD_User_ID(order.getBill_User_ID());
|
||||||
|
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.setC_OrderLine_ID(orderLine.get_ID());
|
||||||
|
invoiceLine.setLine(10);
|
||||||
|
invoiceLine.setProduct(product);
|
||||||
|
invoiceLine.setQty(BigDecimal.TEN);
|
||||||
|
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());
|
||||||
|
|
||||||
|
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(new BigDecimal(9));
|
||||||
|
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());
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MInvoice creditMemo = new MInvoice(Env.getCtx(), 0, getTrxName());
|
||||||
|
creditMemo.setOrder(order);
|
||||||
|
creditMemo.setDateAcct(order.getDateOrdered());
|
||||||
|
creditMemo.setSalesRep_ID(order.getSalesRep_ID());
|
||||||
|
creditMemo.setC_BPartner_ID(order.getBill_BPartner_ID());
|
||||||
|
creditMemo.setC_BPartner_Location_ID(order.getBill_Location_ID());
|
||||||
|
creditMemo.setAD_User_ID(order.getBill_User_ID());
|
||||||
|
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.setC_OrderLine_ID(orderLine.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());
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
ProductCost pc = new ProductCost (Env.getCtx(), mi.getM_Product_ID(), mi.getM_AttributeSetInstance_ID(), getTrxName());
|
||||||
|
MAccount acctInvClr = pc.getAccount(ProductCost.ACCTTYPE_P_InventoryClearing, as);
|
||||||
|
|
||||||
|
BigDecimal amtAcctDrInvClr = BigDecimal.ZERO;
|
||||||
|
BigDecimal amtAcctCrInvClr = BigDecimal.ZERO;
|
||||||
|
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() == acctInvClr.getAccount_ID() && fa.getQty().compareTo(BigDecimal.ZERO) < 0) {
|
||||||
|
assertTrue(fa.getAmtAcctCr().compareTo(Env.ZERO) >= 0);
|
||||||
|
amtAcctCrInvClr = amtAcctCrInvClr.add(fa.getAmtAcctCr());
|
||||||
|
}
|
||||||
|
else if (fa.getAccount_ID() == acctInvClr.getAccount_ID() && fa.getQty().compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
assertTrue(fa.getAmtAcctDr().compareTo(Env.ZERO) >= 0);
|
||||||
|
amtAcctDrInvClr = amtAcctDrInvClr.add(fa.getAmtAcctDr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(amtAcctDrInvClr.compareTo(amtAcctCrInvClr) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
rollback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue