IDEMPIERE-5768 : Adding Credit manager factory to override or extend … (#1949)

* IDEMPIERE-5768 : Adding Credit manager factory to override or extend credit management

* IDEMPIERE-5768 : Fixing as as per code review comments from Hengsin

* IDEMPIERE-5768: Use component annotation instead of xml service definition

* IDEMPIERE-5768: Adding Unit tests and Making return type of CheckCreditStatus to be object instead of String.

* IDEMPIERE-5768: Fixing as per code review comment.

* IDEMPIERE-5768: Removed explicit rollback from test cases
This commit is contained in:
Deepak Pansheriya 2023-08-23 07:09:24 +05:30 committed by GitHub
parent 5aba739d37
commit 18f925fd2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1019 additions and 233 deletions

View File

@ -48,6 +48,7 @@ import org.compiere.model.MPaymentProcessor;
import org.compiere.model.MSysConfig; import org.compiere.model.MSysConfig;
import org.compiere.model.MTaxProvider; import org.compiere.model.MTaxProvider;
import org.compiere.model.ModelValidator; import org.compiere.model.ModelValidator;
import org.compiere.model.PO;
import org.compiere.model.PaymentInterface; import org.compiere.model.PaymentInterface;
import org.compiere.model.PaymentProcessor; import org.compiere.model.PaymentProcessor;
import org.compiere.model.StandardTaxProvider; import org.compiere.model.StandardTaxProvider;
@ -1087,4 +1088,42 @@ public class Core {
} }
return null; return null;
} }
/**
* get Credit Manager
*
* @param PO
* @return instance of the ICreditManager
*/
public static ICreditManager getCreditManager(PO po)
{
if (po == null)
{
s_log.log(Level.SEVERE, "Invalid PO");
return null;
}
ICreditManager myCreditManager = null;
List<ICreditManagerFactory> factoryList = Service.locator().list(ICreditManagerFactory.class).getServices();
if (factoryList != null)
{
for (ICreditManagerFactory factory : factoryList)
{
myCreditManager = factory.getCreditManager(po);
if (myCreditManager != null)
{
break;
}
}
}
if (myCreditManager == null)
{
s_log.log(Level.CONFIG, "For " + po.get_TableName() + " not found any service/extension registry.");
return null;
}
return myCreditManager;
} // getCreditManager
} }

View File

@ -0,0 +1,58 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.adempiere.base;
/**
* POJO for Credit Status
*
* @author Logilite Technologies
* @since August 04, 2023
*/
public class CreditStatus
{
protected String errorMsg;
protected boolean isError;
/**
* Credit Status Load Constructor
*
* @param errorMsg
* @param isError
*/
public CreditStatus(String errorMsg, boolean isError)
{
super();
this.errorMsg = errorMsg;
this.isError = isError;
}
public String getErrorMsg()
{
return errorMsg;
}
public void setErrorMsg(String errorMsg)
{
this.errorMsg = errorMsg;
}
public boolean isError()
{
return isError;
}
public void setError(boolean isError)
{
this.isError = isError;
}
}

View File

@ -0,0 +1,50 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.adempiere.base;
import org.compiere.model.MInOut;
import org.compiere.model.MInvoice;
import org.compiere.model.MOrder;
import org.compiere.model.MPayment;
import org.compiere.model.PO;
import org.compiere.model.credit.CreditManagerInOut;
import org.compiere.model.credit.CreditManagerInvoice;
import org.compiere.model.credit.CreditManagerOrder;
import org.compiere.model.credit.CreditManagerPayment;
import org.osgi.service.component.annotations.Component;
/**
* Default Credit Manager Factory
*
* @author Logilite Technologies
* @since June 25, 2023
*/
@Component(immediate = true, service = ICreditManagerFactory.class)
public class DefaultCreditManagerFactory implements ICreditManagerFactory
{
@Override
public ICreditManager getCreditManager(PO po)
{
if (po instanceof MOrder)
return new CreditManagerOrder((MOrder) po);
else if (po instanceof MInvoice)
return new CreditManagerInvoice((MInvoice) po);
else if (po instanceof MPayment)
return new CreditManagerPayment((MPayment) po);
else if (po instanceof MInOut)
return new CreditManagerInOut((MInOut) po);
return null;
}
}

View File

@ -0,0 +1,30 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.adempiere.base;
/**
* Interface for Credit Manager implementor
*
* @author Logilite Technologies
* @since June 25, 2023
*/
public interface ICreditManager
{
/**
* Check credit status as per document action
*
* @param docAction Document Action
* @return Credit Status POJO
*/
public CreditStatus checkCreditStatus(String docAction);
}

View File

@ -0,0 +1,32 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.adempiere.base;
import org.compiere.model.PO;
/**
* Interface for Credit Manager Factory
*
* @author Logilite Technologies
* @since June 25, 2023
*/
public interface ICreditManagerFactory
{
/**
* get Credit Manager
*
* @param po Model class
* @return instance of the ICreditManager
*/
public ICreditManager getCreditManager(PO po);
}

View File

