IDEMPIERE-4127 GL Inventory Clearing - Minor rounding variances
This commit is contained in:
parent
5b9f61ae52
commit
7173b78f68
|
@ -31,12 +31,12 @@ import org.compiere.model.MAllocationHdr;
|
|||
import org.compiere.model.MAllocationLine;
|
||||
import org.compiere.model.MCashLine;
|
||||
import org.compiere.model.MConversionRate;
|
||||
import org.compiere.model.MCurrency;
|
||||
import org.compiere.model.MFactAcct;
|
||||
import org.compiere.model.MInvoice;
|
||||
import org.compiere.model.MInvoiceLine;
|
||||
import org.compiere.model.MOrder;
|
||||
import org.compiere.model.MPayment;
|
||||
import org.compiere.model.MPeriod;
|
||||
import org.compiere.util.CLogger;
|
||||
import org.compiere.util.DB;
|
||||
import org.compiere.util.Env;
|
||||
|
@ -75,6 +75,9 @@ public class Doc_AllocationHdr extends Doc
|
|||
private static final BigDecimal TOLERANCE = BigDecimal.valueOf(0.02);
|
||||
/** Facts */
|
||||
private ArrayList<Fact> m_facts = null;
|
||||
BigDecimal gainLossAmt =Env.ZERO;
|
||||
private BigDecimal cmGainLossAmt=Env.ZERO;
|
||||
private ArrayList<FactLine> gainLossFactList;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -174,6 +177,12 @@ public class Doc_AllocationHdr extends Doc
|
|||
Fact fact = new Fact(this, as, Fact.POST_Actual);
|
||||
Fact factForRGL = new Fact(this, as, Fact.POST_Actual); // dummy fact (not posted) to calculate Realized Gain & Loss
|
||||
boolean isInterOrg = isInterOrg(as);
|
||||
BigDecimal paymentSelectAmt = Env.ZERO;
|
||||
BigDecimal totalAllocationSource = Env.ZERO;
|
||||
MPayment payment = null;
|
||||
int lineID = 0;
|
||||
MAccount bpAcct = null; // Liability/Receivables
|
||||
gainLossFactList = new ArrayList<FactLine>();
|
||||
|
||||
for (int i = 0; i < p_lines.length; i++)
|
||||
{
|
||||
|
@ -199,10 +208,7 @@ public class Doc_AllocationHdr extends Doc
|
|||
|
||||
FactLine fl = null;
|
||||
FactLine flForRGL = null;
|
||||
MAccount bpAcct = null; // Liability/Receivables
|
||||
MAccount payAcct = null; // Payment Selection
|
||||
//
|
||||
MPayment payment = null;
|
||||
if (line.getC_Payment_ID() != 0)
|
||||
payment = new MPayment (getCtx(), line.getC_Payment_ID(), getTrxName());
|
||||
MInvoice invoice = null;
|
||||
|
@ -254,7 +260,8 @@ public class Doc_AllocationHdr extends Doc
|
|||
// if not using clearing accounts, then don't post amtsource
|
||||
// change the allocationsource to be writeoff + discount
|
||||
allocationSource = line.getDiscountAmt().add(line.getWriteOffAmt());
|
||||
payAcct = getPaymentAcct(as, line.getC_Payment_ID());
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// Normal behavior -- unchanged if using clearing accounts
|
||||
|
@ -262,11 +269,14 @@ public class Doc_AllocationHdr extends Doc
|
|||
// Payment/Cash DR
|
||||
if (line.getC_Payment_ID() != 0)
|
||||
{
|
||||
payAcct = getPaymentAcct(as, line.getC_Payment_ID());
|
||||
fl = fact.createLine (line, payAcct,
|
||||
fl = fact.createLine (line, getPaymentAcct(as, line.getC_Payment_ID()),
|
||||
getC_Currency_ID(), line.getAmtSource(), null);
|
||||
if (fl != null && payment != null)
|
||||
fl.setAD_Org_ID(payment.getAD_Org_ID());
|
||||
if (payment.getReversal_ID() > 0 )
|
||||
paymentSelectAmt= paymentSelectAmt.add(fl.getAcctBalance().negate());
|
||||
else
|
||||
paymentSelectAmt= paymentSelectAmt.add(fl.getAcctBalance());
|
||||
}
|
||||
else if (line.getC_CashLine_ID() != 0)
|
||||
{
|
||||
|
@ -393,11 +403,11 @@ public class Doc_AllocationHdr extends Doc
|
|||
// Payment/Cash CR
|
||||
if (isUsingClearing && line.getC_Payment_ID() != 0) // Avoid usage of clearing accounts
|
||||
{
|
||||
payAcct = getPaymentAcct(as, line.getC_Payment_ID());
|
||||
fl = fact.createLine (line, payAcct,
|
||||
fl = fact.createLine (line, getPaymentAcct(as, line.getC_Payment_ID()),
|
||||
getC_Currency_ID(), null, line.getAmtSource().negate());
|
||||
if (fl != null && payment != null)
|
||||
fl.setAD_Org_ID(payment.getAD_Org_ID());
|
||||
paymentSelectAmt= paymentSelectAmt.add(fl.getAcctBalance().negate());
|
||||
}
|
||||
else if (isUsingClearing && line.getC_CashLine_ID() != 0) // Avoid usage of clearing accounts
|
||||
{
|
||||
|
@ -435,14 +445,31 @@ public class Doc_AllocationHdr extends Doc
|
|||
&& (getC_Currency_ID() != as.getC_Currency_ID() // payment allocation in foreign currency
|
||||
|| getC_Currency_ID() != line.getInvoiceC_Currency_ID())) // allocation <> invoice currency
|
||||
{
|
||||
p_Error = createRealizedGainLoss (line, as, fact, bpAcct, invoice, payAcct, payment,
|
||||
allocationSourceForRGL, allocationAccounted);
|
||||
p_Error = createRealizedGainLoss (line, as, fact, bpAcct, invoice,
|
||||
allocationSource, allocationAccounted);
|
||||
if (p_Error != null)
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
totalAllocationSource = totalAllocationSource.add(line.getAmtSource());
|
||||
lineID = line.get_ID();
|
||||
} // for all lines
|
||||
|
||||
// rounding correction
|
||||
if (payment != null && getC_Currency_ID() != as.getC_Currency_ID() ) // payment allocation in foreign currency
|
||||
{
|
||||
p_Error = createPaymentGainLoss (as, fact, getPaymentAcct(as, payment.get_ID()), payment,
|
||||
totalAllocationSource, paymentSelectAmt, lineID);
|
||||
if (p_Error != null)
|
||||
return null;
|
||||
}
|
||||
|
||||
if (getC_Currency_ID() != as.getC_Currency_ID())
|
||||
{
|
||||
p_Error = createInvoiceRounding (as, fact, bpAcct);
|
||||
if (p_Error != null)
|
||||
return null;
|
||||
}
|
||||
|
||||
// FR [ 1840016 ] Avoid usage of clearing accounts - subject to C_AcctSchema.IsPostIfClearingEqual
|
||||
if ( (!as.isPostIfClearingEqual()) && p_lines.length > 0 && (!isInterOrg)) {
|
||||
boolean allEquals = true;
|
||||
|
@ -652,7 +679,7 @@ public class Doc_AllocationHdr extends Doc
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.log(Level.SEVERE, sql, e);
|
||||
throw new RuntimeException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -697,21 +724,17 @@ public class Doc_AllocationHdr extends Doc
|
|||
* Accounted Amount of the Allocation
|
||||
* @param as accounting schema
|
||||
* @param fact fact
|
||||
* @param invAcct invoice account
|
||||
* @param acct account
|
||||
* @param invoice invoice
|
||||
* @param payAcct payment account
|
||||
* @param payment payment
|
||||
* @param allocationSource source amt
|
||||
* @param allocationAccounted acct amt
|
||||
* @return Error Message or null if OK
|
||||
*/
|
||||
private String createRealizedGainLoss (DocLine line, MAcctSchema as, Fact fact, MAccount invAcct,
|
||||
MInvoice invoice, MAccount payAcct, MPayment payment, BigDecimal allocationSource, BigDecimal allocationAccounted)
|
||||
private String createRealizedGainLoss (DocLine line, MAcctSchema as, Fact fact, MAccount acct,
|
||||
MInvoice invoice, BigDecimal allocationSource, BigDecimal allocationAccounted)
|
||||
{
|
||||
BigDecimal invoiceSource = null;
|
||||
BigDecimal invoiceAccounted = null;
|
||||
BigDecimal paymentSource = null;
|
||||
BigDecimal paymentAccounted = null;
|
||||
//
|
||||
StringBuilder sql = new StringBuilder()
|
||||
.append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)")
|
||||
|
@ -732,129 +755,66 @@ public class Doc_AllocationHdr extends Doc
|
|||
invoiceAccounted = (BigDecimal) valuesInv.get(3); // AmtAcctCr
|
||||
}
|
||||
}
|
||||
|
||||
// Requires that Invoice is Posted
|
||||
if (invoiceSource == null || invoiceAccounted == null)
|
||||
return "Gain/Loss - Invoice not posted yet";
|
||||
//
|
||||
String invoiceCur = MCurrency.get(getCtx(), invoice.getC_Currency_ID()).getISO_Code();
|
||||
String allocCur = MCurrency.get(getCtx(), getC_Currency_ID()).getISO_Code();
|
||||
StringBuilder descriptionInv = new StringBuilder("Invoice=(").append(invoiceCur).append(")").append(invoiceSource).append("/").append(invoiceAccounted)
|
||||
.append(" - Allocation=(").append(allocCur).append(")").append(allocationSource).append("/").append(allocationAccounted);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(descriptionInv.toString());
|
||||
StringBuilder description = new StringBuilder("Invoice=(").append(invoice.getC_Currency_ID()).append(")").append(invoiceSource).append("/").append(invoiceAccounted)
|
||||
.append(" - Allocation=(").append(getC_Currency_ID()).append(")").append(allocationSource).append("/").append(allocationAccounted);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(description.toString());
|
||||
// Allocation not Invoice Currency
|
||||
BigDecimal allocationInvoiceSource = allocationSource;
|
||||
if (getC_Currency_ID() != invoice.getC_Currency_ID())
|
||||
{
|
||||
allocationInvoiceSource = MConversionRate.convert(getCtx(),
|
||||
BigDecimal allocationSourceNew = MConversionRate.convert(getCtx(),
|
||||
allocationSource, getC_Currency_ID(),
|
||||
invoice.getC_Currency_ID(), getDateAcct(),
|
||||
invoice.getC_ConversionType_ID(), invoice.getAD_Client_ID(), invoice.getAD_Org_ID());
|
||||
if (allocationInvoiceSource == null)
|
||||
if (allocationSourceNew == null)
|
||||
return "Gain/Loss - No Conversion from Allocation->Invoice";
|
||||
StringBuilder d2 = new StringBuilder("Allocation=(").append(allocCur).append(")").append(allocationSource)
|
||||
.append("->(").append(invoiceCur).append(")").append(allocationInvoiceSource);
|
||||
StringBuilder d2 = new StringBuilder("Allocation=(").append(getC_Currency_ID()).append(")").append(allocationSource)
|
||||
.append("->(").append(invoice.getC_Currency_ID()).append(")").append(allocationSourceNew);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||
descriptionInv.append(" - ").append(d2);
|
||||
description.append(" - ").append(d2);
|
||||
allocationSource = allocationSourceNew;
|
||||
}
|
||||
|
||||
BigDecimal invoiceDifference = null; // gain is negative
|
||||
// Full Invoice in currency
|
||||
if (allocationInvoiceSource.compareTo(invoiceSource) == 0)
|
||||
BigDecimal acctDifference = null; // gain is negative
|
||||
//reversal entry
|
||||
if (allocationSource.signum() > 0 )
|
||||
{
|
||||
invoiceDifference = invoiceAccounted.subtract(allocationAccounted); // gain is negative
|
||||
StringBuilder d2 = new StringBuilder("(full) = ").append(invoiceDifference);
|
||||
acctDifference = invoiceAccounted.subtract(allocationAccounted.abs());
|
||||
}
|
||||
// Full Payment in currency
|
||||
if (allocationSource.compareTo(invoiceSource) == 0)
|
||||
{
|
||||
acctDifference = invoiceAccounted.subtract(allocationAccounted.abs()); // gain is negative
|
||||
|
||||
StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||
descriptionInv.append(" - ").append(d2);
|
||||
description.append(" - ").append(d2);
|
||||
}
|
||||
else // partial or MC
|
||||
{
|
||||
// percent of total payment
|
||||
double multiplier = allocationInvoiceSource.doubleValue() / invoiceSource.doubleValue();
|
||||
double multiplier = allocationSource.doubleValue() / invoiceSource.doubleValue();
|
||||
// Reduce Orig Invoice Accounted
|
||||
invoiceAccounted = invoiceAccounted.multiply(BigDecimal.valueOf(multiplier));
|
||||
// Difference based on percentage of Orig Invoice
|
||||
invoiceDifference = invoiceAccounted.subtract(allocationAccounted); // gain is negative
|
||||
acctDifference = invoiceAccounted.subtract(allocationAccounted); // gain is negative
|
||||
// ignore Tolerance
|
||||
if (invoiceDifference.abs().compareTo(TOLERANCE) < 0)
|
||||
invoiceDifference = Env.ZERO;
|
||||
if (acctDifference.abs().compareTo(TOLERANCE) < 0)
|
||||
acctDifference = Env.ZERO;
|
||||
// Round
|
||||
int precision = as.getStdPrecision();
|
||||
if (invoiceDifference.scale() > precision)
|
||||
invoiceDifference = invoiceDifference.setScale(precision, RoundingMode.HALF_UP);
|
||||
StringBuilder d2 = new StringBuilder("(partial) = ").append(invoiceDifference).append(" - Multiplier=").append(multiplier);
|
||||
if (acctDifference.scale() > precision)
|
||||
acctDifference = acctDifference.setScale(precision, RoundingMode.HALF_UP);
|
||||
StringBuilder d2 = new StringBuilder("(partial) = ").append(acctDifference).append(" - Multiplier=").append(multiplier);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||
descriptionInv.append(" - ").append(d2);
|
||||
description.append(" - ").append(d2);
|
||||
}
|
||||
|
||||
// For Payment
|
||||
BigDecimal paymentDifference = Env.ZERO;
|
||||
StringBuilder descriptionPay = null;
|
||||
if (payment != null && payment.getC_Payment_ID() > 0) {
|
||||
List<Object> valuesPay = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(),
|
||||
MPayment.Table_ID, payment.getC_Payment_ID(), as.getC_AcctSchema_ID());
|
||||
if (valuesPay != null) {
|
||||
if (invoice.isSOTrx()) {
|
||||
paymentSource = (BigDecimal) valuesPay.get(2); // AmtSourceCr
|
||||
paymentAccounted = (BigDecimal) valuesPay.get(3); // AmtAcctCr
|
||||
} else {
|
||||
paymentSource = (BigDecimal) valuesPay.get(0); // AmtSourceDr
|
||||
paymentAccounted = (BigDecimal) valuesPay.get(1); // AmtAcctDr
|
||||
}
|
||||
}
|
||||
// Requires that Payment is Posted
|
||||
if (paymentSource == null || paymentAccounted == null)
|
||||
return "Gain/Loss - Payment not posted yet";
|
||||
//
|
||||
String paymentCur = MCurrency.get(getCtx(), payment.getC_Currency_ID()).getISO_Code();
|
||||
descriptionPay = new StringBuilder("Payment=(").append(paymentCur).append(")").append(paymentSource).append("/").append(paymentAccounted)
|
||||
.append(" - Allocation=(").append(allocCur).append(")").append(allocationSource).append("/").append(allocationAccounted);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(descriptionPay.toString());
|
||||
// Allocation not Payment Currency
|
||||
BigDecimal allocationPaymentSource = allocationSource;
|
||||
if (getC_Currency_ID() != payment.getC_Currency_ID())
|
||||
{
|
||||
allocationPaymentSource = MConversionRate.convert(getCtx(),
|
||||
allocationSource, getC_Currency_ID(),
|
||||
payment.getC_Currency_ID(), getDateAcct(),
|
||||
payment.getC_ConversionType_ID(), payment.getAD_Client_ID(), payment.getAD_Org_ID());
|
||||
if (allocationPaymentSource == null)
|
||||
return "Gain/Loss - No Conversion from Allocation->Payment";
|
||||
StringBuilder d2 = new StringBuilder("Allocation=(").append(allocCur).append(")").append(allocationSource)
|
||||
.append("->(").append(paymentCur).append(")").append(allocationPaymentSource);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||
descriptionPay.append(" - ").append(d2);
|
||||
}
|
||||
|
||||
// Full Payment in currency
|
||||
if (allocationPaymentSource.compareTo(paymentSource) == 0)
|
||||
{
|
||||
paymentDifference = paymentAccounted.subtract(allocationAccounted); // gain is negative
|
||||
StringBuilder d2 = new StringBuilder("(full) = ").append(paymentDifference);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||
descriptionPay.append(" - ").append(d2);
|
||||
}
|
||||
else // partial or MC
|
||||
{
|
||||
// percent of total payment
|
||||
double multiplier = allocationPaymentSource.doubleValue() / paymentSource.doubleValue();
|
||||
// Reduce Orig Payment Accounted
|
||||
paymentAccounted = paymentAccounted.multiply(BigDecimal.valueOf(multiplier));
|
||||
// Difference based on percentage of Orig Payment
|
||||
paymentDifference = paymentAccounted.subtract(allocationAccounted); // gain is negative
|
||||
// ignore Tolerance
|
||||
if (paymentDifference.abs().compareTo(TOLERANCE) < 0)
|
||||
paymentDifference = Env.ZERO;
|
||||
// Round
|
||||
int precision = as.getStdPrecision();
|
||||
if (paymentDifference.scale() > precision)
|
||||
paymentDifference = paymentDifference.setScale(precision, RoundingMode.HALF_UP);
|
||||
StringBuilder d2 = new StringBuilder("(partial) = ").append(paymentDifference).append(" - Multiplier=").append(multiplier);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||
descriptionPay.append(" - ").append(d2);
|
||||
}
|
||||
}
|
||||
|
||||
if (invoiceDifference.signum() == 0 && paymentDifference.signum() == 0)
|
||||
if (acctDifference.signum() == 0)
|
||||
{
|
||||
log.fine("No Difference");
|
||||
return null;
|
||||
|
@ -862,51 +822,28 @@ public class Doc_AllocationHdr extends Doc
|
|||
|
||||
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
|
||||
MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct());
|
||||
if (invoice.isCreditMemo() || invoice.getReversal_ID() > 0)
|
||||
cmGainLossAmt = cmGainLossAmt.add(acctDifference);
|
||||
else
|
||||
gainLossAmt = gainLossAmt.add(acctDifference);
|
||||
//
|
||||
BigDecimal acctDifference = invoiceDifference.subtract(paymentDifference);
|
||||
|
||||
if (invoice.isSOTrx())
|
||||
{
|
||||
if (acctDifference.signum() != 0) {
|
||||
FactLine fl = fact.createLine (line, loss, gain, as.getC_Currency_ID(), acctDifference);
|
||||
StringBuilder description = new StringBuilder(descriptionInv);
|
||||
if (paymentDifference.signum() != 0 && descriptionPay != null) {
|
||||
description.append(" / ").append(descriptionPay);
|
||||
}
|
||||
fl.setDescription(description.toString());
|
||||
fl.setAD_Org_ID(invoice.getAD_Org_ID());
|
||||
}
|
||||
if (invoiceDifference.signum() != 0) {
|
||||
FactLine fl = fact.createLine (line, invAcct, as.getC_Currency_ID(), invoiceDifference.negate());
|
||||
fl.setDescription(descriptionInv.toString());
|
||||
fl.setAD_Org_ID(invoice.getAD_Org_ID());
|
||||
}
|
||||
if (paymentDifference.signum() != 0) {
|
||||
FactLine fl = fact.createLine (line, payAcct, as.getC_Currency_ID(), paymentDifference);
|
||||
fl.setDescription(descriptionPay.toString());
|
||||
fl.setAD_Org_ID(invoice.getAD_Org_ID());
|
||||
}
|
||||
FactLine fl = fact.createLine (line, loss, gain, as.getC_Currency_ID(), acctDifference);
|
||||
fl.setDescription(description.toString());
|
||||
fl = fact.createLine (line, acct, as.getC_Currency_ID(), acctDifference.negate());
|
||||
gainLossFactList.add(fl);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (invoiceDifference.signum() != 0) {
|
||||
FactLine fl = fact.createLine (line, invAcct, as.getC_Currency_ID(), invoiceDifference);
|
||||
fl.setDescription(descriptionInv.toString());
|
||||
fl.setAD_Org_ID(invoice.getAD_Org_ID());
|
||||
}
|
||||
if (paymentDifference.signum() != 0) {
|
||||
FactLine fl = fact.createLine (line, payAcct, as.getC_Currency_ID(), paymentDifference.negate());
|
||||
fl.setDescription(descriptionPay.toString());
|
||||
fl.setAD_Org_ID(invoice.getAD_Org_ID());
|
||||
}
|
||||
if (acctDifference.signum() != 0) {
|
||||
FactLine fl = fact.createLine (line, loss, gain, as.getC_Currency_ID(), acctDifference.negate());
|
||||
StringBuilder description = new StringBuilder(descriptionInv);
|
||||
if (paymentDifference.signum() != 0 && descriptionPay != null) {
|
||||
description.append(" / ").append(descriptionPay);
|
||||
}
|
||||
fl.setDescription(description.toString());
|
||||
fl.setAD_Org_ID(invoice.getAD_Org_ID());
|
||||
}
|
||||
FactLine fl = fact.createLine (line, acct,
|
||||
as.getC_Currency_ID(), acctDifference);
|
||||
gainLossFactList.add(fl);
|
||||
fl = fact.createLine (line, loss, gain,
|
||||
as.getC_Currency_ID(), acctDifference.negate());
|
||||
fl.setDescription(description.toString());
|
||||
|
||||
}
|
||||
return null;
|
||||
} // createRealizedGainLoss
|
||||
|
@ -947,7 +884,7 @@ public class Doc_AllocationHdr extends Doc
|
|||
// Get Source Amounts with account
|
||||
String sql = "SELECT * "
|
||||
+ "FROM Fact_Acct "
|
||||
+ "WHERE AD_Table_ID=318 AND Record_ID=?" // Invoice
|
||||
+ "WHERE AD_Table_ID=? AND Record_ID=?" // Invoice
|
||||
+ " AND C_AcctSchema_ID=?"
|
||||
+ " AND Line_ID IS NULL"; // header lines like tax or total
|
||||
PreparedStatement pstmt = null;
|
||||
|
@ -955,15 +892,16 @@ public class Doc_AllocationHdr extends Doc
|
|||
try
|
||||
{
|
||||
pstmt = DB.prepareStatement(sql, getTrxName());
|
||||
pstmt.setInt(1, line.getC_Invoice_ID());
|
||||
pstmt.setInt(2, as.getC_AcctSchema_ID());
|
||||
pstmt.setInt(1, MInvoice.Table_ID);
|
||||
pstmt.setInt(2, line.getC_Invoice_ID());
|
||||
pstmt.setInt(3, as.getC_AcctSchema_ID());
|
||||
rs = pstmt.executeQuery();
|
||||
while (rs.next())
|
||||
tax.addInvoiceFact (new MFactAcct(getCtx(), rs, fact.get_TrxName()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.log(Level.SEVERE, sql, e);
|
||||
throw new RuntimeException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
finally {
|
||||
DB.close(rs, pstmt);
|
||||
|
@ -982,6 +920,430 @@ public class Doc_AllocationHdr extends Doc
|
|||
|
||||
} // createTaxCorrection
|
||||
|
||||
/**************************************************************************
|
||||
* Create Rounding Correction.
|
||||
* Compares the Accounted Amount of the Payment to the
|
||||
* Accounted Amount of the Allocation
|
||||
* @param as accounting schema
|
||||
* @param fact fact
|
||||
* @param acct account
|
||||
* @param payment payment
|
||||
* @param paymentSource source amt
|
||||
* @param paymentAccounted acct amt
|
||||
* @return Error Message or null if OK
|
||||
*/
|
||||
private String createPaymentGainLoss (MAcctSchema as, Fact fact, MAccount acct,
|
||||
MPayment payment, BigDecimal allocationSource, BigDecimal totalAllocationAccounted, int lineID)
|
||||
{
|
||||
BigDecimal paymentSource = null;
|
||||
BigDecimal paymentAccounted = null;
|
||||
//
|
||||
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 Payment
|
||||
List<Object> valuesPay = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(),
|
||||
MPayment.Table_ID, payment.getC_Payment_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID());
|
||||
if (valuesPay != null) {
|
||||
if (payment.isReceipt()) {
|
||||
paymentSource = (BigDecimal) valuesPay.get(2); // AmtSourceCr
|
||||
paymentAccounted = (BigDecimal) valuesPay.get(3); // AmtAcctCr
|
||||
} else {
|
||||
paymentSource = (BigDecimal) valuesPay.get(0); // AmtSourceDr
|
||||
paymentAccounted = (BigDecimal) valuesPay.get(1); // AmtAcctDr
|
||||
}
|
||||
}
|
||||
|
||||
// Requires that Allocation is Posted
|
||||
if (paymentSource == null || paymentAccounted == null)
|
||||
return null; //"Gain/Loss - Payment not posted yet";
|
||||
//
|
||||
StringBuilder description = new StringBuilder("Payment=(").append(payment.getC_Currency_ID()).append(")").append(paymentSource).append("/").append(paymentAccounted)
|
||||
.append(" - Allocation=(").append(getC_Currency_ID()).append(")").append(allocationSource).append("/").append(totalAllocationAccounted);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(description.toString());
|
||||
|
||||
boolean isSameSourceDiffPeriod = false;
|
||||
BigDecimal acctDifference = null; // gain is negative
|
||||
// Full Payment in currency
|
||||
if (allocationSource.abs().compareTo(paymentSource.abs()) == 0)
|
||||
{
|
||||
acctDifference = totalAllocationAccounted.subtract(paymentAccounted.abs()); // gain is negative
|
||||
StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||
description.append(" - ").append(d2);
|
||||
|
||||
// Different period
|
||||
if (MPeriod.getC_Period_ID(getCtx(), payment.getDateAcct(), payment.getAD_Org_ID()) !=
|
||||
MPeriod.getC_Period_ID(getCtx(), getDateAcct(), getAD_Org_ID()))
|
||||
{
|
||||
BigDecimal allocationAccounted0 = MConversionRate.convert(getCtx(),
|
||||
allocationSource, getC_Currency_ID(),
|
||||
as.getC_Currency_ID(), payment.getDateAcct(),
|
||||
payment.getC_ConversionType_ID(), payment.getAD_Client_ID(), payment.getAD_Org_ID());
|
||||
BigDecimal paymentAccounted0 = MConversionRate.convert(getCtx(),
|
||||
paymentSource, getC_Currency_ID(),
|
||||
as.getC_Currency_ID(), getDateAcct(),
|
||||
getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID());
|
||||
isSameSourceDiffPeriod = allocationAccounted0.abs().compareTo(paymentAccounted.abs()) == 0 &&
|
||||
paymentAccounted0.abs().compareTo(totalAllocationAccounted.abs()) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (acctDifference == null || acctDifference.signum() == 0)
|
||||
{
|
||||
log.fine("No Difference");
|
||||
return null;
|
||||
}
|
||||
|
||||
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
|
||||
MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct());
|
||||
//
|
||||
if (payment.isReceipt())
|
||||
{
|
||||
FactLine fl = fact.createLine (null, acct,as.getC_Currency_ID(), acctDifference.negate());
|
||||
fl.setDescription(description.toString());
|
||||
fl.setLine_ID(lineID);
|
||||
if (!fact.isAcctBalanced())
|
||||
{
|
||||
if (!isSameSourceDiffPeriod && as.isCurrencyBalancing() && as.getC_Currency_ID() != payment.getC_Currency_ID() )
|
||||
{
|
||||
fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference);
|
||||
} else
|
||||
{
|
||||
fl = fact.createLine (null, loss, gain,as.getC_Currency_ID(), acctDifference);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FactLine fl = fact.createLine (null, acct,as.getC_Currency_ID(), acctDifference);
|
||||
fl.setDescription(description.toString());
|
||||
fl.setLine_ID(lineID);
|
||||
if (!fact.isAcctBalanced())
|
||||
{
|
||||
if (!isSameSourceDiffPeriod && as.isCurrencyBalancing() && as.getC_Currency_ID() != payment.getC_Currency_ID() )
|
||||
{
|
||||
fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference.negate());
|
||||
} else {
|
||||
fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference.negate());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Create Rounding Correction.
|
||||
* Compares the Accounted Amount of the AR/AP Invoice to the
|
||||
* Accounted Amount of the AR/AP Allocation
|
||||
* @param as accounting schema
|
||||
* @param fact fact
|
||||
* @param bpAcct account
|
||||
* @param payment payment
|
||||
* @return Error Message or null if OK
|
||||
*/
|
||||
private String createInvoiceRounding(MAcctSchema as, Fact fact, MAccount bpAcct) {
|
||||
PreparedStatement pstmt = null;
|
||||
ResultSet rs = null;
|
||||
// Invoice AR/AP
|
||||
BigDecimal totalInvoiceSource = BigDecimal.ZERO;
|
||||
BigDecimal totalInvoiceAccounted = BigDecimal.ZERO;
|
||||
boolean isCMReversal =false ;
|
||||
MInvoice invoice = null;
|
||||
MPayment payment = null;
|
||||
ArrayList<MInvoice> invList = new ArrayList<MInvoice>();
|
||||
for (int i = 0; i < p_lines.length; i++)
|
||||
{
|
||||
DocLine_Allocation line = (DocLine_Allocation)p_lines[i];
|
||||
if (line.getC_Invoice_ID() != 0)
|
||||
invoice = new MInvoice (getCtx(), line.getC_Invoice_ID(), getTrxName());
|
||||
if (line.getC_Payment_ID() != 0)
|
||||
payment = new MPayment (getCtx(), line.getC_Payment_ID(), getTrxName());
|
||||
|
||||
if (invoice != null )
|
||||
{
|
||||
boolean isDebit = false;
|
||||
// to cater for invoice reverse-accrual.
|
||||
if (invoice.isSOTrx() && !invoice.isCreditMemo())
|
||||
isDebit = true;
|
||||
else if (!invoice.isSOTrx() && invoice.isCreditMemo() && invoice.getReversal_ID() > 0 )
|
||||
isDebit = true;
|
||||
else if (invoice.isSOTrx() && invoice.isCreditMemo() && invoice.getReversal_ID() == 0)
|
||||
isDebit = true;
|
||||
//
|
||||
StringBuilder sql = new StringBuilder("SELECT ")
|
||||
.append((isDebit )
|
||||
? "SUM(AmtSourceDr), SUM(AmtAcctDr)" // so
|
||||
: "SUM(AmtSourceCr), SUM(AmtAcctCr)") // po
|
||||
.append(" FROM Fact_Acct ")
|
||||
.append("WHERE AD_Table_ID=? AND Record_ID=?") // Invoice
|
||||
.append(" AND C_AcctSchema_ID=?")
|
||||
.append(" AND PostingType='A'")
|
||||
.append(" AND Account_ID= ? ");
|
||||
pstmt = null;
|
||||
rs = null;
|
||||
try
|
||||
{
|
||||
pstmt = DB.prepareStatement(sql.toString(), getTrxName());
|
||||
pstmt.setInt(1, MInvoice.Table_ID);
|
||||
pstmt.setInt(2, invoice.getC_Invoice_ID());
|
||||
pstmt.setInt(3, as.getC_AcctSchema_ID());
|
||||
pstmt.setInt(4, bpAcct.getAccount_ID());
|
||||
rs = pstmt.executeQuery();
|
||||
if (rs.next())
|
||||
{
|
||||
BigDecimal invoiceSource = rs.getBigDecimal(1);
|
||||
BigDecimal invoiceAccounted = rs.getBigDecimal(2);
|
||||
if ( !invList.contains(invoice))
|
||||
{
|
||||
totalInvoiceSource =totalInvoiceSource.add(invoiceSource);
|
||||
totalInvoiceAccounted =totalInvoiceAccounted.add(invoiceAccounted);
|
||||
}
|
||||
|
||||
|
||||
invList.add(invoice);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
finally {
|
||||
DB.close(rs, pstmt);
|
||||
rs = null; pstmt = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
BigDecimal allocInvoiceSource = BigDecimal.ZERO;
|
||||
BigDecimal allocInvoiceAccounted = BigDecimal.ZERO;
|
||||
MAllocationLine allocationLine = null;
|
||||
FactLine[] factlines = fact.getLines();
|
||||
boolean isExcludeCMGainLoss = false;
|
||||
for (FactLine factLine : factlines) {
|
||||
if (bpAcct != null) {
|
||||
if (factLine.getAccount_ID() == bpAcct.getAccount_ID() )
|
||||
{
|
||||
if (factLine.getLine_ID() != 0 )
|
||||
allocationLine = new MAllocationLine(getCtx(), factLine.getLine_ID(), getTrxName());
|
||||
|
||||
if (allocationLine != null)
|
||||
invoice = allocationLine.getInvoice();
|
||||
|
||||
if (invoice.isSOTrx())
|
||||
{
|
||||
if (factLine.getC_Currency_ID() != as.getC_Currency_ID())
|
||||
allocInvoiceSource = allocInvoiceSource.add(factLine.getAmtSourceCr());
|
||||
if (!gainLossFactList.contains(factLine) && !invoice.isCreditMemo())
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctCr());
|
||||
|
||||
if (!gainLossFactList.contains(factLine) && invoice.isCreditMemo() && invoice.getReversal_ID() > 0 ) {
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctDr());
|
||||
if (!invoice.getDateAcct().equals(getDateAcct()))
|
||||
isCMReversal =true;
|
||||
}
|
||||
if (invoice!=null)
|
||||
{
|
||||
if (invoice.isCreditMemo() || invoice.getReversal_ID() > 0 ) {
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(cmGainLossAmt.abs());
|
||||
cmGainLossAmt = Env.ZERO;
|
||||
}
|
||||
if (gainLossFactList.contains(factLine)) {
|
||||
isExcludeCMGainLoss = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (payment != null && payment.getReversal_ID() > 0 && !gainLossFactList.contains(factLine))
|
||||
{
|
||||
allocInvoiceSource = allocInvoiceSource.add(factLine.getAmtSourceDr());
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctDr());
|
||||
}
|
||||
|
||||
} else
|
||||
{
|
||||
if (as.getC_Currency_ID() != factLine.getC_Currency_ID())
|
||||
allocInvoiceSource = allocInvoiceSource.add(factLine.getAmtSourceDr());
|
||||
|
||||
if (!gainLossFactList.contains(factLine) && !invoice.isCreditMemo())
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctDr());
|
||||
|
||||
if (!gainLossFactList.contains(factLine) && invoice.isCreditMemo() && invoice.getReversal_ID() > 0 ) {
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctCr());
|
||||
// this is to cater for reverse-accrual.
|
||||
if (!invoice.getDateAcct().equals(getDateAcct()))
|
||||
isCMReversal =true;
|
||||
}
|
||||
|
||||
if (invoice!=null )
|
||||
{
|
||||
if (invoice.isCreditMemo() || invoice.getReversal_ID() > 0 ) {
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(cmGainLossAmt.abs());
|
||||
cmGainLossAmt = Env.ZERO;
|
||||
}
|
||||
if (gainLossFactList.contains(factLine)) {
|
||||
isExcludeCMGainLoss = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BigDecimal acctDifference = null; // gain is negative
|
||||
//
|
||||
StringBuilder description = new StringBuilder("Invoice=(").append(getC_Currency_ID()).append(")").append(allocInvoiceSource).append("/").append(allocInvoiceAccounted);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(description.toString());
|
||||
boolean isBPartnerAdjust = true;
|
||||
if (allocInvoiceSource.abs().compareTo(totalInvoiceSource.abs()) == 0)
|
||||
{
|
||||
if (isExcludeCMGainLoss)
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(cmGainLossAmt);
|
||||
if (payment != null && payment.getReversal_ID() > 0 )
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.subtract(gainLossAmt);
|
||||
else
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(gainLossAmt);
|
||||
if (isCMReversal)
|
||||
acctDifference = totalInvoiceAccounted.subtract(allocInvoiceAccounted.abs());
|
||||
else
|
||||
acctDifference = allocInvoiceAccounted.subtract(totalInvoiceAccounted.abs()); // gain is positive for receipt
|
||||
|
||||
StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||
description.append(" - ").append(d2);
|
||||
} else{
|
||||
MAllocationHdr[] allocations = MAllocationHdr.getOfInvoice(getCtx(), invoice.get_ID(), getTrxName());
|
||||
for (MAllocationHdr alloc : allocations)
|
||||
{
|
||||
StringBuilder sql = new StringBuilder("SELECT ")
|
||||
.append(invoice.isSOTrx()
|
||||
? "SUM(AmtSourceCr), SUM(AmtAcctCr), SUM(AmtAcctDr)" // so
|
||||
: "SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtAcctCr)") // po
|
||||
.append(" FROM Fact_Acct ")
|
||||
.append("WHERE AD_Table_ID=? AND Record_ID=?") // allocation
|
||||
.append(" AND C_AcctSchema_ID=?")
|
||||
.append(" AND PostingType='A'")
|
||||
.append(" AND Account_ID= ? ");
|
||||
pstmt = null;
|
||||
rs = null;
|
||||
try
|
||||
{
|
||||
pstmt = DB.prepareStatement(sql.toString(), getTrxName());
|
||||
pstmt.setInt(1, MAllocationHdr.Table_ID);
|
||||
pstmt.setInt(2, alloc.get_ID());
|
||||
pstmt.setInt(3, as.getC_AcctSchema_ID());
|
||||
pstmt.setInt(4, bpAcct.getAccount_ID());
|
||||
rs = pstmt.executeQuery();
|
||||
if (rs.next())
|
||||
{
|
||||
BigDecimal allocateSource = rs.getBigDecimal(1);
|
||||
BigDecimal allocateAccounted = rs.getBigDecimal(2);
|
||||
BigDecimal allocateCredit = rs.getBigDecimal(3);
|
||||
allocInvoiceSource =allocInvoiceSource.add(allocateSource != null ? allocateSource: BigDecimal.ZERO);
|
||||
allocInvoiceAccounted =allocInvoiceAccounted.add(allocateAccounted != null ? allocateAccounted : BigDecimal.ZERO);
|
||||
allocInvoiceAccounted= allocInvoiceAccounted.subtract(allocateCredit != null ? allocateCredit : BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
finally {
|
||||
DB.close(rs, pstmt);
|
||||
rs = null; pstmt = null;
|
||||
}
|
||||
}
|
||||
double multiplier = allocInvoiceSource.doubleValue() / totalInvoiceSource.doubleValue();
|
||||
|
||||
// Reduce Orig Invoice Accounted
|
||||
BigDecimal reduceOrigAccounted = totalInvoiceAccounted.multiply(BigDecimal.valueOf(multiplier));
|
||||
if (reduceOrigAccounted.compareTo(totalInvoiceAccounted) < 0 )
|
||||
totalInvoiceAccounted = reduceOrigAccounted;
|
||||
if (isExcludeCMGainLoss)
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(cmGainLossAmt);
|
||||
|
||||
allocInvoiceAccounted = allocInvoiceAccounted.add(gainLossAmt);
|
||||
|
||||
// Difference based on percentage of Orig Invoice
|
||||
acctDifference = allocInvoiceAccounted.subtract(totalInvoiceAccounted);
|
||||
// ignore Tolerance
|
||||
if (acctDifference.abs().compareTo(BigDecimal.valueOf(0.01)) < 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).append(" - Multiplier=").append(multiplier);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(d2.toString());
|
||||
description.append(" - ").append(d2);
|
||||
}
|
||||
|
||||
if (acctDifference.signum() == 0)
|
||||
{
|
||||
log.fine("No Difference");
|
||||
return null;
|
||||
}
|
||||
|
||||
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
|
||||
MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct());
|
||||
//
|
||||
if (acctDifference.abs().compareTo(TOLERANCE) <= 0)
|
||||
{
|
||||
if (invoice.isSOTrx())
|
||||
{
|
||||
FactLine fl = null;
|
||||
if (!isBPartnerAdjust)
|
||||
fl = fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference);
|
||||
else
|
||||
fl = fact.createLine (null, bpAcct,as.getC_Currency_ID(), acctDifference);
|
||||
fl.setDescription(description.toString());
|
||||
|
||||
if (!fact.isAcctBalanced())
|
||||
{
|
||||
if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID() )
|
||||
{
|
||||
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());
|
||||
|
||||
}
|
||||
}
|
||||
}else
|
||||
{
|
||||
FactLine fl = null;
|
||||
if (!isBPartnerAdjust)
|
||||
fl = fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference.negate());
|
||||
else
|
||||
fl = fact.createLine (null, bpAcct,as.getC_Currency_ID(), acctDifference.negate());
|
||||
|
||||
fl.setDescription(description.toString());
|
||||
if (!fact.isAcctBalanced())
|
||||
{
|
||||
if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID() )
|
||||
{
|
||||
fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference);
|
||||
} else {
|
||||
fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
} // createInvoiceRounding
|
||||
|
||||
} // Doc_Allocation
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.sql.Savepoint;
|
|||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
@ -43,6 +44,7 @@ import org.compiere.model.MMatchInv;
|
|||
import org.compiere.model.MOrderLandedCostAllocation;
|
||||
import org.compiere.model.ProductCost;
|
||||
import org.compiere.model.X_M_Cost;
|
||||
import org.compiere.util.DB;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.Trx;
|
||||
|
||||
|
@ -286,6 +288,21 @@ public class Doc_MatchInv extends Doc
|
|||
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.getAmtSourceDr(), dr.getAmtAcctDr());
|
||||
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.getAmtSourceCr(), cr.getAmtAcctCr());
|
||||
if (p_Error != null)
|
||||
return null;
|
||||
}
|
||||
|
||||
if (m_matchInv.getReversal_ID() == 0)
|
||||
{
|
||||
cr.setC_Activity_ID(m_invoiceLine.getC_Activity_ID());
|
||||
|
@ -831,4 +848,361 @@ public class Doc_MatchInv extends Doc
|
|||
factLine.setM_Product_ID(m_invoiceLine.getM_Product_ID());
|
||||
factLine.setQty(getQty());
|
||||
}
|
||||
|
||||
private String createInvoiceGainLoss(MAcctSchema as, Fact fact, MAccount acct,
|
||||
MInvoice invoice, BigDecimal matchInvSource, BigDecimal matchInvAccounted)
|
||||
{
|
||||
BigDecimal invoiceSource = null;
|
||||
BigDecimal invoiceAccounted = null;
|
||||
//
|
||||
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) {
|
||||
invoiceSource = (BigDecimal) valuesInv.get(0); // AmtSourceDr
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Requires that Invoice is Posted
|
||||
if (invoiceSource == null || invoiceAccounted == null)
|
||||
return null;
|
||||
//
|
||||
|
||||
if (m_matchInv.getReversal_ID() == 0 || m_matchInv.get_ID() < m_matchInv.getReversal_ID())
|
||||
{
|
||||
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());
|
||||
StringBuffer s = new StringBuffer();
|
||||
|
||||
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 (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);
|
||||
acctDifference = totalAmtAcctCr.negate();
|
||||
}
|
||||
else
|
||||
{
|
||||
matchInvSource = matchInvSource.add(totalAmtSourceCr);
|
||||
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr);
|
||||
acctDifference = totalAmtSourceDr.negate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(description.toString());
|
||||
//
|
||||
|
||||
// Full Payment in currency
|
||||
if (acctDifference == null && matchInvSource.compareTo(invoiceSource) == 0)
|
||||
{
|
||||
acctDifference = matchInvAccounted.subtract(invoiceAccounted.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");
|
||||
return null;
|
||||
}
|
||||
|
||||
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
|
||||
MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct());
|
||||
//
|
||||
if (invoice.isSOTrx())
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
return null;
|
||||
} // createInvoiceGainLoss
|
||||
|
||||
private String createReceiptGainLoss(MAcctSchema as, Fact fact, MAccount acct,
|
||||
MInOut receipt, BigDecimal matchInvSource, BigDecimal matchInvAccounted)
|
||||
{
|
||||
BigDecimal receiptSource = null;
|
||||
BigDecimal receiptAccounted = null;
|
||||
//
|
||||
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 Receipt
|
||||
List<Object> valuesInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(),
|
||||
MInOut.Table_ID, receipt.getM_InOut_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID());
|
||||
if (valuesInv != null) {
|
||||
receiptSource = (BigDecimal) valuesInv.get(0); // AmtSourceDr
|
||||
receiptAccounted = (BigDecimal) valuesInv.get(1); // AmtAcctDr
|
||||
if (receiptSource.signum() == 0 && receiptAccounted.signum() == 0) {
|
||||
receiptSource = (BigDecimal) valuesInv.get(2); // AmtSourceCr
|
||||
receiptAccounted = (BigDecimal) valuesInv.get(3); // AmtAcctCr
|
||||
}
|
||||
}
|
||||
|
||||
// Requires that Receipt is Posted
|
||||
if (receiptSource == null || receiptAccounted == null)
|
||||
return null;
|
||||
//
|
||||
if (m_matchInv.getReversal_ID() == 0 || m_matchInv.get_ID() < m_matchInv.getReversal_ID())
|
||||
{
|
||||
String matchInvLineSql = "SELECT M_MatchInv_ID FROM M_MatchInv "
|
||||
+ "WHERE M_InOutLine_ID IN (SELECT M_InOutLine_ID FROM M_InOutLine WHERE M_InOut_ID=?) "
|
||||
+ "AND COALESCE(Reversal_ID,0)=0";
|
||||
List<List<Object>> list = DB.getSQLArrayObjectsEx(getTrxName(), matchInvLineSql, receipt.get_ID());
|
||||
StringBuffer s = new StringBuffer();
|
||||
|
||||
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 (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 (m_matchInv.getReversal_ID() == 0 || m_matchInv.get_ID() < m_matchInv.getReversal_ID())
|
||||
{
|
||||
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.negate();
|
||||
}
|
||||
else
|
||||
{
|
||||
matchInvSource = matchInvSource.add(totalAmtSourceCr);
|
||||
matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr);
|
||||
acctDifference = totalAmtAcctDr.negate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder description = new StringBuilder("InOut=(").append(m_invoiceLine.getParent().getC_Currency_ID()).append(")").append(receiptSource).append("/").append(receiptAccounted)
|
||||
.append(" - MatchInv=(").append(getC_Currency_ID()).append(")").append(matchInvSource).append("/").append(matchInvAccounted);
|
||||
if (log.isLoggable(Level.FINE)) log.fine(description.toString());
|
||||
|
||||
// Full Payment in currency
|
||||
if (acctDifference == null && matchInvSource.compareTo(receiptSource) == 0)
|
||||
{
|
||||
acctDifference = matchInvAccounted.subtract(receiptAccounted.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");
|
||||
return null;
|
||||
}
|
||||
|
||||
MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct());
|
||||
MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct());
|
||||
//
|
||||
if (!receipt.isSOTrx())
|
||||
{
|
||||
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() != m_invoiceLine.getParent().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);
|
||||
}
|
||||
else
|
||||
{
|
||||
FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference);
|
||||
fl.setDescription(description.toString());
|
||||
updateFactLine(fl);
|
||||
|
||||
if (as.isCurrencyBalancing() && as.getC_Currency_ID() != m_invoiceLine.getParent().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);
|
||||
}
|
||||
return null;
|
||||
} // createReceiptGainLoss
|
||||
} // Doc_MatchInv
|
||||
|
|
|
@ -1124,6 +1124,8 @@ public final class FactLine extends X_Fact_Acct
|
|||
if (MMovement.Table_ID == AD_Table_ID)
|
||||
sql.append(" AND M_Locator_ID=?");
|
||||
// end MZ
|
||||
sql.append(" ORDER BY Fact_Acct_ID ");
|
||||
|
||||
PreparedStatement pstmt = null;
|
||||
ResultSet rs = null;
|
||||
try
|
||||
|
|
Loading…
Reference in New Issue