IDEMPIERE-4567 Resetting payment allocation to a charge leaves wrong … (#414)
* IDEMPIERE-4567 Resetting payment allocation to a charge leaves wrong BP Balance MAllocationHdr.updateBP was broken, the form Payment Allocation worked just because it was calling bpartner.setTotalOpenBalance() at the end so, I refactored the MAllocationHdr.updateBP and before/afterDelete to call bpartner.setTotalOpenBalance instead if the broken algorithm Unit test added for allocating and deleting a customer invoice, a vendor invoice, and a charge * IDEMPIERE-4567 Resetting payment allocation to a charge leaves wrong BP Balance - Fix peer review changes requested by @hengsin - Detected double call to bpartner.setTotalOpenBalance in WAllocation - Detected some business partners in GardenWorld with wrong TotalOpenBalance or SO_CreditUsed, added migration script to fix it, it made more complicate to write unit tests as the data was wrong
This commit is contained in:
parent
6eeabc49ba
commit
460f7116a2
|
@ -0,0 +1,33 @@
|
||||||
|
SET SQLBLANKLINES ON
|
||||||
|
SET DEFINE OFF
|
||||||
|
|
||||||
|
-- IDEMPIERE-4567 Resetting payment allocation to a charge leaves wrong BP Balance
|
||||||
|
-- Nov 26, 2020, 9:35:38 AM CET
|
||||||
|
UPDATE C_BPartner bp
|
||||||
|
SET
|
||||||
|
SO_CreditUsed =
|
||||||
|
COALESCE((SELECT SUM(currencyBase(invoiceOpen(i.C_Invoice_ID,i.C_InvoicePaySchedule_ID),i.C_Currency_ID,i.DateInvoiced, i.AD_Client_ID,i.AD_Org_ID)) FROM C_Invoice_v i
|
||||||
|
WHERE i.C_BPartner_ID=bp.C_BPartner_ID AND i.IsSOTrx='Y' AND i.IsPaid='N' AND i.DocStatus IN ('CO','CL')),0),
|
||||||
|
TotalOpenBalance =
|
||||||
|
COALESCE((SELECT SUM(currencyBase(invoiceOpen(i.C_Invoice_ID,i.C_InvoicePaySchedule_ID),i.C_Currency_ID,i.DateInvoiced, i.AD_Client_ID,i.AD_Org_ID)*i.MultiplierAP) FROM C_Invoice_v i
|
||||||
|
WHERE i.C_BPartner_ID=bp.C_BPartner_ID AND i.IsPaid='N' AND i.DocStatus IN ('CO','CL')),0) -
|
||||||
|
COALESCE((SELECT SUM(currencyBase(Paymentavailable(p.C_Payment_ID),p.C_Currency_ID,p.DateTrx,p.AD_Client_ID,p.AD_Org_ID)) FROM C_Payment_v p
|
||||||
|
WHERE p.C_BPartner_ID=bp.C_BPartner_ID AND p.IsAllocated='N'
|
||||||
|
AND p.C_Charge_ID IS NULL AND p.DocStatus IN ('CO','CL')),0)
|
||||||
|
WHERE AD_Client_ID = 11 AND (
|
||||||
|
SO_CreditUsed !=
|
||||||
|
COALESCE((SELECT SUM(currencyBase(invoiceOpen(i.C_Invoice_ID,i.C_InvoicePaySchedule_ID),i.C_Currency_ID,i.DateInvoiced, i.AD_Client_ID,i.AD_Org_ID)) FROM C_Invoice_v i
|
||||||
|
WHERE i.C_BPartner_ID=bp.C_BPartner_ID AND i.IsSOTrx='Y' AND i.IsPaid='N' AND i.DocStatus IN ('CO','CL')),0)
|
||||||
|
OR
|
||||||
|
TotalOpenBalance !=
|
||||||
|
COALESCE((SELECT SUM(currencyBase(invoiceOpen(i.C_Invoice_ID,i.C_InvoicePaySchedule_ID),i.C_Currency_ID,i.DateInvoiced, i.AD_Client_ID,i.AD_Org_ID)*i.MultiplierAP) FROM C_Invoice_v i
|
||||||
|
WHERE i.C_BPartner_ID=bp.C_BPartner_ID AND i.IsPaid='N' AND i.DocStatus IN ('CO','CL')),0) -
|
||||||
|
COALESCE((SELECT SUM(currencyBase(Paymentavailable(p.C_Payment_ID),p.C_Currency_ID,p.DateTrx,p.AD_Client_ID,p.AD_Org_ID)) FROM C_Payment_v p
|
||||||
|
WHERE p.C_BPartner_ID=bp.C_BPartner_ID AND p.IsAllocated='N'
|
||||||
|
AND p.C_Charge_ID IS NULL AND p.DocStatus IN ('CO','CL')),0)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
SELECT register_migration_script('202011260936_IDEMPIERE-4567.sql') FROM dual
|
||||||
|
;
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
-- IDEMPIERE-4567 Resetting payment allocation to a charge leaves wrong BP Balance
|
||||||
|
-- Nov 26, 2020, 9:35:38 AM CET
|
||||||
|
UPDATE C_BPartner bp
|
||||||
|
SET
|
||||||
|
SO_CreditUsed =
|
||||||
|
COALESCE((SELECT SUM(currencyBase(invoiceOpen(i.C_Invoice_ID,i.C_InvoicePaySchedule_ID),i.C_Currency_ID,i.DateInvoiced, i.AD_Client_ID,i.AD_Org_ID)) FROM C_Invoice_v i
|
||||||
|
WHERE i.C_BPartner_ID=bp.C_BPartner_ID AND i.IsSOTrx='Y' AND i.IsPaid='N' AND i.DocStatus IN ('CO','CL')),0),
|
||||||
|
TotalOpenBalance =
|
||||||
|
COALESCE((SELECT SUM(currencyBase(invoiceOpen(i.C_Invoice_ID,i.C_InvoicePaySchedule_ID),i.C_Currency_ID,i.DateInvoiced, i.AD_Client_ID,i.AD_Org_ID)*i.MultiplierAP) FROM C_Invoice_v i
|
||||||
|
WHERE i.C_BPartner_ID=bp.C_BPartner_ID AND i.IsPaid='N' AND i.DocStatus IN ('CO','CL')),0) -
|
||||||
|
COALESCE((SELECT SUM(currencyBase(Paymentavailable(p.C_Payment_ID),p.C_Currency_ID,p.DateTrx,p.AD_Client_ID,p.AD_Org_ID)) FROM C_Payment_v p
|
||||||
|
WHERE p.C_BPartner_ID=bp.C_BPartner_ID AND p.IsAllocated='N'
|
||||||
|
AND p.C_Charge_ID IS NULL AND p.DocStatus IN ('CO','CL')),0)
|
||||||
|
WHERE AD_Client_ID = 11 AND (
|
||||||
|
SO_CreditUsed !=
|
||||||
|
COALESCE((SELECT SUM(currencyBase(invoiceOpen(i.C_Invoice_ID,i.C_InvoicePaySchedule_ID),i.C_Currency_ID,i.DateInvoiced, i.AD_Client_ID,i.AD_Org_ID)) FROM C_Invoice_v i
|
||||||
|
WHERE i.C_BPartner_ID=bp.C_BPartner_ID AND i.IsSOTrx='Y' AND i.IsPaid='N' AND i.DocStatus IN ('CO','CL')),0)
|
||||||
|
OR
|
||||||
|
TotalOpenBalance !=
|
||||||
|
COALESCE((SELECT SUM(currencyBase(invoiceOpen(i.C_Invoice_ID,i.C_InvoicePaySchedule_ID),i.C_Currency_ID,i.DateInvoiced, i.AD_Client_ID,i.AD_Org_ID)*i.MultiplierAP) FROM C_Invoice_v i
|
||||||
|
WHERE i.C_BPartner_ID=bp.C_BPartner_ID AND i.IsPaid='N' AND i.DocStatus IN ('CO','CL')),0) -
|
||||||
|
COALESCE((SELECT SUM(currencyBase(Paymentavailable(p.C_Payment_ID),p.C_Currency_ID,p.DateTrx,p.AD_Client_ID,p.AD_Org_ID)) FROM C_Payment_v p
|
||||||
|
WHERE p.C_BPartner_ID=bp.C_BPartner_ID AND p.IsAllocated='N'
|
||||||
|
AND p.C_Charge_ID IS NULL AND p.DocStatus IN ('CO','CL')),0)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
SELECT register_migration_script('202011260936_IDEMPIERE-4567.sql') FROM dual
|
||||||
|
;
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.compiere.model;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
@ -56,9 +55,7 @@ public class MAllocationHdr extends X_C_AllocationHdr implements DocAction
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = -7787519874581251920L;
|
private static final long serialVersionUID = -7787519874581251920L;
|
||||||
/** Tolerance Gain and Loss */
|
|
||||||
private static final BigDecimal TOLERANCE = BigDecimal.valueOf(0.02);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Allocations of Payment
|
* Get Allocations of Payment
|
||||||
* @param ctx context
|
* @param ctx context
|
||||||
|
@ -306,6 +303,8 @@ public class MAllocationHdr extends X_C_AllocationHdr implements DocAction
|
||||||
return true;
|
return true;
|
||||||
} // beforeSave
|
} // beforeSave
|
||||||
|
|
||||||
|
private List<Integer> m_bps_beforeDelete = new ArrayList<Integer>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Before Delete.
|
* Before Delete.
|
||||||
* @return true if acct was deleted
|
* @return true if acct was deleted
|
||||||
|
@ -327,17 +326,32 @@ public class MAllocationHdr extends X_C_AllocationHdr implements DocAction
|
||||||
|
|
||||||
// Unlink
|
// Unlink
|
||||||
getLines(true);
|
getLines(true);
|
||||||
if (!updateBP(true))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
m_bps_beforeDelete.clear();
|
||||||
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];
|
||||||
|
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);
|
line.deleteEx(true, trxName);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} // beforeDelete
|
} // 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
|
* After Save
|
||||||
* @param newRecord
|
* @param newRecord
|
||||||
|
@ -515,7 +529,7 @@ public class MAllocationHdr extends X_C_AllocationHdr implements DocAction
|
||||||
|
|
||||||
// Link
|
// Link
|
||||||
getLines(false);
|
getLines(false);
|
||||||
if(!updateBP(isReversal()))
|
if(!updateBP())
|
||||||
return DocAction.STATUS_Invalid;
|
return DocAction.STATUS_Invalid;
|
||||||
|
|
||||||
for (int i = 0; i < m_lines.length; i++)
|
for (int i = 0; i < m_lines.length; i++)
|
||||||
|
@ -570,7 +584,7 @@ public class MAllocationHdr extends X_C_AllocationHdr implements DocAction
|
||||||
|
|
||||||
// Set lines to 0
|
// Set lines to 0
|
||||||
MAllocationLine[] lines = getLines(true);
|
MAllocationLine[] lines = getLines(true);
|
||||||
if(!updateBP(true))
|
if(!updateBP())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (int i = 0; i < lines.length; i++)
|
for (int i = 0; i < lines.length; i++)
|
||||||
|
@ -894,7 +908,7 @@ public class MAllocationHdr extends X_C_AllocationHdr implements DocAction
|
||||||
|
|
||||||
// Unlink Invoices
|
// Unlink Invoices
|
||||||
getLines(true);
|
getLines(true);
|
||||||
if(!updateBP(true))
|
if(!updateBP())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (int i = 0; i < m_lines.length; i++)
|
for (int i = 0; i < m_lines.length; i++)
|
||||||
|
@ -918,227 +932,19 @@ public class MAllocationHdr extends X_C_AllocationHdr implements DocAction
|
||||||
return true;
|
return true;
|
||||||
} // reverse
|
} // reverse
|
||||||
|
|
||||||
private boolean updateBP(boolean reverse)
|
private boolean updateBP()
|
||||||
{
|
{
|
||||||
|
List<Integer> bps = new ArrayList<Integer>();
|
||||||
getLines(false);
|
getLines(false);
|
||||||
for (MAllocationLine line : m_lines) {
|
for (MAllocationLine line : m_lines) {
|
||||||
int C_Payment_ID = line.getC_Payment_ID();
|
|
||||||
int C_BPartner_ID = line.getC_BPartner_ID();
|
int C_BPartner_ID = line.getC_BPartner_ID();
|
||||||
int M_Invoice_ID = line.getC_Invoice_ID();
|
if (! bps.contains(C_BPartner_ID)) {
|
||||||
|
bps.add(C_BPartner_ID);
|
||||||
if ((C_BPartner_ID == 0) || ((M_Invoice_ID == 0) && (C_Payment_ID == 0)))
|
MBPartner bpartner = new MBPartner(Env.getCtx(), C_BPartner_ID, get_TrxName());
|
||||||
continue;
|
bpartner.setTotalOpenBalance();
|
||||||
|
bpartner.saveEx();
|
||||||
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 = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingAllocationCurrencyToBaseCurrency",
|
|
||||||
getC_Currency_ID(), MClient.get(getCtx()).getC_Currency_ID(), convTypeID, paymentDate, get_TrxName());
|
|
||||||
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 = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingAllocationCurrencyToBaseCurrency",
|
|
||||||
getC_Currency_ID(), MClient.get(getCtx()).getC_Currency_ID(), convTypeID, paymentDate, get_TrxName());
|
|
||||||
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 = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingAllocationCurrencyToBaseCurrency",
|
|
||||||
getC_Currency_ID(), MClient.get(getCtx()).getC_Currency_ID(), convTypeID, getDateAcct(), get_TrxName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
openBalanceDiff = openBalanceDiff.add(allocAmtBase);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (invoice != null)
|
|
||||||
{
|
|
||||||
// adjust open balance by discount and write off amounts.
|
|
||||||
BigDecimal amt = MConversionRate.convertBase(getCtx(), allocAmt.negate(),
|
|
||||||
getC_Currency_ID(), invoice.getDateAcct(), invoice.getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID());
|
|
||||||
if (amt == null)
|
|
||||||
{
|
|
||||||
m_processMsg = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingAllocationCurrencyToBaseCurrency",
|
|
||||||
getC_Currency_ID(), MClient.get(getCtx()).getC_Currency_ID(), invoice.getC_ConversionType_ID(), invoice.getDateAcct(), get_TrxName());
|
|
||||||
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 = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingAllocationCurrencyToInvoiceCurrency",
|
|
||||||
getC_Currency_ID(), invoice.getC_Currency_ID(), invoice.getC_ConversionType_ID(), getDateAcct(), get_TrxName());
|
|
||||||
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 = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingInvoiceCurrencyToBaseCurrency",
|
|
||||||
invoice.getC_Currency_ID(), MClient.get(getCtx()).getC_Currency_ID(), invoice.getC_ConversionType_ID(), invoice.getDateAcct(), get_TrxName());
|
|
||||||
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 = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingInvoiceCurrencyToBaseCurrency",
|
|
||||||
invoice.getC_Currency_ID(), MClient.get(getCtx()).getC_Currency_ID(), invoice.getC_ConversionType_ID(), getDateAcct(), get_TrxName());
|
|
||||||
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(BigDecimal.valueOf(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, RoundingMode.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
|
} // for all lines
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // updateBP
|
} // updateBP
|
||||||
|
|
||||||
|
|
|
@ -710,6 +710,7 @@ public class MBPartner extends X_C_BPartner implements ImmutablePOSupport
|
||||||
*/
|
*/
|
||||||
public void setTotalOpenBalance ()
|
public void setTotalOpenBalance ()
|
||||||
{
|
{
|
||||||
|
log.info("");
|
||||||
BigDecimal SO_CreditUsed = null;
|
BigDecimal SO_CreditUsed = null;
|
||||||
BigDecimal TotalOpenBalance = null;
|
BigDecimal TotalOpenBalance = null;
|
||||||
//AZ Goodwill -> BF2041226 : only count completed/closed docs.
|
//AZ Goodwill -> BF2041226 : only count completed/closed docs.
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.adempiere.exceptions.AdempiereException;
|
||||||
import org.compiere.minigrid.IMiniTable;
|
import org.compiere.minigrid.IMiniTable;
|
||||||
import org.compiere.model.MAllocationHdr;
|
import org.compiere.model.MAllocationHdr;
|
||||||
import org.compiere.model.MAllocationLine;
|
import org.compiere.model.MAllocationLine;
|
||||||
import org.compiere.model.MBPartner;
|
|
||||||
import org.compiere.model.MDocType;
|
import org.compiere.model.MDocType;
|
||||||
import org.compiere.model.MInvoice;
|
import org.compiere.model.MInvoice;
|
||||||
import org.compiere.model.MPayment;
|
import org.compiere.model.MPayment;
|
||||||
|
@ -808,9 +807,6 @@ public class Allocation
|
||||||
if (log.isLoggable(Level.CONFIG)) log.config("Payment #" + i + (pay.isAllocated() ? " not" : " is")
|
if (log.isLoggable(Level.CONFIG)) log.config("Payment #" + i + (pay.isAllocated() ? " not" : " is")
|
||||||
+ " fully allocated");
|
+ " fully allocated");
|
||||||
}
|
}
|
||||||
MBPartner bpartner = new MBPartner(Env.getCtx(), m_C_BPartner_ID, trxName);
|
|
||||||
bpartner.setTotalOpenBalance();
|
|
||||||
bpartner.saveEx();
|
|
||||||
paymentList.clear();
|
paymentList.clear();
|
||||||
amountList.clear();
|
amountList.clear();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
/***********************************************************************
|
||||||
|
* This file is part of iDempiere ERP Open Source *
|
||||||
|
* http://www.idempiere.org *
|
||||||
|
* *
|
||||||
|
* Copyright (C) Contributors *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston, *
|
||||||
|
* MA 02110-1301, USA. *
|
||||||
|
* *
|
||||||
|
* Contributors: *
|
||||||
|
* - Carlos Ruiz - globalqss *
|
||||||
|
**********************************************************************/
|
||||||
|
package org.idempiere.test.model;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.logging.LogRecord;
|
||||||
|
|
||||||
|
import org.compiere.model.MAllocationHdr;
|
||||||
|
import org.compiere.model.MAllocationLine;
|
||||||
|
import org.compiere.model.MBPartner;
|
||||||
|
import org.compiere.model.MDocType;
|
||||||
|
import org.compiere.model.MInvoice;
|
||||||
|
import org.compiere.model.MInvoiceLine;
|
||||||
|
import org.compiere.model.MPayment;
|
||||||
|
import org.compiere.process.DocAction;
|
||||||
|
import org.compiere.process.ProcessInfo;
|
||||||
|
import org.compiere.util.CLogErrorBuffer;
|
||||||
|
import org.compiere.util.Env;
|
||||||
|
import org.compiere.util.TimeUtil;
|
||||||
|
import org.compiere.wf.MWorkflow;
|
||||||
|
import org.idempiere.test.AbstractTestCase;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Carlos Ruiz - globalqss
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AllocationTest extends AbstractTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public AllocationTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
final static int BP_C_AND_W = 117;
|
||||||
|
final static int BP_TREEFARM = 114;
|
||||||
|
|
||||||
|
final static int PAYMENT_TERM_IMMEDIATE = 105;
|
||||||
|
final static int CHARGE_FREIGHT = 200000;
|
||||||
|
final static int CURRENCY_USD = 100;
|
||||||
|
final static int BANK_ACCOUNT_1234 = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://idempiere.atlassian.net/browse/IDEMPIERE-4567
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAllocateCharge() {
|
||||||
|
int severeCount = 0;
|
||||||
|
LogRecord[] errorLogs = CLogErrorBuffer.get(true).getRecords(true);
|
||||||
|
if (errorLogs != null)
|
||||||
|
severeCount = errorLogs.length;
|
||||||
|
|
||||||
|
Properties ctx = Env.getCtx();
|
||||||
|
String trxName = getTrxName();
|
||||||
|
|
||||||
|
// Get the OpenBalance of C&W
|
||||||
|
MBPartner bpartner = new MBPartner(ctx, BP_C_AND_W, trxName);
|
||||||
|
BigDecimal initialBalance = bpartner.getTotalOpenBalance();
|
||||||
|
|
||||||
|
// Pay $100
|
||||||
|
MPayment payment1 = new MPayment(ctx, 0, trxName);
|
||||||
|
payment1.setC_BPartner_ID(BP_C_AND_W);
|
||||||
|
payment1.setC_DocType_ID(true); // Receipt
|
||||||
|
payment1.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
payment1.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
payment1.setPayAmt(Env.ONEHUNDRED);
|
||||||
|
payment1.setTenderType(MPayment.TENDERTYPE_Check);
|
||||||
|
payment1.setC_BankAccount_ID(BANK_ACCOUNT_1234);
|
||||||
|
payment1.setC_Currency_ID(CURRENCY_USD);
|
||||||
|
payment1.setDateTrx(TimeUtil.getDay(null));
|
||||||
|
payment1.setDateAcct(TimeUtil.getDay(null));
|
||||||
|
payment1.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(payment1, DocAction.ACTION_Complete);
|
||||||
|
payment1.load(trxName);
|
||||||
|
assertFalse(info.isError(), "Error processing payment: " + info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, payment1.getDocStatus(), "Payment document status is not completed: " + payment1.getDocStatus());
|
||||||
|
assertTrue(payment1.isPosted(), "Payment not posted");
|
||||||
|
|
||||||
|
bpartner.load(trxName);
|
||||||
|
BigDecimal actualBalance = bpartner.getTotalOpenBalance();
|
||||||
|
assertTrue(actualBalance.compareTo(initialBalance.subtract(Env.ONEHUNDRED)) == 0);
|
||||||
|
|
||||||
|
// Create allocation to allocate payment to charge
|
||||||
|
MAllocationHdr alloc = new MAllocationHdr (Env.getCtx(), true, // manual
|
||||||
|
payment1.getDateTrx(), CURRENCY_USD, Env.getContext(Env.getCtx(), "#AD_User_Name"), trxName);
|
||||||
|
alloc.setAD_Org_ID(payment1.getAD_Org_ID());
|
||||||
|
int doctypeAlloc = MDocType.getDocType("CMA");
|
||||||
|
alloc.setC_DocType_ID(doctypeAlloc);
|
||||||
|
alloc.setDescription(alloc.getDescriptionForManualAllocation(payment1.getC_BPartner_ID(), trxName));
|
||||||
|
alloc.saveEx();
|
||||||
|
|
||||||
|
MAllocationLine aLine1 = new MAllocationLine (alloc, Env.ONEHUNDRED,
|
||||||
|
Env.ZERO, Env.ZERO, Env.ZERO);
|
||||||
|
aLine1.setDocInfo(BP_C_AND_W, 0, 0);
|
||||||
|
aLine1.setPaymentInfo(payment1.getC_Payment_ID(), 0);
|
||||||
|
aLine1.saveEx();
|
||||||
|
|
||||||
|
MAllocationLine aLine2 = new MAllocationLine (alloc, Env.ONEHUNDRED.negate(),
|
||||||
|
Env.ZERO, Env.ZERO, Env.ZERO);
|
||||||
|
aLine2.setC_Charge_ID(CHARGE_FREIGHT);
|
||||||
|
aLine2.setC_BPartner_ID(BP_C_AND_W);
|
||||||
|
aLine2.saveEx();
|
||||||
|
|
||||||
|
assertTrue(alloc.processIt(DocAction.ACTION_Complete));
|
||||||
|
alloc.saveEx();
|
||||||
|
|
||||||
|
payment1.load(trxName);
|
||||||
|
assertTrue(payment1.isAllocated(), "Payment not allocated");
|
||||||
|
|
||||||
|
bpartner.load(trxName);
|
||||||
|
actualBalance = bpartner.getTotalOpenBalance();
|
||||||
|
assertTrue(actualBalance.compareTo(initialBalance) == 0);
|
||||||
|
|
||||||
|
// Delete Allocation
|
||||||
|
MAllocationHdr[] alloc1 = MAllocationHdr.getOfPayment(ctx, payment1.getC_Payment_ID(), trxName);
|
||||||
|
assertTrue(alloc1[0].delete(true, trxName));
|
||||||
|
|
||||||
|
payment1.load(trxName);
|
||||||
|
assertFalse(payment1.isAllocated(), "Payment not allocated");
|
||||||
|
|
||||||
|
bpartner.load(trxName);
|
||||||
|
actualBalance = bpartner.getTotalOpenBalance();
|
||||||
|
assertTrue(actualBalance.compareTo(initialBalance.subtract(Env.ONEHUNDRED)) == 0);
|
||||||
|
|
||||||
|
errorLogs = CLogErrorBuffer.get(true).getRecords(true);
|
||||||
|
if (errorLogs != null)
|
||||||
|
assertEquals(severeCount, errorLogs.length, "Severe errors recorded in log: " + errorLogs.length);
|
||||||
|
|
||||||
|
rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllocateCustomerInvoice() {
|
||||||
|
int severeCount = 0;
|
||||||
|
LogRecord[] errorLogs = CLogErrorBuffer.get(true).getRecords(true);
|
||||||
|
if (errorLogs != null)
|
||||||
|
severeCount = errorLogs.length;
|
||||||
|
|
||||||
|
Properties ctx = Env.getCtx();
|
||||||
|
String trxName = getTrxName();
|
||||||
|
|
||||||
|
// Get the OpenBalance of C&W
|
||||||
|
MBPartner bpartner = new MBPartner(ctx, BP_C_AND_W, trxName);
|
||||||
|
BigDecimal initialBalance = bpartner.getTotalOpenBalance();
|
||||||
|
|
||||||
|
// Create Invoice $100
|
||||||
|
MInvoice invoice = new MInvoice(ctx, 0, trxName);
|
||||||
|
invoice.setBPartner(MBPartner.get(ctx, BP_C_AND_W));
|
||||||
|
invoice.setC_DocTypeTarget_ID(MDocType.DOCBASETYPE_ARInvoice);
|
||||||
|
invoice.setC_DocType_ID(invoice.getC_DocTypeTarget_ID()); // required to avoid runDocumentActionWorkflow exception
|
||||||
|
invoice.setPaymentRule(MInvoice.PAYMENTRULE_Check);
|
||||||
|
invoice.setC_PaymentTerm_ID(PAYMENT_TERM_IMMEDIATE); // Immediate
|
||||||
|
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
|
||||||
|
invoice.setDateInvoiced(today);
|
||||||
|
invoice.setDateAcct(today);
|
||||||
|
invoice.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
invoice.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
invoice.saveEx();
|
||||||
|
|
||||||
|
MInvoiceLine line1 = new MInvoiceLine(invoice);
|
||||||
|
line1.setLine(10);
|
||||||
|
line1.setC_Charge_ID(CHARGE_FREIGHT);
|
||||||
|
line1.setQty(new BigDecimal("1"));
|
||||||
|
line1.setPrice(Env.ONEHUNDRED);
|
||||||
|
line1.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Complete);
|
||||||
|
invoice.load(trxName);
|
||||||
|
assertFalse(info.isError(), "Error processing invoice: " + info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, invoice.getDocStatus(), "Invoice document status is not completed: " + invoice.getDocStatus());
|
||||||
|
assertTrue(Env.ONEHUNDRED.compareTo(invoice.getGrandTotal()) == 0, "Invoice grand total not as expected: " + invoice.getGrandTotal().toPlainString());
|
||||||
|
assertTrue(invoice.isPosted(), "Invoice not posted");
|
||||||
|
|
||||||
|
bpartner.load(trxName);
|
||||||
|
BigDecimal actualBalance = bpartner.getTotalOpenBalance();
|
||||||
|
assertTrue(actualBalance.compareTo(initialBalance.add(Env.ONEHUNDRED)) == 0);
|
||||||
|
|
||||||
|
// Pay $100
|
||||||
|
MPayment payment1 = new MPayment(ctx, 0, trxName);
|
||||||
|
payment1.setC_Invoice_ID(invoice.getC_Invoice_ID());
|
||||||
|
payment1.setC_BPartner_ID(invoice.getC_BPartner_ID());
|
||||||
|
payment1.setC_DocType_ID(true); // Receipt
|
||||||
|
payment1.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
payment1.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
payment1.setPayAmt(Env.ONEHUNDRED);
|
||||||
|
payment1.setTenderType(MPayment.TENDERTYPE_Check);
|
||||||
|
payment1.setC_BankAccount_ID(BANK_ACCOUNT_1234);
|
||||||
|
payment1.setC_Currency_ID(CURRENCY_USD);
|
||||||
|
payment1.setDateTrx(invoice.getDateInvoiced());
|
||||||
|
payment1.setDateAcct(invoice.getDateInvoiced());
|
||||||
|
payment1.saveEx();
|
||||||
|
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(payment1, DocAction.ACTION_Complete);
|
||||||
|
payment1.load(trxName);
|
||||||
|
assertFalse(info.isError(), "Error processing payment: " + info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, payment1.getDocStatus(), "Payment document status is not completed: " + payment1.getDocStatus());
|
||||||
|
assertEquals(false, invoice.isPaid(), "Invoice isPaid() is not false");
|
||||||
|
assertTrue(payment1.isPosted(), "Payment not posted");
|
||||||
|
|
||||||
|
bpartner.load(trxName);
|
||||||
|
actualBalance = bpartner.getTotalOpenBalance();
|
||||||
|
assertTrue(actualBalance.compareTo(initialBalance) == 0);
|
||||||
|
|
||||||
|
// Delete Allocation
|
||||||
|
MAllocationHdr[] alloc1 = MAllocationHdr.getOfPayment(ctx, payment1.getC_Payment_ID(), trxName);
|
||||||
|
assertTrue(alloc1[0].delete(true, trxName));
|
||||||
|
|
||||||
|
bpartner.load(trxName);
|
||||||
|
actualBalance = bpartner.getTotalOpenBalance();
|
||||||
|
assertTrue(actualBalance.compareTo(initialBalance) == 0);
|
||||||
|
|
||||||
|
errorLogs = CLogErrorBuffer.get(true).getRecords(true);
|
||||||
|
if (errorLogs != null)
|
||||||
|
assertEquals(severeCount, errorLogs.length, "Severe errors recorded in log: " + errorLogs.length);
|
||||||
|
|
||||||
|
rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllocateVendorInvoice() {
|
||||||
|
int severeCount = 0;
|
||||||
|
LogRecord[] errorLogs = CLogErrorBuffer.get(true).getRecords(true);
|
||||||
|
if (errorLogs != null)
|
||||||
|
severeCount = errorLogs.length;
|
||||||
|
|
||||||
|
Properties ctx = Env.getCtx();
|
||||||
|
String trxName = getTrxName();
|
||||||
|
|
||||||
|
// Get the OpenBalance of C&W
|
||||||
|
MBPartner bpartner = new MBPartner(ctx, BP_TREEFARM, trxName);
|
||||||
|
BigDecimal initialBalance = bpartner.getTotalOpenBalance();
|
||||||
|
|
||||||
|
// Create Invoice $100
|
||||||
|
MInvoice invoice = new MInvoice(ctx, 0, trxName);
|
||||||
|
invoice.setBPartner(MBPartner.get(ctx, BP_TREEFARM));
|
||||||
|
invoice.setC_DocTypeTarget_ID(MDocType.DOCBASETYPE_APInvoice);
|
||||||
|
invoice.setC_DocType_ID(invoice.getC_DocTypeTarget_ID()); // required to avoid runDocumentActionWorkflow exception
|
||||||
|
invoice.setPaymentRule(MInvoice.PAYMENTRULE_Check);
|
||||||
|
invoice.setC_PaymentTerm_ID(PAYMENT_TERM_IMMEDIATE); // Immediate
|
||||||
|
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
|
||||||
|
invoice.setDateInvoiced(today);
|
||||||
|
invoice.setDateAcct(today);
|
||||||
|
invoice.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
invoice.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
invoice.saveEx();
|
||||||
|
|
||||||
|
MInvoiceLine line1 = new MInvoiceLine(invoice);
|
||||||
|
line1.setLine(10);
|
||||||
|
line1.setC_Charge_ID(CHARGE_FREIGHT);
|
||||||
|
line1.setQty(new BigDecimal("1"));
|
||||||
|
line1.setPrice(Env.ONEHUNDRED);
|
||||||
|
line1.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Complete);
|
||||||
|
invoice.load(trxName);
|
||||||
|
assertFalse(info.isError(), "Error processing invoice: " + info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, invoice.getDocStatus(), "Invoice document status is not completed: " + invoice.getDocStatus());
|
||||||
|
assertTrue(Env.ONEHUNDRED.compareTo(invoice.getGrandTotal()) == 0, "Invoice grand total not as expected: " + invoice.getGrandTotal().toPlainString());
|
||||||
|
assertTrue(invoice.isPosted(), "Invoice not posted");
|
||||||
|
|
||||||
|
bpartner.load(trxName);
|
||||||
|
BigDecimal actualBalance = bpartner.getTotalOpenBalance();
|
||||||
|
assertTrue(actualBalance.compareTo(initialBalance.subtract(Env.ONEHUNDRED)) == 0);
|
||||||
|
|
||||||
|
// Pay $100
|
||||||
|
MPayment payment1 = new MPayment(ctx, 0, trxName);
|
||||||
|
payment1.setC_Invoice_ID(invoice.getC_Invoice_ID());
|
||||||
|
payment1.setC_BPartner_ID(invoice.getC_BPartner_ID());
|
||||||
|
payment1.setC_DocType_ID(false); // Payment
|
||||||
|
payment1.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
payment1.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
payment1.setPayAmt(Env.ONEHUNDRED);
|
||||||
|
payment1.setTenderType(MPayment.TENDERTYPE_Check);
|
||||||
|
payment1.setC_BankAccount_ID(BANK_ACCOUNT_1234);
|
||||||
|
payment1.setC_Currency_ID(CURRENCY_USD);
|
||||||
|
payment1.setDateTrx(invoice.getDateInvoiced());
|
||||||
|
payment1.setDateAcct(invoice.getDateInvoiced());
|
||||||
|
payment1.saveEx();
|
||||||
|
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(payment1, DocAction.ACTION_Complete);
|
||||||
|
payment1.load(trxName);
|
||||||
|
assertFalse(info.isError(), "Error processing payment: " + info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, payment1.getDocStatus(), "Payment document status is not completed: " + payment1.getDocStatus());
|
||||||
|
assertEquals(false, invoice.isPaid(), "Invoice isPaid() is not false");
|
||||||
|
assertTrue(payment1.isPosted(), "Payment not posted");
|
||||||
|
|
||||||
|
bpartner.load(trxName);
|
||||||
|
actualBalance = bpartner.getTotalOpenBalance();
|
||||||
|
assertTrue(actualBalance.compareTo(initialBalance) == 0);
|
||||||
|
|
||||||
|
// Delete Allocation
|
||||||
|
MAllocationHdr[] alloc1 = MAllocationHdr.getOfPayment(ctx, payment1.getC_Payment_ID(), trxName);
|
||||||
|
assertTrue(alloc1[0].delete(true, trxName));
|
||||||
|
|
||||||
|
bpartner.load(trxName);
|
||||||
|
actualBalance = bpartner.getTotalOpenBalance();
|
||||||
|
assertTrue(actualBalance.compareTo(initialBalance) == 0);
|
||||||
|
|
||||||
|
errorLogs = CLogErrorBuffer.get(true).getRecords(true);
|
||||||
|
if (errorLogs != null)
|
||||||
|
assertEquals(severeCount, errorLogs.length, "Severe errors recorded in log: " + errorLogs.length);
|
||||||
|
|
||||||
|
rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue