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.

This commit is contained in:
Heng Sin Low 2013-04-09 11:12:06 +08:00
parent 4f0c58a751
commit 558641d959
7 changed files with 473 additions and 119 deletions

View File

@ -22,8 +22,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
@ -58,6 +56,8 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
*/ */
private static final long serialVersionUID = 8726957992840702609L; private static final long serialVersionUID = 8726957992840702609L;
/** Tolerance Gain and Loss */
private static final BigDecimal TOLERANCE = new BigDecimal (0.02);
/** /**
* Get Allocations of Payment * Get Allocations of Payment
@ -321,19 +321,17 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
} }
// Mark as Inactive // Mark as Inactive
setIsActive(false); // updated DB for line delete/process 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 // Unlink
getLines(true); getLines(true);
HashSet<Integer> bps = new HashSet<Integer>(); if (!updateBP(true))
return false;
for (int i = 0; i < m_lines.length; i++) for (int i = 0; i < m_lines.length; i++)
{ {
MAllocationLine line = m_lines[i]; MAllocationLine line = m_lines[i];
bps.add(new Integer(line.getC_BPartner_ID()));
line.deleteEx(true, trxName); line.deleteEx(true, trxName);
} }
updateBP(bps);
return true; return true;
} // beforeDelete } // beforeDelete
@ -400,7 +398,7 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
// Std Period open? // Std Period open?
MPeriod.testPeriodOpen(getCtx(), getDateAcct(), MDocType.DOCBASETYPE_PaymentAllocation, getAD_Org_ID()); MPeriod.testPeriodOpen(getCtx(), getDateAcct(), MDocType.DOCBASETYPE_PaymentAllocation, getAD_Org_ID());
getLines(false); getLines(true);
if (m_lines.length == 0) if (m_lines.length == 0)
{ {
m_processMsg = "@NoLines@"; m_processMsg = "@NoLines@";
@ -497,13 +495,14 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
// Link // Link
getLines(false); getLines(false);
HashSet<Integer> bps = new HashSet<Integer>(); if(!updateBP(isReversal()))
return DocAction.STATUS_Invalid;
for (int i = 0; i < m_lines.length; i++) for (int i = 0; i < m_lines.length; i++)
{ {
MAllocationLine line = m_lines[i]; MAllocationLine line = m_lines[i];
bps.add(new Integer(line.processIt(false))); // not reverse line.processIt(isReversal());
} }
updateBP(bps);
// User Validation // User Validation
String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE);
@ -527,11 +526,50 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
{ {
if (log.isLoggable(Level.INFO)) log.info(toString()); 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 // Before Void
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID);
if (m_processMsg != null) if (m_processMsg != null)
return false; 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; boolean accrual = false;
try try
{ {
@ -542,13 +580,18 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
accrual = true; accrual = true;
} }
boolean retValue = reverseIt(accrual); if (accrual)
return reverseAccrualIt();
else
return reverseCorrectIt();
}
// After Void // After Void
m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID);
if (m_processMsg != null) if (m_processMsg != null)
return false; return false;
setProcessed(true);
setDocAction(DOCACTION_None); setDocAction(DOCACTION_None);
return retValue; return retValue;
@ -752,8 +795,17 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
*/ */
private boolean reverseIt(boolean accrual) private boolean reverseIt(boolean accrual)
{ {
if (!isActive()) if (!isActive()
throw new IllegalStateException("Allocation already reversed (not active)"); || 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(); Timestamp reversalDate = accrual ? Env.getContextAsDate(getCtx(), "#Date") : getDateAcct();
if (reversalDate == null) { if (reversalDate == null) {
@ -787,6 +839,8 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
return false; return false;
} }
} }
reversal.setReversal(true);
reversal.setDocumentNo(getDocumentNo()+"^");
reversal.addDescription("{->" + getDocumentNo() + ")"); reversal.addDescription("{->" + getDocumentNo() + ")");
// //
if (!DocumentEngine.processIt(reversal, DocAction.ACTION_Complete)) if (!DocumentEngine.processIt(reversal, DocAction.ACTION_Complete))
@ -820,40 +874,259 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
// Unlink Invoices // Unlink Invoices
getLines(true); getLines(true);
HashSet<Integer> bps = new HashSet<Integer>(); if(!updateBP(true))
return false;
for (int i = 0; i < m_lines.length; i++) for (int i = 0; i < m_lines.length; i++)
{ {
MAllocationLine line = m_lines[i]; MAllocationLine line = m_lines[i];
line.setIsActive(false); line.setIsActive(false);
line.setAmount(Env.ZERO);
line.setDiscountAmt(Env.ZERO);
line.setWriteOffAmt(Env.ZERO);
line.setOverUnderAmt(Env.ZERO);
line.saveEx(); line.saveEx();
bps.add(new Integer(line.processIt(true))); // reverse line.processIt(true); // reverse
} }
updateBP(bps);
addDescription(Msg.getMsg(getCtx(), "Voided"));
} }
setProcessed(true);
setDocStatus(DOCSTATUS_Reversed); // may come from void
setDocAction(DOCACTION_None);
return true; return true;
} // reverse } // reverse
private boolean updateBP(boolean reverse)
{
/** getLines(false);
* Update Open Balance of BP's for (MAllocationLine line : m_lines) {
* @param bps list of business partners int C_Payment_ID = line.getC_Payment_ID();
*/ int C_BPartner_ID = line.getC_BPartner_ID();
private void updateBP(HashSet<Integer> bps) 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)
{ {
if (log.isLoggable(Level.INFO)) log.info("#" + bps.size()); MPayment payment = null;
Iterator<Integer> it = bps.iterator(); int convTypeID = 0;
while (it.hasNext()) 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)
{ {
int C_BPartner_ID = it.next(); if (invoice != null)
MBPartner bp = new MBPartner(getCtx(), C_BPartner_ID, get_TrxName()); {
bp.setTotalOpenBalance(); // recalculates from scratch // If payment is already processed, only adjust open balance by discount and write off amounts.
// bp.setSOCreditStatus(); // called automatically BigDecimal amt = MConversionRate.convertBase(getCtx(), line.getWriteOffAmt().add(line.getDiscountAmt()),
if (bp.save()) { getC_Currency_ID(), paymentDate, convTypeID, getAD_Client_ID(), getAD_Org_ID());
if (log.isLoggable(Level.FINE)) log.fine(bp.toString()); 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 { } else {
log.log(Level.SEVERE, "BP not updated - " + bp); // 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 } // updateBP
/** /**
@ -938,8 +1211,30 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
line.saveEx(); line.saveEx();
count++; count++;
} }
if (fromLines.length != count)
log.log(Level.WARNING, "Line difference - From=" + fromLines.length + " <> Saved=" + count);
return count; return count;
} // copyLinesFrom } // 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 } // MAllocation

View File

@ -289,8 +289,6 @@ public class MBPartner extends X_C_BPartner
private Integer m_primaryC_BPartner_Location_ID = null; private Integer m_primaryC_BPartner_Location_ID = null;
/** Prim User */ /** Prim User */
private Integer m_primaryAD_User_ID = null; private Integer m_primaryAD_User_ID = null;
/** Credit Limit recently calculated */
private boolean m_TotalOpenBalanceSet = false;
/** BP Group */ /** BP Group */
private MBPGroup m_group = null; private MBPGroup m_group = null;
@ -692,7 +690,6 @@ public class MBPartner extends X_C_BPartner
pstmt = null; pstmt = null;
} }
// //
m_TotalOpenBalanceSet = true;
if (SO_CreditUsed != null) if (SO_CreditUsed != null)
super.setSO_CreditUsed (SO_CreditUsed); super.setSO_CreditUsed (SO_CreditUsed);
if (TotalOpenBalance != null) if (TotalOpenBalance != null)
@ -736,19 +733,6 @@ public class MBPartner extends X_C_BPartner
super.setActualLifeTimeValue (ActualLifeTimeValue); super.setActualLifeTimeValue (ActualLifeTimeValue);
} // setActualLifeTimeValue } // 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 * Set Credit Status
*/ */
@ -762,7 +746,7 @@ public class MBPartner extends X_C_BPartner
return; return;
// Above Credit Limit // Above Credit Limit
if (creditLimit.compareTo(getTotalOpenBalance(!m_TotalOpenBalanceSet)) < 0) if (creditLimit.compareTo(getTotalOpenBalance()) < 0)
setSOCreditStatus(SOCREDITSTATUS_CreditHold); setSOCreditStatus(SOCREDITSTATUS_CreditHold);
else else
{ {
@ -795,7 +779,7 @@ public class MBPartner extends X_C_BPartner
// Above (reduced) Credit Limit // Above (reduced) Credit Limit
creditLimit = creditLimit.subtract(additionalAmt); creditLimit = creditLimit.subtract(additionalAmt);
if (creditLimit.compareTo(getTotalOpenBalance(!m_TotalOpenBalanceSet)) < 0) if (creditLimit.compareTo(getTotalOpenBalance()) < 0)
return SOCREDITSTATUS_CreditHold; return SOCREDITSTATUS_CreditHold;
// Above Watch Limit // Above Watch Limit
@ -827,16 +811,6 @@ public class MBPartner extends X_C_BPartner
|| SOCREDITSTATUS_CreditHold.equals(status); || SOCREDITSTATUS_CreditHold.equals(status);
} // isCreditStopHold } // isCreditStopHold
/**
* Set Total Open Balance
* @param TotalOpenBalance
*/
public void setTotalOpenBalance (BigDecimal TotalOpenBalance)
{
m_TotalOpenBalanceSet = false;
super.setTotalOpenBalance (TotalOpenBalance);
} // setTotalOpenBalance
/** /**
* Get BP Group * Get BP Group
* @return group * @return group

View File

@ -26,6 +26,7 @@ import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.AdempiereException;
import org.adempiere.exceptions.PeriodClosedException;
import org.compiere.print.MPrintFormat; import org.compiere.print.MPrintFormat;
import org.compiere.print.ReportEngine; import org.compiere.print.ReportEngine;
import org.compiere.process.DocAction; import org.compiere.process.DocAction;
@ -2021,10 +2022,6 @@ public class MInOut extends X_M_InOut implements DocAction
public boolean voidIt() public boolean voidIt()
{ {
if (log.isLoggable(Level.INFO)) log.info(toString()); 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()) if (DOCSTATUS_Closed.equals(getDocStatus())
|| DOCSTATUS_Reversed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus())
@ -2041,6 +2038,11 @@ public class MInOut extends X_M_InOut implements DocAction
|| DOCSTATUS_Approved.equals(getDocStatus()) || DOCSTATUS_Approved.equals(getDocStatus())
|| DOCSTATUS_NotApproved.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 // Set lines to 0
MInOutLine[] lines = getLines(false); MInOutLine[] lines = getLines(false);
for (int i = 0; i < lines.length; i++) for (int i = 0; i < lines.length; i++)
@ -2063,6 +2065,19 @@ public class MInOut extends X_M_InOut implements DocAction
} }
else 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(); return reverseCorrectIt();
} }

View File

@ -24,6 +24,7 @@ import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.exceptions.PeriodClosedException;
import org.compiere.process.DocAction; import org.compiere.process.DocAction;
import org.compiere.process.DocumentEngine; import org.compiere.process.DocumentEngine;
import org.compiere.util.CCache; import org.compiere.util.CCache;
@ -665,10 +666,6 @@ public class MInventory extends X_M_Inventory implements DocAction
public boolean voidIt() public boolean voidIt()
{ {
if (log.isLoggable(Level.INFO)) log.info(toString()); 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()) if (DOCSTATUS_Closed.equals(getDocStatus())
|| DOCSTATUS_Reversed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus())
@ -685,6 +682,11 @@ public class MInventory extends X_M_Inventory implements DocAction
|| DOCSTATUS_Approved.equals(getDocStatus()) || DOCSTATUS_Approved.equals(getDocStatus())
|| DOCSTATUS_NotApproved.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 // Set lines to 0
MInventoryLine[] lines = getLines(false); MInventoryLine[] lines = getLines(false);
for (int i = 0; i < lines.length; i++) for (int i = 0; i < lines.length; i++)
@ -705,6 +707,19 @@ public class MInventory extends X_M_Inventory implements DocAction
} }
else else
{ {
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(); return reverseCorrectIt();
} }

View File

@ -30,6 +30,7 @@ import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.AdempiereException;
import org.adempiere.exceptions.BPartnerNoAddressException; import org.adempiere.exceptions.BPartnerNoAddressException;
import org.adempiere.exceptions.DBException; import org.adempiere.exceptions.DBException;
import org.adempiere.exceptions.PeriodClosedException;
import org.compiere.print.MPrintFormat; import org.compiere.print.MPrintFormat;
import org.compiere.print.ReportEngine; import org.compiere.print.ReportEngine;
import org.compiere.process.DocAction; import org.compiere.process.DocAction;
@ -1863,6 +1864,7 @@ public class MInvoice extends X_C_Invoice implements DocAction
// Update BP Statistics // Update BP Statistics
MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); 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) // Update total revenue and balance / credit limit (reversed on AllocationLine.processIt)
BigDecimal invAmt = MConversionRate.convertBase(getCtx(), getGrandTotal(true), // CM adjusted BigDecimal invAmt = MConversionRate.convertBase(getCtx(), getGrandTotal(true), // CM adjusted
getC_Currency_ID(), getDateAcct(), getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); 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; return DocAction.STATUS_Invalid;
} }
// Total Balance // Total Balance
BigDecimal newBalance = bp.getTotalOpenBalance(false); BigDecimal newBalance = bp.getTotalOpenBalance();
if (newBalance == null) if (newBalance == null)
newBalance = Env.ZERO; newBalance = Env.ZERO;
if (isSOTrx()) 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 if (log.isLoggable(Level.FINE)) log.fine("GrandTotal=" + getGrandTotal(true) + "(" + invAmt
+ ") BP Life=" + bp.getActualLifeTimeValue() + "->" + newLifeAmt + ") BP Life=" + bp.getActualLifeTimeValue() + "->" + newLifeAmt
+ ", Credit=" + bp.getSO_CreditUsed() + "->" + newCreditAmt + ", Credit=" + bp.getSO_CreditUsed() + "->" + newCreditAmt
+ ", Balance=" + bp.getTotalOpenBalance(false) + " -> " + newBalance); + ", Balance=" + bp.getTotalOpenBalance() + " -> " + newBalance);
bp.setActualLifeTimeValue(newLifeAmt); bp.setActualLifeTimeValue(newLifeAmt);
bp.setSO_CreditUsed(newCreditAmt); bp.setSO_CreditUsed(newCreditAmt);
} // SO } // SO
@ -1904,7 +1906,7 @@ public class MInvoice extends X_C_Invoice implements DocAction
{ {
newBalance = newBalance.subtract(invAmt); newBalance = newBalance.subtract(invAmt);
if (log.isLoggable(Level.FINE)) log.fine("GrandTotal=" + getGrandTotal(true) + "(" + 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.setTotalOpenBalance(newBalance);
bp.setSOCreditStatus(); bp.setSOCreditStatus();
@ -2169,10 +2171,6 @@ public class MInvoice extends X_C_Invoice implements DocAction
public boolean voidIt() public boolean voidIt()
{ {
if (log.isLoggable(Level.INFO)) log.info(toString()); 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()) if (DOCSTATUS_Closed.equals(getDocStatus())
|| DOCSTATUS_Reversed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus())
@ -2190,6 +2188,11 @@ public class MInvoice extends X_C_Invoice implements DocAction
|| DOCSTATUS_Approved.equals(getDocStatus()) || DOCSTATUS_Approved.equals(getDocStatus())
|| DOCSTATUS_NotApproved.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 // Set lines to 0
MInvoiceLine[] lines = getLines(false); MInvoiceLine[] lines = getLines(false);
for (int i = 0; i < lines.length; i++) for (int i = 0; i < lines.length; i++)
@ -2221,6 +2224,19 @@ public class MInvoice extends X_C_Invoice implements DocAction
} }
else 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(); return reverseCorrectIt();
} }

View File

@ -24,6 +24,7 @@ import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.exceptions.PeriodClosedException;
import org.compiere.process.DocAction; import org.compiere.process.DocAction;
import org.compiere.process.DocumentEngine; import org.compiere.process.DocumentEngine;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
@ -610,10 +611,6 @@ public class MMovement extends X_M_Movement implements DocAction
public boolean voidIt() public boolean voidIt()
{ {
if (log.isLoggable(Level.INFO)) log.info(toString()); 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()) if (DOCSTATUS_Closed.equals(getDocStatus())
|| DOCSTATUS_Reversed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus())
@ -630,6 +627,11 @@ public class MMovement extends X_M_Movement implements DocAction
|| DOCSTATUS_Approved.equals(getDocStatus()) || DOCSTATUS_Approved.equals(getDocStatus())
|| DOCSTATUS_NotApproved.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 // Set lines to 0
MMovementLine[] lines = getLines(false); MMovementLine[] lines = getLines(false);
for (int i = 0; i < lines.length; i++) for (int i = 0; i < lines.length; i++)
@ -646,6 +648,19 @@ public class MMovement extends X_M_Movement implements DocAction
} }
else else
{ {
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(); return reverseCorrectIt();
} }
// After Void // After Void

View File

@ -28,6 +28,7 @@ import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.AdempiereException;
import org.adempiere.exceptions.PeriodClosedException;
import org.adempiere.util.IProcessUI; import org.adempiere.util.IProcessUI;
import org.adempiere.util.PaymentUtil; import org.adempiere.util.PaymentUtil;
import org.compiere.process.DocAction; 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) 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()); MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName());
DB.getDatabase().forUpdate(bp, 0);
// Update total balance to include this payment // Update total balance to include this payment
BigDecimal payAmt = MConversionRate.convertBase(getCtx(), getPayAmt(), BigDecimal payAmt = MConversionRate.convertBase(getCtx(), getPayAmt(),
getC_Currency_ID(), getDateAcct(), getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); 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; return DocAction.STATUS_Invalid;
} }
// Total Balance // Total Balance
BigDecimal newBalance = bp.getTotalOpenBalance(false); BigDecimal newBalance = bp.getTotalOpenBalance();
if (newBalance == null) if (newBalance == null)
newBalance = Env.ZERO; newBalance = Env.ZERO;
if (isReceipt()) if (isReceipt())
@ -2361,6 +2363,12 @@ public final class MPayment extends X_C_Payment
for (int i = 0; i < allocations.length; i++) for (int i = 0; i < allocations.length; i++)
{ {
allocations[i].set_TrxName(get_TrxName()); allocations[i].set_TrxName(get_TrxName());
if (DOCSTATUS_Reversed.equals(allocations[i].getDocStatus())
|| DOCSTATUS_Voided.equals(allocations[i].getDocStatus()))
{
continue;
}
if (accrual) if (accrual)
{ {
allocations[i].setDocAction(DocAction.ACTION_Reverse_Accrual); allocations[i].setDocAction(DocAction.ACTION_Reverse_Accrual);
@ -2410,10 +2418,6 @@ public final class MPayment extends X_C_Payment
public boolean voidIt() public boolean voidIt()
{ {
if (log.isLoggable(Level.INFO)) log.info(toString()); 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()) if (DOCSTATUS_Closed.equals(getDocStatus())
|| DOCSTATUS_Reversed.equals(getDocStatus()) || DOCSTATUS_Reversed.equals(getDocStatus())
@ -2434,6 +2438,11 @@ public final class MPayment extends X_C_Payment
|| DOCSTATUS_Approved.equals(getDocStatus()) || DOCSTATUS_Approved.equals(getDocStatus())
|| DOCSTATUS_NotApproved.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()) if (!voidOnlinePayment())
return false; return false;
@ -2446,8 +2455,23 @@ public final class MPayment extends X_C_Payment
// Unlink & De-Allocate // Unlink & De-Allocate
deAllocate(false); deAllocate(false);
} }
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 else
return reverseCorrectIt(); return reverseCorrectIt();
}
// //
// After Void // After Void