@ -29,6 +29,8 @@ import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.base.Core; import org.adempiere.base.Core;
import org.adempiere.base.CreditStatus;
import org.adempiere.base.ICreditManager;
import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.AdempiereException;
import org.adempiere.exceptions.DBException; import org.adempiere.exceptions.DBException;
import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.adempiere.exceptions.NegativeInventoryDisallowedException;
@ -1454,40 +1456,14 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess
} }
// Credit Check // Credit Check
if (isSOTrx() && !isReversal() && !isCustomerReturn()) ICreditManager creditManager = Core.getCreditManager(this);
if (creditManager != null)
{ {
I_C_Order order = getC_Order(); CreditStatus status = creditManager.checkCreditStatus(DOCACTION_Prepare);
if (order != null && MDocType.DOCSUBTYPESO_PrepayOrder.equals(order.getC_DocType().getDocSubTypeSO()) if (status.isError())
&& !MSysConfig.getBooleanValue(MSysConfig.CHECK_CREDIT_ON_PREPAY_ORDER, true, getAD_Client_ID(), getAD_Org_ID())) { {
// ignore -- don't validate Prepay Orders depending on sysconfig parameter m_processMsg = status.getErrorMsg();
} else { return DocAction.STATUS_Invalid;
MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName());
if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus()))
{
m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@="
+ bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
return DocAction.STATUS_Invalid;
}
if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus()))
{
m_processMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@="
+ bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
return DocAction.STATUS_Invalid;
}
if (!MBPartner.SOCREDITSTATUS_NoCreditCheck.equals(bp.getSOCreditStatus())
&& Env.ZERO.compareTo(bp.getSO_CreditLimit()) != 0)
{
BigDecimal notInvoicedAmt = MBPartner.getNotInvoicedAmt(getC_BPartner_ID());
if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus(notInvoicedAmt)))
{
m_processMsg = "@BPartnerOverSCreditHold@ - @TotalOpenBalance@="
+ bp.getTotalOpenBalance() + ", @NotInvoicedAmt@=" + notInvoicedAmt
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
return DocAction.STATUS_Invalid;
}
}
} }
} }
@ -1557,7 +1533,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess
* Check if Document is Customer Return. * Check if Document is Customer Return.
* @return True if Document is Customer Return * @return True if Document is Customer Return
*/ */
private boolean isCustomerReturn() { public boolean isCustomerReturn() {
MDocType doctype = MDocType.get(getC_DocType_ID()); MDocType doctype = MDocType.get(getC_DocType_ID());
if(isSOTrx() && doctype.getDocBaseType().equals("MMR") && doctype.isSOTrx()) if(isSOTrx() && doctype.getDocBaseType().equals("MMR") && doctype.isSOTrx())
return true; return true;

View File

@ -32,6 +32,8 @@ import java.util.Vector;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.base.Core; import org.adempiere.base.Core;
import org.adempiere.base.CreditStatus;
import org.adempiere.base.ICreditManager;
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;
@ -1737,22 +1739,14 @@ public class MInvoice extends X_C_Invoice implements DocAction, IDocsPostProcess
} }
// Credit Status // Credit Status
if (isSOTrx()) ICreditManager creditManager = Core.getCreditManager(this);
if (creditManager != null)
{ {
MDocType doc = (MDocType) getC_DocTypeTarget(); CreditStatus status = creditManager.checkCreditStatus(DOCACTION_Prepare);
// IDEMPIERE-365 - just check credit if is going to increase the debt if (status.isError())
if ( (doc.getDocBaseType().equals(MDocType.DOCBASETYPE_ARCreditMemo) && getGrandTotal().signum() < 0 ) ||
(doc.getDocBaseType().equals(MDocType.DOCBASETYPE_ARInvoice) && getGrandTotal().signum() > 0 )
)
{ {
MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), null); m_processMsg = status.getErrorMsg();
if ( MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus()) ) return DocAction.STATUS_Invalid;
{
m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@="
+ bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
return DocAction.STATUS_Invalid;
}
} }
} }
@ -2144,76 +2138,15 @@ public class MInvoice extends X_C_Invoice implements DocAction, IDocsPostProcess
if (matchPO > 0) if (matchPO > 0)
info.append(" @M_MatchPO_ID@#").append(matchPO).append(" "); info.append(" @M_MatchPO_ID@#").append(matchPO).append(" ");
ICreditManager creditManager = Core.getCreditManager(this);
if (creditManager != null)
// 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 = null;
int baseCurrencyId = Env.getContextAsInt(getCtx(), Env.C_CURRENCY_ID);
if (getC_Currency_ID() != baseCurrencyId && isOverrideCurrencyRate())
{ {
invAmt = getGrandTotal(true).multiply(getCurrencyRate()); CreditStatus status = creditManager.checkCreditStatus(DOCACTION_Complete);
int stdPrecision = MCurrency.getStdPrecision(getCtx(), baseCurrencyId); if (status.isError())
if (invAmt.scale() > stdPrecision) {
invAmt = invAmt.setScale(stdPrecision, RoundingMode.HALF_UP); m_processMsg = status.getErrorMsg();
} return DocAction.STATUS_Invalid;
else }
{
invAmt = MConversionRate.convertBase(getCtx(), getGrandTotal(true), // CM adjusted
getC_Currency_ID(), getDateAcct(), getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID());
}
if (invAmt == null)
{
m_processMsg = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingCurrencyToBaseCurrency",
getC_Currency_ID(), MClient.get(getCtx()).getC_Currency_ID(), getC_ConversionType_ID(), getDateAcct(), get_TrxName());
return DocAction.STATUS_Invalid;
}
// Total Balance
BigDecimal newBalance = bp.getTotalOpenBalance();
if (newBalance == null)
newBalance = Env.ZERO;
if (isSOTrx())
{
newBalance = newBalance.add(invAmt);
//
if (bp.getFirstSale() == null)
bp.setFirstSale(getDateInvoiced());
BigDecimal newLifeAmt = bp.getActualLifeTimeValue();
if (newLifeAmt == null)
newLifeAmt = invAmt;
else
newLifeAmt = newLifeAmt.add(invAmt);
BigDecimal newCreditAmt = bp.getSO_CreditUsed();
if (newCreditAmt == null)
newCreditAmt = invAmt;
else
newCreditAmt = newCreditAmt.add(invAmt);
//
if (log.isLoggable(Level.FINE)) log.fine("GrandTotal=" + getGrandTotal(true) + "(" + invAmt
+ ") BP Life=" + bp.getActualLifeTimeValue() + "->" + newLifeAmt
+ ", Credit=" + bp.getSO_CreditUsed() + "->" + newCreditAmt
+ ", Balance=" + bp.getTotalOpenBalance() + " -> " + newBalance);
bp.setActualLifeTimeValue(newLifeAmt);
bp.setSO_CreditUsed(newCreditAmt);
} // SO
else
{
newBalance = newBalance.subtract(invAmt);
if (log.isLoggable(Level.FINE)) log.fine("GrandTotal=" + getGrandTotal(true) + "(" + invAmt
+ ") Balance=" + bp.getTotalOpenBalance() + " -> " + newBalance);
}
// the payment just created already updated the open balance
if ( ! (PAYMENTRULE_Cash.equals(getPaymentRule()) && !fromPOS ) )
{
bp.setTotalOpenBalance(newBalance);
}
bp.setSOCreditStatus();
if (!bp.save(get_TrxName()))
{
m_processMsg = "Could not update Business Partner";
return DocAction.STATUS_Invalid;
} }
// User - Last Result/Contact // User - Last Result/Contact

View File

