/****************************************************************************** * 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.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.PeriodClosedException; import org.compiere.process.DocAction; import org.compiere.process.DocumentEngine; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.Util; /** * Allocation Model. * Allocation Trigger update of C_BPartner balance. * * @author Jorg Janke * @version $Id: MAllocationHdr.java,v 1.3 2006/07/30 00:51:03 jjanke Exp $ * @author victor.perez@e-evolution.com, e-Evolution http://www.e-evolution.com *
  • FR [ 1866214 ] *
  • https://sourceforge.net/p/adempiere/feature-requests/298/ *
  • FR [ 2520591 ] Support multiples calendar for Org *
  • https://sourceforge.net/p/adempiere/feature-requests/631/ *
  • BF [ 2880182 ] Error you can allocate a payment to invoice that was paid *
  • https://sourceforge.net/p/adempiere/bugs/2181/ */ public class MAllocationHdr extends X_C_AllocationHdr implements DocAction { /** * generated serial id */ private static final long serialVersionUID = -7787519874581251920L; /** * Get Allocations of Payment * @param ctx context * @param C_Payment_ID payment * @param trxName transaction * @return allocations of payment */ public static MAllocationHdr[] getOfPayment (Properties ctx, int C_Payment_ID, String trxName) { String sql = "SELECT * FROM C_AllocationHdr h " + "WHERE IsActive='Y'" + " AND EXISTS (SELECT * FROM C_AllocationLine l " + "WHERE h.C_AllocationHdr_ID=l.C_AllocationHdr_ID AND l.C_Payment_ID=?)"; ArrayList list = new ArrayList(); PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql, trxName); pstmt.setInt(1, C_Payment_ID); rs = pstmt.executeQuery(); while (rs.next()) list.add (new MAllocationHdr(ctx, rs, trxName)); } catch (Exception e) { s_log.log(Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } MAllocationHdr[] retValue = new MAllocationHdr[list.size()]; list.toArray(retValue); return retValue; } // getOfPayment /** * Get Allocations of Invoice * @param ctx context * @param C_Invoice_ID payment * @param trxName transaction * @return allocations of payment */ public static MAllocationHdr[] getOfInvoice (Properties ctx, int C_Invoice_ID, String trxName) { String sql = "SELECT * FROM C_AllocationHdr h " + "WHERE IsActive='Y'" + " AND EXISTS (SELECT * FROM C_AllocationLine l " + "WHERE h.C_AllocationHdr_ID=l.C_AllocationHdr_ID AND l.C_Invoice_ID=?)"; ArrayList list = new ArrayList(); PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql, trxName); pstmt.setInt(1, C_Invoice_ID); rs = pstmt.executeQuery(); while (rs.next()) list.add (new MAllocationHdr(ctx, rs, trxName)); } catch (Exception e) { s_log.log(Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } MAllocationHdr[] retValue = new MAllocationHdr[list.size()]; list.toArray(retValue); return retValue; } // getOfInvoice /** * Get Allocations of Cash * @param ctx context * @param C_Cash_ID Cash ID * @param trxName transaction * @return allocations of payment */ public static MAllocationHdr[] getOfCash (Properties ctx, int C_Cash_ID, String trxName) { final String whereClause = "IsActive='Y'" + " AND EXISTS (SELECT 1 FROM C_CashLine cl, C_AllocationLine al " + "where cl.C_Cash_ID=? and al.C_CashLine_ID=cl.C_CashLine_ID " + "and C_AllocationHdr.C_AllocationHdr_ID=al.C_AllocationHdr_ID)"; Query query = MTable.get(ctx, I_C_AllocationHdr.Table_ID) .createQuery(whereClause, trxName); query.setParameters(C_Cash_ID); List list = query.list(); MAllocationHdr[] retValue = new MAllocationHdr[list.size()]; list.toArray(retValue); return retValue; } // getOfCash /** Logger */ private static CLogger s_log = CLogger.getCLogger(MAllocationHdr.class); /** * UUID based Constructor * @param ctx Context * @param C_AllocationHdr_UU UUID key * @param trxName Transaction */ public MAllocationHdr(Properties ctx, String C_AllocationHdr_UU, String trxName) { super(ctx, C_AllocationHdr_UU, trxName); if (Util.isEmpty(C_AllocationHdr_UU)) setInitialDefaults(); } /** * Standard Constructor * @param ctx context * @param C_AllocationHdr_ID id * @param trxName transaction */ public MAllocationHdr (Properties ctx, int C_AllocationHdr_ID, String trxName) { super (ctx, C_AllocationHdr_ID, trxName); if (C_AllocationHdr_ID == 0) setInitialDefaults(); } // MAllocation /** * Set the initial defaults for a new record */ private void setInitialDefaults() { setDateTrx (new Timestamp(System.currentTimeMillis())); setDateAcct (getDateTrx()); setDocAction (DOCACTION_Complete); setDocStatus (DOCSTATUS_Drafted); setApprovalAmt (Env.ZERO); setIsApproved (false); setIsManual (false); // setPosted (false); setProcessed (false); setProcessing(false); setC_DocType_ID(MDocType.getDocType("CMA")); } /** * Mandatory New Constructor * @param ctx context * @param IsManual manual trx * @param DateTrx date (if null today) * @param C_Currency_ID currency * @param description description * @param trxName transaction */ public MAllocationHdr (Properties ctx, boolean IsManual, Timestamp DateTrx, int C_Currency_ID, String description, String trxName) { this (ctx, 0, trxName); setIsManual(IsManual); if (DateTrx != null) { setDateTrx (DateTrx); setDateAcct (DateTrx); } setC_Currency_ID (C_Currency_ID); if (description != null) setDescription(description); } // create Allocation /** * Load Constructor * @param ctx context * @param rs result set * @param trxName transaction */ public MAllocationHdr (Properties ctx, ResultSet rs, String trxName) { super(ctx, rs, trxName); } // MAllocation /** Lines */ private MAllocationLine[] m_lines = null; /** * Get Allocation Lines * @param requery if true requery * @return allocation lines */ public MAllocationLine[] getLines (boolean requery) { if (m_lines != null && m_lines.length != 0 && !requery) { set_TrxName(m_lines, get_TrxName()); return m_lines; } // String sql = "SELECT * FROM C_AllocationLine WHERE C_AllocationHdr_ID=?"; ArrayList list = new ArrayList(); PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement (sql, get_TrxName()); pstmt.setInt (1, getC_AllocationHdr_ID()); rs = pstmt.executeQuery (); while (rs.next ()) { MAllocationLine line = new MAllocationLine(getCtx(), rs, get_TrxName()); line.setParent(this); list.add (line); } } catch (Exception e) { log.log(Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } // m_lines = new MAllocationLine[list.size ()]; list.toArray (m_lines); return m_lines; } // getLines /** * Set Processed * @param processed Processed */ @Override public void setProcessed (boolean processed) { super.setProcessed (processed); if (get_ID() == 0) return; StringBuilder sql = new StringBuilder("UPDATE C_AllocationHdr SET Processed='") .append((processed ? "Y" : "N")) .append("' WHERE C_AllocationHdr_ID=").append(getC_AllocationHdr_ID()); int no = DB.executeUpdate(sql.toString(), get_TrxName()); m_lines = null; if (log.isLoggable(Level.FINE)) log.fine(processed + " - #" + no); } // setProcessed /** * Before Save * @param newRecord * @return save */ @Override protected boolean beforeSave (boolean newRecord) { // Changed from Not to Active if (!newRecord && is_ValueChanged("IsActive") && isActive()) { log.severe ("Cannot Re-Activate deactivated Allocations"); return false; } return true; } // beforeSave private List m_bps_beforeDelete = new ArrayList(); /** * Before Delete. * @return true if acct was deleted */ @Override protected boolean beforeDelete () { String trxName = get_TrxName(); if (trxName == null || trxName.length() == 0) log.warning ("No transaction"); if (isPosted()) { MPeriod.testPeriodOpen(getCtx(), getDateTrx(), MDocType.DOCBASETYPE_PaymentAllocation, getAD_Org_ID()); setPosted(false); MFactAcct.deleteEx (Table_ID, get_ID(), trxName); } // Mark as Inactive setIsActive(false); // updated DB for line delete/process this.saveEx(); // Unlink getLines(true); m_bps_beforeDelete.clear(); for (int i = 0; i < m_lines.length; i++) { MAllocationLine line = m_lines[i]; int C_BPartner_ID = line.getC_BPartner_ID(); if (! m_bps_beforeDelete.contains(C_BPartner_ID)) m_bps_beforeDelete.add(C_BPartner_ID); line.deleteEx(true, trxName); } return true; } // beforeDelete @Override protected boolean afterDelete(boolean success) { if (success) { for (int C_BPartner_ID : m_bps_beforeDelete) { MBPartner bpartner = new MBPartner(Env.getCtx(), C_BPartner_ID, get_TrxName()); bpartner.setTotalOpenBalance(); bpartner.saveEx(); } } m_bps_beforeDelete.clear(); return super.afterDelete(success); } /** * After Save * @param newRecord * @param success * @return success */ @Override protected boolean afterSave (boolean newRecord, boolean success) { return success; } // afterSave /** * 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()); } // processIt /** 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(toString()); setProcessing(false); return true; } // unlockIt /** * Invalidate Document * @return true if success */ @Override public boolean invalidateIt() { if (log.isLoggable(Level.INFO)) log.info(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; // Std Period open? MPeriod.testPeriodOpen(getCtx(), getDateAcct(), MDocType.DOCBASETYPE_PaymentAllocation, getAD_Org_ID()); getLines(true); if (m_lines.length == 0) { m_processMsg = "@NoLines@"; return DocAction.STATUS_Invalid; } // Stop the Document Workflow if invoice to allocate is as paid if (!isReversal()) { for (MAllocationLine line :m_lines) { if (line.getC_Invoice_ID() != 0) { StringBuilder whereClause = new StringBuilder(I_C_Invoice.COLUMNNAME_C_Invoice_ID).append("=? AND ") .append(I_C_Invoice.COLUMNNAME_IsPaid).append("=? AND ") .append(I_C_Invoice.COLUMNNAME_DocStatus).append(" NOT IN (?,?)"); boolean InvoiceIsPaid = new Query(getCtx(), I_C_Invoice.Table_Name, whereClause.toString(), get_TrxName()) .setClient_ID() .setParameters(line.getC_Invoice_ID(), "Y", X_C_Invoice.DOCSTATUS_Voided, X_C_Invoice.DOCSTATUS_Reversed) .match(); if (InvoiceIsPaid && line.getAmount().signum() > 0) throw new AdempiereException("@ValidationError@ @C_Invoice_ID@ @IsPaid@"); } } } // Add up Amounts & validate BigDecimal approval = Env.ZERO; for (int i = 0; i < m_lines.length; i++) { MAllocationLine line = m_lines[i]; approval = approval.add(line.getWriteOffAmt()).add(line.getDiscountAmt()); // Make sure there is BP if (line.getC_BPartner_ID() == 0) { m_processMsg = "No Business Partner"; return DocAction.STATUS_Invalid; } // IDEMPIERE-1850 - validate date against related docs if (line.getC_Invoice_ID() > 0) { if (line.getC_Invoice().getDateAcct().after(getDateAcct())) { m_processMsg = "Wrong allocation date"; return DocAction.STATUS_Invalid; } } if (line.getC_Payment_ID() > 0) { if (line.getC_Payment().getDateAcct().after(getDateAcct())) { m_processMsg = "Wrong allocation date"; return DocAction.STATUS_Invalid; } } } setApprovalAmt(approval); // m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); if (m_processMsg != null) return DocAction.STATUS_Invalid; m_justPrepared = true; if (!DOCACTION_Complete.equals(getDocAction())) setDocAction(DOCACTION_Complete); return DocAction.STATUS_InProgress; } // prepareIt /** * 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; } 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()); // Link getLines(false); if(!updateBP()) return DocAction.STATUS_Invalid; for (int i = 0; i < m_lines.length; i++) { MAllocationLine line = m_lines[i]; line.processIt(isReversal()); } // User Validation String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); if (valid != null) { m_processMsg = valid; return DocAction.STATUS_Invalid; } setProcessed(true); setDocAction(DOCACTION_Close); return DocAction.STATUS_Completed; } // completeIt /** * Void Document. * @return true if success */ @Override public boolean voidIt() { if (log.isLoggable(Level.INFO)) log.info(toString()); boolean retValue = false; 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 MAllocationLine[] lines = getLines(true); if(!updateBP()) return false; for (int i = 0; i < lines.length; i++) { MAllocationLine line = lines[i]; line.setAmount(Env.ZERO); line.setDiscountAmt(Env.ZERO); line.setWriteOffAmt(Env.ZERO); line.setOverUnderAmt(Env.ZERO); line.saveEx(); // Unlink invoices line.processIt(true); } addDescription(Msg.getMsg(getCtx(), "Voided")); retValue = true; } else { boolean accrual = false; try { MPeriod.testPeriodOpen(getCtx(), getDateTrx(), MPeriodControl.DOCBASETYPE_PaymentAllocation, 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 retValue; } // 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; 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 (using original DateAcct) * @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; boolean retValue = reverseIt(false); // After reverseCorrect m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); if (m_processMsg != null) return false; setDocAction(DOCACTION_None); return retValue; } // reverseCorrectionIt /** * Reverse Accrual (using current date as DateAcct) * @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; boolean retValue = reverseIt(true); // After reverseAccrual m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); if (m_processMsg != null) return false; setDocAction(DOCACTION_None); return retValue; } // 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 /** * String Representation * @return info */ @Override public String toString () { StringBuilder sb = new StringBuilder ("MAllocationHdr["); sb.append(get_ID()).append("-").append(getSummary()).append ("]"); return sb.toString (); } // toString /** * Get Document Info * @return document info (untranslated) */ @Override public String getDocumentInfo() { StringBuilder msgreturn = new StringBuilder().append(Msg.getElement(getCtx(), "C_AllocationHdr_ID")).append(" ").append(getDocumentNo()); return msgreturn.toString(); } // getDocumentInfo /** * Create PDF * @return File or null */ @Override public File createPDF () { try { StringBuilder msgctf = new StringBuilder().append(get_TableName()).append(get_ID()).append("_"); File temp = File.createTempFile(msgctf.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 not implemented, always return null */ public File createPDF (File file) { return null; } // createPDF /** * Get Summary * @return Summary of Document */ @Override public String getSummary() { StringBuilder sb = new StringBuilder(); sb.append(getDocumentNo()); // : Total Lines = 123.00 (#1) sb.append(": ") .append(Msg.translate(getCtx(),"ApprovalAmt")).append("=").append(getApprovalAmt()) .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 getCreatedBy(); } // getDoc_User_ID /** * Add to Description * @param description text */ public void addDescription (String description) { String desc = getDescription(); if (desc == null) setDescription(description); else setDescription(desc + " | " + description); } // addDescription /** * Reverse Allocation. * Period needs to be open. * @param accrual true for reverse accrual (current date), false for reverse correct (original accounting date) * @return true if reversed */ private boolean reverseIt(boolean accrual) { if (!isActive() || getDocStatus().equals(DOCSTATUS_Voided) // Goodwill.co.id || getDocStatus().equals(DOCSTATUS_Reversed)) { // Goodwill: don't throw exception here // BF: Reverse is not allowed at Payment void when Allocation is already reversed at Invoice void //throw new IllegalStateException("Allocation already reversed (not active)"); log.warning("Allocation already reversed (not active)"); return true; } Timestamp reversalDate = accrual ? Env.getContextAsDate(getCtx(), Env.DATE) : getDateAcct(); if (reversalDate == null) { reversalDate = new Timestamp(System.currentTimeMillis()); } // Can we delete posting MPeriod.testPeriodOpen(getCtx(), reversalDate, MPeriodControl.DOCBASETYPE_PaymentAllocation, getAD_Org_ID()); if (accrual) { // Deep Copy MAllocationHdr reversal = copyFrom (this, reversalDate, reversalDate, get_TrxName()); if (reversal == null) { m_processMsg = "Could not create Payment Allocation Reversal"; return false; } reversal.setReversal_ID(getC_AllocationHdr_ID()); // Reverse Line Amt MAllocationLine[] rLines = reversal.getLines(false); for (MAllocationLine rLine : rLines) { rLine.setAmount(rLine.getAmount().negate()); rLine.setDiscountAmt(rLine.getDiscountAmt().negate()); rLine.setWriteOffAmt(rLine.getWriteOffAmt().negate()); rLine.setOverUnderAmt(rLine.getOverUnderAmt().negate()); if (!rLine.save(get_TrxName())) { m_processMsg = "Could not correct Payment Allocation Reversal Line"; return false; } } reversal.setReversal(true); reversal.setDocumentNo(getDocumentNo()+"^"); reversal.addDescription("{->" + getDocumentNo() + ")"); // if (!DocumentEngine.processIt(reversal, DocAction.ACTION_Complete)) { m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg(); return false; } DocumentEngine.processIt(reversal, DocAction.ACTION_Close); reversal.setProcessing (false); reversal.setDocStatus(DOCSTATUS_Reversed); reversal.setDocAction(DOCACTION_None); reversal.saveEx(); m_processMsg = reversal.getDocumentNo(); addDescription("(" + reversal.getDocumentNo() + "<-)"); } else { // Set Inactive setIsActive (false); if ( !isPosted() ) setPosted(true); setDocumentNo(getDocumentNo()+"^"); setDocStatus(DOCSTATUS_Reversed); // for direct calls if (!save() || isActive()) throw new IllegalStateException("Cannot de-activate allocation"); // Delete Posting MFactAcct.deleteEx(MAllocationHdr.Table_ID, getC_AllocationHdr_ID(), get_TrxName()); // Unlink Invoices getLines(true); if(!updateBP()) return false; for (int i = 0; i < m_lines.length; i++) { MAllocationLine line = m_lines[i]; line.setIsActive(false); line.setAmount(Env.ZERO); line.setDiscountAmt(Env.ZERO); line.setWriteOffAmt(Env.ZERO); line.setOverUnderAmt(Env.ZERO); line.saveEx(); line.processIt(true); // reverse } addDescription(Msg.getMsg(getCtx(), "Voided")); } setProcessed(true); setDocStatus(DOCSTATUS_Reversed); // may come from void setDocAction(DOCACTION_None); return true; } // reverse /** * Update open balance of BP * @return true if updated successfully */ private boolean updateBP() { List bps = new ArrayList(); getLines(false); for (MAllocationLine line : m_lines) { int C_BPartner_ID = line.getC_BPartner_ID(); if (! bps.contains(C_BPartner_ID)) { bps.add(C_BPartner_ID); MBPartner bpartner = new MBPartner(Env.getCtx(), C_BPartner_ID, get_TrxName()); bpartner.setTotalOpenBalance(); bpartner.saveEx(); } } // for all lines return true; } // updateBP /** * Document Status is Complete, Closed or Reversed * @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 /** * Create new Allocation by copying * @param from source allocation to copy from * @param dateAcct date of the document accounting date * @param dateTrx date of the document transaction. * @param trxName * @return Allocation */ public static MAllocationHdr copyFrom (MAllocationHdr from, Timestamp dateAcct, Timestamp dateTrx, String trxName) { MAllocationHdr to = new MAllocationHdr (from.getCtx(), 0, trxName); PO.copyValues (from, to, from.getAD_Client_ID(), from.getAD_Org_ID()); to.set_ValueNoCheck ("DocumentNo", null); // to.setDocStatus (DOCSTATUS_Drafted); // Draft to.setDocAction(DOCACTION_Complete); // to.setDateTrx (dateAcct); to.setDateAcct (dateTrx); to.setIsManual(false); // to.setIsApproved (false); // to.setPosted (false); to.setProcessed (false); to.saveEx(); // Lines if (to.copyLinesFrom(from) == 0) throw new AdempiereException("Could not create Allocation Lines"); return to; } // copyFrom /** * Copy Lines From other Allocation. * @param otherAllocation other allocation to copy from * @return number of lines copied */ public int copyLinesFrom (MAllocationHdr otherAllocation) { if (isProcessed() || isPosted() || (otherAllocation == null)) return 0; MAllocationLine[] fromLines = otherAllocation.getLines(false); int count = 0; for (MAllocationLine fromLine : fromLines) { MAllocationLine line = new MAllocationLine (getCtx(), 0, get_TrxName()); PO.copyValues (fromLine, line, fromLine.getAD_Client_ID(), fromLine.getAD_Org_ID()); line.setC_AllocationHdr_ID(getC_AllocationHdr_ID()); line.setParent(this); line.set_ValueNoCheck ("C_AllocationLine_ID", I_ZERO); // new if (line.getC_Payment_ID() != 0) { MPayment payment = new MPayment(getCtx(), line.getC_Payment_ID(), get_TrxName()); if (DOCSTATUS_Reversed.equals(payment.getDocStatus())) { MPayment reversal = (MPayment) payment.getReversal(); if (reversal != null) { line.setPaymentInfo(reversal.getC_Payment_ID(), 0); } } } line.saveEx(); count++; } if (fromLines.length != count) log.log(Level.WARNING, "Line difference - From=" + fromLines.length + " <> Saved=" + count); return count; } // copyLinesFrom // Goodwill.co.id /** Reversal Flag */ private boolean m_reversal = false; /** * Set Reversal * @param reversal reversal */ private void setReversal(boolean reversal) { m_reversal = reversal; } // setReversal /** * Is Reversal * @return true if it is a reversal document */ private boolean isReversal() { return m_reversal; } // isReversal /** * @param bpartnerID * @param trxName * @return Returns a description parsing the bpartner in allocation header and then the allocation document itself */ public String getDescriptionForManualAllocation(int bpartnerID, String trxName) { String sysconfig_desc = MSysConfig.getValue(MSysConfig.ALLOCATION_DESCRIPTION, "@#AD_User_Name@", getAD_Client_ID()); String description = ""; if (sysconfig_desc.contains("@")) { description = Env.parseVariable(sysconfig_desc, new MBPartner(getCtx(), bpartnerID, null), trxName, true); description = Env.parseVariable(description, this, trxName, true); description = Msg.parseTranslation(getCtx(), description); } else description = Env.getContext(getCtx(), Env.AD_USER_NAME); // just to be sure return description; } } // MAllocation