/****************************************************************************** * Copyright (C) 2009 Low Heng Sin * * Copyright (C) 2009 Idalica Corporation * * 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. * *****************************************************************************/ package org.compiere.apps.form; import java.math.BigDecimal; import java.sql.Timestamp; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Vector; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; import org.compiere.minigrid.IMiniTable; import org.compiere.model.MAllocationHdr; import org.compiere.model.MAllocationLine; import org.compiere.model.MDocType; import org.compiere.model.MInvoice; import org.compiere.model.MPayment; import org.compiere.model.MSysConfig; import org.compiere.process.DocAction; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.DisplayType; 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; /** * * @author hengsin * */ public class Allocation { protected DecimalFormat format = DisplayType.getNumberFormat(DisplayType.Amount); /** Logger */ protected static final CLogger log = CLogger.getCLogger(Allocation.class); private boolean m_calculating = false; protected int m_C_Currency_ID = 0; protected int m_C_Charge_ID = 0; protected int m_C_DocType_ID = 0; protected int m_C_BPartner_ID = 0; private int m_noInvoices = 0; private int m_noPayments = 0; protected BigDecimal totalInv = Env.ZERO; protected BigDecimal totalPay = Env.ZERO; protected BigDecimal totalDiff = Env.ZERO; protected Timestamp allocDate = null; // Index changed if multi-currency private int i_payment = 7; // private int i_open = 6; private int i_discount = 7; private int i_writeOff = 8; private int i_applied = 9; private int i_overUnder = 10; protected int m_AD_Org_ID = 0; private ArrayList m_bpartnerCheck = new ArrayList(); protected void dynInit() throws Exception { m_C_Currency_ID = Env.getContextAsInt(Env.getCtx(), Env.C_CURRENCY_ID); // default // if (log.isLoggable(Level.INFO)) log.info("Currency=" + m_C_Currency_ID); m_AD_Org_ID = Env.getAD_Org_ID(Env.getCtx()); m_C_DocType_ID= MDocType.getDocType(MDocType.DOCBASETYPE_PaymentAllocation); } /** * Load Business Partner Info * - Payments * - Invoices */ public void checkBPartner() { if (log.isLoggable(Level.CONFIG)) log.config("BPartner=" + m_C_BPartner_ID + ", Cur=" + m_C_Currency_ID); // Need to have both values if (m_C_BPartner_ID == 0 || m_C_Currency_ID == 0) return; // Async BPartner Test Integer key = Integer.valueOf(m_C_BPartner_ID); if (!m_bpartnerCheck.contains(key)) { new Thread() { public void run() { MPayment.setIsAllocated (Env.getCtx(), m_C_BPartner_ID, null); MInvoice.setIsPaid (Env.getCtx(), m_C_BPartner_ID, null); } }.start(); m_bpartnerCheck.add(key); } } /** * @deprecated * @param isMultiCurrency * @param date * @param paymentTable not used * @return list of payment record */ public Vector> getPaymentData(boolean isMultiCurrency, Object date, IMiniTable paymentTable) { return getPaymentData(isMultiCurrency, (Timestamp) date, (String)null); } /** * * @param isMultiCurrency * @param date * @param trxName optional trx name * @return list of payment record */ public Vector> getPaymentData(boolean isMultiCurrency, Timestamp date, String trxName) { return MPayment.getUnAllocatedPaymentData(m_C_BPartner_ID, m_C_Currency_ID, isMultiCurrency, date, m_AD_Org_ID, trxName); } /** * * @param isMultiCurrency * @return column name list for payment data */ public Vector getPaymentColumnNames(boolean isMultiCurrency) { // Header Info Vector columnNames = new Vector(); columnNames.add(Msg.getMsg(Env.getCtx(), "Select")); columnNames.add(Msg.translate(Env.getCtx(), "Date")); columnNames.add(Util.cleanAmp(Msg.translate(Env.getCtx(), "DocumentNo"))); if (isMultiCurrency) { columnNames.add(Msg.getMsg(Env.getCtx(), "TrxCurrency")); columnNames.add(Msg.translate(Env.getCtx(), "Amount")); } columnNames.add(Msg.getMsg(Env.getCtx(), "ConvertedAmount")); columnNames.add(Msg.getMsg(Env.getCtx(), "OpenAmt")); columnNames.add(Msg.getMsg(Env.getCtx(), "AppliedAmt")); return columnNames; } /** * * @param paymentTable * @param isMultiCurrency */ public void setPaymentColumnClass(IMiniTable paymentTable, boolean isMultiCurrency) { int i = 0; paymentTable.setColumnClass(i++, Boolean.class, false); // 0-Selection paymentTable.setColumnClass(i++, Timestamp.class, true); // 1-TrxDate paymentTable.setColumnClass(i++, String.class, true); // 2-Value if (isMultiCurrency) { paymentTable.setColumnClass(i++, String.class, true); // 3-Currency paymentTable.setColumnClass(i++, BigDecimal.class, true); // 4-PayAmt } paymentTable.setColumnClass(i++, BigDecimal.class, true); // 5-ConvAmt paymentTable.setColumnClass(i++, BigDecimal.class, true); // 6-ConvOpen paymentTable.setColumnClass(i++, BigDecimal.class, false); // 7-Allocated // i_payment = isMultiCurrency ? 7 : 5; // Table UI paymentTable.autoSize(); } /** * @deprecated * @param isMultiCurrency * @param date * @param invoiceTable not use * @return list of unpaid invoice data */ public Vector> getInvoiceData(boolean isMultiCurrency, Object date, IMiniTable invoiceTable) { return getInvoiceData(isMultiCurrency, (Timestamp) date, (String)null); } /** * * @param isMultiCurrency * @param date * @param trxName optional trx name * @return list of unpaid invoice data */ public Vector> getInvoiceData(boolean isMultiCurrency, Timestamp date, String trxName) { return MInvoice.getUnpaidInvoiceData(isMultiCurrency, date, m_AD_Org_ID, m_C_Currency_ID, m_C_BPartner_ID, trxName); } /** * * @param isMultiCurrency * @return list of column name/header */ public Vector getInvoiceColumnNames(boolean isMultiCurrency) { // Header Info Vector columnNames = new Vector(); columnNames.add(Msg.getMsg(Env.getCtx(), "Select")); columnNames.add(Msg.translate(Env.getCtx(), "Date")); columnNames.add(Util.cleanAmp(Msg.translate(Env.getCtx(), "DocumentNo"))); if (isMultiCurrency) { columnNames.add(Msg.getMsg(Env.getCtx(), "TrxCurrency")); columnNames.add(Msg.translate(Env.getCtx(), "Amount")); } columnNames.add(Msg.getMsg(Env.getCtx(), "ConvertedAmount")); columnNames.add(Msg.getMsg(Env.getCtx(), "OpenAmt")); columnNames.add(Msg.getMsg(Env.getCtx(), "Discount")); columnNames.add(Msg.getMsg(Env.getCtx(), "WriteOff")); columnNames.add(Msg.getMsg(Env.getCtx(), "AppliedAmt")); columnNames.add(Msg.getMsg(Env.getCtx(), "OverUnderAmt")); return columnNames; } /** * set class type for each column * @param invoiceTable * @param isMultiCurrency */ public void setInvoiceColumnClass(IMiniTable invoiceTable, boolean isMultiCurrency) { int i = 0; invoiceTable.setColumnClass(i++, Boolean.class, false); // 0-Selection invoiceTable.setColumnClass(i++, Timestamp.class, true); // 1-TrxDate invoiceTable.setColumnClass(i++, String.class, true); // 2-Value if (isMultiCurrency) { invoiceTable.setColumnClass(i++, String.class, true); // 3-Currency invoiceTable.setColumnClass(i++, BigDecimal.class, true); // 4-Amt } invoiceTable.setColumnClass(i++, BigDecimal.class, true); // 5-ConvAmt invoiceTable.setColumnClass(i++, BigDecimal.class, true); // 6-ConvAmt Open invoiceTable.setColumnClass(i++, BigDecimal.class, false); // 7-Conv Discount invoiceTable.setColumnClass(i++, BigDecimal.class, false); // 8-Conv WriteOff invoiceTable.setColumnClass(i++, BigDecimal.class, false); // 9-Conv OverUnder invoiceTable.setColumnClass(i++, BigDecimal.class, true); // 10-Conv Applied // Table UI invoiceTable.autoSize(); } /** * set column index for single or multi currency * @param isMultiCurrency */ protected void prepareForCalculate(boolean isMultiCurrency) { i_open = isMultiCurrency ? 6 : 4; i_discount = isMultiCurrency ? 7 : 5; i_writeOff = isMultiCurrency ? 8 : 6; i_applied = isMultiCurrency ? 9 : 7; i_overUnder = isMultiCurrency ? 10 : 8; } // loadBPartner /** * update payment or invoice applied and write off amount * @param row row to update * @param col change is trigger by selected or applied column * @param isInvoice update invoice or payment applied amount * @param payment * @param invoice * @param isAutoWriteOff true to write off difference, false to use over/under for difference * @return warning message (if any) */ public String writeOff(int row, int col, boolean isInvoice, IMiniTable payment, IMiniTable invoice, boolean isAutoWriteOff) { String msg = ""; /** * Setting defaults */ if (m_calculating) // Avoid recursive calls return msg; m_calculating = true; if (log.isLoggable(Level.CONFIG)) log.config("Row=" + row + ", Col=" + col + ", InvoiceTable=" + isInvoice); // Payments if (!isInvoice) { BigDecimal open = (BigDecimal)payment.getValueAt(row, i_open); BigDecimal applied = (BigDecimal)payment.getValueAt(row, i_payment); if (col == 0) { // selection of payment row if (((Boolean)payment.getValueAt(row, 0)).booleanValue()) { applied = open; // Open Amount if (totalDiff.abs().compareTo(applied.abs()) < 0 // where less is available to allocate than open && totalDiff.signum() == -applied.signum() ) // and the available amount has the opposite sign applied = totalDiff.negate(); // reduce the amount applied to what's available } else // de-selected applied = Env.ZERO; } if (col == i_payment) { if (! MSysConfig.getBooleanValue(MSysConfig.ALLOW_APPLY_PAYMENT_TO_CREDITMEMO, false, Env.getAD_Client_ID(Env.getCtx())) && open.signum() > 0 && applied.signum() == -open.signum() ) applied = applied.negate(); if (! MSysConfig.getBooleanValue(MSysConfig.ALLOW_OVER_APPLIED_PAYMENT, false, Env.getAD_Client_ID(Env.getCtx()))) if ( open.abs().compareTo( applied.abs() ) < 0 ) applied = open; } payment.setValueAt(applied, row, i_payment); } // Invoice else { boolean selected = ((Boolean) invoice.getValueAt(row, 0)).booleanValue(); BigDecimal open = (BigDecimal)invoice.getValueAt(row, i_open); BigDecimal discount = (BigDecimal)invoice.getValueAt(row, i_discount); BigDecimal applied = (BigDecimal)invoice.getValueAt(row, i_applied); BigDecimal writeOff = (BigDecimal) invoice.getValueAt(row, i_writeOff); BigDecimal overUnder = (BigDecimal) invoice.getValueAt(row, i_overUnder); int openSign = open.signum(); if (col == 0) //selection { // selected - set applied amount if ( selected ) { applied = open; // Open Amount applied = applied.subtract(discount); writeOff = Env.ZERO; // to be sure overUnder = Env.ZERO; totalDiff = Env.ZERO; if (totalDiff.abs().compareTo(applied.abs()) < 0 // where less is available to allocate than open && totalDiff.signum() == applied.signum() ) // and the available amount has the same sign applied = totalDiff; // reduce the amount applied to what's available if ( isAutoWriteOff ) writeOff = open.subtract(applied.add(discount)); else overUnder = open.subtract(applied.add(discount)); } else // de-selected { writeOff = Env.ZERO; applied = Env.ZERO; overUnder = Env.ZERO; } } // check entered values are sensible and possibly auto write-off if ( selected && col != 0 ) { // values should have same sign as open except possibly over/under if ( discount.signum() == -openSign ) discount = discount.negate(); if ( writeOff.signum() == -openSign) writeOff = writeOff.negate(); if ( applied.signum() == -openSign ) applied = applied.negate(); // discount and write-off must be less than open amount if ( discount.abs().compareTo(open.abs()) > 0) discount = open; if ( writeOff.abs().compareTo(open.abs()) > 0) writeOff = open; /* * Two rules to maintain: * * 1) |writeOff + discount| < |open| * 2) discount + writeOff + overUnder + applied = 0 * * As only one column is edited at a time and the initial position was one of compliance * with the rules, we only need to redistribute the increase/decrease in the edited column to * the others. */ BigDecimal newTotal = discount.add(writeOff).add(applied).add(overUnder); // all have same sign BigDecimal difference = newTotal.subtract(open); // rule 2 BigDecimal diffWOD = writeOff.add(discount).subtract(open); if ( diffWOD.signum() == open.signum() ) // writeOff and discount are too large { if ( col == i_discount ) // then edit writeoff { writeOff = writeOff.subtract(diffWOD); } else // col = i_writeoff { discount = discount.subtract(diffWOD); } difference = difference.subtract(diffWOD); } // rule 1 if ( col == i_applied ) overUnder = overUnder.subtract(difference); else applied = applied.subtract(difference); } // Warning if write Off > 30% if (isAutoWriteOff && writeOff.doubleValue()/open.doubleValue() > .30) msg = "AllocationWriteOffWarn"; invoice.setValueAt(discount, row, i_discount); invoice.setValueAt(applied, row, i_applied); invoice.setValueAt(writeOff, row, i_writeOff); invoice.setValueAt(overUnder, row, i_overUnder); } m_calculating = false; return msg; } /** * perform allocation calculation * @param paymentTable * @param invoiceTable * @param isMultiCurrency */ public void calculate(IMiniTable paymentTable, IMiniTable invoiceTable, boolean isMultiCurrency) { allocDate = null; prepareForCalculate(isMultiCurrency); calculatePayment(paymentTable, isMultiCurrency); calculateInvoice(invoiceTable, isMultiCurrency); calculateDifference(); } /** * Calculate selected payment total * @param payment * @param isMultiCurrency * @return payment summary */ public String calculatePayment(IMiniTable payment, boolean isMultiCurrency) { if (log.isLoggable(Level.CONFIG)) log.config(""); // Payment totalPay = Env.ZERO; int rows = payment.getRowCount(); m_noPayments = 0; for (int i = 0; i < rows; i++) { if (((Boolean)payment.getValueAt(i, 0)).booleanValue()) { Timestamp ts = (Timestamp)payment.getValueAt(i, 1); if ( !isMultiCurrency ) // the converted amounts are only valid for the selected date allocDate = TimeUtil.max(allocDate, ts); BigDecimal bd = (BigDecimal)payment.getValueAt(i, i_payment); totalPay = totalPay.add(bd); // Applied Pay m_noPayments++; if (log.isLoggable(Level.FINE)) log.fine("Payment_" + i + " = " + bd + " - Total=" + totalPay); } } return getPaymentInfoText(); } /** * * @return summary info for payment selected and total applied */ public String getPaymentInfoText() { return String.valueOf(m_noPayments) + " - " + Msg.getMsg(Env.getCtx(), "Sum") + " " + format.format(totalPay) + " "; } /** * calculate selected invoice total * @param invoice * @param isMultiCurrency * @return invoice summary */ public String calculateInvoice(IMiniTable invoice, boolean isMultiCurrency) { // Invoices totalInv = Env.ZERO; int rows = invoice.getRowCount(); m_noInvoices = 0; for (int i = 0; i < rows; i++) { if (((Boolean)invoice.getValueAt(i, 0)).booleanValue()) { Timestamp ts = (Timestamp)invoice.getValueAt(i, 1); if ( !isMultiCurrency ) // converted amounts only valid for selected date allocDate = TimeUtil.max(allocDate, ts); BigDecimal bd = (BigDecimal)invoice.getValueAt(i, i_applied); totalInv = totalInv.add(bd); // Applied Inv m_noInvoices++; if (log.isLoggable(Level.FINE)) log.fine("Invoice_" + i + " = " + bd + " - Total=" + totalPay); } } return getInvoiceInfoText(); } /** * * @return summary info for invoice selected and total applied */ public String getInvoiceInfoText() { return String.valueOf(m_noInvoices) + " - " + Msg.getMsg(Env.getCtx(), "Sum") + " " + format.format(totalInv) + " "; } /** * Save allocation data * @param m_WindowNo * @param dateTrx * @param payment * @param invoice * @param trxName * @return {@link MAllocationHdr} */ public MAllocationHdr saveData(int m_WindowNo, Timestamp dateTrx, IMiniTable payment, IMiniTable invoice, String trxName) { if (m_noInvoices + m_noPayments == 0) return null; // fixed fields int AD_Client_ID = Env.getContextAsInt(Env.getCtx(), m_WindowNo, "AD_Client_ID"); int AD_Org_ID = Env.getContextAsInt(Env.getCtx(), m_WindowNo, "AD_Org_ID"); int C_BPartner_ID = m_C_BPartner_ID; int C_Order_ID = 0; int C_CashLine_ID = 0; int C_Currency_ID = m_C_Currency_ID; // the allocation currency // if (AD_Org_ID == 0) { //ADialog.error(m_WindowNo, this, "Org0NotAllowed", null); throw new AdempiereException("@Org0NotAllowed@"); } // if (log.isLoggable(Level.CONFIG)) log.config("Client=" + AD_Client_ID + ", Org=" + AD_Org_ID + ", BPartner=" + C_BPartner_ID + ", Date=" + dateTrx); // Payment - Loop and add them to paymentList/amountList int pRows = payment.getRowCount(); ArrayList paymentList = new ArrayList(pRows); ArrayList amountList = new ArrayList(pRows); BigDecimal paymentAppliedAmt = Env.ZERO; for (int i = 0; i < pRows; i++) { // Payment line is selected if (((Boolean)payment.getValueAt(i, 0)).booleanValue()) { KeyNamePair pp = (KeyNamePair)payment.getValueAt(i, 2); // Value // Payment variables int C_Payment_ID = pp.getKey(); paymentList.add(Integer.valueOf(C_Payment_ID)); // BigDecimal PaymentAmt = (BigDecimal)payment.getValueAt(i, i_payment); // Applied Payment amountList.add(PaymentAmt); // paymentAppliedAmt = paymentAppliedAmt.add(PaymentAmt); // if (log.isLoggable(Level.FINE)) log.fine("C_Payment_ID=" + C_Payment_ID + " - PaymentAmt=" + PaymentAmt); // + " * " + Multiplier + " = " + PaymentAmtAbs); } } if (log.isLoggable(Level.CONFIG)) log.config("Number of Payments=" + paymentList.size() + " - Total=" + paymentAppliedAmt); // Invoices - Loop and generate allocations int iRows = invoice.getRowCount(); // Create Allocation MAllocationHdr alloc = new MAllocationHdr (Env.getCtx(), true, // manual dateTrx, C_Currency_ID, Env.getContext(Env.getCtx(), Env.AD_USER_NAME), trxName); alloc.setAD_Org_ID(AD_Org_ID); alloc.setC_DocType_ID(m_C_DocType_ID); alloc.setDescription(alloc.getDescriptionForManualAllocation(m_C_BPartner_ID, trxName)); alloc.saveEx(); // For all invoices BigDecimal unmatchedApplied = Env.ZERO; for (int i = 0; i < iRows; i++) { // Invoice line is selected if (((Boolean)invoice.getValueAt(i, 0)).booleanValue()) { KeyNamePair pp = (KeyNamePair)invoice.getValueAt(i, 2); // Value // Invoice variables int C_Invoice_ID = pp.getKey(); BigDecimal AppliedAmt = (BigDecimal)invoice.getValueAt(i, i_applied); // semi-fixed fields (reset after first invoice) BigDecimal DiscountAmt = (BigDecimal)invoice.getValueAt(i, i_discount); BigDecimal WriteOffAmt = (BigDecimal)invoice.getValueAt(i, i_writeOff); // OverUnderAmt needs to be in Allocation Currency BigDecimal OverUnderAmt = ((BigDecimal)invoice.getValueAt(i, i_open)) .subtract(AppliedAmt).subtract(DiscountAmt).subtract(WriteOffAmt); if (log.isLoggable(Level.CONFIG)) log.config("Invoice #" + i + " - AppliedAmt=" + AppliedAmt);// + " -> " + AppliedAbs); // loop through all payments until invoice applied for (int j = 0; j < paymentList.size() && AppliedAmt.signum() != 0; j++) { int C_Payment_ID = ((Integer)paymentList.get(j)).intValue(); BigDecimal PaymentAmt = (BigDecimal)amountList.get(j); if (PaymentAmt.signum() == AppliedAmt.signum()) // only match same sign (otherwise appliedAmt increases) { // and not zero (appliedAmt was checked earlier) if (log.isLoggable(Level.CONFIG)) log.config(".. with payment #" + j + ", Amt=" + PaymentAmt); BigDecimal amount = AppliedAmt; if (amount.abs().compareTo(PaymentAmt.abs()) > 0) // if there's more open on the invoice amount = PaymentAmt; // than left in the payment // Allocation Line MAllocationLine aLine = new MAllocationLine (alloc, amount, DiscountAmt, WriteOffAmt, OverUnderAmt); aLine.setDocInfo(C_BPartner_ID, C_Order_ID, C_Invoice_ID); aLine.setPaymentInfo(C_Payment_ID, C_CashLine_ID); aLine.saveEx(); // Apply Discounts and WriteOff only first time DiscountAmt = Env.ZERO; WriteOffAmt = Env.ZERO; // subtract amount from Payment/Invoice AppliedAmt = AppliedAmt.subtract(amount); PaymentAmt = PaymentAmt.subtract(amount); if (log.isLoggable(Level.FINE)) log.fine("Allocation Amount=" + amount + " - Remaining Applied=" + AppliedAmt + ", Payment=" + PaymentAmt); amountList.set(j, PaymentAmt); // update } // for all applied amounts } // loop through payments for invoice if ( AppliedAmt.signum() == 0 && DiscountAmt.signum() == 0 && WriteOffAmt.signum() == 0) continue; else { // remainder will need to match against other invoices int C_Payment_ID = 0; // Allocation Line MAllocationLine aLine = new MAllocationLine (alloc, AppliedAmt, DiscountAmt, WriteOffAmt, OverUnderAmt); aLine.setDocInfo(C_BPartner_ID, C_Order_ID, C_Invoice_ID); aLine.setPaymentInfo(C_Payment_ID, C_CashLine_ID); aLine.saveEx(); if (log.isLoggable(Level.FINE)) log.fine("Allocation Amount=" + AppliedAmt); unmatchedApplied = unmatchedApplied.add(AppliedAmt); } } // invoice selected } // invoice loop // check for unapplied payment amounts (eg from payment reversals) for (int i = 0; i < paymentList.size(); i++) { BigDecimal payAmt = (BigDecimal) amountList.get(i); if ( payAmt.signum() == 0 ) continue; int C_Payment_ID = ((Integer)paymentList.get(i)).intValue(); if (log.isLoggable(Level.FINE)) log.fine("Payment=" + C_Payment_ID + ", Amount=" + payAmt); // Allocation Line MAllocationLine aLine = new MAllocationLine (alloc, payAmt, Env.ZERO, Env.ZERO, Env.ZERO); aLine.setDocInfo(C_BPartner_ID, 0, 0); aLine.setPaymentInfo(C_Payment_ID, 0); aLine.saveEx(); unmatchedApplied = unmatchedApplied.subtract(payAmt); } // check for charge amount if ( m_C_Charge_ID > 0 && unmatchedApplied.compareTo(Env.ZERO) != 0 ) { BigDecimal chargeAmt = totalDiff; // Allocation Line MAllocationLine aLine = new MAllocationLine (alloc, chargeAmt.negate(), Env.ZERO, Env.ZERO, Env.ZERO); aLine.setC_Charge_ID(m_C_Charge_ID); aLine.setC_BPartner_ID(m_C_BPartner_ID); if (!aLine.save(trxName)) { StringBuilder msg = new StringBuilder("Allocation Line not saved - Charge=").append(m_C_Charge_ID); throw new AdempiereException(msg.toString()); } unmatchedApplied = unmatchedApplied.add(chargeAmt); } if ( unmatchedApplied.signum() != 0 ) throw new AdempiereException("Allocation not balanced -- out by " + unmatchedApplied); // Should start WF if (alloc.get_ID() != 0) { if (!alloc.processIt(DocAction.ACTION_Complete)) throw new AdempiereException("Cannot complete allocation: " + alloc.getProcessMsg()); alloc.saveEx(); } // Test/Set IsPaid for Invoice - requires that allocation is posted for (int i = 0; i < iRows; i++) { // Invoice line is selected if (((Boolean)invoice.getValueAt(i, 0)).booleanValue()) { KeyNamePair pp = (KeyNamePair)invoice.getValueAt(i, 2); // Value // Invoice variables int C_Invoice_ID = pp.getKey(); String sql = "SELECT invoiceOpen(C_Invoice_ID, 0) " + "FROM C_Invoice WHERE C_Invoice_ID=?"; BigDecimal open = DB.getSQLValueBD(trxName, sql, C_Invoice_ID); if (open != null && open.signum() == 0) { sql = "UPDATE C_Invoice SET IsPaid='Y' " + "WHERE C_Invoice_ID=" + C_Invoice_ID; int no = DB.executeUpdate(sql, trxName); if (log.isLoggable(Level.CONFIG)) log.config("Invoice #" + i + " is paid - updated=" + no); } else { if (log.isLoggable(Level.CONFIG)) log.config("Invoice #" + i + " is not paid - " + open); } } } // Test/Set Payment is fully allocated for (int i = 0; i < paymentList.size(); i++) { int C_Payment_ID = ((Integer)paymentList.get(i)).intValue(); MPayment pay = new MPayment (Env.getCtx(), C_Payment_ID, trxName); if (pay.testAllocation()) pay.saveEx(); if (log.isLoggable(Level.CONFIG)) log.config("Payment #" + i + (pay.isAllocated() ? " not" : " is") + " fully allocated"); } paymentList.clear(); amountList.clear(); return alloc; } // saveData /** * * @return C_BPartner_ID */ public int getC_BPartner_ID() { return m_C_BPartner_ID; } /** * * @param C_BPartner_ID */ public void setC_BPartner_ID(int C_BPartner_ID) { this.m_C_BPartner_ID = C_BPartner_ID; } /** * * @return C_Currency_ID */ public int getC_Currency_ID() { return m_C_Currency_ID; } /** * * @param C_Currency_ID */ public void setC_Currency_ID(int C_Currency_ID) { this.m_C_Currency_ID = C_Currency_ID; } /** * * @return C_DocType_ID */ public int getC_DocType_ID() { return m_C_DocType_ID; } /** * * @param C_DocType_ID */ public void setC_DocType_ID(int C_DocType_ID) { this.m_C_DocType_ID = C_DocType_ID; } /** * * @return C_Charge_ID */ public int getC_Charge_ID() { return m_C_Charge_ID; } /** * * @param C_Charge_ID */ public void setC_Charge_ID(int C_Charge_ID) { this.m_C_Charge_ID = C_Charge_ID; } /** * * @return AD_Org_ID */ public int getAD_Org_ID() { return m_AD_Org_ID; } /** * * @param AD_Org_ID */ public void setAD_Org_ID(int AD_Org_ID) { this.m_AD_Org_ID = AD_Org_ID; } /** * * @return number of selected invoice */ public int getSelectedInvoiceCount() { return m_noInvoices; } /** * * @return number of selected payment */ public int getSelectedPaymentCount() { return m_noPayments; } /** * * @return total of invoice applied amount */ public BigDecimal getInvoiceAppliedTotal() { return totalInv; } /** * * @return total of payment applied amount */ public BigDecimal getPaymentAppliedTotal() { return totalPay; } /** * * @return true if all condition is meet to proceed with allocation */ public boolean isOkToAllocate() { return totalDiff.signum() == 0 || getC_Charge_ID() > 0; } /** * * @return difference between invoice and payment applied amount */ public BigDecimal getTotalDifference() { return totalDiff; } /** * calculate difference between invoice and payment applied amount */ public void calculateDifference() { totalDiff = totalPay.subtract(totalInv); } }