From 558641d959196b6b8e233b54ae0bcb686682a44a Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Tue, 9 Apr 2013 11:12:06 +0800 Subject: [PATCH] IDEMPIERE-378 Implement Reverse Accrual. #1. Make consistent the implementation of VoidIt for allocation, invoice, inout, inventory, payment and movement ( Based on patch from Armen Rizal ). #2. Remove the use of conditional calculation for open balance - hard to maintain and source of intermittent bug. #3. More efficient update of open balance for allocation. #4. Lock bp record before open balance update to prevent issue with stale data. --- .../org/compiere/model/MAllocationHdr.java | 409 +++++++++++++++--- .../src/org/compiere/model/MBPartner.java | 30 +- .../src/org/compiere/model/MInOut.java | 27 +- .../src/org/compiere/model/MInventory.java | 27 +- .../src/org/compiere/model/MInvoice.java | 34 +- .../src/org/compiere/model/MMovement.java | 27 +- .../src/org/compiere/model/MPayment.java | 38 +- 7 files changed, 473 insertions(+), 119 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/MAllocationHdr.java b/org.adempiere.base/src/org/compiere/model/MAllocationHdr.java index 9fc4ad8ac6..17c17b148e 100644 --- a/org.adempiere.base/src/org/compiere/model/MAllocationHdr.java +++ b/org.adempiere.base/src/org/compiere/model/MAllocationHdr.java @@ -22,8 +22,6 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Timestamp; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.logging.Level; @@ -58,7 +56,9 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction */ private static final long serialVersionUID = 8726957992840702609L; - + /** Tolerance Gain and Loss */ + private static final BigDecimal TOLERANCE = new BigDecimal (0.02); + /** * Get Allocations of Payment * @param ctx context @@ -321,19 +321,17 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction } // Mark as Inactive setIsActive(false); // updated DB for line delete/process - String sql = "UPDATE C_AllocationHdr SET IsActive='N' WHERE C_AllocationHdr_ID=?"; - DB.executeUpdate(sql, getC_AllocationHdr_ID(), trxName); // Unlink getLines(true); - HashSet bps = new HashSet(); + if (!updateBP(true)) + return false; + for (int i = 0; i < m_lines.length; i++) { MAllocationLine line = m_lines[i]; - bps.add(new Integer(line.getC_BPartner_ID())); line.deleteEx(true, trxName); } - updateBP(bps); return true; } // beforeDelete @@ -400,7 +398,7 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction // Std Period open? MPeriod.testPeriodOpen(getCtx(), getDateAcct(), MDocType.DOCBASETYPE_PaymentAllocation, getAD_Org_ID()); - getLines(false); + getLines(true); if (m_lines.length == 0) { m_processMsg = "@NoLines@"; @@ -497,13 +495,14 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction // Link getLines(false); - HashSet bps = new HashSet(); + if(!updateBP(isReversal())) + return DocAction.STATUS_Invalid; + for (int i = 0; i < m_lines.length; i++) { MAllocationLine line = m_lines[i]; - bps.add(new Integer(line.processIt(false))); // not reverse - } - updateBP(bps); + line.processIt(isReversal()); + } // User Validation String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); @@ -526,29 +525,73 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction public boolean voidIt() { if (log.isLoggable(Level.INFO)) log.info(toString()); - - // Before Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); - if (m_processMsg != null) - return false; - - boolean accrual = false; - try - { - MPeriod.testPeriodOpen(getCtx(), getDateTrx(), MPeriodControl.DOCBASETYPE_PaymentAllocation, getAD_Org_ID()); - } - catch (PeriodClosedException e) - { - accrual = true; - } - boolean retValue = reverseIt(accrual); + 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(true)) + 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; @@ -752,8 +795,17 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction */ private boolean reverseIt(boolean accrual) { - if (!isActive()) - throw new IllegalStateException("Allocation already reversed (not active)"); + 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(), "#Date") : getDateAcct(); if (reversalDate == null) { @@ -787,6 +839,8 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction return false; } } + reversal.setReversal(true); + reversal.setDocumentNo(getDocumentNo()+"^"); reversal.addDescription("{->" + getDocumentNo() + ")"); // if (!DocumentEngine.processIt(reversal, DocAction.ACTION_Complete)) @@ -820,42 +874,261 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction // Unlink Invoices getLines(true); - HashSet bps = new HashSet(); + if(!updateBP(true)) + 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(); - bps.add(new Integer(line.processIt(true))); // reverse - } - updateBP(bps); + 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's - * @param bps list of business partners - */ - private void updateBP(HashSet bps) + private boolean updateBP(boolean reverse) { - if (log.isLoggable(Level.INFO)) log.info("#" + bps.size()); - Iterator it = bps.iterator(); - while (it.hasNext()) - { - int C_BPartner_ID = it.next(); - MBPartner bp = new MBPartner(getCtx(), C_BPartner_ID, get_TrxName()); - bp.setTotalOpenBalance(); // recalculates from scratch - // bp.setSOCreditStatus(); // called automatically - if (bp.save()) { - if (log.isLoggable(Level.FINE)) log.fine(bp.toString()); - } else { - log.log(Level.SEVERE, "BP not updated - " + bp); - } - } - } // updateBP + + getLines(false); + for (MAllocationLine line : m_lines) { + int C_Payment_ID = line.getC_Payment_ID(); + int C_BPartner_ID = line.getC_BPartner_ID(); + int M_Invoice_ID = line.getC_Invoice_ID(); + if ((C_BPartner_ID == 0) || ((M_Invoice_ID == 0) && (C_Payment_ID == 0))) + continue; + + boolean isSOTrxInvoice = false; + MInvoice invoice = M_Invoice_ID > 0 ? new MInvoice (getCtx(), M_Invoice_ID, get_TrxName()) : null; + if (M_Invoice_ID > 0) + isSOTrxInvoice = invoice.isSOTrx(); + + MBPartner bpartner = new MBPartner (getCtx(), line.getC_BPartner_ID(), get_TrxName()); + DB.getDatabase().forUpdate(bpartner, 0); + + BigDecimal allocAmt = line.getAmount().add(line.getDiscountAmt()).add(line.getWriteOffAmt()); + BigDecimal openBalanceDiff = Env.ZERO; + MClient client = MClient.get(getCtx(), getAD_Client_ID()); + + boolean paymentProcessed = false; + boolean paymentIsReceipt = false; + + // Retrieve payment information + if (C_Payment_ID > 0) + { + MPayment payment = null; + int convTypeID = 0; + Timestamp paymentDate = null; + + payment = new MPayment (getCtx(), C_Payment_ID, get_TrxName()); + convTypeID = payment.getC_ConversionType_ID(); + paymentDate = payment.getDateAcct(); + paymentProcessed = payment.isProcessed(); + paymentIsReceipt = payment.isReceipt(); + + // Adjust open amount with allocated amount. + if (paymentProcessed) + { + if (invoice != null) + { + // If payment is already processed, only adjust open balance by discount and write off amounts. + BigDecimal amt = MConversionRate.convertBase(getCtx(), line.getWriteOffAmt().add(line.getDiscountAmt()), + getC_Currency_ID(), paymentDate, convTypeID, getAD_Client_ID(), getAD_Org_ID()); + if (amt == null) + { + m_processMsg = "Could not convert allocation C_Currency_ID=" + getC_Currency_ID() + + " to base C_Currency_ID=" + MClient.get(getCtx()).getC_Currency_ID() + ", C_ConversionType_ID=" + convTypeID + + ", conversion date= " + paymentDate; + return false; + } + openBalanceDiff = openBalanceDiff.add(amt); + } + else + { + // Allocating payment to payment. + BigDecimal amt = MConversionRate.convertBase(getCtx(), allocAmt, + getC_Currency_ID(), paymentDate, convTypeID, getAD_Client_ID(), getAD_Org_ID()); + if (amt == null) + { + m_processMsg = "Could not convert allocation C_Currency_ID=" + getC_Currency_ID() + + " to base C_Currency_ID=" + MClient.get(getCtx()).getC_Currency_ID() + ", C_ConversionType_ID=" + convTypeID + + ", conversion date= " + paymentDate; + return false; + } + openBalanceDiff = openBalanceDiff.add(amt); + } + } else { + // If payment has not been processed, adjust open balance by entire allocated amount. + BigDecimal allocAmtBase = MConversionRate.convertBase(getCtx(), allocAmt, + getC_Currency_ID(), getDateAcct(), convTypeID, getAD_Client_ID(), getAD_Org_ID()); + if (allocAmtBase == null) + { + m_processMsg = "Could not convert allocation C_Currency_ID=" + getC_Currency_ID() + + " to base C_Currency_ID=" + MClient.get(getCtx()).getC_Currency_ID() + ", C_ConversionType_ID=" + convTypeID + + ", conversion date= " + getDateAcct(); + return false; + } + + openBalanceDiff = openBalanceDiff.add(allocAmtBase); + } + } + else if (invoice != null) + { + // adjust open balance by discount and write off amounts. + BigDecimal amt = MConversionRate.convertBase(getCtx(), line.getWriteOffAmt().add(line.getDiscountAmt()), + getC_Currency_ID(), invoice.getDateAcct(), invoice.getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); + if (amt == null) + { + m_processMsg = "Could not convert allocation C_Currency_ID=" + getC_Currency_ID() + + " to base C_Currency_ID=" + MClient.get(getCtx()).getC_Currency_ID() + ", C_ConversionType_ID=" + invoice.getC_ConversionType_ID() + + ", conversion date= " + invoice.getDateAcct(); + return false; + } + openBalanceDiff = openBalanceDiff.add(amt); + } + + // Adjust open amount for currency gain/loss + if ((invoice != null) && + ((getC_Currency_ID() != client.getC_Currency_ID()) || + (getC_Currency_ID() != invoice.getC_Currency_ID()))) + { + if (getC_Currency_ID() != invoice.getC_Currency_ID()) + { + allocAmt = MConversionRate.convert(getCtx(), allocAmt, + getC_Currency_ID(), invoice.getC_Currency_ID(), getDateAcct(), invoice.getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); + if (allocAmt == null) + { + m_processMsg = "Could not convert allocation C_Currency_ID=" + getC_Currency_ID() + + " to invoice C_Currency_ID=" + invoice.getC_Currency_ID() + ", C_ConversionType_ID=" + invoice.getC_ConversionType_ID() + + ", conversion date= " + getDateAcct(); + return false; + } + } + BigDecimal invAmtAccted = MConversionRate.convertBase(getCtx(), invoice.getGrandTotal(), + invoice.getC_Currency_ID(), invoice.getDateAcct(), invoice.getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); + if (invAmtAccted == null) + { + m_processMsg = "Could not convert invoice C_Currency_ID=" + getC_Currency_ID() + + " to base C_Currency_ID=" + invoice.getC_Currency_ID() + ", C_ConversionType_ID=" + invoice.getC_ConversionType_ID() + + ", date= " + invoice.getDateAcct(); + return false; + } + + BigDecimal allocAmtAccted = MConversionRate.convertBase(getCtx(), allocAmt, + invoice.getC_Currency_ID(), getDateAcct(), invoice.getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); + if (allocAmtAccted == null) + { + m_processMsg = "Could not convert invoice C_Currency_ID=" + invoice.getC_Currency_ID() + + " to base C_Currency_ID=" + MClient.get(getCtx()).getC_Currency_ID() + ", C_ConversionType_ID=" + invoice.getC_ConversionType_ID() + + ", conversion date= " + getDateAcct(); + return false; + } + + if (allocAmt.compareTo(invoice.getGrandTotal()) == 0) + { + openBalanceDiff = openBalanceDiff.add(invAmtAccted).subtract(allocAmtAccted); + } + else + { + // allocation as a percentage of the invoice + double multiplier = allocAmt.doubleValue() / invoice.getGrandTotal().doubleValue(); + // Reduce Orig Invoice Accounted + invAmtAccted = invAmtAccted.multiply(new BigDecimal(multiplier)); + // Difference based on percentage of Orig Invoice + openBalanceDiff = openBalanceDiff.add(invAmtAccted).subtract(allocAmtAccted); // gain is negative + // ignore Tolerance + if (openBalanceDiff.abs().compareTo(TOLERANCE) < 0) + openBalanceDiff = Env.ZERO; + // Round + int precision = MCurrency.getStdPrecision(getCtx(), client.getC_Currency_ID()); + if (openBalanceDiff.scale() > precision) + openBalanceDiff = openBalanceDiff.setScale(precision, BigDecimal.ROUND_HALF_UP); + } + } + + // Total Balance + BigDecimal newBalance = bpartner.getTotalOpenBalance(); + if (newBalance == null) + newBalance = Env.ZERO; + + BigDecimal originalBalance = new BigDecimal(newBalance.toString()); + + if (openBalanceDiff.signum() != 0) + { + if (reverse) + newBalance = newBalance.add(openBalanceDiff); + else + newBalance = newBalance.subtract(openBalanceDiff); + } + + // Update BP Credit Used only for Customer Invoices and for payment-to-payment allocations. + BigDecimal newCreditAmt = Env.ZERO; + if (isSOTrxInvoice || (invoice == null && paymentIsReceipt && paymentProcessed)) + { + if (invoice == null) + openBalanceDiff = openBalanceDiff.negate(); + + newCreditAmt = bpartner.getSO_CreditUsed(); + + if(reverse) + { + if (newCreditAmt == null) + newCreditAmt = openBalanceDiff; + else + newCreditAmt = newCreditAmt.add(openBalanceDiff); + } + else + { + if (newCreditAmt == null) + newCreditAmt = openBalanceDiff.negate(); + else + newCreditAmt = newCreditAmt.subtract(openBalanceDiff); + } + + if (log.isLoggable(Level.FINE)) + { + log.fine("TotalOpenBalance=" + bpartner.getTotalOpenBalance() + "(" + openBalanceDiff + + ", Credit=" + bpartner.getSO_CreditUsed() + "->" + newCreditAmt + + ", Balance=" + bpartner.getTotalOpenBalance() + " -> " + newBalance); + } + bpartner.setSO_CreditUsed(newCreditAmt); + } + else + { + if (log.isLoggable(Level.FINE)) + { + log.fine("TotalOpenBalance=" + bpartner.getTotalOpenBalance() + "(" + openBalanceDiff + + ", Balance=" + bpartner.getTotalOpenBalance() + " -> " + newBalance); + } + } + + if (newBalance.compareTo(originalBalance) != 0) + bpartner.setTotalOpenBalance(newBalance); + + bpartner.setSOCreditStatus(); + if (!bpartner.save(get_TrxName())) + { + m_processMsg = "Could not update Business Partner"; + return false; + } + + } // for all lines + + return true; + } // updateBP + /** * Document Status is Complete or Closed * @return true if CO, CL or RE @@ -938,8 +1211,30 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction 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 reversal + */ + private boolean isReversal() + { + return m_reversal; + } // isReversal } // MAllocation diff --git a/org.adempiere.base/src/org/compiere/model/MBPartner.java b/org.adempiere.base/src/org/compiere/model/MBPartner.java index def48ff801..90975de351 100644 --- a/org.adempiere.base/src/org/compiere/model/MBPartner.java +++ b/org.adempiere.base/src/org/compiere/model/MBPartner.java @@ -289,8 +289,6 @@ public class MBPartner extends X_C_BPartner private Integer m_primaryC_BPartner_Location_ID = null; /** Prim User */ private Integer m_primaryAD_User_ID = null; - /** Credit Limit recently calculated */ - private boolean m_TotalOpenBalanceSet = false; /** BP Group */ private MBPGroup m_group = null; @@ -692,7 +690,6 @@ public class MBPartner extends X_C_BPartner pstmt = null; } // - m_TotalOpenBalanceSet = true; if (SO_CreditUsed != null) super.setSO_CreditUsed (SO_CreditUsed); if (TotalOpenBalance != null) @@ -736,19 +733,6 @@ public class MBPartner extends X_C_BPartner super.setActualLifeTimeValue (ActualLifeTimeValue); } // setActualLifeTimeValue - /** - * Get Total Open Balance - * @param calculate if null calculate it - * @return Open Balance - */ - public BigDecimal getTotalOpenBalance (boolean calculate) - { - if (getTotalOpenBalance().signum() == 0 && calculate) - setTotalOpenBalance(); - return super.getTotalOpenBalance (); - } // getTotalOpenBalance - - /** * Set Credit Status */ @@ -762,7 +746,7 @@ public class MBPartner extends X_C_BPartner return; // Above Credit Limit - if (creditLimit.compareTo(getTotalOpenBalance(!m_TotalOpenBalanceSet)) < 0) + if (creditLimit.compareTo(getTotalOpenBalance()) < 0) setSOCreditStatus(SOCREDITSTATUS_CreditHold); else { @@ -795,7 +779,7 @@ public class MBPartner extends X_C_BPartner // Above (reduced) Credit Limit creditLimit = creditLimit.subtract(additionalAmt); - if (creditLimit.compareTo(getTotalOpenBalance(!m_TotalOpenBalanceSet)) < 0) + if (creditLimit.compareTo(getTotalOpenBalance()) < 0) return SOCREDITSTATUS_CreditHold; // Above Watch Limit @@ -827,16 +811,6 @@ public class MBPartner extends X_C_BPartner || SOCREDITSTATUS_CreditHold.equals(status); } // isCreditStopHold - /** - * Set Total Open Balance - * @param TotalOpenBalance - */ - public void setTotalOpenBalance (BigDecimal TotalOpenBalance) - { - m_TotalOpenBalanceSet = false; - super.setTotalOpenBalance (TotalOpenBalance); - } // setTotalOpenBalance - /** * Get BP Group * @return group diff --git a/org.adempiere.base/src/org/compiere/model/MInOut.java b/org.adempiere.base/src/org/compiere/model/MInOut.java index f22bf66490..8d87f8e52d 100644 --- a/org.adempiere.base/src/org/compiere/model/MInOut.java +++ b/org.adempiere.base/src/org/compiere/model/MInOut.java @@ -26,6 +26,7 @@ import java.util.Properties; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.PeriodClosedException; import org.compiere.print.MPrintFormat; import org.compiere.print.ReportEngine; import org.compiere.process.DocAction; @@ -2020,11 +2021,7 @@ public class MInOut extends X_M_InOut implements DocAction */ public boolean voidIt() { - if (log.isLoggable(Level.INFO)) log.info(toString()); - // Before Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); - if (m_processMsg != null) - return false; + if (log.isLoggable(Level.INFO)) log.info(toString()); if (DOCSTATUS_Closed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus()) @@ -2041,6 +2038,11 @@ public class MInOut extends X_M_InOut implements DocAction || 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 MInOutLine[] lines = getLines(false); for (int i = 0; i < lines.length; i++) @@ -2063,7 +2065,20 @@ public class MInOut extends X_M_InOut implements DocAction } else { - return reverseCorrectIt(); + 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 diff --git a/org.adempiere.base/src/org/compiere/model/MInventory.java b/org.adempiere.base/src/org/compiere/model/MInventory.java index 1627cff8a6..ad22cc4cff 100644 --- a/org.adempiere.base/src/org/compiere/model/MInventory.java +++ b/org.adempiere.base/src/org/compiere/model/MInventory.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.exceptions.PeriodClosedException; import org.compiere.process.DocAction; import org.compiere.process.DocumentEngine; import org.compiere.util.CCache; @@ -665,11 +666,7 @@ public class MInventory extends X_M_Inventory implements DocAction public boolean voidIt() { if (log.isLoggable(Level.INFO)) log.info(toString()); - // Before Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); - if (m_processMsg != null) - return false; - + if (DOCSTATUS_Closed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus()) || DOCSTATUS_Voided.equals(getDocStatus())) @@ -685,6 +682,11 @@ public class MInventory extends X_M_Inventory implements DocAction || 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 MInventoryLine[] lines = getLines(false); for (int i = 0; i < lines.length; i++) @@ -705,7 +707,20 @@ public class MInventory extends X_M_Inventory implements DocAction } else { - return reverseCorrectIt(); + boolean accrual = false; + try + { + MPeriod.testPeriodOpen(getCtx(), getMovementDate(), getC_DocType_ID(), getAD_Org_ID()); + } + catch (PeriodClosedException e) + { + accrual = true; + } + + if (accrual) + return reverseAccrualIt(); + else + return reverseCorrectIt(); } // After Void diff --git a/org.adempiere.base/src/org/compiere/model/MInvoice.java b/org.adempiere.base/src/org/compiere/model/MInvoice.java index 5afa5c3865..8cd679090e 100644 --- a/org.adempiere.base/src/org/compiere/model/MInvoice.java +++ b/org.adempiere.base/src/org/compiere/model/MInvoice.java @@ -30,6 +30,7 @@ import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.BPartnerNoAddressException; import org.adempiere.exceptions.DBException; +import org.adempiere.exceptions.PeriodClosedException; import org.compiere.print.MPrintFormat; import org.compiere.print.ReportEngine; import org.compiere.process.DocAction; @@ -1863,6 +1864,7 @@ public class MInvoice extends X_C_Invoice implements DocAction // Update BP Statistics MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + DB.getDatabase().forUpdate(bp, 0); // Update total revenue and balance / credit limit (reversed on AllocationLine.processIt) BigDecimal invAmt = MConversionRate.convertBase(getCtx(), getGrandTotal(true), // CM adjusted getC_Currency_ID(), getDateAcct(), getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); @@ -1873,7 +1875,7 @@ public class MInvoice extends X_C_Invoice implements DocAction return DocAction.STATUS_Invalid; } // Total Balance - BigDecimal newBalance = bp.getTotalOpenBalance(false); + BigDecimal newBalance = bp.getTotalOpenBalance(); if (newBalance == null) newBalance = Env.ZERO; if (isSOTrx()) @@ -1896,7 +1898,7 @@ public class MInvoice extends X_C_Invoice implements DocAction if (log.isLoggable(Level.FINE)) log.fine("GrandTotal=" + getGrandTotal(true) + "(" + invAmt + ") BP Life=" + bp.getActualLifeTimeValue() + "->" + newLifeAmt + ", Credit=" + bp.getSO_CreditUsed() + "->" + newCreditAmt - + ", Balance=" + bp.getTotalOpenBalance(false) + " -> " + newBalance); + + ", Balance=" + bp.getTotalOpenBalance() + " -> " + newBalance); bp.setActualLifeTimeValue(newLifeAmt); bp.setSO_CreditUsed(newCreditAmt); } // SO @@ -1904,7 +1906,7 @@ public class MInvoice extends X_C_Invoice implements DocAction { newBalance = newBalance.subtract(invAmt); if (log.isLoggable(Level.FINE)) log.fine("GrandTotal=" + getGrandTotal(true) + "(" + invAmt - + ") Balance=" + bp.getTotalOpenBalance(false) + " -> " + newBalance); + + ") Balance=" + bp.getTotalOpenBalance() + " -> " + newBalance); } bp.setTotalOpenBalance(newBalance); bp.setSOCreditStatus(); @@ -2169,11 +2171,7 @@ public class MInvoice extends X_C_Invoice implements DocAction public boolean voidIt() { if (log.isLoggable(Level.INFO)) log.info(toString()); - // Before Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); - if (m_processMsg != null) - return false; - + if (DOCSTATUS_Closed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus()) || DOCSTATUS_Voided.equals(getDocStatus())) @@ -2190,6 +2188,11 @@ public class MInvoice extends X_C_Invoice implements DocAction || 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++) @@ -2221,7 +2224,20 @@ public class MInvoice extends X_C_Invoice implements DocAction } else { - return reverseCorrectIt(); + 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 diff --git a/org.adempiere.base/src/org/compiere/model/MMovement.java b/org.adempiere.base/src/org/compiere/model/MMovement.java index 8db1ed5be4..eabeaaf9ee 100644 --- a/org.adempiere.base/src/org/compiere/model/MMovement.java +++ b/org.adempiere.base/src/org/compiere/model/MMovement.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.exceptions.PeriodClosedException; import org.compiere.process.DocAction; import org.compiere.process.DocumentEngine; import org.compiere.util.CLogger; @@ -610,11 +611,7 @@ public class MMovement extends X_M_Movement implements DocAction public boolean voidIt() { if (log.isLoggable(Level.INFO)) log.info(toString()); - // Before Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); - if (m_processMsg != null) - return false; - + if (DOCSTATUS_Closed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus()) || DOCSTATUS_Voided.equals(getDocStatus())) @@ -630,6 +627,11 @@ public class MMovement extends X_M_Movement implements DocAction || 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 MMovementLine[] lines = getLines(false); for (int i = 0; i < lines.length; i++) @@ -646,7 +648,20 @@ public class MMovement extends X_M_Movement implements DocAction } else { - return reverseCorrectIt(); + boolean accrual = false; + try + { + MPeriod.testPeriodOpen(getCtx(), getMovementDate(), 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); diff --git a/org.adempiere.base/src/org/compiere/model/MPayment.java b/org.adempiere.base/src/org/compiere/model/MPayment.java index d19ffd1160..f1e8f204ad 100644 --- a/org.adempiere.base/src/org/compiere/model/MPayment.java +++ b/org.adempiere.base/src/org/compiere/model/MPayment.java @@ -28,6 +28,7 @@ import java.util.Properties; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.PeriodClosedException; import org.adempiere.util.IProcessUI; import org.adempiere.util.PaymentUtil; import org.compiere.process.DocAction; @@ -1920,6 +1921,7 @@ public final class MPayment extends X_C_Payment if (getC_BPartner_ID() != 0 && getC_Invoice_ID() == 0 && getC_Charge_ID() == 0 && MPaymentAllocate.get(this).length == 0) { MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + DB.getDatabase().forUpdate(bp, 0); // Update total balance to include this payment BigDecimal payAmt = MConversionRate.convertBase(getCtx(), getPayAmt(), getC_Currency_ID(), getDateAcct(), getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); @@ -1930,7 +1932,7 @@ public final class MPayment extends X_C_Payment return DocAction.STATUS_Invalid; } // Total Balance - BigDecimal newBalance = bp.getTotalOpenBalance(false); + BigDecimal newBalance = bp.getTotalOpenBalance(); if (newBalance == null) newBalance = Env.ZERO; if (isReceipt()) @@ -2361,6 +2363,12 @@ public final class MPayment extends X_C_Payment for (int i = 0; i < allocations.length; i++) { allocations[i].set_TrxName(get_TrxName()); + if (DOCSTATUS_Reversed.equals(allocations[i].getDocStatus()) + || DOCSTATUS_Voided.equals(allocations[i].getDocStatus())) + { + continue; + } + if (accrual) { allocations[i].setDocAction(DocAction.ACTION_Reverse_Accrual); @@ -2409,11 +2417,7 @@ public final class MPayment extends X_C_Payment */ public boolean voidIt() { - if (log.isLoggable(Level.INFO)) log.info(toString()); - // Before Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); - if (m_processMsg != null) - return false; + if (log.isLoggable(Level.INFO)) log.info(toString()); if (DOCSTATUS_Closed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus()) @@ -2434,6 +2438,11 @@ public final class MPayment extends X_C_Payment || 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; + if (!voidOnlinePayment()) return false; @@ -2447,7 +2456,22 @@ public final class MPayment extends X_C_Payment deAllocate(false); } else - return reverseCorrectIt(); + { + 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