/******************************************************************************
* 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.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Level;
import org.adempiere.base.Core;
import org.adempiere.base.CreditStatus;
import org.adempiere.base.ICreditManager;
import org.adempiere.exceptions.AdempiereException;
import org.adempiere.exceptions.BPartnerNoAddressException;
import org.adempiere.exceptions.DBException;
import org.adempiere.exceptions.PeriodClosedException;
import org.adempiere.model.ITaxProvider;
import org.compiere.print.MPrintFormat;
import org.compiere.print.ReportEngine;
import org.compiere.process.DocAction;
import org.compiere.process.DocumentEngine;
import org.compiere.process.IDocsPostProcess;
import org.compiere.process.ProcessInfo;
import org.compiere.process.ServerProcessCtl;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
import org.compiere.util.Msg;
import org.compiere.util.TimeUtil;
import org.compiere.util.Util;
import org.eevolution.model.MPPProductBOM;
import org.eevolution.model.MPPProductBOMLine;
/**
*
* Invoice Model.
* Please do not set DocStatus and C_DocType_ID directly.
* They are set in the process() method.
* Use DocAction and C_DocTypeTarget_ID instead.
*
* @author Jorg Janke
* @version $Id: MInvoice.java,v 1.2 2006/07/30 00:51:02 jjanke Exp $
* @author victor.perez@e-evolution.com, e-Evolution http://www.e-evolution.com
* @see https://sourceforge.net/p/adempiere/feature-requests/412/
* FR [ 2520591 ] Support multiples calendar for Org
* @see https://sourceforge.net/p/adempiere/feature-requests/631/
* Modifications: Added RMA functionality (Ashley Ramdass)
* Modifications: Generate DocNo^ instead of using a new number when an invoice is reversed (Diego Ruiz-globalqss)
*/
public class MInvoice extends X_C_Invoice implements DocAction, IDocsPostProcess
{
/**
* generated serial id
*/
private static final long serialVersionUID = 9166700544471146864L;
public static final String MATCH_TO_RECEIPT_SQL =
"""
SELECT hdr.C_Invoice_ID, hdr.DocumentNo, hdr.DateInvoiced, bp.Name, hdr.C_BPartner_ID,
lin.Line, lin.C_InvoiceLine_ID, p.Name, lin.M_Product_ID,
CASE WHEN dt.DocBaseType='APC' THEN lin.QtyInvoiced * -1 ELSE lin.QtyInvoiced END,SUM(NVL(mi.Qty,0)), org.Name, hdr.AD_Org_ID
FROM C_Invoice hdr
INNER JOIN AD_Org org ON (hdr.AD_Org_ID=org.AD_Org_ID)
INNER JOIN C_BPartner bp ON (hdr.C_BPartner_ID=bp.C_BPartner_ID)
INNER JOIN C_InvoiceLine lin ON (hdr.C_Invoice_ID=lin.C_Invoice_ID)
INNER JOIN M_Product p ON (lin.M_Product_ID=p.M_Product_ID)
INNER JOIN C_DocType dt ON (hdr.C_DocType_ID=dt.C_DocType_ID AND dt.DocBaseType IN ('API','APC'))
FULL JOIN M_MatchInv mi ON (lin.C_InvoiceLine_ID=mi.C_InvoiceLine_ID)
WHERE hdr.DocStatus IN ('CO','CL')
""";
/** Matching to Receipt Group By Template */
private static final String BASE_MATCHING_GROUP_BY_SQL =
"""
GROUP BY hdr.C_Invoice_ID,hdr.DocumentNo,hdr.DateInvoiced,bp.Name,hdr.C_BPartner_ID,
lin.Line,lin.C_InvoiceLine_ID,p.Name,lin.M_Product_ID,dt.DocBaseType,lin.QtyInvoiced, org.Name, hdr.AD_Org_ID, dt.DocBaseType
HAVING %s <> SUM(NVL(mi.Qty,0))
""";
public static final String NOT_FULLY_MATCHED_TO_RECEIPT_GROUP_BY = BASE_MATCHING_GROUP_BY_SQL
.formatted("CASE WHEN dt.DocBaseType='APC' THEN lin.QtyInvoiced * -1 ELSE lin.QtyInvoiced END");
public static final String FULL_OR_PARTIALLY_MATCHED_TO_RECEIPT_GROUP_BY = BASE_MATCHING_GROUP_BY_SQL.formatted("0");
/**
* @param C_BPartner_ID
* @param M_Product_ID
* @param M_InOutLine_ID
* @param from
* @param to
* @param trxName
* @return list of invoices not fully matched to receipt
*/
public static List getNotFullyMatchedToReceipt(int C_BPartner_ID, int M_Product_ID, int M_InOutLine_ID, Timestamp from, Timestamp to, String trxName) {
StringBuilder builder = new StringBuilder(MATCH_TO_RECEIPT_SQL);
if (M_InOutLine_ID > 0) {
builder.append(" AND mi.M_InOutLine_ID = ").append(M_InOutLine_ID);
}
if (M_Product_ID > 0) {
builder.append(" AND lin.M_Product_ID=").append(M_Product_ID);
}
if (C_BPartner_ID > 0) {
builder.append(" AND hdr.C_BPartner_ID=").append(C_BPartner_ID);
}
if (from != null) {
builder.append(" AND ").append("hdr.DateInvoiced").append(" >= ").append(DB.TO_DATE(from));
}
if (to != null) {
builder.append(" AND ").append("hdr.DateInvoiced").append(" <= ").append(DB.TO_DATE(to));
}
String sql = MRole.getDefault().addAccessSQL(
builder.toString(), "hdr", MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO)
+ NOT_FULLY_MATCHED_TO_RECEIPT_GROUP_BY;
List records = new ArrayList<>();
try (PreparedStatement stmt = DB.prepareStatement(sql, trxName)) {
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
MatchingRecord matchingRecord = new MatchingRecord(rs.getInt(1), rs.getString(2), rs.getTimestamp(3), rs.getString(4), rs.getInt(5), rs.getInt(6), rs.getInt(7),
rs.getString(8), rs.getInt(9), rs.getBigDecimal(10), rs.getBigDecimal(11), rs.getString(12), rs.getInt(13));
records.add(matchingRecord);
}
} catch (SQLException e) {
throw new DBException(e.getMessage(), e);
}
return records;
}
/**
* @param C_BPartner_ID
* @param M_Product_ID
* @param M_InOutLine_ID
* @param from
* @param to
* @param trxName
* @return list of invoices full or partially match to receipt
*/
public static List getFullOrPartiallyMatchedToReceipt(int C_BPartner_ID, int M_Product_ID, int M_InOutLine_ID, Timestamp from, Timestamp to, String trxName) {
StringBuilder builder = new StringBuilder(MATCH_TO_RECEIPT_SQL);
if (M_InOutLine_ID > 0) {
builder.append(" AND mi.M_InOutLine_ID = ").append(M_InOutLine_ID);
}
if (M_Product_ID > 0) {
builder.append(" AND lin.M_Product_ID=").append(M_Product_ID);
}
if (C_BPartner_ID > 0) {
builder.append(" AND hdr.C_BPartner_ID=").append(C_BPartner_ID);
}
if (from != null) {
builder.append(" AND ").append("hdr.DateInvoiced").append(" >= ").append(DB.TO_DATE(from));
}
if (to != null) {
builder.append(" AND ").append("hdr.DateInvoiced").append(" <= ").append(DB.TO_DATE(to));
}
String sql = MRole.getDefault().addAccessSQL(
builder.toString(), "hdr", MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO)
+ FULL_OR_PARTIALLY_MATCHED_TO_RECEIPT_GROUP_BY;
List records = new ArrayList<>();
try (PreparedStatement stmt = DB.prepareStatement(sql, trxName)) {
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
MatchingRecord matchingRecord = new MatchingRecord(rs.getInt(1), rs.getString(2), rs.getTimestamp(3), rs.getString(4), rs.getInt(5), rs.getInt(6), rs.getInt(7),
rs.getString(8), rs.getInt(9), rs.getBigDecimal(10), rs.getBigDecimal(11), rs.getString(12), rs.getInt(13));
records.add(matchingRecord);
}
} catch (SQLException e) {
throw new DBException(e.getMessage(), e);
}
return records;
}
/**
* record for matchings
*/
public static record MatchingRecord(int C_Invoice_ID, String documentNo, Timestamp documentDate, String businessPartnerName, int C_BPartner_ID, int line, int C_InvoiceLine_ID,
String productName, int M_Product_ID, BigDecimal qtyInvoiced, BigDecimal matchedQty, String organizationName, int AD_Org_ID) {}
/**
* Get invoices Of BPartner
* @param ctx context
* @param C_BPartner_ID id
* @param trxName transaction
* @return array of invoice
*/
public static MInvoice[] getOfBPartner (Properties ctx, int C_BPartner_ID, String trxName)
{
List list = new Query(ctx, Table_Name, COLUMNNAME_C_BPartner_ID+"=?", trxName)
.setParameters(C_BPartner_ID)
.list();
return list.toArray(new MInvoice[list.size()]);
} // getOfBPartner
/**
* Create new Invoice by copying
* @param from invoice
* @param dateDoc date of the document date
* @param dateAcct original account date
* @param C_DocTypeTarget_ID target doc type
* @param isSOTrx sales order
* @param counter create counter links
* @param trxName trx
* @param setOrder set Order links
* @return Invoice
*/
public static MInvoice copyFrom (MInvoice from, Timestamp dateDoc, Timestamp dateAcct,
int C_DocTypeTarget_ID, boolean isSOTrx, boolean counter,
String trxName, boolean setOrder)
{
return copyFrom (from, dateDoc, dateAcct,
C_DocTypeTarget_ID, isSOTrx, counter,
trxName, setOrder,null);
}
/**
* Create new Invoice by copying
* @param from invoice
* @param dateDoc date of the document date
* @param dateAcct original account date
* @param C_DocTypeTarget_ID target doc type
* @param isSOTrx sales order
* @param counter create counter links
* @param trxName trx
* @param setOrder set Order links
* @param documentNo Document Number for reversed invoices
* @return Invoice
*/
public static MInvoice copyFrom (MInvoice from, Timestamp dateDoc, Timestamp dateAcct,
int C_DocTypeTarget_ID, boolean isSOTrx, boolean counter,
String trxName, boolean setOrder, String documentNo)
{
MInvoice to = new MInvoice (from.getCtx(), 0, trxName);
PO.copyValues (from, to, from.getAD_Client_ID(), from.getAD_Org_ID());
to.set_ValueNoCheck ("C_Invoice_ID", I_ZERO);
to.set_ValueNoCheck ("DocumentNo", documentNo);
//
to.setDocStatus (DOCSTATUS_Drafted); // Draft
to.setDocAction(DOCACTION_Complete);
//
to.setC_DocType_ID(0);
to.setC_DocTypeTarget_ID (C_DocTypeTarget_ID);
to.setIsSOTrx(isSOTrx);
//
to.setDateInvoiced (dateDoc);
to.setDateAcct (dateAcct);
to.setDatePrinted(null);
to.setIsPrinted (false);
//
to.setIsApproved (false);
to.setC_Payment_ID(0);
to.setC_CashLine_ID(0);
to.setIsPaid (false);
to.setIsInDispute(false);
//
// Amounts are updated by trigger when adding lines
to.setGrandTotal(Env.ZERO);
to.setTotalLines(Env.ZERO);
//
to.setIsTransferred (false);
to.setPosted (false);
to.setProcessed (false);
//[ 1633721 ] Reverse Documents- Processing=Y
to.setProcessing(false);
// delete references
to.setIsSelfService(false);
if (!setOrder)
to.setC_Order_ID(0);
if (counter)
{
to.setRef_Invoice_ID(from.getC_Invoice_ID());
MOrg org = MOrg.get(from.getAD_Org_ID());
int counterC_BPartner_ID = org.getLinkedC_BPartner_ID(trxName);
if (counterC_BPartner_ID == 0)
return null;
to.setBPartner(MBPartner.get(from.getCtx(), counterC_BPartner_ID));
// Try to find Order link
if (from.getC_Order_ID() != 0)
{
MOrder peer = new MOrder (from.getCtx(), from.getC_Order_ID(), from.get_TrxName());
if (peer.getRef_Order_ID() != 0)
to.setC_Order_ID(peer.getRef_Order_ID());
}
// Try to find RMA link
if (from.getM_RMA_ID() != 0)
{
MRMA peer = new MRMA (from.getCtx(), from.getM_RMA_ID(), from.get_TrxName());
if (peer.getRef_RMA_ID() > 0)
to.setM_RMA_ID(peer.getRef_RMA_ID());
}
//
}
else
to.setRef_Invoice_ID(0);
to.saveEx(trxName);
if (counter)
from.setRef_Invoice_ID(to.getC_Invoice_ID());
// Lines
if (to.copyLinesFrom(from, counter, setOrder) == 0)
throw new IllegalStateException("Could not create Invoice Lines");
return to;
}
/**
* @deprecated
* Create new Invoice by copying
* @param from invoice
* @param dateDoc date of the document date
* @param C_DocTypeTarget_ID target doc type
* @param isSOTrx sales order
* @param counter create counter links
* @param trxName trx
* @param setOrder set Order links
* @return Invoice
*/
@Deprecated
public static MInvoice copyFrom (MInvoice from, Timestamp dateDoc,
int C_DocTypeTarget_ID, boolean isSOTrx, boolean counter,
String trxName, boolean setOrder)
{
MInvoice to = copyFrom ( from, dateDoc, dateDoc,
C_DocTypeTarget_ID, isSOTrx, counter,
trxName, setOrder);
return to;
} // copyFrom
/**
* Get PDF File Name
* @param documentDir directory
* @param C_Invoice_ID invoice
* @return file name
*/
public static String getPDFFileName (String documentDir, int C_Invoice_ID)
{
StringBuilder sb = new StringBuilder (documentDir);
if (sb.length() == 0)
sb.append(".");
if (!sb.toString().endsWith(File.separator))
sb.append(File.separator);
sb.append("C_Invoice_ID_")
.append(C_Invoice_ID)
.append(".pdf");
return sb.toString();
} // getPDFFileName
/**
* Get MInvoice from DB
* @param C_Invoice_ID id
* @return MInvoice
*/
public static MInvoice get (int C_Invoice_ID)
{
return get(Env.getCtx(), C_Invoice_ID);
}
/**
* Get MInvoice from DB
* @param C_Invoice_ID id
* @return MInvoice
*/
public static MInvoice get (Properties ctx, int C_Invoice_ID)
{
MInvoice retValue = new MInvoice(ctx, C_Invoice_ID, (String)null);
if (retValue.get_ID () == C_Invoice_ID)
{
return retValue;
}
return null;
} // get
/**
* UUID based Constructor
* @param ctx Context
* @param C_Invoice_UU UUID key
* @param trxName Transaction
*/
public MInvoice(Properties ctx, String C_Invoice_UU, String trxName) {
super(ctx, C_Invoice_UU, trxName);
if (Util.isEmpty(C_Invoice_UU))
setInitialDefaults();
}
/**
* Invoice Constructor
* @param ctx context
* @param C_Invoice_ID invoice or 0 for new
* @param trxName trx name
*/
public MInvoice (Properties ctx, int C_Invoice_ID, String trxName)
{
this (ctx, C_Invoice_ID, trxName, (String[]) null);
} // MInvoice
/**
* @param ctx
* @param C_Invoice_ID
* @param trxName
* @param virtualColumns
*/
public MInvoice(Properties ctx, int C_Invoice_ID, String trxName, String... virtualColumns) {
super(ctx, C_Invoice_ID, trxName, virtualColumns);
if (C_Invoice_ID == 0)
setInitialDefaults();
}
/**
* Set the initial defaults for a new record
*/
private void setInitialDefaults() {
setDocStatus (DOCSTATUS_Drafted); // Draft
setDocAction (DOCACTION_Complete);
//
setPaymentRule(PAYMENTRULE_OnCredit); // Payment Terms
setDateInvoiced (new Timestamp (System.currentTimeMillis ()));
setDateAcct (new Timestamp (System.currentTimeMillis ()));
//
setChargeAmt (Env.ZERO);
setTotalLines (Env.ZERO);
setGrandTotal (Env.ZERO);
//
setIsSOTrx (true);
setIsTaxIncluded (false);
setIsApproved (false);
setIsDiscountPrinted (false);
setIsPaid (false);
setSendEMail (false);
setIsPrinted (false);
setIsTransferred (false);
setIsSelfService(false);
setIsPayScheduleValid(false);
setIsInDispute(false);
setPosted(false);
super.setProcessed (false);
setProcessing(false);
}
/**
* Load Constructor
* @param ctx context
* @param rs result set record
* @param trxName transaction
*/
public MInvoice (Properties ctx, ResultSet rs, String trxName)
{
super(ctx, rs, trxName);
} // MInvoice
/**
* Create Invoice from Order
* @param order order
* @param C_DocTypeTarget_ID target document type
* @param invoiceDate date or null
*/
public MInvoice (MOrder order, int C_DocTypeTarget_ID, Timestamp invoiceDate)
{
this (order.getCtx(), 0, order.get_TrxName());
setClientOrg(order);
setOrder(order); // set base settings
//
if (C_DocTypeTarget_ID <= 0)
{
MDocType odt = MDocType.get(order.getC_DocType_ID());
if (odt != null)
{
C_DocTypeTarget_ID = odt.getC_DocTypeInvoice_ID();
if (C_DocTypeTarget_ID <= 0)
throw new AdempiereException("@NotFound@ @C_DocTypeInvoice_ID@ - @C_DocType_ID@:"+odt.get_Translation(MDocType.COLUMNNAME_Name));
}
}
setC_DocTypeTarget_ID(C_DocTypeTarget_ID);
if (invoiceDate != null)
setDateInvoiced(invoiceDate);
setDateAcct(getDateInvoiced());
//
setSalesRep_ID(order.getSalesRep_ID());
//
setC_BPartner_ID(order.getBill_BPartner_ID());
setC_BPartner_Location_ID(order.getBill_Location_ID());
setAD_User_ID(order.getBill_User_ID());
} // MInvoice
/**
* Create Invoice from Shipment/Receipt
* @param ship shipment/receipt
* @param invoiceDate date or null
*/
public MInvoice (MInOut ship, Timestamp invoiceDate)
{
this (ship.getCtx(), 0, ship.get_TrxName());
setClientOrg(ship);
setShipment(ship); // set base settings
//
setC_DocTypeTarget_ID();
if (invoiceDate != null)
setDateInvoiced(invoiceDate);
setDateAcct(getDateInvoiced());
//
if (getSalesRep_ID() == 0)
setSalesRep_ID(ship.getSalesRep_ID());
} // MInvoice
/**
* Create Invoice from Batch Line
* @param batch batch
* @param line batch line
*/
public MInvoice (MInvoiceBatch batch, MInvoiceBatchLine line)
{
this (line.getCtx(), 0, line.get_TrxName());
setClientOrg(line);
setDocumentNo(line.getDocumentNo());
//
setIsSOTrx(batch.isSOTrx());
MBPartner bp = new MBPartner (line.getCtx(), line.getC_BPartner_ID(), line.get_TrxName());
setBPartner(bp); // defaults
//
setIsTaxIncluded(line.isTaxIncluded());
// May conflict with default price list
setC_Currency_ID(batch.getC_Currency_ID());
setC_ConversionType_ID(batch.getC_ConversionType_ID());
//
setDescription(batch.getDescription());
setAD_OrgTrx_ID(line.getAD_OrgTrx_ID());
setC_Project_ID(line.getC_Project_ID());
setC_Activity_ID(line.getC_Activity_ID());
setUser1_ID(line.getUser1_ID());
setUser2_ID(line.getUser2_ID());
//
setC_DocTypeTarget_ID(line.getC_DocType_ID());
setDateInvoiced(line.getDateInvoiced());
setDateAcct(line.getDateAcct());
//
setSalesRep_ID(batch.getSalesRep_ID());
//
setC_BPartner_ID(line.getC_BPartner_ID());
setC_BPartner_Location_ID(line.getC_BPartner_Location_ID());
setAD_User_ID(line.getAD_User_ID());
} // MInvoice
/**
* Copy constructor
* @param copy
*/
public MInvoice(MInvoice copy)
{
this(Env.getCtx(), copy);
}
/**
* Copy constructor
* @param ctx
* @param copy
*/
public MInvoice(Properties ctx, MInvoice copy)
{
this(ctx, copy, (String) null);
}
/**
* Copy constructor
* @param ctx
* @param copy
* @param trxName
*/
public MInvoice(Properties ctx, MInvoice copy, String trxName)
{
this(ctx, 0, trxName);
copyPO(copy);
this.m_openAmt = copy.m_openAmt;
this.m_lines = copy.m_lines != null ? Arrays.stream(copy.m_lines).map(e -> {var v = new MInvoiceLine(ctx, e, trxName); v.m_parent=this; return v;}).toArray(MInvoiceLine[]::new) : null;
this.m_taxes = copy.m_taxes != null ? Arrays.stream(copy.m_taxes).map(e -> {return new MInvoiceTax(ctx, e, trxName);}).toArray(MInvoiceTax[]::new) : null;
}
/** Open Amount */
private BigDecimal m_openAmt = null;
/** Invoice Lines */
private MInvoiceLine[] m_lines;
/** Invoice Taxes */
private MInvoiceTax[] m_taxes;
/** Logger */
private static CLogger s_log = CLogger.getCLogger(MInvoice.class);
/**
* Overwrite Client/Org if required
* @param AD_Client_ID client
* @param AD_Org_ID org
*/
@Override
public void setClientOrg (int AD_Client_ID, int AD_Org_ID)
{
super.setClientOrg(AD_Client_ID, AD_Org_ID);
} // setClientOrg
/**
* Set Business Partner Defaults and Details
* @param bp business partner
*/
public void setBPartner (MBPartner bp)
{
if (bp == null)
return;
setC_BPartner_ID(bp.getC_BPartner_ID());
// Set Defaults
int ii = 0;
if (isSOTrx())
ii = bp.getC_PaymentTerm_ID();
else
ii = bp.getPO_PaymentTerm_ID();
if (ii != 0)
setC_PaymentTerm_ID(ii);
//
if (isSOTrx())
ii = bp.getM_PriceList_ID();
else
ii = bp.getPO_PriceList_ID();
if (ii != 0)
setM_PriceList_ID(ii);
//
String ss = bp.getPaymentRule();
if (ss != null)
setPaymentRule(ss);
// Set Locations
MBPartnerLocation[] locs = bp.getLocations(false);
if (locs != null)
{
for (int i = 0; i < locs.length; i++)
{
if ((locs[i].isBillTo() && isSOTrx())
|| (locs[i].isPayFrom() && !isSOTrx()))
setC_BPartner_Location_ID(locs[i].getC_BPartner_Location_ID());
}
// set to first
if (getC_BPartner_Location_ID() == 0 && locs.length > 0)
setC_BPartner_Location_ID(locs[0].getC_BPartner_Location_ID());
}
if (getC_BPartner_Location_ID() == 0)
log.log(Level.SEVERE, new BPartnerNoAddressException(bp).getLocalizedMessage()); //TODO: throw exception?
// Set Contact
MUser[] contacts = bp.getContacts(false);
if (contacts != null && contacts.length > 0) // get first User
setAD_User_ID(contacts[0].getAD_User_ID());
} // setBPartner
/**
* Set Order Reference
* @param order order
*/
public void setOrder (MOrder order)
{
if (order == null)
return;
setC_Order_ID(order.getC_Order_ID());
setIsSOTrx(order.isSOTrx());
setIsDiscountPrinted(order.isDiscountPrinted());
setIsSelfService(order.isSelfService());
setSendEMail(order.isSendEMail());
//
setM_PriceList_ID(order.getM_PriceList_ID());
setIsTaxIncluded(order.isTaxIncluded());
setC_Currency_ID(order.getC_Currency_ID());
setC_ConversionType_ID(order.getC_ConversionType_ID());
//
setPaymentRule(order.getPaymentRule());
setC_PaymentTerm_ID(order.getC_PaymentTerm_ID());
setPOReference(order.getPOReference());
setDescription(order.getDescription());
setDateOrdered(order.getDateOrdered());
//
setAD_OrgTrx_ID(order.getAD_OrgTrx_ID());
setC_Project_ID(order.getC_Project_ID());
setC_Campaign_ID(order.getC_Campaign_ID());
setC_Activity_ID(order.getC_Activity_ID());
setUser1_ID(order.getUser1_ID());
setUser2_ID(order.getUser2_ID());
} // setOrder
/**
* Set Shipment Reference
* @param ship shipment
*/
public void setShipment (MInOut ship)
{
if (ship == null)
return;
setIsSOTrx(ship.isSOTrx());
//
MBPartner bp = new MBPartner (getCtx(), ship.getC_BPartner_ID(), null);
setBPartner (bp);
//
setAD_User_ID(ship.getAD_User_ID());
//
setSendEMail(ship.isSendEMail());
//
setPOReference(ship.getPOReference());
setDescription(ship.getDescription());
setDateOrdered(ship.getDateOrdered());
//
setAD_OrgTrx_ID(ship.getAD_OrgTrx_ID());
setC_Project_ID(ship.getC_Project_ID());
setC_Campaign_ID(ship.getC_Campaign_ID());
setC_Activity_ID(ship.getC_Activity_ID());
setUser1_ID(ship.getUser1_ID());
setUser2_ID(ship.getUser2_ID());
//
if (ship.getC_Order_ID() != 0)
{
setC_Order_ID(ship.getC_Order_ID());
MOrder order = new MOrder (getCtx(), ship.getC_Order_ID(), get_TrxName());
setIsDiscountPrinted(order.isDiscountPrinted());
setM_PriceList_ID(order.getM_PriceList_ID());
setIsTaxIncluded(order.isTaxIncluded());
setC_Currency_ID(order.getC_Currency_ID());
setC_ConversionType_ID(order.getC_ConversionType_ID());
setPaymentRule(order.getPaymentRule());
setC_PaymentTerm_ID(order.getC_PaymentTerm_ID());
//
MDocType dt = MDocType.get(order.getC_DocType_ID());
if (dt.getC_DocTypeInvoice_ID() != 0)
setC_DocTypeTarget_ID(dt.getC_DocTypeInvoice_ID());
// Overwrite Invoice BPartner
setC_BPartner_ID(order.getBill_BPartner_ID());
// Overwrite Invoice Address
setC_BPartner_Location_ID(order.getBill_Location_ID());
// Overwrite Contact
setAD_User_ID(order.getBill_User_ID());
setSalesRep_ID(order.getSalesRep_ID());
//
}
// Check if Shipment/Receipt is based on RMA
if (ship.getM_RMA_ID() != 0)
{
setM_RMA_ID(ship.getM_RMA_ID());
MRMA rma = new MRMA(getCtx(), ship.getM_RMA_ID(), get_TrxName());
// Retrieves the invoice DocType
MDocType dt = MDocType.get(rma.getC_DocType_ID());
if (dt.getC_DocTypeInvoice_ID() != 0)
{
setC_DocTypeTarget_ID(dt.getC_DocTypeInvoice_ID());
}
setIsSOTrx(rma.isSOTrx());
MOrder rmaOrder = rma.getOriginalOrder();
if (rmaOrder != null) {
setM_PriceList_ID(rmaOrder.getM_PriceList_ID());
setIsTaxIncluded(rmaOrder.isTaxIncluded());
setC_Currency_ID(rmaOrder.getC_Currency_ID());
setC_ConversionType_ID(rmaOrder.getC_ConversionType_ID());
setPaymentRule(rmaOrder.getPaymentRule());
setC_PaymentTerm_ID(rmaOrder.getC_PaymentTerm_ID());
setC_BPartner_Location_ID(rmaOrder.getBill_Location_ID());
}
}
} // setShipment
/**
* Set Target Document Type
* @param DocBaseType doc type MDocType.DOCBASETYPE_
*/
public void setC_DocTypeTarget_ID (String DocBaseType)
{
String sql = "SELECT C_DocType_ID FROM C_DocType "
+ "WHERE AD_Client_ID=? AND AD_Org_ID in (0,?) AND DocBaseType=?"
+ " AND IsActive='Y' "
+ "ORDER BY IsDefault DESC, AD_Org_ID DESC";
int C_DocType_ID = DB.getSQLValueEx(null, sql, getAD_Client_ID(), getAD_Org_ID(), DocBaseType);
if (C_DocType_ID <= 0)
log.log(Level.SEVERE, "Not found for AD_Client_ID="
+ getAD_Client_ID() + " - " + DocBaseType);
else
{
log.fine(DocBaseType);
setC_DocTypeTarget_ID (C_DocType_ID);
boolean isSOTrx = MDocType.DOCBASETYPE_ARInvoice.equals(DocBaseType)
|| MDocType.DOCBASETYPE_ARCreditMemo.equals(DocBaseType);
setIsSOTrx (isSOTrx);
}
} // setC_DocTypeTarget_ID
/**
* Set Target Document Type.
* Based on SO flag AR/AP Invoice.
*/
public void setC_DocTypeTarget_ID ()
{
if (getC_DocTypeTarget_ID() > 0)
return;
if (isSOTrx())
setC_DocTypeTarget_ID(MDocType.DOCBASETYPE_ARInvoice);
else
setC_DocTypeTarget_ID(MDocType.DOCBASETYPE_APInvoice);
} // setC_DocTypeTarget_ID
/**
* Get Grand Total
* @param creditMemoAdjusted true to negate amount return
* @return grand total
*/
public BigDecimal getGrandTotal (boolean creditMemoAdjusted)
{
if (!creditMemoAdjusted)
return super.getGrandTotal();
//
BigDecimal amt = getGrandTotal();
if (isCreditMemo())
return amt.negate();
return amt;
} // getGrandTotal
/**
* Get Total Lines
* @param creditMemoAdjusted true to negate amount return
* @return total lines
*/
public BigDecimal getTotalLines (boolean creditMemoAdjusted)
{
if (!creditMemoAdjusted)
return super.getTotalLines();
//
BigDecimal amt = getTotalLines();
if (isCreditMemo())
return amt.negate();
return amt;
} // getTotalLines
/**
* Get Invoice Lines of Invoice
* @param whereClause must start with AND
* @return lines
*/
private MInvoiceLine[] getLines (String whereClause)
{
String whereClauseFinal = "C_Invoice_ID=? ";
if (whereClause != null)
whereClauseFinal += whereClause;
List list = new Query(getCtx(), I_C_InvoiceLine.Table_Name, whereClauseFinal, get_TrxName())
.setParameters(getC_Invoice_ID())
.setOrderBy("Line, C_InvoiceLine_ID")
.list();
return list.toArray(new MInvoiceLine[list.size()]);
} // getLines
/**
* Get Invoice Lines
* @param requery
* @return lines
*/
public MInvoiceLine[] getLines (boolean requery)
{
if (m_lines == null || m_lines.length == 0 || requery)
{
m_lines = getLines(null);
}
set_TrxName(m_lines, get_TrxName());
return m_lines;
} // getLines
/**
* Get Lines of Invoice
* @return lines
*/
public MInvoiceLine[] getLines()
{
return getLines(false);
} // getLines
/**
* Renumber Lines
* @param step start and step to increment
*/
public void renumberLines (int step)
{
int number = step;
MInvoiceLine[] lines = getLines(false);
for (int i = 0; i < lines.length; i++)
{
MInvoiceLine line = lines[i];
line.setLine(number);
line.saveEx();
number += step;
}
m_lines = null;
} // renumberLines
/**
* Copy Lines From other Invoice.
* @param otherInvoice invoice
* @param counter create counter links
* @param setOrder set order links
* @return number of lines copied
*/
public int copyLinesFrom (MInvoice otherInvoice, boolean counter, boolean setOrder){
return copyLinesFrom (otherInvoice, counter, setOrder, true);
}
/**
* Copy Lines From other Invoice.
* @param otherInvoice invoice
* @param counter create counter links
* @param setOrder set order links
* @param copyClientOrg copy also Client and Org
* @return number of lines copied
*/
public int copyLinesFrom (MInvoice otherInvoice, boolean counter, boolean setOrder, boolean copyClientOrg)
{
if (isProcessed() || isPosted() || otherInvoice == null)
return 0;
MInvoiceLine[] fromLines = otherInvoice.getLines(false);
int count = 0;
for (int i = 0; i < fromLines.length; i++)
{
MInvoiceLine line = new MInvoiceLine (getCtx(), 0, get_TrxName());
MInvoiceLine fromLine = fromLines[i];
if (counter || !copyClientOrg) // header
PO.copyValues (fromLine, line, getAD_Client_ID(), getAD_Org_ID());
else
PO.copyValues (fromLine, line, fromLine.getAD_Client_ID(), fromLine.getAD_Org_ID());
line.setC_Invoice_ID(getC_Invoice_ID());
line.setInvoice(this);
line.set_ValueNoCheck ("C_InvoiceLine_ID", I_ZERO); // new
// Reset
if (!setOrder)
line.setC_OrderLine_ID(0);
line.setRef_InvoiceLine_ID(0);
line.setM_InOutLine_ID(0);
line.setA_Asset_ID(0);
line.setM_AttributeSetInstance_ID(0);
line.setS_ResourceAssignment_ID(0);
// New Tax
if (getC_BPartner_ID() != otherInvoice.getC_BPartner_ID())
line.setTax(); // recalculate
//
if (counter)
{
line.setRef_InvoiceLine_ID(fromLine.getC_InvoiceLine_ID());
if (fromLine.getC_OrderLine_ID() != 0)
{
MOrderLine peer = new MOrderLine (getCtx(), fromLine.getC_OrderLine_ID(), get_TrxName());
if (peer.getRef_OrderLine_ID() != 0)
line.setC_OrderLine_ID(peer.getRef_OrderLine_ID());
}
line.setM_InOutLine_ID(0);
if (fromLine.getM_InOutLine_ID() != 0)
{
MInOutLine peer = new MInOutLine (getCtx(), fromLine.getM_InOutLine_ID(), get_TrxName());
if (peer.getRef_InOutLine_ID() != 0)
line.setM_InOutLine_ID(peer.getRef_InOutLine_ID());
}
}
//
line.setProcessed(false);
if (line.save(get_TrxName()))
count++;
// Cross Link
if (counter)
{
fromLine.setRef_InvoiceLine_ID(line.getC_InvoiceLine_ID());
fromLine.saveEx(get_TrxName());
}
// copy the landed cost
line.copyLandedCostFrom(fromLine);
line.allocateLandedCosts();
}
if (fromLines.length != count)
log.log(Level.SEVERE, "Line difference - From=" + fromLines.length + " <> Saved=" + count);
return count;
} // copyLinesFrom
/** Reversal Flag */
private boolean m_reversal = false;
/**
* Set Reversal state (in memory flag)
* @param reversal reversal
*/
public void setReversal(boolean reversal)
{
m_reversal = reversal;
} // setReversal
/**
* Is Reversal
* @return reversal state (in memory flag)
*/
public boolean isReversal()
{
return m_reversal;
} // isReversal
/**
* Get Taxes
* @param requery true to requery from DB
* @return array of taxes
*/
public MInvoiceTax[] getTaxes (boolean requery)
{
if (m_taxes != null && !requery)
return m_taxes;
final String whereClause = MInvoiceTax.COLUMNNAME_C_Invoice_ID+"=?";
List list = new Query(getCtx(), I_C_InvoiceTax.Table_Name, whereClause, get_TrxName())
.setParameters(get_ID())
.list();
m_taxes = list.toArray(new MInvoiceTax[list.size()]);
return m_taxes;
} // getTaxes
/**
* 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
/**
* Is it a Credit Memo?
* @return true if this is CM document (DOCBASETYPE_APCreditMemo or DOCBASETYPE_ARCreditMemo)
*/
public boolean isCreditMemo()
{
MDocType dt = MDocType.get(getC_DocType_ID()==0 ? getC_DocTypeTarget_ID() : getC_DocType_ID());
return MDocType.DOCBASETYPE_APCreditMemo.equals(dt.getDocBaseType())
|| MDocType.DOCBASETYPE_ARCreditMemo.equals(dt.getDocBaseType());
} // isCreditMemo
/**
* Set Processed.
* Propagate to Lines/Taxes.
* @param processed processed
*/
@Override
public void setProcessed (boolean processed)
{
super.setProcessed (processed);
if (get_ID() == 0)
return;
StringBuilder set = new StringBuilder("SET Processed='")
.append((processed ? "Y" : "N"))
.append("' WHERE C_Invoice_ID=").append(getC_Invoice_ID());
StringBuilder msgdb = new StringBuilder("UPDATE C_InvoiceLine ").append(set);
int noLine = DB.executeUpdate(msgdb.toString(), get_TrxName());
msgdb = new StringBuilder("UPDATE C_InvoiceTax ").append(set);
int noTax = DB.executeUpdate(msgdb.toString(), get_TrxName());
m_lines = null;
m_taxes = null;
if (log.isLoggable(Level.FINE)) log.fine(processed + " - Lines=" + noLine + ", Tax=" + noTax);
} // setProcessed
/**
* Validate Invoice Pay Schedule
* @return true if pay schedule is valid
*/
public boolean validatePaySchedule()
{
MInvoicePaySchedule[] schedule = MInvoicePaySchedule.getInvoicePaySchedule
(getCtx(), getC_Invoice_ID(), 0, get_TrxName());
if (log.isLoggable(Level.FINE)) log.fine("#" + schedule.length);
if (schedule.length == 0)
{
setIsPayScheduleValid(false);
return false;
}
// Add up due amounts
BigDecimal total = Env.ZERO;
for (int i = 0; i < schedule.length; i++)
{
schedule[i].setParent(this);
BigDecimal due = schedule[i].getDueAmt();
if (due != null)
total = total.add(due);
}
boolean valid = getGrandTotal().compareTo(total) == 0;
setIsPayScheduleValid(valid);
// Update Schedule Lines
for (int i = 0; i < schedule.length; i++)
{
if (schedule[i].isValid() != valid)
{
schedule[i].setIsValid(valid);
schedule[i].saveEx(get_TrxName());
}
}
return valid;
} // validatePaySchedule
private volatile static boolean recursiveCall = false;
/**
* Before Save
* @param newRecord new
* @return true
*/
@Override
protected boolean beforeSave (boolean newRecord)
{
if (log.isLoggable(Level.FINE)) log.fine("");
// No Partner Info - set Template
if (getC_BPartner_ID() == 0)
setBPartner(MBPartner.getTemplate(getCtx(), getAD_Client_ID()));
if (getC_BPartner_Location_ID() == 0)
setBPartner(new MBPartner(getCtx(), getC_BPartner_ID(), null));
// Price List
if (getM_PriceList_ID() == 0)
{
int ii = Env.getContextAsInt(getCtx(), Env.M_PRICELIST_ID);
if (ii != 0)
{
MPriceList pl = new MPriceList(getCtx(), ii, null);
if (isSOTrx() == pl.isSOPriceList())
setM_PriceList_ID(ii);
}
if (getM_PriceList_ID() == 0)
{
String sql = "SELECT M_PriceList_ID FROM M_PriceList WHERE AD_Client_ID=? AND IsSOPriceList=? AND IsActive='Y' ORDER BY IsDefault DESC";
ii = DB.getSQLValue (null, sql, getAD_Client_ID(), isSOTrx());
if (ii != 0)
setM_PriceList_ID (ii);
}
}
// Currency
if (getC_Currency_ID() == 0)
{
String sql = "SELECT C_Currency_ID FROM M_PriceList WHERE M_PriceList_ID=?";
int ii = DB.getSQLValue (null, sql, getM_PriceList_ID());
if (ii != 0)
setC_Currency_ID (ii);
else
setC_Currency_ID(Env.getContextAsInt(getCtx(), Env.C_CURRENCY_ID));
}
// Sales Rep
if (getSalesRep_ID() == 0)
{
int ii = Env.getContextAsInt(getCtx(), Env.SALESREP_ID);
if (ii != 0)
setSalesRep_ID (ii);
}
// Document Type
if (getC_DocType_ID() == 0)
setC_DocType_ID (0); // make sure it's set to 0
if (getC_DocTypeTarget_ID() == 0)
setC_DocTypeTarget_ID(isSOTrx() ? MDocType.DOCBASETYPE_ARInvoice : MDocType.DOCBASETYPE_APInvoice);
// Payment Term
if (getC_PaymentTerm_ID() == 0)
{
int ii = Env.getContextAsInt(getCtx(), Env.C_PAYMENTTERM_ID);
if (ii != 0)
setC_PaymentTerm_ID (ii);
else
{
String sql = "SELECT C_PaymentTerm_ID FROM C_PaymentTerm WHERE AD_Client_ID=? AND IsDefault='Y' AND IsActive='Y'";
ii = DB.getSQLValue(null, sql, getAD_Client_ID());
if (ii != 0)
setC_PaymentTerm_ID (ii);
}
}
// assign cash plan line from order
if (getC_Order_ID() > 0 && getC_CashPlanLine_ID() <= 0) {
MOrder order = new MOrder(getCtx(), getC_Order_ID(), get_TrxName());
if (order.getC_CashPlanLine_ID() > 0)
setC_CashPlanLine_ID(order.getC_CashPlanLine_ID());
}
// IDEMPIERE-1597 Price List and Date must be not-updateable
if (!newRecord && (is_ValueChanged(COLUMNNAME_M_PriceList_ID) || is_ValueChanged(COLUMNNAME_DateInvoiced))) {
int cnt = DB.getSQLValueEx(get_TrxName(), "SELECT COUNT(*) FROM C_InvoiceLine WHERE C_Invoice_ID=? AND M_Product_ID>0", getC_Invoice_ID());
if (cnt > 0) {
if (is_ValueChanged(COLUMNNAME_M_PriceList_ID)) {
log.saveError("Error", Msg.getMsg(getCtx(), "CannotChangePlIn"));
return false;
}
if (is_ValueChanged(COLUMNNAME_DateInvoiced)) {
MPriceList pList = MPriceList.get(getCtx(), getM_PriceList_ID(), null);
MPriceListVersion plOld = pList.getPriceListVersion((Timestamp)get_ValueOld(COLUMNNAME_DateInvoiced));
MPriceListVersion plNew = pList.getPriceListVersion((Timestamp)get_Value(COLUMNNAME_DateInvoiced));
if (plNew == null || !plNew.equals(plOld)) {
log.saveError("Error", Msg.getMsg(getCtx(), "CannotChangeDateInvoiced"));
return false;
}
}
}
}
if (! recursiveCall && (!newRecord && is_ValueChanged(COLUMNNAME_C_PaymentTerm_ID))) {
recursiveCall = true;
try {
MPaymentTerm pt = new MPaymentTerm (getCtx(), getC_PaymentTerm_ID(), get_TrxName());
boolean valid = pt.apply(this);
setIsPayScheduleValid(valid);
} catch (Exception e) {
throw e;
} finally {
recursiveCall = false;
}
}
if (!isProcessed())
{
MClientInfo info = MClientInfo.get(getCtx(), getAD_Client_ID(), get_TrxName());
MAcctSchema as = MAcctSchema.get (getCtx(), info.getC_AcctSchema1_ID(), get_TrxName());
if (as.getC_Currency_ID() != getC_Currency_ID())
{
if (isOverrideCurrencyRate())
{
if(getCurrencyRate() == null || getCurrencyRate().signum() == 0)
{
log.saveError("FillMandatory", Msg.getElement(getCtx(), COLUMNNAME_CurrencyRate));
return false;
}
}
else
{
setCurrencyRate(null);
}
}
else
{
setCurrencyRate(null);
}
}
return true;
} // beforeSave
/**
* Before Delete
* @return true if it can be deleted
*/
@Override
protected boolean beforeDelete ()
{
if (getC_Order_ID() != 0)
{
//Load invoice lines for afterDelete()
getLines();
}
return true;
} // beforeDelete
/**
* After Delete
* @param success success
* @return true if deleted
*/
@Override
protected boolean afterDelete(boolean success) {
// If delete invoice failed then do nothing
if (!success)
return success;
if (getC_Order_ID() != 0) {
// reset shipment line invoiced flag
MInvoiceLine[] lines = getLines(false);
for (int i = 0; i < lines.length; i++) {
if (lines[i].getM_InOutLine_ID() > 0) {
MInOutLine sLine = new MInOutLine(getCtx(), lines[i].getM_InOutLine_ID(), get_TrxName());
sLine.setIsInvoiced(false);
sLine.saveEx();
}
}
}
return true;
} //afterDelete
/**
* String Representation
* @return info
*/
@Override
public String toString ()
{
StringBuilder sb = new StringBuilder ("MInvoice[")
.append(get_ID()).append("-").append(getDocumentNo())
.append(",GrandTotal=").append(getGrandTotal());
if (m_lines != null)
sb.append(" (#").append(m_lines.length).append(")");
sb.append ("]");
return sb.toString ();
} // toString
/**
* Get Document Info
* @return document info (untranslated)
*/
@Override
public String getDocumentInfo()
{
MDocType dt;
if (getC_DocType_ID() == 0) {
dt = MDocType.get(getCtx(), getC_DocTypeTarget_ID());
} else {
dt = MDocType.get(getCtx(), getC_DocType_ID());
}
StringBuilder msgreturn = new StringBuilder().append(dt.getNameTrl()).append(" ").append(getDocumentNo());
return msgreturn.toString();
} // getDocumentInfo
/**
* After Save
* @param newRecord new
* @param success success
* @return success
*/
@Override
protected boolean afterSave (boolean newRecord, boolean success)
{
if (!success || newRecord)
return success;
if (is_ValueChanged("AD_Org_ID"))
{
StringBuilder sql = new StringBuilder("UPDATE C_InvoiceLine ol")
.append(" SET AD_Org_ID =")
.append("(SELECT AD_Org_ID")
.append(" FROM C_Invoice o WHERE ol.C_Invoice_ID=o.C_Invoice_ID) ")
.append("WHERE C_Invoice_ID=").append(getC_Invoice_ID());
int no = DB.executeUpdate(sql.toString(), get_TrxName());
if (log.isLoggable(Level.FINE)) log.fine("Lines -> #" + no);
}
return true;
} // afterSave
/**
* Set Price List (and Currency) when valid
* @param M_PriceList_ID price list
*/
@Override
public void setM_PriceList_ID (int M_PriceList_ID)
{
MPriceList pl = MPriceList.get(getCtx(), M_PriceList_ID, null);
if (pl != null) {
setC_Currency_ID(pl.getC_Currency_ID());
super.setM_PriceList_ID(M_PriceList_ID);
}
} // setM_PriceList_ID
/**
* Get Allocated Amt in Invoice Currency
* @return pos/neg amount or null
*/
public BigDecimal getAllocatedAmt ()
{
BigDecimal retValue = null;
String sql = "SELECT SUM(currencyConvert(al.Amount+al.DiscountAmt+al.WriteOffAmt,"
+ "ah.C_Currency_ID, i.C_Currency_ID,ah.DateTrx,COALESCE(i.C_ConversionType_ID,0), al.AD_Client_ID,al.AD_Org_ID)) "
+ "FROM C_AllocationLine al"
+ " INNER JOIN C_AllocationHdr ah ON (al.C_AllocationHdr_ID=ah.C_AllocationHdr_ID)"
+ " INNER JOIN C_Invoice i ON (al.C_Invoice_ID=i.C_Invoice_ID) "
+ "WHERE al.C_Invoice_ID=?"
+ " AND ah.IsActive='Y' AND al.IsActive='Y'";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql, get_TrxName());
pstmt.setInt(1, getC_Invoice_ID());
rs = pstmt.executeQuery();
if (rs.next())
{
retValue = rs.getBigDecimal(1);
}
}
catch (SQLException e)
{
throw new DBException(e, sql);
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
return retValue;
} // getAllocatedAmt
/**
* Test Allocation (and set paid flag)
* @param beingCompleted true if call during processing of Complete document action
* @return true if updated IsPaid
*/
public boolean testAllocation(boolean beingCompleted)
{
boolean change = false;
if ( isProcessed() || beingCompleted) {
BigDecimal alloc = getAllocatedAmt(); // absolute
if (alloc == null)
alloc = Env.ZERO;
BigDecimal total = getGrandTotal();
if (!isSOTrx())
total = total.negate();
if (isCreditMemo())
total = total.negate();
boolean test = total.compareTo(alloc) == 0;
change = test != isPaid();
if (change)
setIsPaid(test);
if (log.isLoggable(Level.FINE)) log.fine("Paid=" + test
+ " (" + alloc + "=" + total + ")");
}
return change;
} // testAllocation
/**
* Test Allocation (and set paid flag)
* @return true if updated IsPaid
*/
public boolean testAllocation() {
return testAllocation(false);
}
/**
* Set Paid Flag for invoices
* @param ctx context
* @param C_BPartner_ID if 0 all
* @param trxName transaction
*/
public static void setIsPaid (Properties ctx, int C_BPartner_ID, String trxName)
{
List params = new ArrayList();
StringBuilder whereClause = new StringBuilder("IsPaid='N' AND DocStatus IN ('CO','CL')");
if (C_BPartner_ID > 1)
{
whereClause.append(" AND C_BPartner_ID=?");
params.add(C_BPartner_ID);
}
else
{
whereClause.append(" AND AD_Client_ID=?");
params.add(Env.getAD_Client_ID(ctx));
}
POResultSet rs = new Query(ctx, MInvoice.Table_Name, whereClause.toString(), trxName)
.setParameters(params)
.scroll();
int counter = 0;
try {
while(rs.hasNext()) {
MInvoice invoice = rs.next();
if (invoice.testAllocation())
if (invoice.save())
counter++;
}
}
finally {
DB.close(rs);
}
if (s_log.isLoggable(Level.CONFIG)) s_log.config("#" + counter);
} // setIsPaid
/**
* Get Open Amount.
* @return Open Amount
*/
public BigDecimal getOpenAmt ()
{
return getOpenAmt (true, null, false);
} // getOpenAmt
/**
* @param creditMemoAdjusted
* @param paymentDate
* @return Open Amount
*/
public BigDecimal getOpenAmt (boolean creditMemoAdjusted, Timestamp paymentDate)
{
return getOpenAmt(creditMemoAdjusted, paymentDate, false);
}
/**
* Get Open Amount
* @param creditMemoAdjusted true to negate return amount
* @param paymentDate Payment Date
* @return Open Amt
*/
public BigDecimal getOpenAmt (boolean creditMemoAdjusted, Timestamp paymentDate, boolean requery)
{
if (isPaid() && paymentDate == null)
return Env.ZERO;
//
if (paymentDate != null || m_openAmt == null || requery) {
BigDecimal l_openAmt = getOpenAmt(paymentDate);
if (paymentDate != null) {
if (isCreditMemo() && creditMemoAdjusted)
return l_openAmt.negate();
return l_openAmt;
} else {
m_openAmt = l_openAmt;
}
}
//
if (isCreditMemo() && creditMemoAdjusted)
return m_openAmt.negate();
return m_openAmt;
} // getOpenAmt
/**
* Get open amt depending on payment date
* @param paymentDate
* @return open Amt
*/
public BigDecimal getOpenAmt (Timestamp paymentDate)
{
BigDecimal retValue;
if (paymentDate == null) {
retValue = DB.getSQLValueBDEx(get_TrxName(),
"SELECT invoiceOpen(?,?) FROM DUAL",
getC_Invoice_ID(), 0);
} else {
retValue = DB.getSQLValueBDEx(get_TrxName(),
"SELECT invoiceOpenToDate(?,?,?) FROM DUAL",
getC_Invoice_ID(), 0, paymentDate);
}
return retValue;
} // getOpenAmt
/**
* Get discount amt depending on payment date
* @param paymentDate
* @return discount Amt
*/
public BigDecimal getDiscountAmt(Timestamp paymentDate)
{
BigDecimal retValue = DB.getSQLValueBDEx(get_TrxName(),
"SELECT invoiceDiscount(?,?,?) FROM DUAL",
getC_Invoice_ID(), paymentDate, 0);
return retValue;
}
/**
* Get Document Status Name
* @return Document Status Name
*/
public String getDocStatusName()
{
return MRefList.getListName(getCtx(), 131, getDocStatus());
} // getDocStatusName
/**
* Create PDF
* @return File or null
*/
@Override
public File createPDF ()
{
try
{
StringBuilder msgfile = new StringBuilder().append(get_TableName()).append(get_ID()).append("_");
File temp = File.createTempFile(msgfile.toString(), ".pdf");
return createPDF (temp);
}
catch (Exception e)
{
log.severe("Could not create PDF - " + e.getMessage());
}
return null;
} // getPDF
/**
* Create PDF file
* @param file output file
* @return file if success
*/
public File createPDF (File file)
{
ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID(), get_TrxName());
if (re == null)
return null;
MPrintFormat format = re.getPrintFormat();
// We have a Jasper Print Format
// ==============================
if(format.getJasperProcess_ID() > 0)
{
ProcessInfo pi = new ProcessInfo ("", format.getJasperProcess_ID());
pi.setRecord_ID ( getC_Invoice_ID() );
pi.setIsBatch(true);
ServerProcessCtl.process(pi, null);
return pi.getPDFReport();
}
// Standard Print Format (Non-Jasper)
// ==================================
return re.getPDF(file);
} // createPDF
/**
* Get PDF File Name
* @param documentDir directory
* @return file name
*/
public String getPDFFileName (String documentDir)
{
return getPDFFileName (documentDir, getC_Invoice_ID());
} // getPDFFileName
/**
* Get ISO Code of Currency
* @return Currency ISO
*/
public String getCurrencyISO()
{
return MCurrency.getISO_Code (getCtx(), getC_Currency_ID());
} // getCurrencyISO
/**
* Get Currency Precision
* @return precision
*/
public int getPrecision()
{
return MCurrency.getStdPrecision(getCtx(), getC_Currency_ID());
} // getPrecision
/**
* Process document
* @param processAction document action
* @return true if performed
*/
@Override
public boolean processIt (String processAction)
{
m_processMsg = null;
DocumentEngine engine = new DocumentEngine (this, getDocStatus());
return engine.processIt (processAction, getDocAction());
} // process
/** Process Message */
private String m_processMsg = null;
/** Just Prepared Flag */
private boolean m_justPrepared = false;
/**
* Unlock Document.
* @return true if success
*/
@Override
public boolean unlockIt()
{
if (log.isLoggable(Level.INFO)) log.info("unlockIt - " + toString());
setProcessing(false);
return true;
} // unlockIt
/**
* Invalidate Document
* @return true if success
*/
@Override
public boolean invalidateIt()
{
if (log.isLoggable(Level.INFO)) log.info("invalidateIt - " + toString());
setDocAction(DOCACTION_Prepare);
return true;
} // invalidateIt
/**
* Prepare Document
* @return new status (In Progress or Invalid)
*/
@Override
public String prepareIt()
{
if (log.isLoggable(Level.INFO)) log.info(toString());
m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE);
if (m_processMsg != null)
return DocAction.STATUS_Invalid;
MPeriod.testPeriodOpen(getCtx(), getDateAcct(), getC_DocTypeTarget_ID(), getAD_Org_ID());
// Lines
MInvoiceLine[] lines = getLines(true);
if (lines.length == 0)
{
m_processMsg = "@NoLines@";
return DocAction.STATUS_Invalid;
}
// Convert/Check DocType
if (getC_DocType_ID() != getC_DocTypeTarget_ID() )
setC_DocType_ID(getC_DocTypeTarget_ID());
if (getC_DocType_ID() == 0)
{
m_processMsg = "No Document Type";
return DocAction.STATUS_Invalid;
}
explodeBOM();
if (!calculateTaxTotal()) // setTotals
{
m_processMsg = "Error calculating Tax";
return DocAction.STATUS_Invalid;
}
if ( getGrandTotal().signum() != 0
&& (PAYMENTRULE_OnCredit.equals(getPaymentRule()) || PAYMENTRULE_DirectDebit.equals(getPaymentRule())))
{
if (!createPaySchedule())
{
m_processMsg = "@ErrorPaymentSchedule@";
return DocAction.STATUS_Invalid;
}
} else {
if (MInvoicePaySchedule.getInvoicePaySchedule(getCtx(), getC_Invoice_ID(), 0, get_TrxName()).length > 0)
{
m_processMsg = "@ErrorPaymentSchedule@";
return DocAction.STATUS_Invalid;
}
}
// Credit Status
ICreditManager creditManager = Core.getCreditManager(this);
if (creditManager != null)
{
CreditStatus status = creditManager.checkCreditStatus(DOCACTION_Prepare);
if (status.isError())
{
m_processMsg = status.getErrorMsg();
return DocAction.STATUS_Invalid;
}
}
// Landed Costs
if (!isSOTrx())
{
for (int i = 0; i < lines.length; i++)
{
MInvoiceLine line = lines[i];
String error = line.allocateLandedCosts();
if (error != null && error.length() > 0)
{
m_processMsg = error;
return DocAction.STATUS_Invalid;
}
}
}
m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE);
if (m_processMsg != null)
return DocAction.STATUS_Invalid;
// Add up Amounts
m_justPrepared = true;
if (!DOCACTION_Complete.equals(getDocAction()))
setDocAction(DOCACTION_Complete);
return DocAction.STATUS_InProgress;
} // prepareIt
/**
* Explode non stocked BOM (IsBOM=Y and IsStocked=N)
*/
private void explodeBOM ()
{
String where = "AND IsActive='Y' AND EXISTS "
+ "(SELECT * FROM M_Product p WHERE C_InvoiceLine.M_Product_ID=p.M_Product_ID"
+ " AND p.IsBOM='Y' AND p.IsVerified='Y' AND p.IsStocked='N')";
//
String sql = "SELECT COUNT(*) FROM C_InvoiceLine "
+ "WHERE C_Invoice_ID=? " + where;
int count = DB.getSQLValueEx(get_TrxName(), sql, getC_Invoice_ID());
while (count != 0)
{
renumberLines (100);
// Order Lines with non-stocked BOMs
MInvoiceLine[] lines = getLines (where);
for (int i = 0; i < lines.length; i++)
{
MInvoiceLine line = lines[i];
MProduct product = MProduct.get (getCtx(), line.getM_Product_ID());
if (log.isLoggable(Level.FINE)) log.fine(product.getName());
// New Lines
int lineNo = line.getLine ();
MPPProductBOM bom = MPPProductBOM.getDefault(product, get_TrxName());
if (bom == null)
continue;
for (MPPProductBOMLine bomLine : bom.getLines())
{
MInvoiceLine newLine = new MInvoiceLine(this);
newLine.setLine(++lineNo);
newLine.setM_Product_ID(bomLine.getM_Product_ID(), true);
newLine.setQty(line.getQtyInvoiced().multiply(bomLine.getQtyBOM()));
if (bomLine.getDescription() != null)
newLine.setDescription(bomLine.getDescription());
newLine.setPrice();
newLine.saveEx(get_TrxName());
}
// Convert into Comment Line
line.setM_Product_ID (0);
line.setM_AttributeSetInstance_ID (0);
line.setPriceEntered (Env.ZERO);
line.setPriceActual (Env.ZERO);
line.setPriceLimit (Env.ZERO);
line.setPriceList (Env.ZERO);
line.setLineNetAmt (Env.ZERO);
//
StringBuilder description = new StringBuilder().append(product.getName ());
if (product.getDescription () != null)
description.append(" ").append(product.getDescription ());
if (line.getDescription () != null)
description.append(" ").append(line.getDescription ());
line.setDescription (description.toString());
line.saveEx (get_TrxName());
} // for all lines with BOM
m_lines = null;
count = DB.getSQLValue (get_TrxName(), sql, getC_Invoice_ID ());
renumberLines (10);
} // while count != 0
} // explodeBOM
/**
* Calculate Tax and Total
* @return true if calculated
*/
public boolean calculateTaxTotal()
{
if (log.isLoggable(Level.FINE)) log.fine("");
// Delete Taxes
StringBuilder msgdb = new StringBuilder("DELETE FROM C_InvoiceTax WHERE C_Invoice_ID=").append(getC_Invoice_ID());
DB.executeUpdateEx(msgdb.toString(), get_TrxName());
m_taxes = null;
MTaxProvider[] providers = getTaxProviders();
for (MTaxProvider provider : providers)
{
ITaxProvider calculator = Core.getTaxProvider(provider);
if (calculator == null)
throw new AdempiereException(Msg.getMsg(getCtx(), "TaxNoProvider"));
if (!calculator.calculateInvoiceTaxTotal(provider, this))
return false;
}
return true;
} // calculateTaxTotal
/**
* (Re) Create Pay Schedule
* @return true if valid schedule
*/
private boolean createPaySchedule()
{
if (getC_PaymentTerm_ID() == 0)
return false;
MPaymentTerm pt = new MPaymentTerm(getCtx(), getC_PaymentTerm_ID(), null);
if (log.isLoggable(Level.FINE)) log.fine(pt.toString());
int numSchema = pt.getSchedule(false).length;
MInvoicePaySchedule[] schedule = MInvoicePaySchedule.getInvoicePaySchedule
(getCtx(), getC_Invoice_ID(), 0, get_TrxName());
if (schedule.length > 0) {
if (numSchema == 0)
return false; // created a schedule for a payment term that doesn't manage schedule
return validatePaySchedule();
} else {
boolean isValid = pt.apply(this); // calls validate pay schedule
if (numSchema == 0)
return true; // no schedule, no schema, OK
else
return isValid;
}
} // createPaySchedule
/**
* Approve Document
* @return true if success
*/
@Override
public boolean approveIt()
{
if (log.isLoggable(Level.INFO)) log.info(toString());
setIsApproved(true);
return true;
} // approveIt
/**
* Reject Approval
* @return true if success
*/
@Override
public boolean rejectIt()
{
if (log.isLoggable(Level.INFO)) log.info(toString());
setIsApproved(false);
return true;
} // rejectIt
/**
* Complete Document
* @return new status (Complete, In Progress, Invalid, Waiting ..)
*/
@Override
public String completeIt()
{
// Re-Check
if (!m_justPrepared)
{
String status = prepareIt();
m_justPrepared = false;
if (!DocAction.STATUS_InProgress.equals(status))
return status;
}
// Set the definite document number after completed (if needed)
setDefiniteDocumentNo();
m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE);
if (m_processMsg != null)
return DocAction.STATUS_Invalid;
// Implicit Approval
if (!isApproved())
approveIt();
if (log.isLoggable(Level.INFO)) log.info(toString());
StringBuilder info = new StringBuilder();
// POS supports multiple payments
boolean fromPOS = false;
if ( getC_Order_ID() > 0 )
{
fromPOS = getC_Order().getC_POS_ID() > 0;
}
// Create Cash Payment
if (PAYMENTRULE_Cash.equals(getPaymentRule()) && !fromPOS )
{
String whereClause = "AD_Org_ID=? AND C_Currency_ID=?";
MBankAccount ba = new Query(getCtx(),MBankAccount.Table_Name,whereClause,get_TrxName())
.setParameters(getAD_Org_ID(), getC_Currency_ID())
.setOnlyActiveRecords(true)
.setOrderBy("IsDefault DESC")
.first();
if (ba == null) {
m_processMsg = "@NoAccountOrgCurrency@";
return DocAction.STATUS_Invalid;
}
String docBaseType = "";
if (isSOTrx())
docBaseType=MDocType.DOCBASETYPE_ARReceipt;
else
docBaseType=MDocType.DOCBASETYPE_APPayment;
MDocType[] doctypes = MDocType.getOfDocBaseType(getCtx(), docBaseType);
if (doctypes == null || doctypes.length == 0) {
m_processMsg = "No document type ";
return DocAction.STATUS_Invalid;
}
MDocType doctype = null;
for (MDocType doc : doctypes) {
if (doc.getAD_Org_ID() == this.getAD_Org_ID()) {
doctype = doc;
break;
}
}
if (doctype == null)
doctype = doctypes[0];
MPayment payment = new MPayment(getCtx(), 0, get_TrxName());
payment.setAD_Org_ID(getAD_Org_ID());
payment.setTenderType(MPayment.TENDERTYPE_Cash);
payment.setC_BankAccount_ID(ba.getC_BankAccount_ID());
payment.setC_BPartner_ID(getC_BPartner_ID());
payment.setC_Invoice_ID(getC_Invoice_ID());
payment.setC_Currency_ID(getC_Currency_ID());
payment.setC_DocType_ID(doctype.getC_DocType_ID());
if (isCreditMemo())
payment.setPayAmt(getGrandTotal().negate());
else
payment.setPayAmt(getGrandTotal());
payment.setIsPrepayment(false);
payment.setDateAcct(getDateAcct());
payment.setDateTrx(getDateInvoiced());
// Save payment
payment.saveEx();
payment.setDocAction(MPayment.DOCACTION_Complete);
if (!payment.processIt(MPayment.DOCACTION_Complete)) {
m_processMsg = "Cannot Complete the Payment : [" + payment.getProcessMsg() + "] " + payment;
return DocAction.STATUS_Invalid;
}
payment.saveEx();
info.append("@C_Payment_ID@: " + payment.getDocumentInfo());
// IDEMPIERE-2588 - add the allocation generation with the payment
if (payment.getJustCreatedAllocInv() != null)
addDocsPostProcess(payment.getJustCreatedAllocInv());
} // Payment
// Update Order & Match
int matchInv = 0;
int matchPO = 0;
MInvoiceLine[] lines = getLines(false);
for (int i = 0; i < lines.length; i++)
{
MInvoiceLine line = lines[i];
// Matching - Inv-Shipment
if (!isSOTrx()
&& line.getM_InOutLine_ID() != 0
&& line.getM_Product_ID() != 0
&& !isReversal())
{
MInOutLine receiptLine = new MInOutLine (getCtx(),line.getM_InOutLine_ID(), get_TrxName());
MInOut receipt = receiptLine.getParent();
if (receipt.isProcessed()){
BigDecimal movementQty = receiptLine.getM_InOut().getMovementType().charAt(1) == '-' ? receiptLine.getMovementQty().negate() : receiptLine.getMovementQty();
BigDecimal matchQty = isCreditMemo() ? line.getQtyInvoiced().negate() : line.getQtyInvoiced();
if (movementQty.compareTo(matchQty) < 0)
matchQty = movementQty;
MMatchInv inv = new MMatchInv(line, getDateInvoiced(), matchQty);
if (!inv.save(get_TrxName()))
{
m_processMsg = CLogger.retrieveErrorString("Could not create Invoice Matching");
return DocAction.STATUS_Invalid;
}
matchInv++;
addDocsPostProcess(inv);
}
}
// Update Order Line
MOrderLine ol = null;
if (line.getC_OrderLine_ID() != 0)
{
if (isSOTrx()
|| line.getM_Product_ID() == 0)
{
ol = new MOrderLine (getCtx(), line.getC_OrderLine_ID(), get_TrxName());
if (line.getQtyInvoiced() != null) {
ol.setQtyInvoiced(ol.getQtyInvoiced().add(isCreditMemo() ? line.getQtyInvoiced().negate() : line.getQtyInvoiced()));
}
if (!ol.save(get_TrxName()))
{
m_processMsg = "Could not update Order Line";
return DocAction.STATUS_Invalid;
}
}
// Order Invoiced Qty updated via Matching Inv-PO
else if (!isSOTrx()
&& line.getM_Product_ID() != 0
&& !isReversal())
{
// MatchPO is created also from MInOut when Invoice exists before Shipment
BigDecimal matchQty = isCreditMemo() ? line.getQtyInvoiced().negate() : line.getQtyInvoiced();
MMatchPO po = MMatchPO.create (line, null,
getDateInvoiced(), matchQty);
if (po != null)
{
if (!po.save(get_TrxName()))
{
m_processMsg = "Could not create PO Matching";
return DocAction.STATUS_Invalid;
}
matchPO++;
if (!po.isPosted())
addDocsPostProcess(po);
MMatchInv[] matchInvoices = MMatchInv.getInvoiceLine(getCtx(), line.getC_InvoiceLine_ID(), get_TrxName());
if (matchInvoices != null && matchInvoices.length > 0)
{
for(MMatchInv matchInvoice : matchInvoices)
{
if (!matchInvoice.isPosted())
{
addDocsPostProcess(matchInvoice);
}
if (matchInvoice.getRef_MatchInv_ID() > 0)
{
MMatchInv refMatchInv = new MMatchInv(getCtx(), matchInvoice.getRef_MatchInv_ID(), get_TrxName());
if (!refMatchInv.isPosted())
addDocsPostProcess(refMatchInv);
}
}
}
}
}
}
//Update QtyInvoiced RMA Line
if (line.getM_RMALine_ID() != 0)
{
MRMALine rmaLine = new MRMALine (getCtx(),line.getM_RMALine_ID(), get_TrxName());
if (rmaLine.getQtyInvoiced() != null)
rmaLine.setQtyInvoiced(rmaLine.getQtyInvoiced().add(line.getQtyInvoiced()));
else
rmaLine.setQtyInvoiced(line.getQtyInvoiced());
if (!rmaLine.save(get_TrxName()))
{
m_processMsg = "Could not update RMA Line";
return DocAction.STATUS_Invalid;
}
}
//
} // for all lines
if (matchInv > 0)
info.append(" @M_MatchInv_ID@#").append(matchInv).append(" ");
if (matchPO > 0)
info.append(" @M_MatchPO_ID@#").append(matchPO).append(" ");
ICreditManager creditManager = Core.getCreditManager(this);
if (creditManager != null)
{
CreditStatus status = creditManager.checkCreditStatus(DOCACTION_Complete);
if (status.isError())
{
m_processMsg = status.getErrorMsg();
return DocAction.STATUS_Invalid;
}
}
// User - Last Result/Contact
if (getAD_User_ID() != 0)
{
MUser user = new MUser (getCtx(), getAD_User_ID(), get_TrxName());
user.setLastContact(new Timestamp(System.currentTimeMillis()));
StringBuilder msgset = new StringBuilder().append(Msg.translate(getCtx(), "C_Invoice_ID")).append(": ").append(getDocumentNo());
user.setLastResult(msgset.toString());
if (!user.save(get_TrxName()))
{
m_processMsg = "Could not update Business Partner User";
return DocAction.STATUS_Invalid;
}
} // user
// Update Project
if (isSOTrx() && getC_Project_ID() != 0)
{
MProject project = new MProject (getCtx(), getC_Project_ID(), get_TrxName());
BigDecimal amt = getGrandTotal(true);
int C_CurrencyTo_ID = project.getC_Currency_ID();
if (C_CurrencyTo_ID != getC_Currency_ID())
amt = MConversionRate.convert(getCtx(), amt, getC_Currency_ID(), C_CurrencyTo_ID,
getDateAcct(), 0, getAD_Client_ID(), getAD_Org_ID());
if (amt == null)
{
m_processMsg = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingCurrencyToProjectCurrency",
getC_Currency_ID(), C_CurrencyTo_ID, 0, getDateAcct(), get_TrxName());
return DocAction.STATUS_Invalid;
}
BigDecimal newAmt = project.getInvoicedAmt();
if (newAmt == null)
newAmt = amt;
else
newAmt = newAmt.add(amt);
if (log.isLoggable(Level.FINE)) log.fine("GrandTotal=" + getGrandTotal(true) + "(" + amt
+ ") Project " + project.getName()
+ " - Invoiced=" + project.getInvoicedAmt() + "->" + newAmt);
project.setInvoicedAmt(newAmt);
if (!project.save(get_TrxName()))
{
m_processMsg = "Could not update Project";
return DocAction.STATUS_Invalid;
}
} // project
// auto delay capture authorization payment
if (isSOTrx() && !isReversal())
{
StringBuilder whereClause = new StringBuilder();
whereClause.append("C_Order_ID IN (");
whereClause.append("SELECT C_Order_ID ");
whereClause.append("FROM C_OrderLine ");
whereClause.append("WHERE C_OrderLine_ID IN (");
whereClause.append("SELECT C_OrderLine_ID ");
whereClause.append("FROM C_InvoiceLine ");
whereClause.append("WHERE C_Invoice_ID = ");
whereClause.append(getC_Invoice_ID()).append("))");
int[] orderIDList = MOrder.getAllIDs(MOrder.Table_Name, whereClause.toString(), get_TrxName());
int[] ids = MPaymentTransaction.getAuthorizationPaymentTransactionIDs(orderIDList, getC_Invoice_ID(), get_TrxName());
if (ids.length > 0)
{
boolean pureCIM = true;
ArrayList ptList = new ArrayList();
BigDecimal totalPayAmt = BigDecimal.ZERO;
for (int id : ids)
{
MPaymentTransaction pt = new MPaymentTransaction(getCtx(), id, get_TrxName());
if (!pt.setPaymentProcessor())
{
if (pt.getC_PaymentProcessor_ID() > 0)
{
MPaymentProcessor pp = new MPaymentProcessor(getCtx(), pt.getC_PaymentProcessor_ID(), get_TrxName());
m_processMsg = Msg.getMsg(getCtx(), "PaymentNoProcessorModel") + ": " + pp.toString();
}
else
m_processMsg = Msg.getMsg(getCtx(), "PaymentNoProcessorModel");
return DocAction.STATUS_Invalid;
}
boolean isCIM = pt.getC_PaymentProcessor_ID() > 0 && pt.getCustomerPaymentProfileID() != null && pt.getCustomerPaymentProfileID().length() > 0;
if (pureCIM && !isCIM)
pureCIM = false;
totalPayAmt = totalPayAmt.add(pt.getPayAmt());
ptList.add(pt);
}
// automatically void authorization payment and create a new sales payment when invoiced amount is NOT equals to the authorized amount (applied to CIM payment processor)
if (getGrandTotal().compareTo(totalPayAmt) != 0 && ptList.size() > 0 && pureCIM)
{
// create a new sales payment
MPaymentTransaction newSalesPT = MPaymentTransaction.copyFrom(ptList.get(0), new Timestamp(System.currentTimeMillis()), MPayment.TRXTYPE_Sales, "", get_TrxName());
newSalesPT.setIsApproved(false);
newSalesPT.setIsVoided(false);
newSalesPT.setIsDelayedCapture(false);
newSalesPT.setDescription("InvoicedAmt: " + getGrandTotal() + " <> TotalAuthorizedAmt: " + totalPayAmt);
newSalesPT.setC_Invoice_ID(getC_Invoice_ID());
newSalesPT.setPayAmt(getGrandTotal());
// void authorization payment
for (MPaymentTransaction pt : ptList)
{
pt.setDescription("InvoicedAmt: " + getGrandTotal() + " <> AuthorizedAmt: " + pt.getPayAmt());
boolean ok = pt.voidOnlineAuthorizationPaymentTransaction();
pt.saveEx();
if (!ok)
{
m_processMsg = Msg.getMsg(getCtx(), "VoidAuthorizationPaymentFailed") + ": " + pt.getErrorMessage();
return DocAction.STATUS_Invalid;
}
}
// process the new sales payment
boolean ok = newSalesPT.processOnline();
newSalesPT.saveEx();
if (!ok)
{
m_processMsg = Msg.getMsg(getCtx(), "CreateNewSalesPaymentFailed") + ": " + newSalesPT.getErrorMessage();
return DocAction.STATUS_Invalid;
}
}
else if (getGrandTotal().compareTo(totalPayAmt) != 0 && ptList.size() > 0)
{
m_processMsg = "InvoicedAmt: " + getGrandTotal() + " <> AuthorizedAmt: " + totalPayAmt;
return DocAction.STATUS_Invalid;
}
else
{
// delay capture authorization payment
for (MPaymentTransaction pt : ptList)
{
boolean ok = pt.delayCaptureOnlineAuthorizationPaymentTransaction(getC_Invoice_ID());
pt.saveEx();
if (!ok)
{
m_processMsg = Msg.getMsg(getCtx(), "DelayCaptureAuthFailed") + ": " + pt.getErrorMessage();
return DocAction.STATUS_Invalid;
}
}
}
if (testAllocation(true)) {
saveEx();
}
}
}
if (PAYMENTRULE_Cash.equals(getPaymentRule())) {
if (testAllocation(true)) {
saveEx();
}
}
// User Validation
String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE);
if (valid != null)
{
m_processMsg = valid;
return DocAction.STATUS_Invalid;
}
// Counter Documents
MInvoice counter = createCounterDoc();
if (counter != null)
info.append(" - @CounterDoc@: @C_Invoice_ID@=").append(counter.getDocumentNo());
m_processMsg = info.toString().trim();
setProcessed(true);
setDocAction(DOCACTION_Close);
return DocAction.STATUS_Completed;
} // completeIt
/* Save array of documents to process AFTER completing this one */
protected ArrayList docsPostProcess = new ArrayList();
/**
* Add doc for post processing (after processing of document action)
* @param doc
*/
private void addDocsPostProcess(PO doc) {
docsPostProcess.add(doc);
}
@Override
public List getDocsPostProcess() {
return docsPostProcess;
}
/**
* Set the definite document number after completed
*/
private void setDefiniteDocumentNo() {
if (isReversal() && ! MSysConfig.getBooleanValue(MSysConfig.Invoice_ReverseUseNewNumber, true, getAD_Client_ID())) // IDEMPIERE-1771
return;
MDocType dt = MDocType.get(getC_DocType_ID());
if (dt.isOverwriteDateOnComplete()) {
setDateInvoiced(TimeUtil.getDay(0));
if (getDateAcct().before(getDateInvoiced())) {
setDateAcct(getDateInvoiced());
MPeriod.testPeriodOpen(getCtx(), getDateAcct(), getC_DocType_ID(), getAD_Org_ID());
}
}
if (dt.isOverwriteSeqOnComplete()) {
String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this);
if (value != null)
setDocumentNo(value);
}
}
/**
* Create Counter Document
* @return counter invoice
*/
private MInvoice createCounterDoc()
{
// Is this a counter doc ?
if (getRef_Invoice_ID() != 0)
return null;
// Org Must be linked to BPartner
MOrg org = MOrg.get(getAD_Org_ID());
int counterC_BPartner_ID = org.getLinkedC_BPartner_ID(get_TrxName());
if (counterC_BPartner_ID == 0)
return null;
// Business Partner needs to be linked to Org
MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), null);
int counterAD_Org_ID = bp.getAD_OrgBP_ID();
if (counterAD_Org_ID == 0)
return null;
MBPartner counterBP = new MBPartner (getCtx(), counterC_BPartner_ID, null);
if (log.isLoggable(Level.INFO)) log.info("Counter BP=" + counterBP.getName());
// Document Type
int C_DocTypeTarget_ID = 0;
MDocTypeCounter counterDT = MDocTypeCounter.getCounterDocType(getCtx(), getC_DocType_ID());
if (counterDT != null)
{
if (log.isLoggable(Level.FINE)) log.fine(counterDT.toString());
if (!counterDT.isCreateCounter() || !counterDT.isValid())
return null;
C_DocTypeTarget_ID = counterDT.getCounter_C_DocType_ID();
}
else // indirect
{
C_DocTypeTarget_ID = MDocTypeCounter.getCounterDocType_ID(getCtx(), getC_DocType_ID());
if (log.isLoggable(Level.FINE)) log.fine("Indirect C_DocTypeTarget_ID=" + C_DocTypeTarget_ID);
if (C_DocTypeTarget_ID <= 0)
return null;
}
// Deep Copy
MInvoice counter = copyFrom(this, getDateInvoiced(), getDateAcct(),
C_DocTypeTarget_ID, !isSOTrx(), true, get_TrxName(), true);
//
counter.setAD_Org_ID(counterAD_Org_ID);
// References (Should not be required)
counter.setSalesRep_ID(getSalesRep_ID());
counter.saveEx(get_TrxName());
// Update copied lines
MInvoiceLine[] counterLines = counter.getLines(true);
for (int i = 0; i < counterLines.length; i++)
{
MInvoiceLine counterLine = counterLines[i];
counterLine.setClientOrg(counter);
counterLine.setInvoice(counter); // copies header values (BP, etc.)
counterLine.setPrice();
counterLine.setTax();
//
counterLine.saveEx(get_TrxName());
}
if (log.isLoggable(Level.FINE)) log.fine(counter.toString());
// Document Action
if (counterDT != null)
{
if (counterDT.getDocAction() != null)
{
counter.setDocAction(counterDT.getDocAction());
// added AdempiereException by zuhri
if (!counter.processIt(counterDT.getDocAction()))
throw new AdempiereException(Msg.getMsg(getCtx(), "FailedProcessingDocument") + " - " + counter.getProcessMsg());
// end added
counter.saveEx(get_TrxName());
}
}
return counter;
} // createCounterDoc
/**
* Void Document.
* @return true if success
*/
@Override
public boolean voidIt()
{
if (log.isLoggable(Level.INFO)) log.info(toString());
if (DOCSTATUS_Closed.equals(getDocStatus())
|| DOCSTATUS_Reversed.equals(getDocStatus())
|| DOCSTATUS_Voided.equals(getDocStatus()))
{
m_processMsg = "Document Closed: " + getDocStatus();
setDocAction(DOCACTION_None);
return false;
}
// Not Processed
if (DOCSTATUS_Drafted.equals(getDocStatus())
|| DOCSTATUS_Invalid.equals(getDocStatus())
|| DOCSTATUS_InProgress.equals(getDocStatus())
|| DOCSTATUS_Approved.equals(getDocStatus())
|| DOCSTATUS_NotApproved.equals(getDocStatus()) )
{
// Before Void
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID);
if (m_processMsg != null)
return false;
// Set lines to 0
MInvoiceLine[] lines = getLines(false);
for (int i = 0; i < lines.length; i++)
{
MInvoiceLine line = lines[i];
BigDecimal old = line.getQtyInvoiced();
if (old.compareTo(Env.ZERO) != 0)
{
line.setQty(Env.ZERO);
line.setTaxAmt(Env.ZERO);
line.setLineNetAmt(Env.ZERO);
line.setLineTotalAmt(Env.ZERO);
StringBuilder msgadd = new StringBuilder(Msg.getMsg(getCtx(), "Voided")).append(" (").append(old).append(")");
line.addDescription(msgadd.toString());
// Unlink Shipment
if (line.getM_InOutLine_ID() != 0)
{
MInOutLine ioLine = new MInOutLine(getCtx(), line.getM_InOutLine_ID(), get_TrxName());
ioLine.setIsInvoiced(false);
ioLine.saveEx(get_TrxName());
line.setM_InOutLine_ID(0);
}
line.saveEx(get_TrxName());
}
}
addDescription(Msg.getMsg(getCtx(), "Voided"));
setIsPaid(true);
setC_Payment_ID(0);
}
else
{
boolean accrual = false;
try
{
MPeriod.testPeriodOpen(getCtx(), getDateAcct(), getC_DocType_ID(), getAD_Org_ID());
}
catch (PeriodClosedException e)
{
accrual = true;
}
if (accrual)
return reverseAccrualIt();
else
return reverseCorrectIt();
}
// After Void
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID);
if (m_processMsg != null)
return false;
setProcessed(true);
setDocAction(DOCACTION_None);
return true;
} // voidIt
/**
* Close Document.
* @return true if success
*/
@Override
public boolean closeIt()
{
if (log.isLoggable(Level.INFO)) log.info(toString());
// Before Close
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE);
if (m_processMsg != null)
return false;
setProcessed(true);
setDocAction(DOCACTION_None);
// After Close
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE);
if (m_processMsg != null)
return false;
return true;
} // closeIt
/**
* Reverse Correction - same date
* @return true if success
*/
@Override
public boolean reverseCorrectIt()
{
if (log.isLoggable(Level.INFO)) log.info(toString());
// Before reverseCorrect
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT);
if (m_processMsg != null)
return false;
MInvoice reversal = reverse(false);
if (reversal == null)
return false;
// After reverseCorrect
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT);
if (m_processMsg != null)
return false;
m_processMsg = reversal.getDocumentNo();
return true;
} // reverseCorrectIt
/**
* Reverse this document
* @param accrual true to use current date, false to use this document's accounting date
* @return reversal invoice document
*/
private MInvoice reverse(boolean accrual) {
Timestamp reversalDate = accrual ? Env.getContextAsDate(getCtx(), Env.DATE) : getDateAcct();
if (reversalDate == null) {
reversalDate = new Timestamp(System.currentTimeMillis());
}
Timestamp reversalDateInvoiced = accrual ? reversalDate : getDateInvoiced();
MPeriod.testPeriodOpen(getCtx(), reversalDate, getC_DocType_ID(), getAD_Org_ID());
//
reverseAllocations(accrual, getC_Invoice_ID());
// Reverse/Delete Matching
if (!isSOTrx())
{
MatchPOAutoMatch.unmatch(getCtx(), getC_Invoice_ID(), get_TrxName());
MMatchInv[] mInv = MMatchInv.getInvoice(getCtx(), getC_Invoice_ID(), get_TrxName());
for (int i = 0; i < mInv.length; i++)
{
if (mInv[i].getReversal_ID() > 0)
continue;
if (!mInv[i].reverse(reversalDate))
{
m_processMsg = "Could not Reverse MatchInv";
return null;
}
addDocsPostProcess(new MMatchInv(Env.getCtx(), mInv[i].getReversal_ID(), get_TrxName()));
}
MMatchPO[] mPO = MMatchPO.getInvoice(getCtx(), getC_Invoice_ID(), get_TrxName());
for (int i = 0; i < mPO.length; i++)
{
if (mPO[i].getReversal_ID() > 0)
continue;
if (mPO[i].getM_InOutLine_ID() == 0)
{
if(mPO[i].isPosted())
{
if (!mPO[i].reverse(reversalDate))
{
m_processMsg = "Could not Reverse MatchPO";
return null;
}
addDocsPostProcess(new MMatchPO(Env.getCtx(), mPO[i].getReversal_ID(), get_TrxName()));
}
else
{
mPO[i].deleteEx(true);
}
}
else
{
mPO[i].setC_InvoiceLine_ID(null);
mPO[i].saveEx(get_TrxName());
}
}
}
//
load(get_TrxName()); // reload allocation reversal info
// Deep Copy
MInvoice reversal = null;
if (MSysConfig.getBooleanValue(MSysConfig.Invoice_ReverseUseNewNumber, true, getAD_Client_ID()))
reversal = copyFrom (this, reversalDateInvoiced, reversalDate, getC_DocType_ID(), isSOTrx(), false, get_TrxName(), true);
else
reversal = copyFrom (this, reversalDateInvoiced, reversalDate, getC_DocType_ID(), isSOTrx(), false, get_TrxName(), true, getDocumentNo()+"^");
if (reversal == null)
{
m_processMsg = "Could not create Invoice Reversal";
return null;
}
reversal.setReversal(true);
// Reverse Line Qty
MInvoiceLine[] oLines = getLines(false);
MInvoiceLine[] rLines = reversal.getLines(true);
for (int i = 0; i < rLines.length; i++)
{
MInvoiceLine rLine = rLines[i];
rLine.getParent().setReversal(true);
MInvoiceLine oLine = oLines[i];
rLine.setQtyEntered(oLine.getQtyEntered().negate());
rLine.setQtyInvoiced(oLine.getQtyInvoiced().negate());
rLine.setLineNetAmt(oLine.getLineNetAmt().negate());
rLine.setTaxAmt(oLine.getTaxAmt().negate());
rLine.setLineTotalAmt(oLine.getLineTotalAmt().negate());
rLine.setPriceActual(oLine.getPriceActual());
rLine.setPriceList(oLine.getPriceList());
rLine.setPriceLimit(oLine.getPriceLimit());
rLine.setPriceEntered(oLine.getPriceEntered());
rLine.setC_UOM_ID(oLine.getC_UOM_ID());
if (!rLine.save(get_TrxName()))
{
m_processMsg = "Could not correct Invoice Reversal Line";
return null;
}
}
reversal.setC_Order_ID(getC_Order_ID());
StringBuilder msgadd = new StringBuilder("{->").append(getDocumentNo()).append(")");
reversal.addDescription(msgadd.toString());
//FR1948157
reversal.setReversal_ID(getC_Invoice_ID());
reversal.saveEx(get_TrxName());
//
reversal.docsPostProcess = this.docsPostProcess;
this.docsPostProcess = new ArrayList();
//
if (!reversal.processIt(DocAction.ACTION_Complete))
{
m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg();
return null;
}
//
reverseAllocations(accrual, reversal.getC_Invoice_ID());
reversal.setC_Payment_ID(0);
reversal.setIsPaid(true);
reversal.closeIt();
reversal.setProcessing (false);
reversal.setDocStatus(DOCSTATUS_Reversed);
reversal.setDocAction(DOCACTION_None);
reversal.saveEx(get_TrxName());
//
msgadd = new StringBuilder("(").append(reversal.getDocumentNo()).append("<-)");
addDescription(msgadd.toString());
// Clean up Reversed (this)
MInvoiceLine[] iLines = getLines(false);
for (int i = 0; i < iLines.length; i++)
{
MInvoiceLine iLine = iLines[i];
if (iLine.getM_InOutLine_ID() != 0)
{
MInOutLine ioLine = new MInOutLine(getCtx(), iLine.getM_InOutLine_ID(), get_TrxName());
ioLine.setIsInvoiced(false);
ioLine.saveEx(get_TrxName());
// Reconsiliation
iLine.setM_InOutLine_ID(0);
iLine.saveEx(get_TrxName());
}
}
setProcessed(true);
//FR1948157
setReversal_ID(reversal.getC_Invoice_ID());
setDocStatus(DOCSTATUS_Reversed); // may come from void
setDocAction(DOCACTION_None);
setC_Payment_ID(0);
setIsPaid(true);
// Create Allocation
StringBuilder msgall = new StringBuilder().append(Msg.translate(getCtx(), "C_Invoice_ID")).append(": ").append(getDocumentNo()).append("/").append(reversal.getDocumentNo());
MAllocationHdr alloc = new MAllocationHdr(getCtx(), false, reversalDate,
getC_Currency_ID(),
msgall.toString(),
get_TrxName());
alloc.setAD_Org_ID(getAD_Org_ID());
alloc.saveEx();
// Amount
BigDecimal gt = getGrandTotal(true);
if (!isSOTrx())
gt = gt.negate();
// Orig Line
MAllocationLine aLine = new MAllocationLine (alloc, gt,
Env.ZERO, Env.ZERO, Env.ZERO);
aLine.setC_Invoice_ID(getC_Invoice_ID());
aLine.saveEx();
// Reversal Line
MAllocationLine rLine = new MAllocationLine (alloc, gt.negate(),
Env.ZERO, Env.ZERO, Env.ZERO);
rLine.setC_Invoice_ID(reversal.getC_Invoice_ID());
rLine.saveEx();
// added AdempiereException by zuhri
if (!alloc.processIt(DocAction.ACTION_Complete))
throw new AdempiereException(Msg.getMsg(getCtx(), "FailedProcessingDocument") + " - " + alloc.getProcessMsg());
// end added
alloc.saveEx();
return reversal;
}
/**
* Reverse allocations
* @param accrual
* @param invoiceID
*/
private void reverseAllocations(boolean accrual, int invoiceID) {
for (MAllocationHdr allocation : MAllocationHdr.getOfInvoice(getCtx(), invoiceID, get_TrxName())) {
if (accrual) {
allocation.setDocAction(DocAction.ACTION_Reverse_Accrual);
allocation.reverseAccrualIt();
} else {
allocation.setDocAction(DocAction.ACTION_Reverse_Correct);
allocation.reverseCorrectIt();
}
allocation.saveEx(get_TrxName());
}
}
/**
* Reverse Accrual - use current date
* @return false
*/
@Override
public boolean reverseAccrualIt()
{
if (log.isLoggable(Level.INFO)) log.info(toString());
// Before reverseAccrual
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL);
if (m_processMsg != null)
return false;
MInvoice reversal = reverse(true);
if (reversal == null)
return false;
// After reverseAccrual
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL);
if (m_processMsg != null)
return false;
m_processMsg = reversal.getDocumentNo();
return true;
} // reverseAccrualIt
/**
* Re-activate
* @return false
*/
@Override
public boolean reActivateIt()
{
if (log.isLoggable(Level.INFO)) log.info(toString());
// Before reActivate
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE);
if (m_processMsg != null)
return false;
// After reActivate
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE);
if (m_processMsg != null)
return false;
return false;
} // reActivateIt
/**
* Get Summary
* @return Summary of Document
*/
@Override
public String getSummary()
{
StringBuilder sb = new StringBuilder();
sb.append(getDocumentNo());
// : Grand Total = 123.00 (#1)
sb.append(": ").
append(Msg.translate(getCtx(),"GrandTotal")).append("=").append(getGrandTotal())
.append(" (#").append(getLines(false).length).append(")");
// - Description
if (getDescription() != null && getDescription().length() > 0)
sb.append(" - ").append(getDescription());
return sb.toString();
} // getSummary
/**
* Get Process Message
* @return clear text error message
*/
@Override
public String getProcessMsg()
{
return m_processMsg;
} // getProcessMsg
/**
* Get Document Owner (Responsible)
* @return AD_User_ID
*/
@Override
public int getDoc_User_ID()
{
return getSalesRep_ID();
} // getDoc_User_ID
/**
* Get Document Approval Amount
* @return amount
*/
@Override
public BigDecimal getApprovalAmt()
{
return getGrandTotal();
} // getApprovalAmt
/**
* @param rma
*/
public void setRMA(MRMA rma)
{
setM_RMA_ID(rma.getM_RMA_ID());
setAD_Org_ID(rma.getAD_Org_ID());
setDescription(rma.getDescription());
setC_BPartner_ID(rma.getC_BPartner_ID());
setSalesRep_ID(rma.getSalesRep_ID());
setGrandTotal(rma.getAmt());
setIsSOTrx(rma.isSOTrx());
setTotalLines(rma.getAmt());
MInvoice originalInvoice = rma.getOriginalInvoice();
if (originalInvoice == null)
{
throw new IllegalStateException("Not invoiced - RMA: " + rma.getDocumentNo());
}
setC_BPartner_Location_ID(originalInvoice.getC_BPartner_Location_ID());
setAD_User_ID(originalInvoice.getAD_User_ID());
setC_Currency_ID(originalInvoice.getC_Currency_ID());
setIsTaxIncluded(originalInvoice.isTaxIncluded());
setM_PriceList_ID(originalInvoice.getM_PriceList_ID());
setC_Project_ID(originalInvoice.getC_Project_ID());
setC_Activity_ID(originalInvoice.getC_Activity_ID());
setC_Campaign_ID(originalInvoice.getC_Campaign_ID());
setUser1_ID(originalInvoice.getUser1_ID());
setUser2_ID(originalInvoice.getUser2_ID());
}
/**
* Document Status is Complete or Closed
* @return true if CO, CL or RE
*/
public boolean isComplete()
{
String ds = getDocStatus();
return DOCSTATUS_Completed.equals(ds)
|| DOCSTATUS_Closed.equals(ds)
|| DOCSTATUS_Reversed.equals(ds);
} // isComplete
/**
* Get original order
* @return order
*/
public MOrder getOriginalOrder()
{
if (getM_RMA_ID() > 0)
{
MRMA rma = new MRMA(getCtx(), getM_RMA_ID(), get_TrxName());
MOrder originalOrder = rma.getOriginalOrder();
if (originalOrder != null)
return originalOrder;
MInvoice originalInvoice = rma.getOriginalInvoice();
if (originalInvoice.getC_Order_ID() > 0)
{
originalOrder = new MOrder(getCtx(), originalInvoice.getC_Order_ID(), get_TrxName());
if (originalOrder != null)
return originalOrder;
}
}
else if (getC_Order_ID() > 0)
return new MOrder(getCtx(), getC_Order_ID(), get_TrxName());
return null;
}
/**
* Set process message
* @param processMsg
*/
public void setProcessMessage(String processMsg)
{
m_processMsg = processMsg;
}
/**
* Get tax providers
* @return array of tax provider
*/
public MTaxProvider[] getTaxProviders()
{
Hashtable providers = new Hashtable();
MInvoiceLine[] lines = getLines();
for (MInvoiceLine line : lines)
{
if (line.isDescription())
continue;
MTax tax = new MTax(line.getCtx(), line.getC_Tax_ID(), line.get_TrxName());
MTaxProvider provider = providers.get(tax.getC_TaxProvider_ID());
if (provider == null)
providers.put(tax.getC_TaxProvider_ID(), new MTaxProvider(tax.getCtx(), tax.getC_TaxProvider_ID(), tax.get_TrxName()));
}
MTaxProvider[] retValue = new MTaxProvider[providers.size()];
providers.values().toArray(retValue);
return retValue;
}
/**
* Get C_DocType_ID (or C_DocTypeTarget_ID if C_DocType_ID is not set)
* @return C_DocType_ID
*/
public int getDocTypeID()
{
return getC_DocType_ID() > 0 ? getC_DocType_ID() : getC_DocTypeTarget_ID();
}
/**
* Index constant for Vector record return by getUnpaidInvoiceData.
* Use MULTI_CURRENCY index if isMultiCurrency=true.
* Use SINGLE_CURRENCY index if isMultiCurrency=false.
*/
//selected row, boolean
public static final int UNPAID_INVOICE_SELECTED = 0;
//transaction date, timestamp
public static final int UNPAID_INVOICE_TRX_DATE = 1;
//KeyNamePair, DocumentNo and C_Invoice_ID
public static final int UNPAID_INVOICE_DOCUMENT_KEY_NAME_PAIR = 2;
//multi currency record, invoice currency iso code
public static final int UNPAID_INVOICE_MULTI_CURRENCY_ISO = 3;
//multi currency record, invoice amount
public static final int UNPAID_INVOICE_MULTI_CURRENCY_INVOICE_AMT = 4;
//multi currency record, invoice amount converted to base currency
public static final int UNPAID_INVOICE_MULTI_CURRENCY_CONVERTED_AMT = 5;
//multi currency record, open invoice amount
public static final int UNPAID_INVOICE_MULTI_CURRENCY_OPEN_AMT = 6;
//multi currency record, discount amount converted to base currency
public static final int UNPAID_INVOICE_MULTI_CURRENCY_CONVERTED_DISCOUNT_AMT = 7;
//multi currency record, write off amount
public static final int UNPAID_INVOICE_MULTI_CURRENCY_WRITE_OFF_AMT = 8;
//multi currency record, invoice applied amount
public static final int UNPAID_INVOICE_MULTI_CURRENCY_APPLIED_AMT = 9;
//multi currency record, over or under applied amount
public static final int UNPAID_INVOICE_MULTI_CURRENCY_OVER_UNDER_AMT = 10;
//single currency record, invoice amount
public static final int UNPAID_INVOICE_SINGLE_CURRENCY_INVOICE_AMT = 3;
//single currency record, open invoice amount
public static final int UNPAID_INVOICE_SINGLE_CURRENCY_OPEN_AMT = 4;
//single currency record, discount amount
public static final int UNPAID_INVOICE_SINGLE_CURRENCY_DISCOUNT_AMT = 5;
//single currency record, write off amount
public static final int UNPAID_INVOICE_SINGLE_CURRENCY_WRITE_OFF_AMT = 6;
//single currency record, invoice applied amount
public static final int UNPAID_INVOICE_SINGLE_CURRENCY_APPLIED_AMT = 7;
//single currency record, over or under applied amount
public static final int UNPAID_INVOICE_SINGLE_CURRENCY_OVER_UNDER_AMT = 8;
/**
* Get unpaid invoices
* @param isMultiCurrency false to apply currency filter
* @param date invoice open amount as at date
* @param AD_Org_ID 0 for all orgs
* @param C_Currency_ID mandatory, use as invoice document filter if isMultiCurrency is false
* @param C_BPartner_ID mandatory bpartner filter
* @param trxName optional trx name
* @return list of unpaid invoice data
*/
public static Vector> getUnpaidInvoiceData(boolean isMultiCurrency, Timestamp date, int AD_Org_ID, int C_Currency_ID,
int C_BPartner_ID, String trxName)
{
/********************************
* Load unpaid Invoices
* 1-TrxDate, 2-Value, (3-Currency, 4-InvAmt,)
* 5-ConvAmt, 6-ConvOpen, 7-ConvDisc, 8-WriteOff, 9-Applied
*
SELECT i.DateInvoiced,i.DocumentNo,i.C_Invoice_ID,c.ISO_Code,
i.GrandTotal*i.MultiplierAP "GrandTotal",
currencyConvert(i.GrandTotal*i.MultiplierAP,i.C_Currency_ID,i.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID) "GrandTotal $",
invoiceOpen(C_Invoice_ID,C_InvoicePaySchedule_ID) "Open",
currencyConvert(invoiceOpen(C_Invoice_ID,C_InvoicePaySchedule_ID),i.C_Currency_ID,i.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)*i.MultiplierAP "Open $",
invoiceDiscount(i.C_Invoice_ID,getDate(),C_InvoicePaySchedule_ID) "Discount",
currencyConvert(invoiceDiscount(i.C_Invoice_ID,getDate(),C_InvoicePaySchedule_ID),i.C_Currency_ID,i.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)*i.Multiplier*i.MultiplierAP "Discount $",
i.MultiplierAP, i.Multiplier
FROM C_Invoice_v i INNER JOIN C_Currency c ON (i.C_Currency_ID=c.C_Currency_ID)
WHERE -- i.IsPaid='N' AND i.Processed='Y' AND i.C_BPartner_ID=1000001
*/
Vector> data = new Vector>();
StringBuilder sql = new StringBuilder("SELECT i.DateInvoiced,i.DocumentNo,i.C_Invoice_ID," // 1..3
+ "c.ISO_Code,i.GrandTotal*i.MultiplierAP, " // 4..5 Orig Currency
+ "currencyConvertInvoice(i.C_Invoice_ID,?,i.GrandTotal*i.MultiplierAP,?), " // 6 #1 Converted, #2 Date
+ "currencyConvertInvoice(i.C_Invoice_ID,?,invoiceOpen(C_Invoice_ID,C_InvoicePaySchedule_ID),?)*i.MultiplierAP, " // 7 #3, #4 Converted Open
+ "currencyConvertInvoice(i.C_Invoice_ID" // 8 AllowedDiscount
+ ",?,invoiceDiscount(i.C_Invoice_ID,?,C_InvoicePaySchedule_ID),i.DateInvoiced)*i.Multiplier*i.MultiplierAP," // #5, #6
+ "i.MultiplierAP "
+ "FROM C_Invoice_v i" // corrected for CM/Split
+ " INNER JOIN C_Currency c ON (i.C_Currency_ID=c.C_Currency_ID) "
+ "WHERE i.IsPaid='N' AND i.Processed='Y'"
+ " AND i.C_BPartner_ID=?"); // #7
if (!isMultiCurrency)
sql.append(" AND i.C_Currency_ID=?"); // #8
if (AD_Org_ID != 0 )
sql.append(" AND i.AD_Org_ID=" + AD_Org_ID);
sql.append(" ORDER BY i.DateInvoiced, i.DocumentNo");
if (s_log.isLoggable(Level.FINE)) s_log.fine("InvSQL=" + sql.toString());
// role security
sql = new StringBuilder( MRole.getDefault(Env.getCtx(), false).addAccessSQL( sql.toString(), "i", MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO ) );
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql.toString(), trxName);
pstmt.setInt(1, C_Currency_ID);
pstmt.setTimestamp(2, date);
pstmt.setInt(3, C_Currency_ID);
pstmt.setTimestamp(4, date);
pstmt.setInt(5, C_Currency_ID);
pstmt.setTimestamp(6, date);
pstmt.setInt(7, C_BPartner_ID);
if (!isMultiCurrency)
pstmt.setInt(8, C_Currency_ID);
rs = pstmt.executeQuery();
while (rs.next())
{
Vector line = new Vector();
line.add(Boolean.FALSE); // 0-Selection
line.add(rs.getTimestamp(1)); // 1-TrxDate
KeyNamePair pp = new KeyNamePair(rs.getInt(3), rs.getString(2));
line.add(pp); // 2-Value
if (isMultiCurrency)
{
line.add(rs.getString(4)); // 3-Currency
line.add(rs.getBigDecimal(5)); // 4-Orig Amount
}
line.add(rs.getBigDecimal(6)); // 3/5-ConvAmt
BigDecimal open = rs.getBigDecimal(7);
if (open == null) // no conversion rate
open = Env.ZERO;
line.add(open); // 4/6-ConvOpen
BigDecimal discount = rs.getBigDecimal(8);
if (discount == null) // no concersion rate
discount = Env.ZERO;
line.add(discount); // 5/7-ConvAllowedDisc
line.add(Env.ZERO); // 6/8-WriteOff
line.add(Env.ZERO); // 7/9-Applied
line.add(open); // 8/10-OverUnder
// Add when open <> 0 (i.e. not if no conversion rate)
if (Env.ZERO.compareTo(open) != 0)
data.add(line);
}
}
catch (SQLException e)
{
s_log.log(Level.SEVERE, sql.toString(), e);
}
finally
{
DB.close(rs, pstmt);
}
return data;
}
/**
* Create Line from orderline/inoutline/rmaline
* @param C_OrderLine_ID
* @param M_InOutLine_ID
* @param M_RMALine_ID
* @param M_Product_ID
* @param C_UOM_ID
* @param Qty
*/
public void createLineFrom(int C_OrderLine_ID, int M_InOutLine_ID, int M_RMALine_ID,
int M_Product_ID, int C_UOM_ID, BigDecimal Qty)
{
MInvoiceLine invoiceLine = new MInvoiceLine (this);
invoiceLine.setM_Product_ID(M_Product_ID, C_UOM_ID); // Line UOM
invoiceLine.setQty(Qty); // Invoiced/Entered
BigDecimal QtyInvoiced = null;
MProduct product = MProduct.get(Env.getCtx(), M_Product_ID);
if (M_Product_ID > 0 && product.getC_UOM_ID() != C_UOM_ID) {
QtyInvoiced = MUOMConversion.convertProductFrom(Env.getCtx(), M_Product_ID, C_UOM_ID, Qty);
}
if (QtyInvoiced == null)
QtyInvoiced = Qty;
invoiceLine.setQtyInvoiced(QtyInvoiced);
// Info
MOrderLine orderLine = null;
if (C_OrderLine_ID != 0)
orderLine = new MOrderLine (Env.getCtx(), C_OrderLine_ID, get_TrxName());
//
MRMALine rmaLine = null;
if (M_RMALine_ID > 0)
rmaLine = new MRMALine (Env.getCtx(), M_RMALine_ID, get_TrxName());
//
MInOutLine inoutLine = null;
if (M_InOutLine_ID != 0)
{
inoutLine = new MInOutLine (Env.getCtx(), M_InOutLine_ID, get_TrxName());
if (orderLine == null && inoutLine.getC_OrderLine_ID() != 0)
{
C_OrderLine_ID = inoutLine.getC_OrderLine_ID();
orderLine = new MOrderLine (Env.getCtx(), C_OrderLine_ID, get_TrxName());
}
}
else if (C_OrderLine_ID > 0)
{
String whereClause = "EXISTS (SELECT 1 FROM M_InOut io WHERE io.M_InOut_ID=M_InOutLine.M_InOut_ID AND io.DocStatus IN ('CO','CL'))";
MInOutLine[] lines = MInOutLine.getOfOrderLine(Env.getCtx(),
C_OrderLine_ID, whereClause, get_TrxName());
if (s_log.isLoggable(Level.FINE)) s_log.fine ("Receipt Lines with OrderLine = #" + lines.length);
if (lines.length > 0)
{
for (int j = 0; j < lines.length; j++)
{
MInOutLine line = lines[j];
// qty matched
BigDecimal qtyMatched = Env.ZERO;
for (MMatchInv match : MMatchInv.getInOutLine(Env.getCtx(), line.getM_InOutLine_ID(), get_TrxName())) {
qtyMatched = qtyMatched.add(match.getQty());
}
if (line.getQtyEntered().subtract(qtyMatched).compareTo(Qty) == 0)
{
inoutLine = line;
M_InOutLine_ID = inoutLine.getM_InOutLine_ID();
break;
}
}
}
}
else if (M_RMALine_ID != 0)
{
String whereClause = "EXISTS (SELECT 1 FROM M_InOut io WHERE io.M_InOut_ID=M_InOutLine.M_InOut_ID AND io.DocStatus IN ('CO','CL'))";
MInOutLine[] lines = MInOutLine.getOfRMALine(Env.getCtx(), M_RMALine_ID, whereClause, get_TrxName());
if (s_log.isLoggable(Level.FINE)) s_log.fine ("Receipt Lines with RMALine = #" + lines.length);
if (lines.length > 0)
{
for (int j = 0; j < lines.length; j++)
{
MInOutLine line = lines[j];
BigDecimal alreadyInvoiced = rmaLine.getQtyInvoiced() != null ? rmaLine.getQtyInvoiced() : BigDecimal.ZERO;
if (rmaLine.getQty().subtract(alreadyInvoiced).compareTo(Qty) >= 0)
{
inoutLine = line;
M_InOutLine_ID = inoutLine.getM_InOutLine_ID();
break;
}
}
if (rmaLine == null)
{
inoutLine = lines[0]; // first as default
M_InOutLine_ID = inoutLine.getM_InOutLine_ID();
}
}
}
// get Ship info
// Shipment Info
if (inoutLine != null)
{
invoiceLine.setShipLine(inoutLine); // overwrites
if(invoiceLine.getC_UOM_ID()!=inoutLine.getC_UOM_ID()) {
invoiceLine.setC_UOM_ID(inoutLine.getC_UOM_ID());
BigDecimal PriceEntered = MUOMConversion.convertProductFrom (Env.getCtx(), M_Product_ID,
inoutLine.getC_UOM_ID(), invoiceLine.getPriceEntered(), 12);
if (PriceEntered == null)
throw new AdempiereException("No Conversion For Price=" + invoiceLine.getPriceEntered());
invoiceLine.setPriceEntered(PriceEntered);
}
}
else {
if (s_log.isLoggable(Level.FINE)) s_log.fine("No Receipt Line");
// Order Info
if (orderLine != null)
{
invoiceLine.setOrderLine(orderLine); // overwrites
}
else
{
if (s_log.isLoggable(Level.FINE)) s_log.fine("No Order Line");
invoiceLine.setPrice();
invoiceLine.setTax();
}
//RMA Info
if (rmaLine != null)
{
invoiceLine.setRMALine(rmaLine); // overwrites
}
else
{
if (s_log.isLoggable(Level.FINE)) s_log.fine("No RMA Line");
}
}
invoiceLine.saveEx();
}
/**
* Update from order
* @param order
*/
public void updateFrom(MOrder order)
{
if (order != null)
{
setPaymentRule(order.getPaymentRule());
setC_PaymentTerm_ID(order.getC_PaymentTerm_ID());
setSalesRep_ID(order.getSalesRep_ID());
saveEx();
load(get_TrxName()); // refresh from DB
// copy payment schedule from order if invoice doesn't have a current payment schedule
MOrderPaySchedule[] opss = MOrderPaySchedule.getOrderPaySchedule(Env.getCtx(), order.getC_Order_ID(), 0, get_TrxName());
MInvoicePaySchedule[] ipss = MInvoicePaySchedule.getInvoicePaySchedule(Env.getCtx(), getC_Invoice_ID(), 0, get_TrxName());
if (ipss.length == 0 && opss.length > 0)
{
BigDecimal ogt = order.getGrandTotal();
BigDecimal igt = getGrandTotal();
BigDecimal percent = Env.ONE;
if (ogt.compareTo(igt) != 0)
percent = igt.divide(ogt, 10, RoundingMode.HALF_UP);
MCurrency cur = MCurrency.get(order.getCtx(), order.getC_Currency_ID());
int scale = cur.getStdPrecision();
for (MOrderPaySchedule ops : opss)
{
MInvoicePaySchedule ips = new MInvoicePaySchedule(Env.getCtx(), 0, get_TrxName());
PO.copyValues(ops, ips);
if (percent != Env.ONE) {
BigDecimal propDueAmt = ops.getDueAmt().multiply(percent);
if (propDueAmt.scale() > scale)
propDueAmt = propDueAmt.setScale(scale, RoundingMode.HALF_UP);
ips.setDueAmt(propDueAmt);
}
ips.setC_Invoice_ID(getC_Invoice_ID());
ips.setAD_Org_ID(ops.getAD_Org_ID());
ips.setProcessing(ops.isProcessing());
ips.setIsActive(ops.isActive());
ips.saveEx();
}
validatePaySchedule();
saveEx();
}
}
}
} // MInvoice