@ -30,6 +30,8 @@ import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.base.Core; import org.adempiere.base.Core;
import org.adempiere.base.CreditStatus;
import org.adempiere.base.ICreditManager;
import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.AdempiereException;
import org.adempiere.exceptions.BPartnerNoBillToAddressException; import org.adempiere.exceptions.BPartnerNoBillToAddressException;
import org.adempiere.exceptions.BPartnerNoShipToAddressException; import org.adempiere.exceptions.BPartnerNoShipToAddressException;
@ -1683,46 +1685,14 @@ public class MOrder extends X_C_Order implements DocAction
} }
// Credit Check // Credit Check
if (isSOTrx()) ICreditManager creditManager = Core.getCreditManager(this);
if (creditManager != null)
{ {
if ( MDocType.DOCSUBTYPESO_POSOrder.equals(dt.getDocSubTypeSO()) CreditStatus status = creditManager.checkCreditStatus(DOCACTION_Prepare);
&& PAYMENTRULE_Cash.equals(getPaymentRule()) if (status.isError())
&& !MSysConfig.getBooleanValue(MSysConfig.CHECK_CREDIT_ON_CASH_POS_ORDER, true, getAD_Client_ID(), getAD_Org_ID())) { {
// ignore -- don't validate for Cash POS Orders depending on sysconfig parameter m_processMsg = status.getErrorMsg();
} else if (MDocType.DOCSUBTYPESO_PrepayOrder.equals(dt.getDocSubTypeSO()) return DocAction.STATUS_Invalid;
&& !MSysConfig.getBooleanValue(MSysConfig.CHECK_CREDIT_ON_PREPAY_ORDER, true, getAD_Client_ID(), getAD_Org_ID())) {
// ignore -- don't validate Prepay Orders depending on sysconfig parameter
} else {
MBPartner bp = new MBPartner (getCtx(), getBill_BPartner_ID(), get_TrxName()); // bill bp is guaranteed on beforeSave
if (getGrandTotal().signum() > 0) // IDEMPIERE-365 - just check credit if is going to increase the debt
{
if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus()))
{
m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@="
+ bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
return DocAction.STATUS_Invalid;
}
if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus()))
{
m_processMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@="
+ bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
return DocAction.STATUS_Invalid;
}
BigDecimal grandTotal = MConversionRate.convertBase(getCtx(),
getGrandTotal(), getC_Currency_ID(), getDateOrdered(),
getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID());
if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus(grandTotal)))
{
m_processMsg = "@BPartnerOverOCreditHold@ - @TotalOpenBalance@="
+ bp.getTotalOpenBalance() + ", @GrandTotal@=" + grandTotal
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
return DocAction.STATUS_Invalid;
}
}
} }
} }

View File

@ -29,6 +29,9 @@ import java.util.Properties;
import java.util.Vector; import java.util.Vector;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.base.Core;
import org.adempiere.base.CreditStatus;
import org.adempiere.base.ICreditManager;
import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.AdempiereException;
import org.adempiere.exceptions.PeriodClosedException; import org.adempiere.exceptions.PeriodClosedException;
import org.adempiere.util.IProcessUI; import org.adempiere.util.IProcessUI;
@ -1985,22 +1988,13 @@ public class MPayment extends X_C_Payment
return DocAction.STATUS_Invalid; return DocAction.STATUS_Invalid;
} }
// Do not pay when Credit Stop/Hold ICreditManager creditManager = Core.getCreditManager(this);
if (!isReceipt()) if (creditManager != null)
{ {
MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); CreditStatus status = creditManager.checkCreditStatus(DOCACTION_Prepare);
if (X_C_BPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus())) if (status.isError())
{ {
m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" m_processMsg = status.getErrorMsg();
+ bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
return DocAction.STATUS_Invalid;
}
if (X_C_BPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus()))
{
m_processMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@="
+ bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
return DocAction.STATUS_Invalid; return DocAction.STATUS_Invalid;
} }
} }
@ -2066,52 +2060,20 @@ public class MPayment extends X_C_Payment
if (log.isLoggable(Level.INFO)) log.info(toString()); if (log.isLoggable(Level.INFO)) log.info(toString());
// Charge Handling // Charge Handling
boolean createdAllocationRecords = false;
if (getC_Charge_ID() != 0) if (getC_Charge_ID() != 0)
{ {
setIsAllocated(true); setIsAllocated(true);
} }
else
{
createdAllocationRecords = allocateIt(); // Create Allocation Records
testAllocation();
}
// Update BP for Prepayments ICreditManager creditManager = Core.getCreditManager(this);
if (getC_BPartner_ID() != 0 && getC_Invoice_ID() == 0 && getC_Charge_ID() == 0 && MPaymentAllocate.get(this).length == 0 && !createdAllocationRecords) if (creditManager != null)
{ {
MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); CreditStatus status = creditManager.checkCreditStatus(DOCACTION_Complete);
DB.getDatabase().forUpdate(bp, 0); if (status.isError())
// Update total balance to include this payment
BigDecimal payAmt = null;
int baseCurrencyId = Env.getContextAsInt(getCtx(), Env.C_CURRENCY_ID);
if (getC_Currency_ID() != baseCurrencyId && isOverrideCurrencyRate())
{ {
payAmt = getConvertedAmt(); m_processMsg = status.getErrorMsg();
return DocAction.STATUS_Invalid;
} }
else
{
payAmt = MConversionRate.convertBase(getCtx(), getPayAmt(),
getC_Currency_ID(), getDateAcct(), getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID());
if (payAmt == null)
{
m_processMsg = MConversionRateUtil.getErrorMessage(getCtx(), "ErrorConvertingCurrencyToBaseCurrency",
getC_Currency_ID(), MClient.get(getCtx()).getC_Currency_ID(), getC_ConversionType_ID(), getDateAcct(), get_TrxName());
return DocAction.STATUS_Invalid;
}
}
// Total Balance
BigDecimal newBalance = bp.getTotalOpenBalance();
if (newBalance == null)
newBalance = Env.ZERO;
if (isReceipt())
newBalance = newBalance.subtract(payAmt);
else
newBalance = newBalance.add(payAmt);
bp.setTotalOpenBalance(newBalance);
bp.setSOCreditStatus();
bp.saveEx();
} }
// Counter Doc // Counter Doc
@ -2832,13 +2794,10 @@ public class MPayment extends X_C_Payment
// //
info.append(" - @C_AllocationHdr_ID@: ").append(alloc.getDocumentNo()); info.append(" - @C_AllocationHdr_ID@: ").append(alloc.getDocumentNo());
ICreditManager creditManager = Core.getCreditManager(this);
// Update BPartner // Update BPartner
if (getC_BPartner_ID() != 0) if (creditManager != null)
{ creditManager.checkCreditStatus(accrual ? DOCACTION_Reverse_Accrual : DOCACTION_Reverse_Correct);
MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName());
bp.setTotalOpenBalance();
bp.saveEx(get_TrxName());
}
return info; return info;
} }

