/******************************************************************************
* 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.model;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import org.adempiere.base.Core;
import org.adempiere.base.IProductPricing;
import org.adempiere.exceptions.AdempiereException;
import org.adempiere.model.ITaxProvider;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
/**
* Invoice Line Model
*
* @author Jorg Janke
* @version $Id: MInvoiceLine.java,v 1.5 2006/07/30 00:51:03 jjanke Exp $
*
* @author Teo Sarca, www.arhipac.ro
*
BF [ 2804142 ] MInvoice.setRMALine should work only for CreditMemo invoices
* https://sourceforge.net/p/adempiere/bugs/1937/
* @author red1 FR: [ 2214883 ] Remove SQL code and Replace for Query
*/
public class MInvoiceLine extends X_C_InvoiceLine
{
/**
* generated serial id
*/
private static final long serialVersionUID = -1590896898028805978L;
/**
* Get Invoice Line referencing InOut Line
* @param sLine shipment line
* @return (first) invoice line
*/
public static MInvoiceLine getOfInOutLine (MInOutLine sLine)
{
if (sLine == null)
return null;
final String whereClause = "C_InvoiceLine.M_InOutLine_ID=? AND C_Invoice.Processed='Y'";
final String joinInvoice = "JOIN C_Invoice ON (C_Invoice.C_Invoice_ID = C_InvoiceLine.C_Invoice_ID)";
List list = new Query(sLine.getCtx(),I_C_InvoiceLine.Table_Name,whereClause,sLine.get_TrxName()).addJoinClause(joinInvoice)
.setParameters(sLine.getM_InOutLine_ID())
.list();
MInvoiceLine retValue = null;
if (list.size() > 0) {
retValue = list.get(0);
if (list.size() > 1)
s_log.warning("More than one C_InvoiceLine of " + sLine);
}
return retValue;
} // getOfInOutLine
/**
* Get Invoice Line referencing InOut Line - from MatchInv
* @param sLine shipment line
* @return (first) invoice line
*/
public static MInvoiceLine getOfInOutLineFromMatchInv(MInOutLine sLine) {
if (sLine == null)
return null;
final String whereClause = "C_InvoiceLine_ID IN (SELECT C_InvoiceLine_ID FROM M_MatchInv WHERE M_InOutLine_ID=?)";
List list = new Query(sLine.getCtx(),I_C_InvoiceLine.Table_Name,whereClause,sLine.get_TrxName())
.setParameters(sLine.getM_InOutLine_ID())
.list();
MInvoiceLine retValue = null;
if (list.size() > 0) {
retValue = list.get(0);
if (list.size() > 1)
s_log.warning("More than one C_InvoiceLine of " + sLine);
}
return retValue;
}
/** Static Logger */
protected static CLogger s_log = CLogger.getCLogger (MInvoiceLine.class);
/** Tax */
protected MTax m_tax = null;
/**
* UUID based Constructor
* @param ctx Context
* @param C_InvoiceLine_UU UUID key
* @param trxName Transaction
*/
public MInvoiceLine(Properties ctx, String C_InvoiceLine_UU, String trxName) {
super(ctx, C_InvoiceLine_UU, trxName);
if (Util.isEmpty(C_InvoiceLine_UU))
setInitialDefaults();
}
/**
* Invoice Line Constructor
* @param ctx context
* @param C_InvoiceLine_ID invoice line or 0
* @param trxName transaction name
*/
public MInvoiceLine (Properties ctx, int C_InvoiceLine_ID, String trxName)
{
this (ctx, C_InvoiceLine_ID, trxName, (String[]) null);
} // MInvoiceLine
/**
* @param ctx
* @param C_InvoiceLine_ID
* @param trxName
* @param virtualColumns
*/
public MInvoiceLine(Properties ctx, int C_InvoiceLine_ID, String trxName, String... virtualColumns) {
super(ctx, C_InvoiceLine_ID, trxName, virtualColumns);
if (C_InvoiceLine_ID == 0)
setInitialDefaults();
}
/**
* Set the initial defaults for a new record
*/
private void setInitialDefaults() {
setIsDescription(false);
setIsPrinted (true);
setLineNetAmt (Env.ZERO);
setPriceEntered (Env.ZERO);
setPriceActual (Env.ZERO);
setPriceLimit (Env.ZERO);
setPriceList (Env.ZERO);
setM_AttributeSetInstance_ID(0);
setTaxAmt(Env.ZERO);
//
setQtyEntered(Env.ZERO);
setQtyInvoiced(Env.ZERO);
}
/**
* Parent Constructor
* @param invoice parent
*/
public MInvoiceLine (MInvoice invoice)
{
this (invoice.getCtx(), 0, invoice.get_TrxName());
if (invoice.get_ID() == 0)
throw new IllegalArgumentException("Header not saved");
setClientOrg(invoice.getAD_Client_ID(), invoice.getAD_Org_ID());
setC_Invoice_ID (invoice.getC_Invoice_ID());
setInvoice(invoice);
} // MInvoiceLine
/**
* Load Constructor
* @param ctx context
* @param rs result set record
* @param trxName transaction
*/
public MInvoiceLine (Properties ctx, ResultSet rs, String trxName)
{
super(ctx, rs, trxName);
} // MInvoiceLine
/**
* Copy constructor
* @param copy
*/
public MInvoiceLine(MInvoiceLine copy)
{
this(Env.getCtx(), copy);
}
/**
* Copy constructor
* @param ctx
* @param copy
*/
public MInvoiceLine(Properties ctx, MInvoiceLine copy)
{
this(ctx, copy, (String) null);
}
/**
* Copy constructor
* @param ctx
* @param copy
* @param trxName
*/
public MInvoiceLine(Properties ctx, MInvoiceLine copy, String trxName)
{
this(ctx, 0, trxName);
copyPO(copy);
this.m_tax = copy.m_tax != null ? new MTax(ctx, copy.m_tax, trxName) : null;
this.m_M_PriceList_ID = copy.m_M_PriceList_ID;
this.m_DateInvoiced = copy.m_DateInvoiced;
this.m_C_BPartner_ID = copy.m_C_BPartner_ID;
this.m_C_BPartner_Location_ID = copy.m_C_BPartner_Location_ID;
this.m_IsSOTrx = copy.m_IsSOTrx;
this.m_product = copy.m_product != null ? new MProduct(ctx, copy.m_product, trxName) : null;
this.m_charge = copy.m_charge != null ? new MCharge(ctx, copy.m_charge, trxName) : null;
this.m_name = copy.m_name;
this.m_precision = copy.m_precision;
this.m_parent = null;
this.m_priceSet = copy.m_priceSet;
}
protected int m_M_PriceList_ID = 0;
protected Timestamp m_DateInvoiced = null;
protected int m_C_BPartner_ID = 0;
protected int m_C_BPartner_Location_ID = 0;
protected boolean m_IsSOTrx = true;
protected boolean m_priceSet = false;
protected MProduct m_product = null;
/** Charge */
protected MCharge m_charge = null;
/** Cached Name of the line */
protected String m_name = null;
/** Cached Precision */
protected Integer m_precision = null;
/** Product Pricing */
protected IProductPricing m_productPricing = null;
/** Parent */
protected MInvoice m_parent = null;
/**
* Set Defaults from Order.
* Called also from copy lines from invoice.
* Does not update C_Invoice_ID column.
* @param invoice invoice
*/
public void setInvoice (MInvoice invoice)
{
m_parent = invoice;
m_M_PriceList_ID = invoice.getM_PriceList_ID();
m_DateInvoiced = invoice.getDateInvoiced();
m_C_BPartner_ID = invoice.getC_BPartner_ID();
m_C_BPartner_Location_ID = invoice.getC_BPartner_Location_ID();
m_IsSOTrx = invoice.isSOTrx();
m_precision = Integer.valueOf(invoice.getPrecision());
} // setOrder
/**
* Get Parent
* @return parent
*/
public MInvoice getParent()
{
if (m_parent == null)
m_parent = new MInvoice(getCtx(), getC_Invoice_ID(), get_TrxName());
return m_parent;
} // getParent
/**
* Set values from Order Line.
* Does not set quantity!
* @param oLine line
*/
public void setOrderLine (MOrderLine oLine)
{
setC_OrderLine_ID(oLine.getC_OrderLine_ID());
//
setLine(oLine.getLine());
setIsDescription(oLine.isDescription());
setDescription(oLine.getDescription());
//
if(oLine.getM_Product_ID() == 0)
setC_Charge_ID(oLine.getC_Charge_ID());
//
setM_Product_ID(oLine.getM_Product_ID());
setM_AttributeSetInstance_ID(oLine.getM_AttributeSetInstance_ID());
setS_ResourceAssignment_ID(oLine.getS_ResourceAssignment_ID());
setC_UOM_ID(oLine.getC_UOM_ID());
//
setPriceEntered(oLine.getPriceEntered());
setPriceActual(oLine.getPriceActual());
setPriceLimit(oLine.getPriceLimit());
setPriceList(oLine.getPriceList());
//
setC_Tax_ID(oLine.getC_Tax_ID());
setLineNetAmt(oLine.getLineNetAmt());
//
setC_Project_ID(oLine.getC_Project_ID());
setC_ProjectPhase_ID(oLine.getC_ProjectPhase_ID());
setC_ProjectTask_ID(oLine.getC_ProjectTask_ID());
setC_Activity_ID(oLine.getC_Activity_ID());
setC_Campaign_ID(oLine.getC_Campaign_ID());
setAD_OrgTrx_ID(oLine.getAD_OrgTrx_ID());
setUser1_ID(oLine.getUser1_ID());
setUser2_ID(oLine.getUser2_ID());
//
setRRAmt(oLine.getRRAmt());
setRRStartDate(oLine.getRRStartDate());
} // setOrderLine
/**
* Set values from Shipment Line.
* Does not set quantity!
* @param sLine ship line
*/
public void setShipLine (MInOutLine sLine)
{
setM_InOutLine_ID(sLine.getM_InOutLine_ID());
setC_OrderLine_ID(sLine.getC_OrderLine_ID());
// Set RMALine ID if shipment/receipt is based on RMA Doc
setM_RMALine_ID(sLine.getM_RMALine_ID());
//
setLine(sLine.getLine());
setIsDescription(sLine.isDescription());
setDescription(sLine.getDescription());
//
setM_Product_ID(sLine.getM_Product_ID());
if (sLine.sameOrderLineUOM() || getProduct() == null)
setC_UOM_ID(sLine.getC_UOM_ID());
else
// use product UOM if the shipment hasn't the same uom than the order
setC_UOM_ID(getProduct().getC_UOM_ID());
setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID());
if(getM_Product_ID() == 0)
setC_Charge_ID(sLine.getC_Charge_ID());
//
int C_OrderLine_ID = sLine.getC_OrderLine_ID();
if (C_OrderLine_ID != 0)
{
MOrderLine oLine = new MOrderLine (getCtx(), C_OrderLine_ID, get_TrxName());
setS_ResourceAssignment_ID(oLine.getS_ResourceAssignment_ID());
//
if (sLine.sameOrderLineUOM())
setPriceEntered(oLine.getPriceEntered());
else
setPriceEntered(oLine.getPriceActual());
setPriceActual(oLine.getPriceActual());
setPriceLimit(oLine.getPriceLimit());
setPriceList(oLine.getPriceList());
//
setC_Tax_ID(oLine.getC_Tax_ID());
setLineNetAmt(oLine.getLineNetAmt());
setC_Project_ID(oLine.getC_Project_ID());
}
// Check if shipment line is based on RMA
else if (sLine.getM_RMALine_ID() != 0)
{
// Set Pricing details from the RMA Line on which it is based
MRMALine rmaLine = new MRMALine(getCtx(), sLine.getM_RMALine_ID(), get_TrxName());
setPrice();
setPrice(rmaLine.getAmt());
setC_Tax_ID(rmaLine.getC_Tax_ID());
setLineNetAmt(rmaLine.getLineNetAmt());
}
else
{
setPrice();
setTax();
}
//
setC_Project_ID(sLine.getC_Project_ID());
setC_ProjectPhase_ID(sLine.getC_ProjectPhase_ID());
setC_ProjectTask_ID(sLine.getC_ProjectTask_ID());
setC_Activity_ID(sLine.getC_Activity_ID());
setC_Campaign_ID(sLine.getC_Campaign_ID());
setAD_OrgTrx_ID(sLine.getAD_OrgTrx_ID());
setUser1_ID(sLine.getUser1_ID());
setUser2_ID(sLine.getUser2_ID());
} // setShipLine
/**
* Add to Description
* @param description text
*/
public void addDescription (String description)
{
String desc = getDescription();
if (desc == null)
setDescription(description);
else{
StringBuilder msgd = new StringBuilder(desc).append(" | ").append(description);
setDescription(msgd.toString());
}
} // addDescription
/**
* Set M_AttributeSetInstance_ID
* @param M_AttributeSetInstance_ID id
*/
@Override
public void setM_AttributeSetInstance_ID (int M_AttributeSetInstance_ID)
{
super.setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID);
} // setM_AttributeSetInstance_ID
/**
* Set Price for Product and PriceList.
* Uses standard SO price list if not set by invoice constructor.
*/
public void setPrice()
{
if (getM_Product_ID() == 0 || isDescription())
return;
if (m_M_PriceList_ID == 0 || m_C_BPartner_ID == 0)
setInvoice(getParent());
if (m_M_PriceList_ID == 0 || m_C_BPartner_ID == 0)
throw new IllegalStateException("setPrice - PriceList unknown!");
setPrice (m_M_PriceList_ID, m_C_BPartner_ID);
} // setPrice
/**
* Set Price for Product
* @param M_PriceList_ID price list
* @param C_BPartner_ID business partner
*/
public void setPrice (int M_PriceList_ID, int C_BPartner_ID)
{
if (getM_Product_ID() == 0 || isDescription())
return;
//
if (log.isLoggable(Level.FINE)) log.fine("M_PriceList_ID=" + M_PriceList_ID);
m_productPricing = Core.getProductPricing();
m_productPricing.setInvoiceLine(this, get_TrxName());
m_productPricing.setM_PriceList_ID(M_PriceList_ID);
//
setPriceActual (m_productPricing.getPriceStd());
setPriceList (m_productPricing.getPriceList());
setPriceLimit (m_productPricing.getPriceLimit());
//
if (getQtyEntered().compareTo(getQtyInvoiced()) == 0)
setPriceEntered(getPriceActual());
else
setPriceEntered(getPriceActual().multiply(getQtyInvoiced()
.divide(getQtyEntered(), 6, RoundingMode.HALF_UP))); // precision
//
if (getC_UOM_ID() == 0)
setC_UOM_ID(m_productPricing.getC_UOM_ID());
//
m_priceSet = true;
} // setPrice
/**
* Set Price Entered/Actual.
* Use this Method if the Line UOM is the Product UOM.
* @param PriceActual price
*/
public void setPrice (BigDecimal PriceActual)
{
setPriceEntered(PriceActual);
setPriceActual (PriceActual);
} // setPrice
/**
* Set Price Actual.
* (actual price is not updateable).
* @param PriceActual actual price
*/
@Override
public void setPriceActual (BigDecimal PriceActual)
{
if (PriceActual == null)
throw new IllegalArgumentException ("PriceActual is mandatory");
set_ValueNoCheck("PriceActual", PriceActual);
} // setPriceActual
/**
* Find and set C_Tax_ID
* @return true if found
*/
public boolean setTax()
{
if (isDescription())
return true;
//
int M_Warehouse_ID = Env.getContextAsInt(getCtx(), Env.M_WAREHOUSE_ID);
//
String deliveryViaRule = null;
if (getC_OrderLine_ID() > 0) {
deliveryViaRule = new MOrderLine(getCtx(), getC_OrderLine_ID(), get_TrxName()).getParent().getDeliveryViaRule();
} else if (getM_InOutLine_ID() > 0) {
deliveryViaRule = new MInOutLine(getCtx(), getM_InOutLine_ID(), get_TrxName()).getParent().getDeliveryViaRule();
} else if (getParent().getC_Order_ID() > 0) {
deliveryViaRule = new MOrder(getCtx(), getParent().getC_Order_ID(), get_TrxName()).getDeliveryViaRule();
}
int C_Tax_ID = Core.getTaxLookup().get(getCtx(), getM_Product_ID(), getC_Charge_ID() , m_DateInvoiced, m_DateInvoiced,
getAD_Org_ID(), M_Warehouse_ID,
m_C_BPartner_Location_ID, // should be bill to
m_C_BPartner_Location_ID, m_IsSOTrx, deliveryViaRule, get_TrxName());
if (C_Tax_ID == 0)
{
log.log(Level.SEVERE, "No Tax found");
return false;
}
setC_Tax_ID (C_Tax_ID);
return true;
} // setTax
/**
* Calculate Tax Amt.
* Assumes Line Net is calculated.
*/
public void setTaxAmt ()
{
BigDecimal TaxAmt = Env.ZERO;
if (getC_Tax_ID() == 0)
return;
setLineNetAmt();
MTax tax = MTax.get (getCtx(), getC_Tax_ID());
if (tax.isDocumentLevel() && m_IsSOTrx) // AR Inv Tax
return;
//
TaxAmt = tax.calculateTax(getLineNetAmt(), isTaxIncluded(), getPrecision());
if (isTaxIncluded())
setLineTotalAmt(getLineNetAmt());
else
setLineTotalAmt(getLineNetAmt().add(TaxAmt));
super.setTaxAmt (TaxAmt);
} // setTaxAmt
/**
* Calculate Line Net Amt.
* Include tax if tax is included in price.
*/
public void setLineNetAmt ()
{
// Calculations & Rounding
BigDecimal bd = getPriceEntered().multiply(getQtyEntered());
int precision = getPrecision();
if (bd.scale() > precision)
bd = bd.setScale(precision, RoundingMode.HALF_UP);
super.setLineNetAmt (bd);
} // setLineNetAmt
/**
* Get Charge
* @return charge or null
*/
public MCharge getCharge()
{
if (m_charge == null && getC_Charge_ID() != 0)
m_charge = MCharge.getCopy(getCtx(), getC_Charge_ID(), get_TrxName());
return m_charge;
}
/**
* Get Tax (immutable)
* @return tax
*/
protected MTax getTax()
{
if (m_tax == null)
m_tax = MTax.get(getCtx(), getC_Tax_ID());
return m_tax;
} // getTax
/**
* Set Qty Invoiced/Entered.
* @param Qty Invoiced/Ordered
*/
public void setQty (int Qty)
{
setQty(new BigDecimal(Qty));
} // setQtyInvoiced
/**
* Set Qty Invoiced/Entered.
* @param Qty Invoiced/Entered
*/
public void setQty (BigDecimal Qty)
{
setQtyEntered(Qty);
setQtyInvoiced(getQtyEntered());
} // setQtyInvoiced
/**
* Set Qty Entered - enforce entered UOM precision
* @param QtyEntered
*/
@Override
public void setQtyEntered (BigDecimal QtyEntered)
{
if (QtyEntered != null && getC_UOM_ID() != 0)
{
int precision = MUOM.getPrecision(getCtx(), getC_UOM_ID());
QtyEntered = QtyEntered.setScale(precision, RoundingMode.HALF_UP);
}
super.setQtyEntered (QtyEntered);
} // setQtyEntered
/**
* Set Qty Invoiced - enforce Product UOM precision
* @param QtyInvoiced
*/
@Override
public void setQtyInvoiced (BigDecimal QtyInvoiced)
{
MProduct product = getProduct();
if (QtyInvoiced != null && product != null)
{
int precision = product.getUOMPrecision();
QtyInvoiced = QtyInvoiced.setScale(precision, RoundingMode.HALF_UP);
}
super.setQtyInvoiced(QtyInvoiced);
} // setQtyInvoiced
/**
* Set Product
* @param product product
*/
public void setProduct (MProduct product)
{
m_product = product;
if (m_product != null)
{
setM_Product_ID(m_product.getM_Product_ID());
setC_UOM_ID (m_product.getC_UOM_ID());
}
else
{
setM_Product_ID(0);
setC_UOM_ID (0);
}
setM_AttributeSetInstance_ID(0);
} // setProduct
/**
* Set M_Product_ID
* @param M_Product_ID product
* @param setUOM true to set UOM from product
*/
public void setM_Product_ID (int M_Product_ID, boolean setUOM)
{
if (setUOM)
setProduct(MProduct.get(getCtx(), M_Product_ID));
else
super.setM_Product_ID (M_Product_ID);
setM_AttributeSetInstance_ID(0);
} // setM_Product_ID
/**
* Set Product and UOM
* @param M_Product_ID product
* @param C_UOM_ID uom
*/
public void setM_Product_ID (int M_Product_ID, int C_UOM_ID)
{
super.setM_Product_ID (M_Product_ID);
super.setC_UOM_ID(C_UOM_ID);
setM_AttributeSetInstance_ID(0);
} // setM_Product_ID
/**
* Get Product
* @return product or null
*/
public MProduct getProduct()
{
if (m_product == null && getM_Product_ID() != 0)
m_product = MProduct.getCopy(getCtx(), getM_Product_ID(), get_TrxName());
return m_product;
} // getProduct
/**
* Get C_Project_ID
* @return C_Project_ID
*/
public int getC_Project_ID()
{
int ii = super.getC_Project_ID ();
if (ii == 0)
ii = getParent().getC_Project_ID();
return ii;
} // getC_Project_ID
/**
* Get C_Activity_ID
* @return C_Activity_ID
*/
public int getC_Activity_ID()
{
int ii = super.getC_Activity_ID ();
if (ii == 0)
ii = getParent().getC_Activity_ID();
return ii;
} // getC_Activity_ID
/**
* Get C_Campaign_ID
* @return C_Campaign_ID
*/
public int getC_Campaign_ID()
{
int ii = super.getC_Campaign_ID ();
if (ii == 0)
ii = getParent().getC_Campaign_ID();
return ii;
} // getC_Campaign_ID
/**
* Get User1_ID
* @return User1_ID
*/
public int getUser1_ID ()
{
int ii = super.getUser1_ID ();
if (ii == 0)
ii = getParent().getUser1_ID();
return ii;
} // getUser1_ID
/**
* Get User2_ID
* @return User2_ID
*/
public int getUser2_ID ()
{
int ii = super.getUser2_ID ();
if (ii == 0)
ii = getParent().getUser2_ID();
return ii;
} // getUser2_ID
/**
* Get AD_OrgTrx_ID
* @return AD_OrgTrx_ID
*/
public int getAD_OrgTrx_ID()
{
int ii = super.getAD_OrgTrx_ID();
if (ii == 0)
ii = getParent().getAD_OrgTrx_ID();
return ii;
} // getAD_OrgTrx_ID
/**
* String Representation
* @return info
*/
@Override
public String toString ()
{
StringBuilder sb = new StringBuilder ("MInvoiceLine[")
.append(get_ID()).append(",").append(getLine())
.append(",QtyInvoiced=").append(getQtyInvoiced())
.append(",LineNetAmt=").append(getLineNetAmt())
.append ("]");
return sb.toString ();
} // toString
/**
* Get (Product/Charge) Name
* @return name
*/
public String getName ()
{
if (m_name == null)
{
String sql = "SELECT COALESCE (p.Name, c.Name) "
+ "FROM C_InvoiceLine il"
+ " LEFT OUTER JOIN M_Product p ON (il.M_Product_ID=p.M_Product_ID)"
+ " LEFT OUTER JOIN C_Charge C ON (il.C_Charge_ID=c.C_Charge_ID) "
+ "WHERE C_InvoiceLine_ID=?";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql, get_TrxName());
pstmt.setInt(1, getC_InvoiceLine_ID());
rs = pstmt.executeQuery();
if (rs.next())
m_name = rs.getString(1);
if (m_name == null)
m_name = "??";
}
catch (Exception e)
{
log.log(Level.SEVERE, "getName", e);
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
}
return m_name;
} // getName
/**
* Set Temporary (cached) Name
* @param tempName Cached Name
*/
public void setName (String tempName)
{
m_name = tempName;
} // setName
/**
* Get Description Text.
* @return description
*/
public String getDescriptionText()
{
return super.getDescription();
} // getDescriptionText
/**
* Get Currency Precision
* @return precision
*/
public int getPrecision()
{
if (m_precision != null)
return m_precision.intValue();
String sql = "SELECT c.StdPrecision "
+ "FROM C_Currency c INNER JOIN C_Invoice x ON (x.C_Currency_ID=c.C_Currency_ID) "
+ "WHERE x.C_Invoice_ID=?";
int i = DB.getSQLValue(get_TrxName(), sql, getC_Invoice_ID());
if (i < 0)
{
log.warning("getPrecision = " + i + " - set to 2");
i = 2;
}
m_precision = Integer.valueOf(i);
return m_precision.intValue();
} // getPrecision
/**
* Is Tax Included in Amount
* @return true if tax is included
*/
public boolean isTaxIncluded()
{
if (m_M_PriceList_ID == 0)
{
m_M_PriceList_ID = DB.getSQLValue(get_TrxName(),
"SELECT M_PriceList_ID FROM C_Invoice WHERE C_Invoice_ID=?",
getC_Invoice_ID());
}
MPriceList pl = MPriceList.get(getCtx(), m_M_PriceList_ID, get_TrxName());
return pl.isTaxIncluded();
} // isTaxIncluded
@Override
protected boolean beforeSave (boolean newRecord)
{
if (log.isLoggable(Level.FINE)) log.fine("New=" + newRecord);
boolean parentComplete = getParent().isProcessed();
boolean isReversal = getParent().isReversal();
if (newRecord && parentComplete) {
log.saveError("ParentComplete", Msg.translate(getCtx(), "C_Invoice_ID"));
return false;
}
// Re-set invoice header (need to update m_IsSOTrx flag)
setInvoice(getParent());
// Do not make changes if parent is complete or this is for reversal
if (!parentComplete && !isReversal) {
if (getC_Charge_ID() != 0)
{
// Reset M_Product_ID to 0 if Charge is fill
if (getM_Product_ID() != 0)
setM_Product_ID(0);
}
else
{
// Set Product Price
if (!m_priceSet
&& Env.ZERO.compareTo(getPriceActual()) == 0
&& Env.ZERO.compareTo(getPriceList()) == 0)
setPrice();
// Enforce PriceLimit
boolean enforce = m_IsSOTrx && getParent().getM_PriceList().isEnforcePriceLimit();
if (enforce && MRole.getDefault().isOverwritePriceLimit())
enforce = false;
if (enforce && getPriceLimit() != Env.ZERO
&& getPriceActual().compareTo(getPriceLimit()) < 0)
{
log.saveError("UnderLimitPrice", "PriceEntered=" + getPriceEntered() + ", PriceLimit=" + getPriceLimit());
return false;
}
}
// Set C_Tax_ID
if (getC_Tax_ID() == 0)
setTax();
// Set Line No
if (getLine() == 0)
{
String sql = "SELECT COALESCE(MAX(Line),0)+10 FROM C_InvoiceLine WHERE C_Invoice_ID=?";
int ii = DB.getSQLValue (get_TrxName(), sql, getC_Invoice_ID());
setLine (ii);
}
// Set default UOM
if (getC_UOM_ID() == 0)
{
int C_UOM_ID = MUOM.getDefault_UOM_ID(getCtx());
if (C_UOM_ID > 0)
setC_UOM_ID (C_UOM_ID);
}
// Enforce Qty Precision (rounding)
if (newRecord || is_ValueChanged("QtyEntered"))
setQtyEntered(getQtyEntered());
if (newRecord || is_ValueChanged("QtyInvoiced"))
setQtyInvoiced(getQtyInvoiced());
// Calculations & Rounding
setLineNetAmt();
// TaxAmt recalculations should be done if the TaxAmt is zero
// or this is an Invoice(Customer)
if (m_IsSOTrx || getTaxAmt().compareTo(Env.ZERO) == 0)
setTaxAmt();
/* Carlos Ruiz - globalqss
* IDEMPIERE-178 Orders and Invoices must disallow amount lines without product/charge
*/
if (getParent().getC_DocTypeTarget().isChargeOrProductMandatory()) {
if (getC_Charge_ID() == 0 && getM_Product_ID() == 0 && (getPriceEntered().signum() != 0 || getQtyEntered().signum() != 0)) {
log.saveError("FillMandatory", Msg.translate(getCtx(), "ChargeOrProductMandatory"));
return false;
}
}
}
return true;
} // beforeSave
/**
* Recalculate invoice tax
* @param oldTax true if the old C_Tax_ID should be used
* @return true if success, false otherwise
*
* author teo_sarca [ 1583825 ]
*/
protected boolean updateInvoiceTax(boolean oldTax) {
int C_Tax_ID = getC_Tax_ID();
boolean isOldTax = oldTax && is_ValueChanged(MInvoiceTax.COLUMNNAME_C_Tax_ID);
if (isOldTax)
{
Object old = get_ValueOld(MInvoiceTax.COLUMNNAME_C_Tax_ID);
if (old == null)
{
return true;
}
C_Tax_ID = ((Integer)old).intValue();
}
if (C_Tax_ID == 0)
{
return true;
}
MTax t = MTax.get(C_Tax_ID);
if (t.isSummary())
{
MInvoiceTax[] invoiceTaxes = MInvoiceTax.getChildTaxes(this, getPrecision(), oldTax, get_TrxName());
if (invoiceTaxes != null && invoiceTaxes.length > 0)
{
for(MInvoiceTax tax : invoiceTaxes)
{
if (!tax.calculateTaxFromLines())
return false;
if (!tax.save(get_TrxName()))
return false;
}
}
}
else
{
MInvoiceTax tax = MInvoiceTax.get (this, getPrecision(), oldTax, get_TrxName());
if (tax != null) {
if (!tax.calculateTaxFromLines())
return false;
// red1 - solving BUGS #[ 1701331 ] , #[ 1786103 ]
if (!tax.save(get_TrxName()))
return false;
}
}
return true;
}
@Override
protected boolean afterSave (boolean newRecord, boolean success)
{
if (!success)
return success;
// Re-calculate tax of document
MTax tax = new MTax(getCtx(), getC_Tax_ID(), get_TrxName());
MTaxProvider provider = new MTaxProvider(tax.getCtx(), tax.getC_TaxProvider_ID(), tax.get_TrxName());
ITaxProvider calculator = Core.getTaxProvider(provider);
if (calculator == null)
throw new AdempiereException(Msg.getMsg(getCtx(), "TaxNoProvider"));
return calculator.recalculateTax(provider, this, newRecord);
} // afterSave
@Override
protected boolean afterDelete (boolean success)
{
if (!success)
return success;
// Reset shipment line IsInvoiced flag
if ( getM_InOutLine_ID() > 0 )
{
MInOutLine sLine = new MInOutLine(getCtx(), getM_InOutLine_ID(), get_TrxName());
sLine.setIsInvoiced(false);
sLine.saveEx();
}
return updateHeaderTax();
} // afterDelete
/**
* Update Tax and Header
* @return true if header updated with tax
*/
public boolean updateHeaderTax()
{
// Update header only if the document is not processed - teo_sarca BF [ 2317305 ]
if (isProcessed() && !is_ValueChanged(COLUMNNAME_Processed))
return true;
// Recalculate Tax for this Tax
MTax tax = new MTax(getCtx(), getC_Tax_ID(), get_TrxName());
MTaxProvider provider = new MTaxProvider(tax.getCtx(), tax.getC_TaxProvider_ID(), tax.get_TrxName());
ITaxProvider calculator = Core.getTaxProvider(provider);
if (calculator == null)
throw new AdempiereException(Msg.getMsg(getCtx(), "TaxNoProvider"));
if (!calculator.updateInvoiceTax(provider, this))
return false;
return calculator.updateHeaderTax(provider, this);
} // updateHeaderTax
/**
* Allocate Landed Costs
* @return error message or ""
*/
public String allocateLandedCosts()
{
if (isProcessed())
return "Processed";
StringBuilder sql = new StringBuilder("DELETE FROM C_LandedCostAllocation WHERE C_InvoiceLine_ID=").append(getC_InvoiceLine_ID());
int no = DB.executeUpdate(sql.toString(), get_TrxName());
if (no != 0)
if (log.isLoggable(Level.INFO)) log.info("Deleted #" + no);
MLandedCost[] lcs = MLandedCost.getLandedCosts(this);
if (lcs.length == 0)
return "";
int inserted = 0;
// *** Single Criteria ***
StringBuilder msgreturn;
if (lcs.length == 1)
{
MLandedCost lc = lcs[0];
if (lc.getM_InOut_ID() != 0 && lc.getM_InOutLine_ID() == 0)
{
// Create List
ArrayList list = new ArrayList();
MInOut ship = new MInOut (getCtx(), lc.getM_InOut_ID(), get_TrxName());
MInOutLine[] lines = ship.getLines();
for (int i = 0; i < lines.length; i++)
{
if (lines[i].isDescription() || lines[i].getM_Product_ID() == 0)
continue;
if (lc.getM_Product_ID() == 0
|| lc.getM_Product_ID() == lines[i].getM_Product_ID())
list.add(lines[i]);
}
if (list.size() == 0)
return "No Matching Lines (with Product) in Shipment";
// Calculate total & base
BigDecimal total = Env.ZERO;
for (int i = 0; i < list.size(); i++)
{
MInOutLine iol = (MInOutLine)list.get(i);
total = total.add(iol.getBase(lc.getLandedCostDistribution()));
}
if (total.signum() == 0){
msgreturn = new StringBuilder("Total of Base values is 0 - ").append(lc.getLandedCostDistribution());
return msgreturn.toString();
}
// Create Allocations
for (int i = 0; i < list.size(); i++)
{
MInOutLine iol = (MInOutLine)list.get(i);
MLandedCostAllocation lca = new MLandedCostAllocation (this, lc.getM_CostElement_ID());
lca.setM_Product_ID(iol.getM_Product_ID());
lca.setM_InOutLine_ID(iol.getM_InOutLine_ID());
lca.setM_AttributeSetInstance_ID(iol.getM_AttributeSetInstance_ID());
BigDecimal base = iol.getBase(lc.getLandedCostDistribution());
lca.setBase(base);
// MZ Goodwill
// add set Qty from InOutLine
lca.setQty(iol.getMovementQty());
// end MZ
if (base.signum() != 0)
{
double result = getLineNetAmt().multiply(base).doubleValue();
result /= total.doubleValue();
lca.setAmt(result, getParent().getC_Currency().getStdPrecision());
}
if (!lca.save()){
msgreturn = new StringBuilder("Cannot save line Allocation = ").append(lca);
return msgreturn.toString();
}
inserted++;
}
if (log.isLoggable(Level.INFO)) log.info("Inserted " + inserted);
allocateLandedCostRounding();
return "";
}
// Single Line
else if (lc.getM_InOutLine_ID() != 0)
{
MInOutLine iol = new MInOutLine (getCtx(), lc.getM_InOutLine_ID(), get_TrxName());
if (iol.isDescription() || iol.getM_Product_ID() == 0){
msgreturn = new StringBuilder("Invalid Receipt Line - ").append(iol);
return msgreturn.toString();
}
MLandedCostAllocation lca = new MLandedCostAllocation (this, lc.getM_CostElement_ID());
lca.setM_Product_ID(iol.getM_Product_ID());
lca.setM_AttributeSetInstance_ID(iol.getM_AttributeSetInstance_ID());
lca.setM_InOutLine_ID(iol.getM_InOutLine_ID());
BigDecimal base = iol.getBase(lc.getLandedCostDistribution());
if (base.signum() == 0)
return "Base value is 0 - " + lc.getLandedCostDistribution();
lca.setBase(base);
lca.setAmt(getLineNetAmt());
// MZ Goodwill
// add set Qty from InOutLine
lca.setQty(iol.getMovementQty());
// end MZ
if (lca.save())
return "";
msgreturn = new StringBuilder("Cannot save single line Allocation = ").append(lc);
return msgreturn.toString();
}
// Single Product
else if (lc.getM_Product_ID() != 0)
{
MLandedCostAllocation lca = new MLandedCostAllocation (this, lc.getM_CostElement_ID());
lca.setM_Product_ID(lc.getM_Product_ID()); // No ASI
lca.setAmt(getLineNetAmt());
if (lc.getLandedCostDistribution().equals(MLandedCost.LANDEDCOSTDISTRIBUTION_Costs))
{
lca.setBase(getLineNetAmt());
lca.setQty(getLineNetAmt());
}
else
{
lca.setBase(getQtyInvoiced());
lca.setQty(getQtyInvoiced());
}
if (lca.save())
return "";
msgreturn = new StringBuilder("Cannot save Product Allocation = ").append(lc);
return msgreturn.toString();
}
else{
msgreturn = new StringBuilder("No Reference for ").append(lc);
return msgreturn.toString();
}
}
// *** Multiple Criteria ***
String LandedCostDistribution = lcs[0].getLandedCostDistribution();
int M_CostElement_ID = lcs[0].getM_CostElement_ID();
for (int i = 0; i < lcs.length; i++)
{
MLandedCost lc = lcs[i];
if (!LandedCostDistribution.equals(lc.getLandedCostDistribution()))
return "Multiple Landed Cost Rules must have consistent Landed Cost Distribution";
if (lc.getM_Product_ID() != 0 && lc.getM_InOut_ID() == 0 && lc.getM_InOutLine_ID() == 0)
return "Multiple Landed Cost Rules cannot directly allocate to a Product";
if (M_CostElement_ID != lc.getM_CostElement_ID())
return "Multiple Landed Cost Rules cannot different Cost Elements";
}
// Create List
ArrayList list = new ArrayList();
for (int ii = 0; ii < lcs.length; ii++)
{
MLandedCost lc = lcs[ii];
if (lc.getM_InOut_ID() != 0 && lc.getM_InOutLine_ID() == 0) // entire receipt
{
MInOut ship = new MInOut (getCtx(), lc.getM_InOut_ID(), get_TrxName());
MInOutLine[] lines = ship.getLines();
for (int i = 0; i < lines.length; i++)
{
if (lines[i].isDescription() // description or no product
|| lines[i].getM_Product_ID() == 0)
continue;
if (lc.getM_Product_ID() == 0 // no restriction or product match
|| lc.getM_Product_ID() == lines[i].getM_Product_ID())
list.add(lines[i]);
}
}
else if (lc.getM_InOutLine_ID() != 0) // receipt line
{
MInOutLine iol = new MInOutLine (getCtx(), lc.getM_InOutLine_ID(), get_TrxName());
if (!iol.isDescription() && iol.getM_Product_ID() != 0)
list.add(iol);
}
}
if (list.size() == 0)
return "No Matching Lines (with Product)";
// Calculate total & base
BigDecimal total = Env.ZERO;
for (int i = 0; i < list.size(); i++)
{
MInOutLine iol = (MInOutLine)list.get(i);
total = total.add(iol.getBase(LandedCostDistribution));
}
if (total.signum() == 0){
msgreturn = new StringBuilder("Total of Base values is 0 - ").append(LandedCostDistribution);
return msgreturn.toString();
}
// Create Allocations
for (int i = 0; i < list.size(); i++)
{
MInOutLine iol = (MInOutLine)list.get(i);
MLandedCostAllocation lca = new MLandedCostAllocation (this, lcs[0].getM_CostElement_ID());
lca.setM_Product_ID(iol.getM_Product_ID());
lca.setM_AttributeSetInstance_ID(iol.getM_AttributeSetInstance_ID());
lca.setM_InOutLine_ID(iol.getM_InOutLine_ID());
BigDecimal base = iol.getBase(LandedCostDistribution);
lca.setBase(base);
// add set Qty from InOutLine
lca.setQty(iol.getMovementQty());
if (base.signum() != 0)
{
double result = getLineNetAmt().multiply(base).doubleValue();
result /= total.doubleValue();
lca.setAmt(result, getParent().getC_Currency().getStdPrecision());
}
if (!lca.save()){
msgreturn = new StringBuilder("Cannot save line Allocation = ").append(lca);
return msgreturn.toString();
}
inserted++;
}
if (log.isLoggable(Level.INFO)) log.info("Inserted " + inserted);
allocateLandedCostRounding();
return "";
} // allocate Costs
/**
* Allocate Landed Cost - Enforce Rounding
*/
protected void allocateLandedCostRounding()
{
MLandedCostAllocation[] allocations = MLandedCostAllocation.getOfInvoiceLine(
getCtx(), getC_InvoiceLine_ID(), get_TrxName());
MLandedCostAllocation largestAmtAllocation = null;
BigDecimal allocationAmt = Env.ZERO;
for (int i = 0; i < allocations.length; i++)
{
MLandedCostAllocation allocation = allocations[i];
if (largestAmtAllocation == null
|| allocation.getAmt().compareTo(largestAmtAllocation.getAmt()) > 0)
largestAmtAllocation = allocation;
allocationAmt = allocationAmt.add(allocation.getAmt());
}
BigDecimal difference = getLineNetAmt().subtract(allocationAmt);
if (difference.signum() != 0)
{
largestAmtAllocation.setAmt(largestAmtAllocation.getAmt().add(difference));
largestAmtAllocation.saveEx();
if (log.isLoggable(Level.CONFIG)) log.config("Difference=" + difference
+ ", C_LandedCostAllocation_ID=" + largestAmtAllocation.getC_LandedCostAllocation_ID()
+ ", Amt" + largestAmtAllocation.getAmt());
}
} // allocateLandedCostRounding
/**
* Get LandedCost of InvoiceLine
* @param whereClause starting with AND
* @return array of landedCost
*/
public MLandedCost[] getLandedCost (String whereClause)
{
ArrayList list = new ArrayList();
String sql = "SELECT * FROM C_LandedCost WHERE C_InvoiceLine_ID=? ";
if (whereClause != null)
sql += whereClause;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql, get_TrxName());
pstmt.setInt(1, getC_InvoiceLine_ID());
rs = pstmt.executeQuery();
while (rs.next())
{
MLandedCost lc = new MLandedCost(getCtx(), rs, get_TrxName());
list.add(lc);
}
}
catch (Exception e)
{
log.log(Level.SEVERE, "getLandedCost", e);
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
//
MLandedCost[] landedCost = new MLandedCost[list.size()];
list.toArray(landedCost);
return landedCost;
} // getLandedCost
/**
* Copy LandedCost From other InvoiceLine.
* @param otherInvoiceLine other invoice line
* @return number of lines copied
*/
public int copyLandedCostFrom (MInvoiceLine otherInvoiceLine)
{
if (otherInvoiceLine == null)
return 0;
MLandedCost[] fromLandedCosts = otherInvoiceLine.getLandedCost(null);
int count = 0;
for (int i = 0; i < fromLandedCosts.length; i++)
{
MLandedCost landedCost = new MLandedCost (getCtx(), 0, get_TrxName());
MLandedCost fromLandedCost = fromLandedCosts[i];
PO.copyValues (fromLandedCost, landedCost, fromLandedCost.getAD_Client_ID(), fromLandedCost.getAD_Org_ID());
landedCost.setC_InvoiceLine_ID(getC_InvoiceLine_ID());
landedCost.set_ValueNoCheck ("C_LandedCost_ID", I_ZERO); // new
if (landedCost.save(get_TrxName()))
count++;
}
if (fromLandedCosts.length != count)
log.log(Level.SEVERE, "LandedCost difference - From=" + fromLandedCosts.length + " <> Saved=" + count);
return count;
} // copyLinesFrom
/**
* @param rmaLine
*/
public void setRMALine(MRMALine rmaLine)
{
// Check if this invoice is CreditMemo - teo_sarca [ 2804142 ]
if (!getParent().isCreditMemo())
{
throw new AdempiereException("InvoiceNotCreditMemo");
}
setAD_Org_ID(rmaLine.getAD_Org_ID());
setM_RMALine_ID(rmaLine.getM_RMALine_ID());
setDescription(rmaLine.getDescription());
setLine(rmaLine.getLine());
setC_Charge_ID(rmaLine.getC_Charge_ID());
setM_Product_ID(rmaLine.getM_Product_ID());
setC_UOM_ID(rmaLine.getC_UOM_ID());
setC_Tax_ID(rmaLine.getC_Tax_ID());
setPrice(rmaLine.getAmt());
BigDecimal qty = rmaLine.getQty();
if (rmaLine.getQtyInvoiced() != null)
qty = qty.subtract(rmaLine.getQtyInvoiced());
setQty(qty);
setLineNetAmt();
setTaxAmt();
setLineTotalAmt(rmaLine.getLineNetAmt());
setC_Project_ID(rmaLine.getC_Project_ID());
setC_Activity_ID(rmaLine.getC_Activity_ID());
setC_Campaign_ID(rmaLine.getC_Campaign_ID());
}
/**
* @return matched qty
*/
public BigDecimal getMatchedQty()
{
String sql = "SELECT COALESCE(SUM("+MMatchInv.COLUMNNAME_Qty+"),0)"
+" FROM "+MMatchInv.Table_Name
+" WHERE "+MMatchInv.COLUMNNAME_C_InvoiceLine_ID+"=?"
+" AND "+MMatchInv.COLUMNNAME_Processed+"=?";
return DB.getSQLValueBDEx(get_TrxName(), sql, getC_InvoiceLine_ID(), true);
}
/**
* Clear cache parent (invoice) reference
*/
public void clearParent()
{
this.m_parent = null;
}
} // MInvoiceLine