IDEMPIERE-4623 Cash POS Sales Order is leaving wrong BP balance (#504)

This commit is contained in:
Carlos Ruiz 2021-01-03 13:08:16 +01:00 committed by GitHub
parent 09d7fea9e1
commit 11dc7a391f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 110 additions and 27 deletions

View File

@ -2076,7 +2076,11 @@ public class MInvoice extends X_C_Invoice implements DocAction
if (log.isLoggable(Level.FINE)) log.fine("GrandTotal=" + getGrandTotal(true) + "(" + invAmt if (log.isLoggable(Level.FINE)) log.fine("GrandTotal=" + getGrandTotal(true) + "(" + invAmt
+ ") Balance=" + bp.getTotalOpenBalance() + " -> " + newBalance); + ") Balance=" + bp.getTotalOpenBalance() + " -> " + newBalance);
} }
bp.setTotalOpenBalance(newBalance); // the payment just created already updated the open balance
if ( ! (PAYMENTRULE_Cash.equals(getPaymentRule()) && !fromPOS ) )
{
bp.setTotalOpenBalance(newBalance);
}
bp.setSOCreditStatus(); bp.setSOCreditStatus();
if (!bp.save(get_TrxName())) if (!bp.save(get_TrxName()))
{ {

View File

@ -30,13 +30,16 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Properties;
import org.compiere.model.MAllocationHdr;
import org.compiere.model.MBPartner; import org.compiere.model.MBPartner;
import org.compiere.model.MInOut; import org.compiere.model.MInOut;
import org.compiere.model.MInOutLine; import org.compiere.model.MInOutLine;
import org.compiere.model.MInvoice; import org.compiere.model.MInvoice;
import org.compiere.model.MOrder; import org.compiere.model.MOrder;
import org.compiere.model.MOrderLine; import org.compiere.model.MOrderLine;
import org.compiere.model.MPayment;
import org.compiere.model.MProduct; import org.compiere.model.MProduct;
import org.compiere.process.DocAction; import org.compiere.process.DocAction;
import org.compiere.process.ProcessInfo; import org.compiere.process.ProcessInfo;
@ -54,6 +57,10 @@ public class SalesOrderTest extends AbstractTestCase {
public SalesOrderTest() { public SalesOrderTest() {
} }
private final static int BP_JOE_BLOCK = 118;
private static final int PRODUCT_OAK_TREE = 123;
private static final int PRODUCT_AZALEA = 128;
@Test @Test
/** /**
* https://idempiere.atlassian.net/browse/IDEMPIERE-235 * https://idempiere.atlassian.net/browse/IDEMPIERE-235
@ -62,7 +69,7 @@ public class SalesOrderTest extends AbstractTestCase {
//first test - invalid with completeorder and multiple datepromised //first test - invalid with completeorder and multiple datepromised
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName()); MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
//Joe Block //Joe Block
order.setBPartner(MBPartner.get(Env.getCtx(), 118)); order.setBPartner(MBPartner.get(Env.getCtx(), BP_JOE_BLOCK));
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard); order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder); order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
order.setDocStatus(DocAction.STATUS_Drafted); order.setDocStatus(DocAction.STATUS_Drafted);
@ -74,7 +81,7 @@ public class SalesOrderTest extends AbstractTestCase {
MOrderLine line1 = new MOrderLine(order); MOrderLine line1 = new MOrderLine(order);
line1.setLine(10); line1.setLine(10);
//Azalea Bush //Azalea Bush
line1.setProduct(MProduct.get(Env.getCtx(), 128)); line1.setProduct(MProduct.get(Env.getCtx(), PRODUCT_AZALEA));
line1.setQty(new BigDecimal("1")); line1.setQty(new BigDecimal("1"));
line1.setDatePromised(today); line1.setDatePromised(today);
line1.saveEx(); line1.saveEx();
@ -82,7 +89,7 @@ public class SalesOrderTest extends AbstractTestCase {
MOrderLine line2 = new MOrderLine(order); MOrderLine line2 = new MOrderLine(order);
line2.setLine(20); line2.setLine(20);
//Oak Tree //Oak Tree
line2.setProduct(MProduct.get(Env.getCtx(), 123)); line2.setProduct(MProduct.get(Env.getCtx(), PRODUCT_OAK_TREE));
line2.setQty(new BigDecimal("1")); line2.setQty(new BigDecimal("1"));
line2.setDatePromised(TimeUtil.addDays(today, 1)); line2.setDatePromised(TimeUtil.addDays(today, 1));
line2.saveEx(); line2.saveEx();
@ -97,7 +104,7 @@ public class SalesOrderTest extends AbstractTestCase {
//second test - ok with completeorder and 1 datepromised //second test - ok with completeorder and 1 datepromised
order = new MOrder(Env.getCtx(), 0, getTrxName()); order = new MOrder(Env.getCtx(), 0, getTrxName());
//Joe Block //Joe Block
order.setBPartner(MBPartner.get(Env.getCtx(), 118)); order.setBPartner(MBPartner.get(Env.getCtx(), BP_JOE_BLOCK));
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard); order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder); order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
order.setDocStatus(DocAction.STATUS_Drafted); order.setDocStatus(DocAction.STATUS_Drafted);
@ -108,7 +115,7 @@ public class SalesOrderTest extends AbstractTestCase {
line1 = new MOrderLine(order); line1 = new MOrderLine(order);
line1.setLine(10); line1.setLine(10);
//Azalea Bush //Azalea Bush
line1.setProduct(MProduct.get(Env.getCtx(), 128)); line1.setProduct(MProduct.get(Env.getCtx(), PRODUCT_AZALEA));
line1.setQty(new BigDecimal("1")); line1.setQty(new BigDecimal("1"));
line1.setDatePromised(today); line1.setDatePromised(today);
line1.saveEx(); line1.saveEx();
@ -116,7 +123,7 @@ public class SalesOrderTest extends AbstractTestCase {
line2 = new MOrderLine(order); line2 = new MOrderLine(order);
line2.setLine(20); line2.setLine(20);
//Oak Tree //Oak Tree
line2.setProduct(MProduct.get(Env.getCtx(), 123)); line2.setProduct(MProduct.get(Env.getCtx(), PRODUCT_OAK_TREE));
line2.setQty(new BigDecimal("1")); line2.setQty(new BigDecimal("1"));
line2.setDatePromised(today); line2.setDatePromised(today);
line2.saveEx(); line2.saveEx();
@ -131,7 +138,7 @@ public class SalesOrderTest extends AbstractTestCase {
//test 3 - ok with !completeorder and multiple datepromised //test 3 - ok with !completeorder and multiple datepromised
order = new MOrder(Env.getCtx(), 0, getTrxName()); order = new MOrder(Env.getCtx(), 0, getTrxName());
//Joe Block //Joe Block
order.setBPartner(MBPartner.get(Env.getCtx(), 118)); order.setBPartner(MBPartner.get(Env.getCtx(), BP_JOE_BLOCK));
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard); order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
order.setDeliveryRule(MOrder.DELIVERYRULE_Availability); order.setDeliveryRule(MOrder.DELIVERYRULE_Availability);
order.setDocStatus(DocAction.STATUS_Drafted); order.setDocStatus(DocAction.STATUS_Drafted);
@ -142,7 +149,7 @@ public class SalesOrderTest extends AbstractTestCase {
line1 = new MOrderLine(order); line1 = new MOrderLine(order);
line1.setLine(10); line1.setLine(10);
//Azalea Bush //Azalea Bush
line1.setProduct(MProduct.get(Env.getCtx(), 128)); line1.setProduct(MProduct.get(Env.getCtx(), PRODUCT_AZALEA));
line1.setQty(new BigDecimal("1")); line1.setQty(new BigDecimal("1"));
line1.setDatePromised(today); line1.setDatePromised(today);
line1.saveEx(); line1.saveEx();
@ -150,7 +157,7 @@ public class SalesOrderTest extends AbstractTestCase {
line2 = new MOrderLine(order); line2 = new MOrderLine(order);
line2.setLine(20); line2.setLine(20);
//Oak Tree //Oak Tree
line2.setProduct(MProduct.get(Env.getCtx(), 123)); line2.setProduct(MProduct.get(Env.getCtx(), PRODUCT_OAK_TREE));
line2.setQty(new BigDecimal("1")); line2.setQty(new BigDecimal("1"));
line2.setDatePromised(TimeUtil.addDays(today, 1)); line2.setDatePromised(TimeUtil.addDays(today, 1));
line2.saveEx(); line2.saveEx();
@ -167,7 +174,7 @@ public class SalesOrderTest extends AbstractTestCase {
public void testQtyReservedForOverAndNegativeShipment() { public void testQtyReservedForOverAndNegativeShipment() {
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName()); MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
//Joe Block //Joe Block
order.setBPartner(MBPartner.get(Env.getCtx(), 118)); order.setBPartner(MBPartner.get(Env.getCtx(), BP_JOE_BLOCK));
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard); order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder); order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
order.setDocStatus(DocAction.STATUS_Drafted); order.setDocStatus(DocAction.STATUS_Drafted);
@ -180,7 +187,7 @@ public class SalesOrderTest extends AbstractTestCase {
MOrderLine line1 = new MOrderLine(order); MOrderLine line1 = new MOrderLine(order);
line1.setLine(10); line1.setLine(10);
//Azalea Bush //Azalea Bush
line1.setProduct(MProduct.get(Env.getCtx(), 128)); line1.setProduct(MProduct.get(Env.getCtx(), PRODUCT_AZALEA));
line1.setQty(new BigDecimal("1")); line1.setQty(new BigDecimal("1"));
line1.setDatePromised(today); line1.setDatePromised(today);
line1.saveEx(); line1.saveEx();
@ -235,7 +242,7 @@ public class SalesOrderTest extends AbstractTestCase {
public void testQtyReservedForNegativeOrderAndShipment() { public void testQtyReservedForNegativeOrderAndShipment() {
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName()); MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
//Joe Block //Joe Block
order.setBPartner(MBPartner.get(Env.getCtx(), 118)); order.setBPartner(MBPartner.get(Env.getCtx(), BP_JOE_BLOCK));
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard); order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder); order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
order.setDocStatus(DocAction.STATUS_Drafted); order.setDocStatus(DocAction.STATUS_Drafted);
@ -248,7 +255,7 @@ public class SalesOrderTest extends AbstractTestCase {
MOrderLine line1 = new MOrderLine(order); MOrderLine line1 = new MOrderLine(order);
line1.setLine(10); line1.setLine(10);
//Azalea Bush //Azalea Bush
line1.setProduct(MProduct.get(Env.getCtx(), 128)); line1.setProduct(MProduct.get(Env.getCtx(), PRODUCT_AZALEA));
line1.setQty(new BigDecimal("-1")); line1.setQty(new BigDecimal("-1"));
line1.setDatePromised(today); line1.setDatePromised(today);
line1.saveEx(); line1.saveEx();
@ -281,31 +288,37 @@ public class SalesOrderTest extends AbstractTestCase {
} }
@Test @Test
public void testPOSOrder() { public void testOnCreditPOSOrder() {
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName()); Properties ctx = Env.getCtx();
//Joe Block String trxName = getTrxName();
order.setBPartner(MBPartner.get(Env.getCtx(), 118));
// Get the OpenBalance of Joe Block
MBPartner bpartner = new MBPartner(ctx, BP_JOE_BLOCK, trxName);
BigDecimal initialBalance = bpartner.getTotalOpenBalance();
MOrder order = new MOrder(ctx, 0, trxName);
order.setBPartner(MBPartner.get(ctx, BP_JOE_BLOCK));
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_POS); order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_POS);
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder); order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
order.setDocStatus(DocAction.STATUS_Drafted); order.setDocStatus(DocAction.STATUS_Drafted);
order.setDocAction(DocAction.ACTION_Complete); order.setDocAction(DocAction.ACTION_Complete);
order.setPaymentRule(MOrder.PAYMENTRULE_OnCredit); // this is the default, just making it explicit
Timestamp today = TimeUtil.getDay(System.currentTimeMillis()); Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
order.setDatePromised(today); order.setDatePromised(today);
order.saveEx(); order.saveEx();
MOrderLine line1 = new MOrderLine(order); MOrderLine line1 = new MOrderLine(order);
line1.setLine(10); line1.setLine(10);
//Azalea Bush line1.setProduct(MProduct.get(ctx, PRODUCT_AZALEA));
line1.setProduct(MProduct.get(Env.getCtx(), 128));
line1.setQty(new BigDecimal("1")); line1.setQty(new BigDecimal("1"));
line1.setDatePromised(today); line1.setDatePromised(today);
line1.saveEx(); line1.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete); ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary()); assertFalse(info.isError(), info.getSummary());
order.load(getTrxName()); order.load(trxName);
assertEquals(DocAction.STATUS_Completed, order.getDocStatus()); assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
line1.load(getTrxName()); line1.load(trxName);
assertEquals(0, line1.getQtyReserved().intValue()); assertEquals(0, line1.getQtyReserved().intValue());
assertEquals(1, line1.getQtyDelivered().intValue()); assertEquals(1, line1.getQtyDelivered().intValue());
assertEquals(1, line1.getQtyInvoiced().intValue()); assertEquals(1, line1.getQtyInvoiced().intValue());
@ -317,5 +330,71 @@ public class SalesOrderTest extends AbstractTestCase {
MInvoice[] invoices = order.getInvoices(); MInvoice[] invoices = order.getInvoices();
assertEquals(1, invoices.length); assertEquals(1, invoices.length);
assertEquals(DocAction.STATUS_Completed, invoices[0].getDocStatus()); assertEquals(DocAction.STATUS_Completed, invoices[0].getDocStatus());
bpartner.load(trxName);
BigDecimal actualBalance = bpartner.getTotalOpenBalance();
// on credit increases the debt
assertTrue(actualBalance.compareTo(initialBalance.add(order.getGrandTotal())) == 0);
} }
@Test
public void testCashPOSOrder() {
Properties ctx = Env.getCtx();
String trxName = getTrxName();
// Get the OpenBalance of Joe Block
MBPartner bpartner = new MBPartner(ctx, BP_JOE_BLOCK, trxName);
BigDecimal initialBalance = bpartner.getTotalOpenBalance();
MOrder order = new MOrder(ctx, 0, trxName);
order.setBPartner(MBPartner.get(ctx, BP_JOE_BLOCK));
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_POS);
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
order.setDocStatus(DocAction.STATUS_Drafted);
order.setDocAction(DocAction.ACTION_Complete);
order.setPaymentRule(MOrder.PAYMENTRULE_Cash);
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
order.setDatePromised(today);
order.saveEx();
MOrderLine line1 = new MOrderLine(order);
line1.setLine(10);
line1.setProduct(MProduct.get(ctx, PRODUCT_AZALEA));
line1.setQty(new BigDecimal("1"));
line1.setDatePromised(today);
line1.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
order.load(trxName);
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
line1.load(trxName);
assertEquals(0, line1.getQtyReserved().intValue());
assertEquals(1, line1.getQtyDelivered().intValue());
assertEquals(1, line1.getQtyInvoiced().intValue());
MInOut[] shipments = order.getShipments();
assertEquals(1, shipments.length);
assertEquals(DocAction.STATUS_Completed, shipments[0].getDocStatus());
MInvoice[] invoices = order.getInvoices();
assertEquals(1, invoices.length);
assertEquals(DocAction.STATUS_Completed, invoices[0].getDocStatus());
assertEquals(true, invoices[0].isPaid(), "Invoice is not paid");
MAllocationHdr[] allocs = MAllocationHdr.getOfInvoice(ctx, invoices[0].getC_Invoice_ID(), trxName);
assertEquals(1, allocs.length);
assertEquals(DocAction.STATUS_Completed, allocs[0].getDocStatus());
int paymentId = allocs[0].getLines(false)[0].getC_Payment_ID();
MPayment payment = new MPayment(ctx, paymentId, trxName);
assertEquals(DocAction.STATUS_Completed, payment.getDocStatus());
assertEquals(true, payment.isAllocated(), "Payment is not allocated");
bpartner.load(trxName);
BigDecimal actualBalance = bpartner.getTotalOpenBalance();
// cash creates payment immediately, so debt is not increased
assertTrue(actualBalance.compareTo(initialBalance) == 0);
}
} }