View File

@ -0,0 +1,89 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.compiere.model.credit;
import java.math.BigDecimal;
import org.adempiere.base.CreditStatus;
import org.adempiere.base.ICreditManager;
import org.compiere.model.I_C_Order;
import org.compiere.model.MBPartner;
import org.compiere.model.MDocType;
import org.compiere.model.MInOut;
import org.compiere.model.MSysConfig;
import org.compiere.util.Env;
import org.compiere.util.Util;
/**
* Credit Manager for InOut
*
* @author Logilite Technologies
* @since June 25, 2023
*/
public class CreditManagerInOut implements ICreditManager
{
private MInOut mInOut;
/**
* InOut Credit Manager Load Constructor
*
* @param po MInOut
*/
public CreditManagerInOut(MInOut po)
{
this.mInOut = po;
}
@Override
public CreditStatus checkCreditStatus(String docAction)
{
String errorMsg = null;
if (MInOut.DOCACTION_Prepare.equals(docAction) && mInOut.isSOTrx() && !mInOut.isReversal() && !mInOut.isCustomerReturn())
{
I_C_Order order = mInOut.getC_Order();
if (order != null
&& MDocType.DOCSUBTYPESO_PrepayOrder.equals(order.getC_DocType().getDocSubTypeSO())
&& !MSysConfig.getBooleanValue(MSysConfig.CHECK_CREDIT_ON_PREPAY_ORDER, true, mInOut.getAD_Client_ID(), mInOut.getAD_Org_ID()))
{
// ignore -- don't validate Prepay Orders depending on sysconfig parameter
}
else
{
MBPartner bp = new MBPartner(mInOut.getCtx(), mInOut.getC_BPartner_ID(), mInOut.get_TrxName());
if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus()))
{
errorMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" + bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
}
if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus()))
{
errorMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@=" + bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
}
if (!MBPartner.SOCREDITSTATUS_NoCreditCheck.equals(bp.getSOCreditStatus())
&& Env.ZERO.compareTo(bp.getSO_CreditLimit()) != 0)
{
BigDecimal notInvoicedAmt = MBPartner.getNotInvoicedAmt(mInOut.getC_BPartner_ID());
if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus(notInvoicedAmt)))
{
errorMsg = "@BPartnerOverSCreditHold@ - @TotalOpenBalance@=" + bp.getTotalOpenBalance()
+ ", @NotInvoicedAmt@=" + notInvoicedAmt
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
}
}
}
}
return new CreditStatus(errorMsg, !Util.isEmpty(errorMsg));
} // creditCheck
}

View File

@ -0,0 +1,168 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.compiere.model.credit;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.logging.Level;
import org.adempiere.base.CreditStatus;
import org.adempiere.base.ICreditManager;
import org.compiere.model.MBPartner;
import org.compiere.model.MClient;
import org.compiere.model.MConversionRate;
import org.compiere.model.MConversionRateUtil;
import org.compiere.model.MCurrency;
import org.compiere.model.MDocType;
import org.compiere.model.MInvoice;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Util;
/**
* Credit Manager for Invoice
*
* @author Logilite Technologies
* @since June 25, 2023
*/
public class CreditManagerInvoice implements ICreditManager
{
/** Logger */
protected transient CLogger log = CLogger.getCLogger(CreditManagerInvoice.class);
private MInvoice mInvoice;
/**
* Invoice Credit Manager Load Constructor
*
* @param po MInvoice
*/
public CreditManagerInvoice(MInvoice po)
{
this.mInvoice = po;
}
@Override
public CreditStatus checkCreditStatus(String docAction)
{
String errorMsg = null;
if (MInvoice.DOCACTION_Prepare.equals(docAction) && mInvoice.isSOTrx())
{
MDocType doc = (MDocType) mInvoice.getC_DocTypeTarget();
// IDEMPIERE-365 - just check credit if is going to increase the debt
if ((doc.getDocBaseType().equals(MDocType.DOCBASETYPE_ARCreditMemo) && mInvoice.getGrandTotal().signum() < 0)
|| (doc.getDocBaseType().equals(MDocType.DOCBASETYPE_ARInvoice) && mInvoice.getGrandTotal().signum() > 0))
{
MBPartner bp = new MBPartner(mInvoice.getCtx(), mInvoice.getC_BPartner_ID(), mInvoice.get_TrxName());
if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus()))
{
errorMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" + bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
}
}
}
else if (MInvoice.DOCACTION_Complete.equals(docAction))
{
// POS supports multiple payments
boolean fromPOS = false;
if (mInvoice.getC_Order_ID() > 0)
{
fromPOS = mInvoice.getC_Order().getC_POS_ID() > 0;
}
// Update BP Statistics
MBPartner bp = new MBPartner(mInvoice.getCtx(), mInvoice.getC_BPartner_ID(), mInvoice.get_TrxName());
DB.getDatabase().forUpdate(bp, 0);
// Update total revenue and balance / credit limit (reversed on
// AllocationLine.processIt)
BigDecimal invAmt = null;
int baseCurrencyId = Env.getContextAsInt(mInvoice.getCtx(), Env.C_CURRENCY_ID);
if (mInvoice.getC_Currency_ID() != baseCurrencyId && mInvoice.isOverrideCurrencyRate())
{
invAmt = mInvoice.getGrandTotal(true).multiply(mInvoice.getCurrencyRate());
int stdPrecision = MCurrency.getStdPrecision(mInvoice.getCtx(), baseCurrencyId);
if (invAmt.scale() > stdPrecision)
invAmt = invAmt.setScale(stdPrecision, RoundingMode.HALF_UP);
}
else
{
invAmt = MConversionRate.convertBase( mInvoice.getCtx(),
mInvoice.getGrandTotal(true), // CM adjusted
mInvoice.getC_Currency_ID(),
mInvoice.getDateAcct(),
mInvoice.getC_ConversionType_ID(),
mInvoice.getAD_Client_ID(),
mInvoice.getAD_Org_ID());
}
if (invAmt == null)
{
errorMsg = MConversionRateUtil.getErrorMessage( mInvoice.getCtx(),
"ErrorConvertingCurrencyToBaseCurrency",
mInvoice.getC_Currency_ID(),
MClient.get(mInvoice.getCtx()).getC_Currency_ID(),
mInvoice.getC_ConversionType_ID(),
mInvoice.getDateAcct(),
mInvoice.get_TrxName());
}
// Total Balance
BigDecimal newBalance = bp.getTotalOpenBalance();
if (newBalance == null)
newBalance = Env.ZERO;
if (mInvoice.isSOTrx())
{
newBalance = newBalance.add(invAmt);
//
if (bp.getFirstSale() == null)
bp.setFirstSale(mInvoice.getDateInvoiced());
BigDecimal newLifeAmt = bp.getActualLifeTimeValue();
if (newLifeAmt == null)
newLifeAmt = invAmt;
else
newLifeAmt = newLifeAmt.add(invAmt);
BigDecimal newCreditAmt = bp.getSO_CreditUsed();
if (newCreditAmt == null)
newCreditAmt = invAmt;
else
newCreditAmt = newCreditAmt.add(invAmt);
//
if (log.isLoggable(Level.FINE))
log.fine( "GrandTotal=" + mInvoice.getGrandTotal(true) + "(" + invAmt
+ ") BP Life=" + bp.getActualLifeTimeValue() + "->" + newLifeAmt
+ ", Credit=" + bp.getSO_CreditUsed() + "->" + newCreditAmt
+ ", Balance=" + bp.getTotalOpenBalance() + " -> " + newBalance);
bp.setActualLifeTimeValue(newLifeAmt);
bp.setSO_CreditUsed(newCreditAmt);
} // SO
else
{
newBalance = newBalance.subtract(invAmt);
if (log.isLoggable(Level.FINE))
log.fine( "GrandTotal=" + mInvoice.getGrandTotal(true) + "(" + invAmt
+ ") Balance=" + bp.getTotalOpenBalance() + " -> " + newBalance);
}
// the payment just created already updated the open balance
if (!(MInvoice.PAYMENTRULE_Cash.equals(mInvoice.getPaymentRule()) && !fromPOS))
{
bp.setTotalOpenBalance(newBalance);
}
bp.setSOCreditStatus();
if (!bp.save(mInvoice.get_TrxName()))
{
errorMsg = "Could not update Business Partner";
}
}
return new CreditStatus(errorMsg, !Util.isEmpty(errorMsg));
} // creditCheck
}

