/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.acct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.adempiere.exceptions.AverageCostingZeroQtyException;
import org.compiere.model.I_M_InOutLine;
import org.compiere.model.MAccount;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClientInfo;
import org.compiere.model.MConversionRate;
import org.compiere.model.MCost;
import org.compiere.model.MCostDetail;
import org.compiere.model.MCostElement;
import org.compiere.model.MCurrency;
import org.compiere.model.MFactAcct;
import org.compiere.model.MInvoice;
import org.compiere.model.MInvoiceLine;
import org.compiere.model.MLandedCostAllocation;
import org.compiere.model.MOrderLandedCostAllocation;
import org.compiere.model.MTax;
import org.compiere.model.ProductCost;
import org.compiere.model.Query;
import org.compiere.model.X_M_Cost;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Trx;
/**
* Post Invoice Documents.
*
* Table: C_Invoice (318)
* Document Types: ARI, ARC, ARF, API, APC
*
* @author Jorg Janke
* @author Armen Rizal, Goodwill Consulting
* BF: 2797257 Landed Cost Detail is not using allocation qty
*
* @version $Id: Doc_Invoice.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $
*/
public class Doc_Invoice extends Doc
{
/**
* Constructor
* @param as accounting schemata
* @param rs record
* @param trxName trx
*/
public Doc_Invoice(MAcctSchema as, ResultSet rs, String trxName)
{
super (as, MInvoice.class, rs, null, trxName);
} // Doc_Invoice
/** Contained Optional Tax Lines */
protected DocTax[] m_taxes = null;
/** Contained Optional Tax Lines Distributed to Line Item */
@SuppressWarnings("unused")
private DocTax[] m_addToLineTaxes = null;
/** Currency Precision */
protected int m_precision = -1;
/** All lines are Service */
protected boolean m_allLinesService = true;
/** All lines are product item */
protected boolean m_allLinesItem = true;
/**
* Load Specific Document Details
* @return error message or null
*/
@Override
protected String loadDocumentDetails ()
{
MInvoice invoice = (MInvoice)getPO();
setDateDoc(invoice.getDateInvoiced());
setIsTaxIncluded(invoice.isTaxIncluded());
// Amounts
setAmount(Doc.AMTTYPE_Gross, invoice.getGrandTotal());
setAmount(Doc.AMTTYPE_Net, invoice.getTotalLines());
setAmount(Doc.AMTTYPE_Charge, invoice.getChargeAmt());
// Contained Objects
m_taxes = loadTaxes();
p_lines = loadLines(invoice);
if (log.isLoggable(Level.FINE)) log.fine("Lines=" + p_lines.length + ", Taxes=" + m_taxes.length);
return null;
} // loadDocumentDetails
/**
* Load Invoice Taxes
* @return DocTax Array
*/
private DocTax[] loadTaxes()
{
ArrayList list = new ArrayList();
ArrayList distributeList = new ArrayList();
String sql = "SELECT it.C_Tax_ID, t.Name, t.Rate, it.TaxBaseAmt, it.TaxAmt, t.IsSalesTax "
+ "FROM C_Tax t, C_InvoiceTax it "
+ "WHERE t.C_Tax_ID=it.C_Tax_ID AND it.C_Invoice_ID=?";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql, getTrxName());
pstmt.setInt(1, get_ID());
rs = pstmt.executeQuery();
//
while (rs.next())
{
int C_Tax_ID = rs.getInt(1);
String name = rs.getString(2);
BigDecimal rate = rs.getBigDecimal(3);
BigDecimal taxBaseAmt = rs.getBigDecimal(4);
BigDecimal amount = rs.getBigDecimal(5);
boolean salesTax = "Y".equals(rs.getString(6));
//
MTax tax = MTax.get(getCtx(), C_Tax_ID);
DocTax taxLine = new DocTax(C_Tax_ID, name, rate,
taxBaseAmt, amount, salesTax);
if (log.isLoggable(Level.FINE)) log.fine(taxLine.toString());
if (!tax.isDistributeTaxWithLineItem())
{
list.add(taxLine);
}
else
{
distributeList.add(taxLine);
}
}
}
catch (SQLException e)
{
log.log(Level.SEVERE, sql, e);
return null;
}
finally {
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
// Return Array
DocTax[] tl = new DocTax[list.size()];
list.toArray(tl);
// Distribute list
m_addToLineTaxes = distributeList.toArray(new DocTax[0]);
return tl;
} // loadTaxes
/**
* Load Invoice Line
* @param invoice invoice
* @return DocLine Array
*/
private DocLine[] loadLines (MInvoice invoice)
{
ArrayList list = new ArrayList();
//
MInvoiceLine[] lines = invoice.getLines(false);
for (int i = 0; i < lines.length; i++)
{
MInvoiceLine line = lines[i];
if (line.isDescription())
continue;
DocLine docLine = new DocLine(line, this);
// Qty
BigDecimal Qty = line.getQtyInvoiced();
boolean cm = getDocumentType().equals(DOCTYPE_ARCredit)
|| getDocumentType().equals(DOCTYPE_APCredit);
docLine.setQty(cm ? Qty.negate() : Qty, invoice.isSOTrx());
//
BigDecimal LineNetAmt = line.getLineNetAmt();
BigDecimal PriceList = line.getPriceList();
int C_Tax_ID = docLine.getC_Tax_ID();
// Correct included Tax
if (isTaxIncluded() && C_Tax_ID != 0)
{
MTax tax = MTax.get(getCtx(), C_Tax_ID);
if (!tax.isZeroTax())
{
BigDecimal LineNetAmtTax = tax.calculateTax(LineNetAmt, true, getStdPrecision());
if (log.isLoggable(Level.FINE)) log.fine("LineNetAmt=" + LineNetAmt + " - Tax=" + LineNetAmtTax);
if (tax.isSummary()) {
LineNetAmt = LineNetAmt.subtract(LineNetAmtTax);
BigDecimal base = LineNetAmt;
BigDecimal sumChildLineNetAmtTax = Env.ZERO;
DocTax taxToApplyDiff = null;
for (MTax childTax : tax.getChildTaxes(false)) {
if (!childTax.isZeroTax())
{
BigDecimal childLineNetAmtTax = childTax.calculateTax(base, false, getStdPrecision());
if (log.isLoggable(Level.FINE)) log.fine("LineNetAmt=" + base + " - Child Tax=" + childLineNetAmtTax);
if (childTax.isDistributeTaxWithLineItem())
{
LineNetAmt = LineNetAmt.add(childLineNetAmtTax);
LineNetAmtTax = LineNetAmtTax.subtract(childLineNetAmtTax);
}
else
{
for (int t = 0; t < m_taxes.length; t++)
{
if (m_taxes[t].getC_Tax_ID() == childTax.getC_Tax_ID())
{
m_taxes[t].addIncludedTax(childLineNetAmtTax);
taxToApplyDiff = m_taxes[t];
sumChildLineNetAmtTax = sumChildLineNetAmtTax.add(childLineNetAmtTax);
break;
}
}
}
}
}
BigDecimal diffChildVsSummary = LineNetAmtTax.subtract(sumChildLineNetAmtTax);
if (diffChildVsSummary.signum() != 0 && taxToApplyDiff != null) {
taxToApplyDiff.addIncludedTax(diffChildVsSummary);
}
} else {
if (!tax.isDistributeTaxWithLineItem())
{
LineNetAmt = LineNetAmt.subtract(LineNetAmtTax);
for (int t = 0; t < m_taxes.length; t++)
{
if (m_taxes[t].getC_Tax_ID() == C_Tax_ID)
{
m_taxes[t].addIncludedTax(LineNetAmtTax);
break;
}
}
}
}
BigDecimal PriceListTax = tax.calculateTax(PriceList, true, getStdPrecision());
PriceList = PriceList.subtract(PriceListTax);
}
} // correct included Tax
else
{
int stdPrecision = MCurrency.getStdPrecision(getCtx(), invoice.getC_Currency_ID());
MTax tax = MTax.get(getCtx(), C_Tax_ID);
if (tax.isSummary())
{
MTax[] cTaxes = tax.getChildTaxes(false);
BigDecimal base = LineNetAmt;
for(MTax cTax : cTaxes)
{
if (cTax.isDistributeTaxWithLineItem())
{
BigDecimal taxAmt = cTax.calculateTax(base, false, stdPrecision);
LineNetAmt = LineNetAmt.add(taxAmt);
}
}
}
else if (tax.isDistributeTaxWithLineItem())
{
BigDecimal taxAmt = tax.calculateTax(LineNetAmt, false, stdPrecision);
LineNetAmt = LineNetAmt.add(taxAmt);
}
}
docLine.setAmount (LineNetAmt, PriceList, Qty); // qty for discount calc
if (docLine.isItem())
m_allLinesService = false;
else
m_allLinesItem = false;
//
if (log.isLoggable(Level.FINE)) log.fine(docLine.toString());
list.add(docLine);
}
// Convert to Array
DocLine[] dls = new DocLine[list.size()];
list.toArray(dls);
// Included Tax - make sure that no difference
if (isTaxIncluded())
{
for (int i = 0; i < m_taxes.length; i++)
{
if (m_taxes[i].isIncludedTaxDifference())
{
BigDecimal diff = m_taxes[i].getIncludedTaxDifference();
for (int j = 0; j < dls.length; j++)
{
MTax lineTax = MTax.get(getCtx(), dls[j].getC_Tax_ID());
MTax[] composingTaxes = null;
if (lineTax.isSummary()) {
composingTaxes = lineTax.getChildTaxes(false);
} else {
composingTaxes = new MTax[1];
composingTaxes[0] = lineTax;
}
for (MTax mTax : composingTaxes) {
if (mTax.getC_Tax_ID() == m_taxes[i].getC_Tax_ID())
{
dls[j].setLineNetAmtDifference(diff);
m_taxes[i].addIncludedTax(diff.negate());
diff = Env.ZERO;
break;
}
}
if (diff.signum() == 0) {
break;
}
} // for all lines
} // tax difference
} // for all taxes
} // Included Tax difference
// Return Array
return dls;
} // loadLines
/**
* Get Currency Precision
* @return precision
*/
private int getStdPrecision()
{
if (m_precision == -1)
m_precision = MCurrency.getStdPrecision(getCtx(), getC_Currency_ID());
return m_precision;
} // getPrecision
/**
* Get Source Currency Balance - subtracts line and tax amounts from total - no rounding
* @return positive amount, if total invoice is bigger than lines
*/
@Override
public BigDecimal getBalance()
{
BigDecimal retValue = Env.ZERO;
StringBuilder sb = new StringBuilder (" [");
// Total
retValue = retValue.add(getAmount(Doc.AMTTYPE_Gross));
sb.append(getAmount(Doc.AMTTYPE_Gross));
// - Header Charge
retValue = retValue.subtract(getAmount(Doc.AMTTYPE_Charge));
sb.append("-").append(getAmount(Doc.AMTTYPE_Charge));
// - Tax
for (int i = 0; i < m_taxes.length; i++)
{
retValue = retValue.subtract(m_taxes[i].getAmount());
sb.append("-").append(m_taxes[i].getAmount());
}
// - Lines
for (int i = 0; i < p_lines.length; i++)
{
retValue = retValue.subtract(p_lines[i].getAmtSource());
sb.append("-").append(p_lines[i].getAmtSource());
}
sb.append("]");
//
if (log.isLoggable(Level.FINE)) log.fine(toString() + " Balance=" + retValue + sb.toString());
return retValue;
} // getBalance
/**
* Create Facts (the accounting logic) for
* ARI, ARC, ARF, API, APC.
*
* ARI, ARF
* Receivables DR
* Charge CR
* TaxDue CR
* Revenue CR
*
* ARC
* Receivables CR
* Charge DR
* TaxDue DR
* Revenue RR
*
* API
* Payables CR
* Charge DR
* TaxCredit DR
* Expense DR
*
* APC
* Payables DR
* Charge CR
* TaxCredit CR
* Expense CR
*
* @param as accounting schema
* @return Fact
*/
@Override
public ArrayList createFacts (MAcctSchema as)
{
//
ArrayList facts = new ArrayList();
// create Fact Header
Fact fact = new Fact(this, as, Fact.POST_Actual);
// Cash based accounting
if (!as.isAccrual())
return facts;
// ** ARI, ARF
if (getDocumentType().equals(DOCTYPE_ARInvoice)
|| getDocumentType().equals(DOCTYPE_ARProForma))
{
BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross);
BigDecimal serviceAmt = Env.ZERO;
// Header Charge CR
BigDecimal amt = getAmount(Doc.AMTTYPE_Charge);
if (amt != null && amt.signum() != 0)
fact.createLine(null, getAccount(Doc.ACCTTYPE_Charge, as),
getC_Currency_ID(), null, amt);
// TaxDue CR
for (int i = 0; i < m_taxes.length; i++)
{
amt = m_taxes[i].getAmount();
if (amt != null && amt.signum() != 0)
{
FactLine tl = fact.createLine(null, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as),
getC_Currency_ID(), null, amt);
if (tl != null)
tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID());
}
}
// Revenue CR
for (int i = 0; i < p_lines.length; i++)
{
amt = p_lines[i].getAmtSource();
BigDecimal dAmt = null;
if (as.isTradeDiscountPosted())
{
BigDecimal discount = p_lines[i].getDiscount();
if (discount != null && discount.signum() != 0)
{
amt = amt.add(discount);
dAmt = discount;
fact.createLine (p_lines[i],
p_lines[i].getAccount(ProductCost.ACCTTYPE_P_TDiscountGrant, as),
getC_Currency_ID(), dAmt, null);
}
}
fact.createLine (p_lines[i],
p_lines[i].getAccount(ProductCost.ACCTTYPE_P_Revenue, as),
getC_Currency_ID(), null, amt);
if (!p_lines[i].isItem())
{
grossAmt = grossAmt.subtract(amt);
serviceAmt = serviceAmt.add(amt);
}
}
// Receivables DR
int receivables_ID = getValidCombination_ID(Doc.ACCTTYPE_C_Receivable, as);
int receivablesServices_ID = receivables_ID; // Receivable Services account Deprecated IDEMPIERE-362
if (m_allLinesItem || !as.isPostServices()
|| receivables_ID == receivablesServices_ID)
{
grossAmt = getAmount(Doc.AMTTYPE_Gross);
serviceAmt = Env.ZERO;
}
else if (m_allLinesService)
{
serviceAmt = getAmount(Doc.AMTTYPE_Gross);
grossAmt = Env.ZERO;
}
if (grossAmt.signum() != 0)
fact.createLine(null, MAccount.get(getCtx(), receivables_ID),
getC_Currency_ID(), grossAmt, null);
if (serviceAmt.signum() != 0)
fact.createLine(null, MAccount.get(getCtx(), receivablesServices_ID),
getC_Currency_ID(), serviceAmt, null);
// Set Locations
FactLine[] fLines = fact.getLines();
for (int i = 0; i < fLines.length; i++)
{
if (fLines[i] != null)
{
fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), true); // from Loc
fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), false); // to Loc
}
}
}
// ARC
else if (getDocumentType().equals(DOCTYPE_ARCredit))
{
BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross);
BigDecimal serviceAmt = Env.ZERO;
// Header Charge DR
BigDecimal amt = getAmount(Doc.AMTTYPE_Charge);
if (amt != null && amt.signum() != 0)
fact.createLine(null, getAccount(Doc.ACCTTYPE_Charge, as),
getC_Currency_ID(), amt, null);
// TaxDue DR
for (int i = 0; i < m_taxes.length; i++)
{
amt = m_taxes[i].getAmount();
if (amt != null && amt.signum() != 0)
{
FactLine tl = fact.createLine(null, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as),
getC_Currency_ID(), amt, null);
if (tl != null)
tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID());
}
}
// Revenue CR
for (int i = 0; i < p_lines.length; i++)
{
amt = p_lines[i].getAmtSource();
BigDecimal dAmt = null;
if (as.isTradeDiscountPosted())
{
BigDecimal discount = p_lines[i].getDiscount();
if (discount != null && discount.signum() != 0)
{
amt = amt.add(discount);
dAmt = discount;
fact.createLine (p_lines[i],
p_lines[i].getAccount (ProductCost.ACCTTYPE_P_TDiscountGrant, as),
getC_Currency_ID(), null, dAmt);
}
}
fact.createLine (p_lines[i],
p_lines[i].getAccount (ProductCost.ACCTTYPE_P_Revenue, as),
getC_Currency_ID(), amt, null);
if (!p_lines[i].isItem())
{
grossAmt = grossAmt.subtract(amt);
serviceAmt = serviceAmt.add(amt);
}
}
// Receivables CR
int receivables_ID = getValidCombination_ID (Doc.ACCTTYPE_C_Receivable, as);
int receivablesServices_ID = receivables_ID; // Receivable Services account Deprecated IDEMPIERE-362
if (m_allLinesItem || !as.isPostServices()
|| receivables_ID == receivablesServices_ID)
{
grossAmt = getAmount(Doc.AMTTYPE_Gross);
serviceAmt = Env.ZERO;
}
else if (m_allLinesService)
{
serviceAmt = getAmount(Doc.AMTTYPE_Gross);
grossAmt = Env.ZERO;
}
if (grossAmt.signum() != 0)
fact.createLine(null, MAccount.get(getCtx(), receivables_ID),
getC_Currency_ID(), null, grossAmt);
if (serviceAmt.signum() != 0)
fact.createLine(null, MAccount.get(getCtx(), receivablesServices_ID),
getC_Currency_ID(), null, serviceAmt);
// Set Locations
FactLine[] fLines = fact.getLines();
for (int i = 0; i < fLines.length; i++)
{
if (fLines[i] != null)
{
fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), true); // from Loc
fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), false); // to Loc
}
}
}
// ** API
else if (getDocumentType().equals(DOCTYPE_APInvoice))
{
MInvoice invoice = (MInvoice)getPO();
MInvoice originalInvoice = null;
if (invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
{
originalInvoice = new MInvoice(Env.getCtx(), invoice.getReversal_ID(), invoice.get_TrxName());
}
BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross);
BigDecimal serviceAmt = Env.ZERO;
// Charge DR
fact.createLine(null, getAccount(Doc.ACCTTYPE_Charge, as),
getC_Currency_ID(), getAmount(Doc.AMTTYPE_Charge), null);
// TaxCredit DR
for (int i = 0; i < m_taxes.length; i++)
{
FactLine tl = fact.createLine(null, m_taxes[i].getAccount(m_taxes[i].getAPTaxType(), as),
getC_Currency_ID(), m_taxes[i].getAmount(), null);
if (tl != null)
tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID());
if (tl != null && invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
{
tl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), 0, BigDecimal.ONE);
}
}
// Expense DR
for (int i = 0; i < p_lines.length; i++)
{
DocLine line = p_lines[i];
boolean landedCost = landedCost(as, fact, line, true);
if (landedCost && as.isExplicitCostAdjustment())
{
fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as),
getC_Currency_ID(), line.getAmtSource(), null);
//
FactLine fl = fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as),
getC_Currency_ID(), null, line.getAmtSource());
String desc = line.getDescription();
if (desc == null)
desc = "100%";
else
desc += " 100%";
fl.setDescription(desc);
if (invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
{
int lineId = 0;
if (originalInvoice != null)
{
MInvoiceLine[] lines = originalInvoice.getLines();
if (lines.length > i)
lineId = lines[i].getC_InvoiceLine_ID();
}
fl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), lineId, BigDecimal.ONE);
}
}
if (!landedCost)
{
MAccount expense = line.getAccount(ProductCost.ACCTTYPE_P_Expense, as);
if (line.isItem())
expense = line.getAccount (ProductCost.ACCTTYPE_P_InventoryClearing, as);
BigDecimal amt = line.getAmtSource();
BigDecimal dAmt = null;
if (as.isTradeDiscountPosted() && !line.isItem())
{
BigDecimal discount = line.getDiscount();
if (discount != null && discount.signum() != 0)
{
amt = amt.add(discount);
dAmt = discount;
MAccount tradeDiscountReceived = line.getAccount(ProductCost.ACCTTYPE_P_TDiscountRec, as);
FactLine fl = fact.createLine (line, tradeDiscountReceived,
getC_Currency_ID(), null, dAmt);
if (fl != null && invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
{
int lineId = 0;
if (originalInvoice != null)
{
MInvoiceLine[] lines = originalInvoice.getLines();
if (lines.length > i)
lineId = lines[i].getC_InvoiceLine_ID();
}
fl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), lineId, BigDecimal.ONE);
}
}
}
FactLine fl = fact.createLine (line, expense,
getC_Currency_ID(), amt, null);
if (fl != null && invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
{
int lineId = 0;
if (originalInvoice != null)
{
MInvoiceLine[] lines = originalInvoice.getLines();
if (lines.length > i)
lineId = lines[i].getC_InvoiceLine_ID();
}
fl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), lineId, BigDecimal.ONE);
}
if (!line.isItem())
{
grossAmt = grossAmt.subtract(amt);
serviceAmt = serviceAmt.add(amt);
}
//
if (line.getM_Product_ID() != 0
&& line.getProduct().isService()) // otherwise Inv Matching
MCostDetail.createInvoice(as, line.getAD_Org_ID(),
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
line.get_ID(), 0, // No Cost Element
line.getAmtSource(), line.getQty(),
line.getDescription(), getTrxName());
}
}
// Liability CR
int payables_ID = getValidCombination_ID (Doc.ACCTTYPE_V_Liability, as);
int payablesServices_ID = payables_ID; // Liability Services account Deprecated IDEMPIERE-362
if (m_allLinesItem || !as.isPostServices()
|| payables_ID == payablesServices_ID)
{
grossAmt = getAmount(Doc.AMTTYPE_Gross);
serviceAmt = Env.ZERO;
}
else if (m_allLinesService)
{
serviceAmt = getAmount(Doc.AMTTYPE_Gross);
grossAmt = Env.ZERO;
}
FactLine fl = null;
if (grossAmt.signum() > 0)
fl = fact.createLine(null, MAccount.get(getCtx(), payables_ID),
getC_Currency_ID(), null, grossAmt);
else if (grossAmt.signum() < 0)
fl = fact.createLine(null, MAccount.get(getCtx(), payables_ID),
getC_Currency_ID(), grossAmt.negate(), null);
if (serviceAmt.signum() > 0)
fl = fact.createLine(null, MAccount.get(getCtx(), payablesServices_ID),
getC_Currency_ID(), null, serviceAmt);
else if (serviceAmt.signum() < 0)
fl = fact.createLine(null, MAccount.get(getCtx(), payablesServices_ID),
getC_Currency_ID(), serviceAmt.negate(), null);
if (fl != null && invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
{
fl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), 0, BigDecimal.ONE);
}
// Set Locations
FactLine[] fLines = fact.getLines();
for (int i = 0; i < fLines.length; i++)
{
if (fLines[i] != null)
{
fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), true); // from Loc
fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), false); // to Loc
}
}
//
updateProductPO(as); // Only API
}
// APC
else if (getDocumentType().equals(DOCTYPE_APCredit))
{
BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross);
BigDecimal serviceAmt = Env.ZERO;
// Charge CR
fact.createLine (null, getAccount(Doc.ACCTTYPE_Charge, as),
getC_Currency_ID(), null, getAmount(Doc.AMTTYPE_Charge));
// TaxCredit CR
for (int i = 0; i < m_taxes.length; i++)
{
FactLine tl = fact.createLine (null, m_taxes[i].getAccount(m_taxes[i].getAPTaxType(), as),
getC_Currency_ID(), null, m_taxes[i].getAmount());
if (tl != null)
tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID());
}
// Expense CR
for (int i = 0; i < p_lines.length; i++)
{
DocLine line = p_lines[i];
boolean landedCost = landedCost(as, fact, line, false);
if (landedCost && as.isExplicitCostAdjustment())
{
fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as),
getC_Currency_ID(), null, line.getAmtSource());
//
FactLine fl = fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as),
getC_Currency_ID(), line.getAmtSource(), null);
String desc = line.getDescription();
if (desc == null)
desc = "100%";
else
desc += " 100%";
fl.setDescription(desc);
}
if (!landedCost)
{
MAccount expense = line.getAccount(ProductCost.ACCTTYPE_P_Expense, as);
if (line.isItem())
expense = line.getAccount (ProductCost.ACCTTYPE_P_InventoryClearing, as);
BigDecimal amt = line.getAmtSource();
BigDecimal dAmt = null;
if (as.isTradeDiscountPosted() && !line.isItem())
{
BigDecimal discount = line.getDiscount();
if (discount != null && discount.signum() != 0)
{
amt = amt.add(discount);
dAmt = discount;
MAccount tradeDiscountReceived = line.getAccount(ProductCost.ACCTTYPE_P_TDiscountRec, as);
fact.createLine (line, tradeDiscountReceived,
getC_Currency_ID(), dAmt, null);
}
}
fact.createLine (line, expense,
getC_Currency_ID(), null, amt);
if (!line.isItem())
{
grossAmt = grossAmt.subtract(amt);
serviceAmt = serviceAmt.add(amt);
}
//
if (line.getM_Product_ID() != 0
&& line.getProduct().isService()) // otherwise Inv Matching
MCostDetail.createInvoice(as, line.getAD_Org_ID(),
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
line.get_ID(), 0, // No Cost Element
line.getAmtSource().negate(), line.getQty(),
line.getDescription(), getTrxName());
}
}
// Liability DR
int payables_ID = getValidCombination_ID (Doc.ACCTTYPE_V_Liability, as);
int payablesServices_ID = payables_ID; // Liability Services account Deprecated IDEMPIERE-362
if (m_allLinesItem || !as.isPostServices()
|| payables_ID == payablesServices_ID)
{
grossAmt = getAmount(Doc.AMTTYPE_Gross);
serviceAmt = Env.ZERO;
}
else if (m_allLinesService)
{
serviceAmt = getAmount(Doc.AMTTYPE_Gross);
grossAmt = Env.ZERO;
}
if (grossAmt.signum() != 0)
fact.createLine(null, MAccount.get(getCtx(), payables_ID),
getC_Currency_ID(), grossAmt, null);
if (serviceAmt.signum() != 0)
fact.createLine(null, MAccount.get(getCtx(), payablesServices_ID),
getC_Currency_ID(), serviceAmt, null);
// Set Locations
FactLine[] fLines = fact.getLines();
for (int i = 0; i < fLines.length; i++)
{
if (fLines[i] != null)
{
fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), true); // from Loc
fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), false); // to Loc
}
}
}
else
{
p_Error = "DocumentType unknown: " + getDocumentType();
log.log(Level.SEVERE, p_Error);
fact = null;
}
//
facts.add(fact);
return facts;
} // createFact
/**
* Create Fact for Cash Based accounting (i.e. only revenue/expense)
* @param as accounting schema
* @param fact fact to add lines to
* @param multiplier source amount multiplier
* @return accounted amount
*/
public BigDecimal createFactCash (MAcctSchema as, Fact fact, BigDecimal multiplier)
{
boolean creditMemo = getDocumentType().equals(DOCTYPE_ARCredit)
|| getDocumentType().equals(DOCTYPE_APCredit);
boolean payables = getDocumentType().equals(DOCTYPE_APInvoice)
|| getDocumentType().equals(DOCTYPE_APCredit);
BigDecimal acctAmt = Env.ZERO;
FactLine fl = null;
// Revenue/Cost
for (int i = 0; i < p_lines.length; i++)
{
DocLine line = p_lines[i];
boolean landedCost = false;
if (payables)
landedCost = landedCost(as, fact, line, false);
if (landedCost && as.isExplicitCostAdjustment())
{
fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as),
getC_Currency_ID(), null, line.getAmtSource());
//
fl = fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as),
getC_Currency_ID(), line.getAmtSource(), null);
String desc = line.getDescription();
if (desc == null)
desc = "100%";
else
desc += " 100%";
fl.setDescription(desc);
}
if (!landedCost)
{
MAccount acct = line.getAccount(
payables ? ProductCost.ACCTTYPE_P_Expense : ProductCost.ACCTTYPE_P_Revenue, as);
if (payables)
{
// if Fixed Asset
if (line.isItem())
acct = line.getAccount (ProductCost.ACCTTYPE_P_InventoryClearing, as);
}
BigDecimal amt = line.getAmtSource().multiply(multiplier);
BigDecimal amt2 = null;
if (creditMemo)
{
amt2 = amt;
amt = null;
}
if (payables) // Vendor = DR
fl = fact.createLine (line, acct,
getC_Currency_ID(), amt, amt2);
else // Customer = CR
fl = fact.createLine (line, acct,
getC_Currency_ID(), amt2, amt);
if (fl != null)
acctAmt = acctAmt.add(fl.getAcctBalance());
}
}
// Tax
for (int i = 0; i < m_taxes.length; i++)
{
BigDecimal amt = m_taxes[i].getAmount();
BigDecimal amt2 = null;
if (creditMemo)
{
amt2 = amt;
amt = null;
}
FactLine tl = null;
if (payables)
tl = fact.createLine (null, m_taxes[i].getAccount(m_taxes[i].getAPTaxType(), as),
getC_Currency_ID(), amt, amt2);
else
tl = fact.createLine (null, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as),
getC_Currency_ID(), amt2, amt);
if (tl != null)
tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID());
}
// Set Locations
FactLine[] fLines = fact.getLines();
for (int i = 0; i < fLines.length; i++)
{
if (fLines[i] != null)
{
if (payables)
{
fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), true); // from Loc
fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), false); // to Loc
}
else
{
fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), true); // from Loc
fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), false); // to Loc
}
}
}
return acctAmt;
} // createFactCash
/**
* Create Landed Cost accounting and Cost lines
* @param as accounting schema
* @param fact fact
* @param line document line
* @param dr true for DR side, false otherwise
* @return true if landed costs were created
*/
protected boolean landedCost (MAcctSchema as, Fact fact, DocLine line, boolean dr)
{
int C_InvoiceLine_ID = line.get_ID();
MLandedCostAllocation[] lcas = MLandedCostAllocation.getOfInvoiceLine(
getCtx(), C_InvoiceLine_ID, getTrxName());
if (lcas.length == 0)
return false;
// Calculate Total Base
double totalBase = 0;
for (int i = 0; i < lcas.length; i++)
totalBase += lcas[i].getBase().doubleValue();
Map costDetailAmtMap = new HashMap<>();
Map mcostQtyMap = new HashMap<>();
// Create New
MInvoiceLine il = new MInvoiceLine (getCtx(), C_InvoiceLine_ID, getTrxName());
for (int i = 0; i < lcas.length; i++)
{
MLandedCostAllocation lca = lcas[i];
if (lca.getBase().signum() == 0)
continue;
double percent = lca.getBase().doubleValue() / totalBase;
String desc = il.getDescription();
if (desc == null)
desc = percent + "%";
else
desc += " - " + percent + "%";
if (line.getDescription() != null)
desc += " - " + line.getDescription();
// Accounting
BigDecimal drAmt = null;
BigDecimal crAmt = null;
MAccount account = null;
ProductCost pc = new ProductCost (Env.getCtx(),
lca.getM_Product_ID(), lca.getM_AttributeSetInstance_ID(), getTrxName());
String costingMethod = pc.getProduct().getCostingMethod(as);
if (X_M_Cost.COSTINGMETHOD_AverageInvoice.equals(costingMethod) || X_M_Cost.COSTINGMETHOD_AveragePO.equals(costingMethod))
{
BigDecimal allocationAmt = lca.getAmt();
boolean reversal = false;
if (allocationAmt.signum() < 0) //reversal
{
allocationAmt = allocationAmt.negate();
reversal = true;
}
BigDecimal estimatedAmt = BigDecimal.ZERO;
BigDecimal costAdjustmentAmt = BigDecimal.ZERO;
boolean usesSchemaCurrency = false;
MInvoiceLine reversalLine = null;
if (reversal)
{
MInvoice invoice = (MInvoice)getPO();
MInvoice reversalInvoice = new MInvoice(getCtx(), invoice.getReversal_ID(), getTrxName());
MInvoiceLine[] lines = invoice.getLines();
MInvoiceLine[] reversalLines = reversalInvoice.getLines();
for(int j = 0; j < lines.length; j++) {
if (lines[j].get_ID() == il.get_ID()) {
reversalLine = reversalLines[j];
break;
}
}
}
else
{
int oCurrencyId = 0;
Timestamp oDateAcct = getDateAcct();
if (lca.getM_InOutLine_ID() > 0)
{
I_M_InOutLine iol = lca.getM_InOutLine();
if (iol.getC_OrderLine_ID() > 0)
{
oCurrencyId = iol.getC_OrderLine().getC_Currency_ID();
oDateAcct = iol.getC_OrderLine().getC_Order().getDateAcct();
MOrderLandedCostAllocation[] allocations = MOrderLandedCostAllocation.getOfOrderLine(iol.getC_OrderLine_ID(), getTrxName());
for(MOrderLandedCostAllocation allocation : allocations)
{
if (allocation.getC_OrderLandedCost().getM_CostElement_ID() != lca.getM_CostElement_ID())
continue;
BigDecimal amt = allocation.getAmt();
BigDecimal qty = allocation.getQty();
if (qty.compareTo(iol.getMovementQty()) != 0)
{
amt = amt.multiply(iol.getMovementQty()).divide(qty, 12, RoundingMode.HALF_UP);
}
estimatedAmt = estimatedAmt.add(amt);
}
}
}
if (estimatedAmt.scale() > as.getCostingPrecision())
{
estimatedAmt = estimatedAmt.setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
}
costAdjustmentAmt = allocationAmt;
if (estimatedAmt.signum() > 0)
{
//get other allocation amt
StringBuilder sql = new StringBuilder("SELECT Sum(Amt) FROM C_LandedCostAllocation WHERE M_InOutLine_ID=? ")
.append("AND C_LandedCostAllocation_ID<>? ")
.append("AND M_CostElement_ID=? ")
.append("AND AD_Client_ID=? ");
BigDecimal otherAmt = DB.getSQLValueBD(getTrxName(), sql.toString(), lca.getM_InOutLine_ID(), lca.getC_LandedCostAllocation_ID(),
lca.getM_CostElement_ID(), lca.getAD_Client_ID());
if (otherAmt != null)
{
estimatedAmt = estimatedAmt.subtract(otherAmt);
}
//added for IDEMPIERE-3014
//convert to accounting schema currency
if (estimatedAmt.signum() > 0 && oCurrencyId != getC_Currency_ID())
{
estimatedAmt = MConversionRate.convert(getCtx(), estimatedAmt,
oCurrencyId, as.getC_Currency_ID(),
oDateAcct, getC_ConversionType_ID(),
getAD_Client_ID(), getAD_Org_ID());
allocationAmt = MConversionRate.convert(getCtx(), allocationAmt,
getC_Currency_ID(), as.getC_Currency_ID(),
getDateAcct(), getC_ConversionType_ID(),
getAD_Client_ID(), getAD_Org_ID());
setC_Currency_ID(as.getC_Currency_ID());
usesSchemaCurrency = true;
}
if (estimatedAmt.signum() > 0)
{
costAdjustmentAmt = allocationAmt.subtract(estimatedAmt);
}
}
if (!dr)
costAdjustmentAmt = costAdjustmentAmt.negate();
}
BigDecimal amtAsset = Env.ZERO;
BigDecimal amtVariance = Env.ZERO;
BigDecimal costDetailQty = lca.getQty();
if (costAdjustmentAmt.signum() != 0 && !reversal)
{
Trx trx = Trx.get(getTrxName(), false);
Savepoint savepoint = null;
try {
savepoint = trx.setSavepoint(null);
amtVariance = Env.ZERO;
amtAsset = costAdjustmentAmt;
if(X_M_Cost.COSTINGMETHOD_AveragePO.equals(costingMethod))
{
int AD_Org_ID = lca.getAD_Org_ID();
int M_AttributeSetInstance_ID = lca.getM_AttributeSetInstance_ID();
if (MAcctSchema.COSTINGLEVEL_Client.equals(as.getCostingLevel()))
{
AD_Org_ID = 0;
M_AttributeSetInstance_ID = 0;
}
else if (MAcctSchema.COSTINGLEVEL_Organization.equals(as.getCostingLevel()))
M_AttributeSetInstance_ID = 0;
else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(as.getCostingLevel()))
AD_Org_ID = 0;
MCostElement ce = MCostElement.getMaterialCostElement(getCtx(), as.getCostingMethod(),
AD_Org_ID);
MCost c = MCost.get(getCtx(), getAD_Client_ID(), AD_Org_ID, lca.getM_Product_ID(),
as.getM_CostType_ID(), as.getC_AcctSchema_ID(), ce.getM_CostElement_ID(),
M_AttributeSetInstance_ID, getTrxName());
if (c != null)
{
BigDecimal mcostQty = c.getCurrentQty();
if (mcostQtyMap.containsKey(c.get_UUID())) {
mcostQty = mcostQty.subtract(mcostQtyMap.get(c.get_UUID()));
if (mcostQty.signum() < 0)
mcostQty = new BigDecimal("0.00");
}
if (mcostQty.compareTo(lca.getQty()) < 0) {
amtAsset = mcostQty.multiply(costAdjustmentAmt.divide(lca.getQty(), as.getCostingPrecision(), RoundingMode.HALF_UP));
amtVariance = costAdjustmentAmt.subtract(amtAsset);
costDetailQty = mcostQty;
}
if (mcostQtyMap.containsKey(c.get_UUID())) {
mcostQtyMap.put(c.get_UUID(), mcostQtyMap.get(c.get_UUID()).add(costDetailQty));
} else {
mcostQtyMap.put(c.get_UUID(), costDetailQty);
}
}
}
BigDecimal costDetailAmt = amtAsset;
//convert to accounting schema currency
if (getC_Currency_ID() != as.getC_Currency_ID())
costDetailAmt = MConversionRate.convert(getCtx(), costDetailAmt,
getC_Currency_ID(), as.getC_Currency_ID(),
getDateAcct(), getC_ConversionType_ID(),
getAD_Client_ID(), getAD_Org_ID());
if (costDetailAmt.scale() > as.getCostingPrecision())
costDetailAmt = costDetailAmt.setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
String key = lca.getM_Product_ID()+"_"+lca.getM_AttributeSetInstance_ID();
BigDecimal prevAmt = costDetailAmtMap.remove(key);
if (prevAmt != null) {
costDetailAmt = costDetailAmt.add(prevAmt);
}
costDetailAmtMap.put(key, costDetailAmt);
if (costDetailAmt.signum() != 0 &&
!MCostDetail.createInvoice(as, lca.getAD_Org_ID(),
lca.getM_Product_ID(), lca.getM_AttributeSetInstance_ID(),
C_InvoiceLine_ID, lca.getM_CostElement_ID(),
costDetailAmt, costDetailQty,
desc, getTrxName())) {
throw new RuntimeException("Failed to create cost detail record.");
}
} catch (SQLException e) {
throw new RuntimeException(e.getLocalizedMessage(), e);
} catch (AverageCostingZeroQtyException e) {
try {
amtAsset = BigDecimal.ZERO;
amtVariance = costAdjustmentAmt;
trx.rollback(savepoint);
savepoint = null;
} catch (SQLException e1) {
throw new RuntimeException(e1.getLocalizedMessage(), e1);
}
} finally {
if (savepoint != null) {
try {
trx.releaseSavepoint(savepoint);
} catch (SQLException e) {}
}
}
} else if (reversal) {
costDetailQty = BigDecimal.ZERO;
int AD_Org_ID = lca.getAD_Org_ID();
int M_AttributeSetInstance_ID = lca.getM_AttributeSetInstance_ID();
if (MAcctSchema.COSTINGLEVEL_Client.equals(as.getCostingLevel()))
{
AD_Org_ID = 0;
M_AttributeSetInstance_ID = 0;
}
else if (MAcctSchema.COSTINGLEVEL_Organization.equals(as.getCostingLevel()))
M_AttributeSetInstance_ID = 0;
else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(as.getCostingLevel()))
AD_Org_ID = 0;
String key = lca.getM_Product_ID()+"_"+M_AttributeSetInstance_ID;
if (!costDetailAmtMap.containsKey(key)) {
costDetailAmtMap.put(key, BigDecimal.ZERO);
amtAsset = BigDecimal.ZERO;
amtVariance = BigDecimal.ZERO;
MAccount varianceAccount = pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, as);
MAccount assetAccount = pc.getAccount(ProductCost.ACCTTYPE_P_Asset, as);
Query query = MFactAcct.createRecordIdQuery(MInvoice.Table_ID, reversalLine.getC_Invoice_ID(), as.getC_AcctSchema_ID(), getTrxName());
List factAccts = query.list();
for(MFactAcct factAcct : factAccts) {
if (factAcct.getM_Product_ID() != lca.getM_Product_ID())
continue;
if (factAcct.getLine_ID() != reversalLine.get_ID())
continue;
if (factAcct.getAccount_ID() == assetAccount.getAccount_ID()) {
if (factAcct.getAmtAcctDr().signum() != 0)
amtAsset = amtAsset.add(factAcct.getAmtAcctDr());
else if (factAcct.getAmtAcctCr().signum() != 0)
amtAsset = amtAsset.subtract(factAcct.getAmtAcctCr());
} else if (factAcct.getAccount_ID() == varianceAccount.getAccount_ID()) {
if (factAcct.getAmtAcctDr().signum() != 0)
amtVariance = amtVariance.add(factAcct.getAmtAcctDr());
else if (factAcct.getAmtAcctCr().signum() != 0)
amtVariance = amtVariance.subtract(factAcct.getAmtAcctCr());
}
}
if (lca.getM_AttributeSetInstance_ID() > 0 && M_AttributeSetInstance_ID == 0) {
String sql =
"""
SELECT SUM(Qty)
FROM M_CostDetail
WHERE C_InvoiceLine_ID=? AND Coalesce(M_CostElement_ID,0)=?
AND M_Product_ID=? AND C_AcctSchema_ID=?
""";
costDetailQty = DB.getSQLValueBDEx(getTrxName(), sql, reversalLine.get_ID(), lca.getM_CostElement_ID(), lca.getM_Product_ID(), as.getC_AcctSchema_ID());
if (costDetailQty == null)
costDetailQty = BigDecimal.ZERO;
} else if (lca.getM_AttributeSetInstance_ID() > 0 && M_AttributeSetInstance_ID > 0) {
MCostDetail cd = MCostDetail.get (as.getCtx(), "C_InvoiceLine_ID=? AND Coalesce(M_CostElement_ID,0)="+lca.getM_CostElement_ID()+" AND M_Product_ID="+lca.getM_Product_ID(),
reversalLine.get_ID(), lca.getM_AttributeSetInstance_ID(), as.getC_AcctSchema_ID(), getTrxName());
costDetailQty = cd != null ? cd.getQty() : BigDecimal.ZERO;
if (cd != null) {
amtAsset = cd.getAmt();
}
if (i > 0) {
for(int j = 0; j < i; j++) {
if (lcas[j].getM_Product_ID() == lca.getM_Product_ID()) {
//variance have been posted by product
amtVariance = BigDecimal.ZERO;
}
}
}
} else {
MCostDetail cd = MCostDetail.get (as.getCtx(), "C_InvoiceLine_ID=? AND Coalesce(M_CostElement_ID,0)="+lca.getM_CostElement_ID()+" AND M_Product_ID="+lca.getM_Product_ID(),
reversalLine.get_ID(), lca.getM_AttributeSetInstance_ID(), as.getC_AcctSchema_ID(), getTrxName());
costDetailQty = cd != null ? cd.getQty() : BigDecimal.ZERO;
}
if (costDetailQty.signum() != 0)
{
MCostElement ce = MCostElement.getMaterialCostElement(getCtx(), as.getCostingMethod(),
AD_Org_ID);
MCost c = MCost.get(getCtx(), getAD_Client_ID(), AD_Org_ID, lca.getM_Product_ID(),
as.getM_CostType_ID(), as.getC_AcctSchema_ID(), ce.getM_CostElement_ID(),
M_AttributeSetInstance_ID, getTrxName());
if (c != null) {
if (c.getCurrentQty().signum() == 0) {
amtVariance = amtVariance.add(amtAsset);
amtAsset = BigDecimal.ZERO;
} else if (c.getCurrentQty().compareTo(costDetailQty) < 0) {
BigDecimal currentAmtAsset = amtAsset;
amtAsset = amtAsset.divide(costDetailQty, RoundingMode.HALF_UP).multiply(c.getCurrentQty());
amtVariance = amtVariance.add(currentAmtAsset.subtract(amtAsset));
costDetailQty = c.getCurrentQty();
}
}
}
if (amtAsset.signum() != 0) {
if (!MCostDetail.createInvoice(as, lca.getAD_Org_ID(),
lca.getM_Product_ID(), lca.getM_AttributeSetInstance_ID(),
C_InvoiceLine_ID, lca.getM_CostElement_ID(),
amtAsset.negate(), costDetailQty,
desc, getTrxName())) {
throw new RuntimeException("Failed to create cost detail record.");
}
}
if (getC_Currency_ID() != as.getC_Currency_ID()) {
usesSchemaCurrency = true;
setC_Currency_ID(as.getC_Currency_ID());
}
}
}
if (allocationAmt.signum() > 0 && !reversal)
{
if (allocationAmt.scale() > as.getStdPrecision())
{
allocationAmt = allocationAmt.setScale(as.getStdPrecision(), RoundingMode.HALF_UP);
}
if (estimatedAmt.scale() > as.getStdPrecision())
{
estimatedAmt = estimatedAmt.setScale(as.getStdPrecision(), RoundingMode.HALF_UP);
}
if (allocationAmt.compareTo(estimatedAmt)!=0)
{
if (estimatedAmt.signum() != 0)
{
drAmt = dr ? (reversal ? null : estimatedAmt): (reversal ? estimatedAmt : null);
crAmt = dr ? (reversal ? estimatedAmt : null): (reversal ? null : estimatedAmt);
account = pc.getAccount(ProductCost.ACCTTYPE_P_LandedCostClearing, as);
FactLine fl = fact.createLine (line, account, getC_Currency_ID(), drAmt, crAmt);
fl.setDescription(desc);
fl.setM_Product_ID(lca.getM_Product_ID());
fl.setQty(line.getQty());
}
if (amtVariance.signum() != 0) {
if (amtVariance.signum() > 0) {
drAmt = dr ? amtVariance : null;
crAmt = dr ? null : amtVariance;
} else {
BigDecimal underAmt = amtVariance.negate();
drAmt = dr ? null : underAmt;
crAmt = dr ? underAmt : null;
}
account = pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, as);
FactLine fl = fact.createLine(line, account, getC_Currency_ID(), drAmt, crAmt);
fl.setDescription(desc);
fl.setM_Product_ID(lca.getM_Product_ID());
fl.setQty(line.getQty());
}
if (amtAsset.signum() != 0) {
if (amtAsset.signum() > 0) {
drAmt = dr ? amtAsset : null;
crAmt = dr ? null : amtAsset;
} else {
BigDecimal underAmt = amtAsset.negate();
drAmt = dr ? null : underAmt;
crAmt = dr ? underAmt : null;
}
account = pc.getAccount(ProductCost.ACCTTYPE_P_Asset, as);
FactLine fl = fact.createLine(line, account, getC_Currency_ID(), drAmt, crAmt);
fl.setDescription(desc);
fl.setM_Product_ID(lca.getM_Product_ID());
fl.setQty(line.getQty());
}
}
else if (allocationAmt.signum() != 0)
{
drAmt = dr ? (reversal ? null : allocationAmt) : (reversal ? allocationAmt : null);
crAmt = dr ? (reversal ? allocationAmt : null) : (reversal ? null : allocationAmt);
account = pc.getAccount(ProductCost.ACCTTYPE_P_LandedCostClearing, as);
FactLine fl = fact.createLine (line, account, getC_Currency_ID(), drAmt, crAmt);
fl.setDescription(desc);
fl.setM_Product_ID(lca.getM_Product_ID());
fl.setQty(line.getQty());
}
} else if (reversal) {
account = pc.getAccount(ProductCost.ACCTTYPE_P_LandedCostClearing, as);
FactLine fl = fact.createLine (line, account, getC_Currency_ID(), BigDecimal.ZERO, BigDecimal.ZERO);
fl.updateReverseLine(MInvoice.Table_ID, reversalLine.getC_Invoice_ID(), reversalLine.get_ID(), BigDecimal.ONE);
if (fl.getAmtAcctCr().signum() == 0 && fl.getAmtAcctDr().signum() == 0)
fact.remove(fl);
if (amtVariance.signum() != 0) {
if (amtVariance.signum() > 0) {
drAmt = dr ? null : amtVariance;
crAmt = dr ? amtVariance : null;
} else {
BigDecimal underAmt = amtVariance.negate();
drAmt = dr ? underAmt : null;
crAmt = dr ? null : underAmt;
}
account = pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, as);
fl = fact.createLine(line, account, getC_Currency_ID(), drAmt, crAmt);
fl.setDescription(desc);
fl.setM_Product_ID(lca.getM_Product_ID());
fl.setQty(line.getQty());
}
if (amtAsset.signum() != 0) {
if (amtAsset.signum() > 0) {
drAmt = dr ? null : amtAsset;
crAmt = dr ? amtAsset : null;
} else {
BigDecimal underAmt = amtAsset.negate();
drAmt = dr ? underAmt : null;
crAmt = dr ? null : underAmt;
}
account = pc.getAccount(ProductCost.ACCTTYPE_P_Asset, as);
fl = fact.createLine(line, account, getC_Currency_ID(), drAmt, crAmt);
fl.setDescription(desc);
fl.setM_Product_ID(lca.getM_Product_ID());
fl.setQty(line.getQty());
}
}
if (usesSchemaCurrency)
setC_Currency_ID(line.getC_Currency_ID());
}
else
{
if (dr)
drAmt = lca.getAmt();
else
crAmt = lca.getAmt();
account = pc.getAccount(ProductCost.ACCTTYPE_P_CostAdjustment, as);
FactLine fl = fact.createLine (line, account, getC_Currency_ID(), drAmt, crAmt);
fl.setDescription(desc);
fl.setM_Product_ID(lca.getM_Product_ID());
fl.setQty(line.getQty());
}
}
if (log.isLoggable(Level.CONFIG)) log.config("Created #" + lcas.length);
return true;
} // landedCosts
/**
* Update ProductPO PriceLastInv
* @param as accounting schema
*/
protected void updateProductPO (MAcctSchema as)
{
MClientInfo ci = MClientInfo.get(getCtx(), as.getAD_Client_ID());
if (ci.getC_AcctSchema1_ID() != as.getC_AcctSchema_ID())
return;
StringBuilder sql = new StringBuilder (
"UPDATE M_Product_PO po ")
.append("SET PriceLastInv = ")
// select
.append("(SELECT currencyConvertInvoice(i.C_Invoice_ID,po.C_Currency_ID,il.PriceActual,i.DateInvoiced) ")
.append("FROM C_Invoice i, C_InvoiceLine il ")
.append("WHERE i.C_Invoice_ID=il.C_Invoice_ID")
.append(" AND po.M_Product_ID=il.M_Product_ID AND po.C_BPartner_ID=i.C_BPartner_ID");
if (DB.isOracle()) //jz
{
sql.append(" AND ROWNUM=1 ");
}
else
{
sql.append(" AND il.C_InvoiceLine_ID = (SELECT MIN(il1.C_InvoiceLine_ID) ")
.append("FROM C_Invoice i1, C_InvoiceLine il1 ")
.append("WHERE i1.C_Invoice_ID=il1.C_Invoice_ID")
.append(" AND po.M_Product_ID=il1.M_Product_ID AND po.C_BPartner_ID=i1.C_BPartner_ID")
.append(" AND i1.C_Invoice_ID=").append(get_ID()).append(") ");
}
sql.append(" AND i.C_Invoice_ID=").append(get_ID()).append(") ")
// update
.append("WHERE EXISTS (SELECT * ")
.append("FROM C_Invoice i, C_InvoiceLine il ")
.append("WHERE i.C_Invoice_ID=il.C_Invoice_ID")
.append(" AND po.M_Product_ID=il.M_Product_ID AND po.C_BPartner_ID=i.C_BPartner_ID")
.append(" AND i.C_Invoice_ID=").append(get_ID()).append(")");
int no = DB.executeUpdate(sql.toString(), getTrxName());
if (log.isLoggable(Level.FINE)) log.fine("Updated=" + no);
} // updateProductPO
@Override
public BigDecimal getCurrencyRate() {
if (getC_Currency_ID() == getAcctSchema().getC_Currency_ID())
return null;
MInvoice inv = (MInvoice)getPO();
int baseCurrencyId = MClientInfo.get(getCtx(), inv.getAD_Client_ID()).getC_Currency_ID();
if (baseCurrencyId != getAcctSchema().getC_Currency_ID())
return null;
if (inv.isOverrideCurrencyRate()) {
return inv.getCurrencyRate();
} else {
return null;
}
}
@Override
public boolean isConvertible (MAcctSchema acctSchema) {
MInvoice inv = (MInvoice)getPO();
if (inv.getC_Currency_ID() != acctSchema.getC_Currency_ID()) {
int baseCurrencyId = MClientInfo.get(getCtx(), inv.getAD_Client_ID()).getC_Currency_ID();
if (baseCurrencyId == acctSchema.getC_Currency_ID() && inv.isOverrideCurrencyRate()) {
return true;
}
}
return super.isConvertible(acctSchema);
}
} // Doc_Invoice