View File

@ -0,0 +1,99 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.compiere.model.credit;
import java.math.BigDecimal;
import java.util.Properties;
import org.adempiere.base.CreditStatus;
import org.adempiere.base.ICreditManager;
import org.compiere.model.MBPartner;
import org.compiere.model.MConversionRate;
import org.compiere.model.MDocType;
import org.compiere.model.MOrder;
import org.compiere.model.MSysConfig;
import org.compiere.util.Util;
/**
* Credit Manager for Order
*
* @author Logilite Technologies
* @since June 25, 2023
*/
public class CreditManagerOrder implements ICreditManager
{
private MOrder order;
/**
* Order Credit Manager Load Constructor
*
* @param po MOrder
*/
public CreditManagerOrder(MOrder po)
{
this.order = po;
}
@Override
public CreditStatus checkCreditStatus(String docAction)
{
String errorMsg = null;
if (MOrder.DOCACTION_Prepare.equals(docAction) && order.isSOTrx())
{
Properties ctx = order.getCtx();
MDocType dt = MDocType.get(ctx, order.getC_DocTypeTarget_ID());
if (MDocType.DOCSUBTYPESO_POSOrder.equals(dt.getDocSubTypeSO())
&& MOrder.PAYMENTRULE_Cash.equals(order.getPaymentRule())
&& !MSysConfig.getBooleanValue(MSysConfig.CHECK_CREDIT_ON_CASH_POS_ORDER, true, order.getAD_Client_ID(), order.getAD_Org_ID()))
{
// ignore -- don't validate for Cash POS Orders depending on sysconfig parameter
}
else if (MDocType.DOCSUBTYPESO_PrepayOrder.equals(dt.getDocSubTypeSO())
&& !MSysConfig.getBooleanValue(MSysConfig.CHECK_CREDIT_ON_PREPAY_ORDER, true, order.getAD_Client_ID(), order.getAD_Org_ID()))
{
// ignore -- don't validate Prepay Orders depending on sysconfig parameter
}
else
{
// bill bp is guaranteed on beforeSave
MBPartner bp = new MBPartner(ctx, order.getBill_BPartner_ID(), order.get_TrxName());
// IDEMPIERE-365 - just check credit if is going to increase the debt
if (order.getGrandTotal().signum() > 0)
{
if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus()))
{
errorMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" + bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
}
if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus()))
{
errorMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@=" + bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
}
BigDecimal grandTotal = MConversionRate.convertBase(ctx, order.getGrandTotal(), order.getC_Currency_ID(),
order.getDateOrdered(), order.getC_ConversionType_ID(),
order.getAD_Client_ID(), order.getAD_Org_ID());
if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus(grandTotal)))
{
errorMsg = "@BPartnerOverOCreditHold@ - @TotalOpenBalance@=" + bp.getTotalOpenBalance()
+ ", @GrandTotal@=" + grandTotal
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
}
}
}
}
return new CreditStatus(errorMsg, !Util.isEmpty(errorMsg));
} // creditCheck
}

View File

@ -0,0 +1,137 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.compiere.model.credit;
import java.math.BigDecimal;
import java.util.Properties;
import org.adempiere.base.CreditStatus;
import org.adempiere.base.ICreditManager;
import org.compiere.model.MBPartner;
import org.compiere.model.MClient;
import org.compiere.model.MConversionRate;
import org.compiere.model.MConversionRateUtil;
import org.compiere.model.MPayment;
import org.compiere.model.MPaymentAllocate;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Util;
/**
* Credit Manager for Payment
*
* @author Logilite Technologies
* @since June 25, 2023
*/
public class CreditManagerPayment implements ICreditManager
{
private MPayment payment;
/**
* Payment Credit Manager Load Constructor
*
* @param po MPayment
*/
public CreditManagerPayment(MPayment po)
{
this.payment = po;
}
@Override
public CreditStatus checkCreditStatus(String docAction)
{
String errorMsg = null;
if (MPayment.DOCACTION_Prepare.equals(docAction) && !payment.isReceipt())
{ // Do not pay when Credit Stop/Hold and issue refund to customer
MBPartner bp = new MBPartner(payment.getCtx(), payment.getC_BPartner_ID(), payment.get_TrxName());
if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus()))
{
errorMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" + bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
}
if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus()))
{
errorMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@=" + bp.getTotalOpenBalance()
+ ", @SO_CreditLimit@=" + bp.getSO_CreditLimit();
}
}
else if (MPayment.DOCACTION_Complete.equals(docAction))
{
// Charge Handling
boolean createdAllocationRecords = false;
if (payment.getC_Charge_ID() == 0)
{
createdAllocationRecords = payment.allocateIt(); // Create Allocation Records
payment.testAllocation();
}
// Update BP for Prepayments
if (payment.getC_BPartner_ID() != 0
&& payment.getC_Invoice_ID() == 0
&& payment.getC_Charge_ID() == 0
&& MPaymentAllocate.get(payment).length == 0
&& !createdAllocationRecords)
{
Properties ctx = payment.getCtx();
MBPartner bp = new MBPartner(ctx, payment.getC_BPartner_ID(), payment.get_TrxName());
DB.getDatabase().forUpdate(bp, 0);
// Update total balance to include this payment
BigDecimal payAmt = null;
int baseCurrencyId = Env.getContextAsInt(ctx, Env.C_CURRENCY_ID);
if (payment.getC_Currency_ID() != baseCurrencyId && payment.isOverrideCurrencyRate())
{
payAmt = payment.getConvertedAmt();
}
else
{
payAmt = MConversionRate.convertBase( ctx, payment.getPayAmt(),
payment.getC_Currency_ID(),
payment.getDateAcct(),
payment.getC_ConversionType_ID(),
payment.getAD_Client_ID(),
payment.getAD_Org_ID());
if (payAmt == null)
{
errorMsg = MConversionRateUtil.getErrorMessage( ctx, "ErrorConvertingCurrencyToBaseCurrency",
payment.getC_Currency_ID(),
MClient.get(ctx).getC_Currency_ID(),
payment.getC_ConversionType_ID(),
payment.getDateAcct(), payment.get_TrxName());
}
}
// Total Balance
BigDecimal newBalance = bp.getTotalOpenBalance();
if (newBalance == null)
newBalance = Env.ZERO;
if (payment.isReceipt())
newBalance = newBalance.subtract(payAmt);
else
newBalance = newBalance.add(payAmt);
bp.setTotalOpenBalance(newBalance);
bp.setSOCreditStatus();
bp.saveEx();
}
}
else if (MPayment.DOCACTION_Reverse_Accrual.equals(docAction) || MPayment.DOCACTION_Reverse_Correct.equals(docAction))
{
if (payment.getC_BPartner_ID() != 0)
{
MBPartner bp = new MBPartner(payment.getCtx(), payment.getC_BPartner_ID(), payment.get_TrxName());
bp.setTotalOpenBalance();
bp.saveEx(payment.get_TrxName());
}
}
return new CreditStatus(errorMsg, !Util.isEmpty(errorMsg));
} // creditCheck
}

View File

@ -121,12 +121,12 @@ public class InOutTest extends AbstractTestCase {
try { try {
MOrder order = createPurchaseOrder(bpartner, currentDate, priceList.getM_PriceList_ID(), Company_ConversionType_ID); MOrder order = createPurchaseOrder(bpartner, currentDate, priceList.getM_PriceList_ID(), Company_ConversionType_ID);
BigDecimal qtyOrdered = new BigDecimal(500); BigDecimal qtyOrdered = new BigDecimal(500);
MOrderLine orderLine = createPurchaseOrderLine(order, 10, product, qtyOrdered, priceInAud); MOrderLine orderLine = createOrderLine(order, 10, product, qtyOrdered, priceInAud);
completeDocument(order); completeDocument(order);
MInOut receipt = createMMReceipt(order, currentDate); MInOut receipt = createMMReceipt(order, currentDate);
BigDecimal qtyDelivered = new BigDecimal(500); BigDecimal qtyDelivered = new BigDecimal(500);
MInOutLine receiptLine = createMMReceiptLine(receipt, orderLine, qtyDelivered); MInOutLine receiptLine = createInOutLine(receipt, orderLine, qtyDelivered);
completeDocument(receipt); completeDocument(receipt);
postDocument(receipt); postDocument(receipt);
@ -162,11 +162,11 @@ public class InOutTest extends AbstractTestCase {
} }
order = createPurchaseOrder(bpartner, currentDate, priceList.getM_PriceList_ID(), Spot_ConversionType_ID); order = createPurchaseOrder(bpartner, currentDate, priceList.getM_PriceList_ID(), Spot_ConversionType_ID);
orderLine = createPurchaseOrderLine(order, 10, product, qtyOrdered, priceInAud); orderLine = createOrderLine(order, 10, product, qtyOrdered, priceInAud);
completeDocument(order); completeDocument(order);
receipt = createMMReceipt(order, currentDate); receipt = createMMReceipt(order, currentDate);
receiptLine = createMMReceiptLine(receipt, orderLine, qtyDelivered); receiptLine = createInOutLine(receipt, orderLine, qtyDelivered);
completeDocument(receipt); completeDocument(receipt);
postDocument(receipt); postDocument(receipt);
@ -251,12 +251,12 @@ public class InOutTest extends AbstractTestCase {
try { try {
MOrder order = createPurchaseOrder(bpartner, currentDate, priceList.getM_PriceList_ID(), Company_ConversionType_ID); MOrder order = createPurchaseOrder(bpartner, currentDate, priceList.getM_PriceList_ID(), Company_ConversionType_ID);
BigDecimal qtyOrdered = BigDecimal.TEN; BigDecimal qtyOrdered = BigDecimal.TEN;
MOrderLine orderLine = createPurchaseOrderLine(order, 10, product, qtyOrdered, priceInAud); MOrderLine orderLine = createOrderLine(order, 10, product, qtyOrdered, priceInAud);
completeDocument(order); completeDocument(order);
MInOut receipt = createMMReceipt(order, currentDate); MInOut receipt = createMMReceipt(order, currentDate);
BigDecimal qtyDelivered = BigDecimal.TEN; BigDecimal qtyDelivered = BigDecimal.TEN;
MInOutLine receiptLine = createMMReceiptLine(receipt, orderLine, qtyDelivered); MInOutLine receiptLine = createInOutLine(receipt, orderLine, qtyDelivered);
completeDocument(receipt); completeDocument(receipt);
postDocument(receipt); postDocument(receipt);
@ -384,22 +384,35 @@ public class InOutTest extends AbstractTestCase {
ConversionRateHelper.deleteConversionRate(cr); ConversionRateHelper.deleteConversionRate(cr);
} }
private MOrder createPurchaseOrder(MBPartner bpartner, Timestamp date, int M_PriceList_ID, int C_ConversionType_ID) { private MOrder createPurchaseOrder(MBPartner bpartner, Timestamp date, int M_PriceList_ID, int C_ConversionType_ID)
{
return createOrder(bpartner, date, M_PriceList_ID, C_ConversionType_ID, false);
}
private MOrder createSalseOrder(MBPartner bpartner, Timestamp date, int M_PriceList_ID, int C_ConversionType_ID)
{
return createOrder(bpartner, date, M_PriceList_ID, C_ConversionType_ID, true);
}
private MOrder createOrder(MBPartner bpartner, Timestamp date, int M_PriceList_ID, int C_ConversionType_ID, boolean isSOTrx)
{
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName()); MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
order.setAD_Org_ID(DictionaryIDs.AD_Org.HQ.id);
order.setBPartner(bpartner); order.setBPartner(bpartner);
order.setIsSOTrx(false); order.setIsSOTrx(isSOTrx);
order.setC_DocTypeTarget_ID(); order.setC_DocTypeTarget_ID();
order.setDateOrdered(date); order.setDateOrdered(date);
order.setDateAcct(date); order.setDateAcct(date);
order.setM_PriceList_ID(M_PriceList_ID); order.setM_PriceList_ID(M_PriceList_ID);
order.setC_ConversionType_ID(C_ConversionType_ID); order.setC_ConversionType_ID(C_ConversionType_ID);
order.setM_Warehouse_ID(DictionaryIDs.M_Warehouse.HQ.id);
order.setDocStatus(DocAction.STATUS_Drafted); order.setDocStatus(DocAction.STATUS_Drafted);
order.setDocAction(DocAction.ACTION_Complete); order.setDocAction(DocAction.ACTION_Complete);
order.saveEx(); order.saveEx();
return order; return order;
} }
private MOrderLine createPurchaseOrderLine(MOrder order, int line, MProduct product, BigDecimal qty, BigDecimal price) { private MOrderLine createOrderLine(MOrder order, int line, MProduct product, BigDecimal qty, BigDecimal price) {
MOrderLine orderLine = new MOrderLine(order); MOrderLine orderLine = new MOrderLine(order);
orderLine.setLine(line); orderLine.setLine(line);
orderLine.setProduct(product); orderLine.setProduct(product);
@ -415,13 +428,20 @@ public class InOutTest extends AbstractTestCase {
return receipt; return receipt;
} }
private MInOutLine createMMReceiptLine(MInOut receipt, MOrderLine orderLine, BigDecimal qty) {
MInOutLine receiptLine = new MInOutLine(receipt); private MInOut createShipment(MOrder order, Timestamp date) {
MInOut receipt = new MInOut(order, DictionaryIDs.C_DocType.MM_SHIPMENT.id, date); // MM Shipment
receipt.saveEx();
return receipt;
}
private MInOutLine createInOutLine(MInOut mInOut, MOrderLine orderLine, BigDecimal qty) {
MInOutLine receiptLine = new MInOutLine(mInOut);
receiptLine.setC_OrderLine_ID(orderLine.get_ID()); receiptLine.setC_OrderLine_ID(orderLine.get_ID());
receiptLine.setLine(orderLine.getLine()); receiptLine.setLine(orderLine.getLine());
receiptLine.setProduct(orderLine.getProduct()); receiptLine.setProduct(orderLine.getProduct());
receiptLine.setQty(qty); receiptLine.setQty(qty);
MWarehouse wh = MWarehouse.get(Env.getCtx(), receipt.getM_Warehouse_ID()); MWarehouse wh = MWarehouse.get(Env.getCtx(), mInOut.getM_Warehouse_ID());
int M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); int M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID();
receiptLine.setM_Locator_ID(M_Locator_ID); receiptLine.setM_Locator_ID(M_Locator_ID);
receiptLine.saveEx(); receiptLine.saveEx();
@ -500,4 +520,46 @@ public class InOutTest extends AbstractTestCase {
assertEquals(shipperAccount, inout.getShipperAccount(), "Unexpected shipper account"); assertEquals(shipperAccount, inout.getShipperAccount(), "Unexpected shipper account");
assertEquals(MInOut.FREIGHTCHARGES_Collect, inout.getFreightCharges(), "Unexpected freight charges rule"); assertEquals(MInOut.FREIGHTCHARGES_Collect, inout.getFreightCharges(), "Unexpected freight charges rule");
} }
/**
* Test cases for Credit Check
*/
@Test
public void testCreditCheckInOut()
{
MBPartner bpartner = MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.TREE_FARM.id, getTrxName());
bpartner.setSOCreditStatus(MBPartner.SOCREDITSTATUS_NoCreditCheck);
bpartner.saveEx();
MProduct product = MProduct.get(Env.getCtx(), DictionaryIDs.M_Product.ELM.id);
Timestamp currentDate = Env.getContextAsDate(Env.getCtx(), "#Date");
MOrder order = createSalseOrder(bpartner, currentDate, DictionaryIDs.M_PriceList.STANDARD.id, DictionaryIDs.C_ConversionType.COMPANY.id);
MOrderLine orderLine = createOrderLine(order, 10, product, new BigDecimal(500), new BigDecimal(23.32));
completeDocument(order);
MInOut receipt = createShipment(order, currentDate);
BigDecimal qtyDelivered = new BigDecimal(500);
createInOutLine(receipt, orderLine, qtyDelivered);
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Prepare);
receipt.load(getTrxName());
assertFalse(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_InProgress, receipt.getDocStatus());
bpartner.setSOCreditStatus(MBPartner.SOCREDITSTATUS_CreditStop);
bpartner.saveEx();
receipt.load(getTrxName());
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Prepare);
assertTrue(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Invalid, receipt.getDocStatus());
bpartner.setSOCreditStatus(MBPartner.SOCREDITSTATUS_CreditHold);
bpartner.saveEx();
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Prepare);
assertTrue(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Invalid, receipt.getDocStatus());
}
} }

View File

@ -0,0 +1,91 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.idempiere.test.base;
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 org.compiere.model.MBPartner;
import org.compiere.model.MInvoice;
import org.compiere.model.MInvoiceLine;
import org.compiere.model.MProduct;
import org.compiere.process.DocAction;
import org.compiere.process.ProcessInfo;
import org.compiere.util.Env;
import org.compiere.util.TimeUtil;
import org.compiere.wf.MWorkflow;
import org.idempiere.test.AbstractTestCase;
import org.idempiere.test.DictionaryIDs;
import org.junit.jupiter.api.Test;
/**
* Invoice Test cases.
*
* @author Logilite Technologies
* @since August 07, 2023
*/
public class InvoiceTest extends AbstractTestCase
{
public InvoiceTest()
{
}
/**
* Test cases for Credit Check
*/
@Test
public void testCreditCheckInvoice()
{
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
// Joe Block
MBPartner bp = MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id, getTrxName());
bp.setSOCreditStatus(MBPartner.SOCREDITSTATUS_CreditStop);
bp.saveEx();
MInvoice invoice = new MInvoice(Env.getCtx(), 0, getTrxName());
invoice.setBPartner(bp);
invoice.setAD_Org_ID(DictionaryIDs.AD_Org.HQ.id);
invoice.setC_DocTypeTarget_ID(MInvoice.DOCBASETYPE_ARInvoice);
invoice.setDateInvoiced(today);
invoice.setDateAcct(today);
invoice.setM_PriceList_ID(DictionaryIDs.M_PriceList.STANDARD.id);
invoice.setPaymentRule(MInvoice.PAYMENTRULE_OnCredit);
invoice.saveEx();
MInvoiceLine invoiceLine = new MInvoiceLine(Env.getCtx(), 0, getTrxName());
invoiceLine.setInvoice(invoice);
invoiceLine.setC_Invoice_ID(invoice.getC_Invoice_ID());
invoiceLine.setAD_Org_ID(DictionaryIDs.AD_Org.HQ.id);
invoiceLine.setLine(10);
invoiceLine.setProduct(MProduct.get(Env.getCtx(), DictionaryIDs.M_Product.FERTILIZER_50.id));
invoiceLine.setQty(new BigDecimal("1"));
invoiceLine.saveEx();
invoice.load(getTrxName());
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Prepare);
assertTrue(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Invalid, invoice.getDocStatus());
bp.setSOCreditStatus(MBPartner.SOCREDITSTATUS_NoCreditCheck);
bp.saveEx();
info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Completed, invoice.getDocStatus());
}
}

View File

@ -25,12 +25,21 @@
package org.idempiere.test.model; package org.idempiere.test.model;
import static org.junit.jupiter.api.Assertions.assertEquals; 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 static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
import java.sql.Timestamp;
import org.compiere.model.MBPartner;
import org.compiere.model.MPayment; import org.compiere.model.MPayment;
import org.compiere.process.DocAction;
import org.compiere.process.ProcessInfo;
import org.compiere.util.DB; import org.compiere.util.DB;
import org.compiere.util.Env; import org.compiere.util.Env;
import org.compiere.util.TimeUtil;
import org.compiere.util.Util; import org.compiere.util.Util;
import org.compiere.wf.MWorkflow;
import org.idempiere.test.AbstractTestCase; import org.idempiere.test.AbstractTestCase;
import org.idempiere.test.DictionaryIDs; import org.idempiere.test.DictionaryIDs;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -63,4 +72,44 @@ public class PaymentTest extends AbstractTestCase {
payment.saveEx(); payment.saveEx();
assertTrue(Util.isEmpty(payment.getCreditCardVV()), "Credit card verification code not clear after change of tender type: "+payment.getCreditCardVV()); assertTrue(Util.isEmpty(payment.getCreditCardVV()), "Credit card verification code not clear after change of tender type: "+payment.getCreditCardVV());
} }
/**
* Test cases for Credit Check
*/
@Test
public void testCreditCheckPayment()
{
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
// Joe Block
MBPartner bp = MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id, getTrxName());
bp.setSOCreditStatus(MBPartner.SOCREDITSTATUS_CreditStop);
bp.saveEx();
MPayment payment = new MPayment(Env.getCtx(), 0, getTrxName());
payment.setC_BPartner_ID(bp.getC_BPartner_ID());
payment.setC_BankAccount_ID(DictionaryIDs.C_BankAccount.HQ_POS_CASH.id);
payment.setC_Currency_ID(DictionaryIDs.C_Currency.USD.id);
payment.setAD_Org_ID(DictionaryIDs.AD_Org.HQ.id);
payment.setC_DocType_ID(false);
payment.setDateTrx(today);
payment.setPayAmt(new BigDecimal(1000));
payment.setDateAcct(today);
payment.saveEx();
payment.load(getTrxName());
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(payment, DocAction.ACTION_Prepare);
assertTrue(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Invalid, payment.getDocStatus());
bp.setSOCreditStatus(MBPartner.SOCREDITSTATUS_NoCreditCheck);
bp.saveEx();
info = MWorkflow.runDocumentActionWorkflow(payment, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Completed, payment.getDocStatus());
info = MWorkflow.runDocumentActionWorkflow(payment, DocAction.ACTION_Reverse_Accrual);
assertFalse(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Reversed, payment.getDocStatus());
}
} }

View File

@ -1596,4 +1596,48 @@ public class SalesOrderTest extends AbstractTestCase {
assertFalse(pi.isError(), pi.getSummary()); assertFalse(pi.isError(), pi.getSummary());
} }
/**
* Test cases for Credit Check
*/
@Test
public void testCreditCheckOrder()
{
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
// Joe Block
MBPartner bp = MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id, getTrxName());
bp.setSOCreditStatus(MBPartner.SOCREDITSTATUS_CreditHold);
bp.saveEx();
// test 2 - credit Check
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
order.setM_Warehouse_ID(DictionaryIDs.AD_Org.HQ.id);
order.setBPartner(bp);
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
order.setDeliveryRule(MOrder.DELIVERYRULE_Availability);
order.setM_Warehouse_ID(DictionaryIDs.M_Warehouse.HQ.id);
order.setDocStatus(DocAction.STATUS_Drafted);
order.setDocAction(DocAction.ACTION_Prepare);
order.setDatePromised(today);
order.saveEx();
MOrderLine line1 = new MOrderLine(order);
line1.setLine(10);
// Azalea Bush
line1.setProduct(MProduct.get(Env.getCtx(), DictionaryIDs.M_Product.AZALEA_BUSH.id));
line1.setQty(new BigDecimal("1"));
line1.setDatePromised(today);
line1.saveEx();
order.load(getTrxName());
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Prepare);
assertTrue(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Invalid, order.getDocStatus());
bp.setSOCreditStatus(MBPartner.SOCREDITSTATUS_CreditStop);
bp.saveEx();
info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Prepare);
assertTrue(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Invalid, order.getDocStatus());
}
} }