IDEMPIERE-5433 Better invoice consolidation rules (#1504)

With acknowledgements to Carlos Ruiz for writing the
test case.
This commit is contained in:
Fr Jeremy Krieg 2022-10-28 20:06:19 +10:30 committed by GitHub
parent 56fe006d29
commit 84dc2b3f31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 126 additions and 5 deletions

View File

@ -91,6 +91,8 @@ public class InvoiceGenerate extends SvrProcess
private BigDecimal p_MinimumAmtInvSched = null; private BigDecimal p_MinimumAmtInvSched = null;
/** Per Invoice Savepoint */ /** Per Invoice Savepoint */
private Savepoint m_savepoint = null; private Savepoint m_savepoint = null;
/** BPartner on the last order processed */
private int m_bpartnerID = 0;
/** /**
* Prepare - e.g., get Parameters. * Prepare - e.g., get Parameters.
@ -177,7 +179,7 @@ public class InvoiceGenerate extends SvrProcess
// //
sql.append(") AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType ") sql.append(") AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType ")
.append("WHERE DocBaseType='SOO' AND DocSubTypeSO NOT IN ('ON','OB','WR')) ") .append("WHERE DocBaseType='SOO' AND DocSubTypeSO NOT IN ('ON','OB','WR')) ")
.append("ORDER BY AD_Org_ID, M_Warehouse_ID, PriorityRule, C_BPartner_ID, Bill_Location_ID, C_Order_ID"); .append("ORDER BY AD_Org_ID, M_Warehouse_ID, PriorityRule, C_BPartner_ID, Bill_Location_ID, Bill_User_ID, C_Order_ID");
} }
// sql += " FOR UPDATE"; // sql += " FOR UPDATE";
@ -228,10 +230,13 @@ public class InvoiceGenerate extends SvrProcess
StringBuilder msgsup = new StringBuilder(Msg.getMsg(getCtx(), "Processing")).append(" ").append(order.getDocumentInfo()); StringBuilder msgsup = new StringBuilder(Msg.getMsg(getCtx(), "Processing")).append(" ").append(order.getDocumentInfo());
statusUpdate(msgsup.toString()); statusUpdate(msgsup.toString());
// New Invoice Location // New BPartner, or new Invoice Location, or new Invoice Contact
if (!p_ConsolidateDocument if (!p_ConsolidateDocument
|| (m_bpartnerID != 0
&& m_bpartnerID != order.getC_BPartner_ID())
|| (m_invoice != null || (m_invoice != null
&& m_invoice.getC_BPartner_Location_ID() != order.getBill_Location_ID()) ) && (m_invoice.getC_BPartner_Location_ID() != order.getBill_Location_ID() ||
m_invoice.getAD_User_ID() != order.getBill_User_ID()) ) )
completeInvoice(); completeInvoice();
boolean completeOrder = MOrder.INVOICERULE_AfterOrderDelivered.equals(order.getInvoiceRule()); boolean completeOrder = MOrder.INVOICERULE_AfterOrderDelivered.equals(order.getInvoiceRule());
@ -350,6 +355,7 @@ public class InvoiceGenerate extends SvrProcess
m_line += 1000; m_line += 1000;
} }
} // complete Order } // complete Order
m_bpartnerID = order.getC_BPartner_ID();
} // for all orders } // for all orders
} }
catch (Exception e) catch (Exception e)

View File

@ -107,6 +107,16 @@ public final class DictionaryIDs {
} }
} }
public enum C_BPartner_Location {
C_AND_W_STAMFORD(112);
public final int id;
private C_BPartner_Location(int id) {
this.id = id;
}
}
public enum C_Charge { public enum C_Charge {
BANK(100), BANK(100),
COMMISSIONS(101), COMMISSIONS(101),

View File

@ -49,6 +49,7 @@ import org.compiere.model.MRMA;
import org.compiere.model.MRMALine; import org.compiere.model.MRMALine;
import org.compiere.model.PO; import org.compiere.model.PO;
import org.compiere.model.SystemIDs; import org.compiere.model.SystemIDs;
import org.compiere.model.X_C_BP_Relation;
import org.compiere.process.DocAction; import org.compiere.process.DocAction;
import org.compiere.process.ProcessInfo; import org.compiere.process.ProcessInfo;
import org.compiere.process.ServerProcessCtl; import org.compiere.process.ServerProcessCtl;
@ -361,4 +362,108 @@ public class InvoiceCustomerTest extends AbstractTestCase {
rmaLine.load(getTrxName()); rmaLine.load(getTrxName());
assertEquals(1, rmaLine.getQtyInvoiced().intValue()); assertEquals(1, rmaLine.getQtyInvoiced().intValue());
} }
@Test
public void testGenerateInvoiceRelatedBP() { // IDEMPIERE-5433
X_C_BP_Relation bpr = new X_C_BP_Relation(Env.getCtx(), 0, getTrxName());
bpr.setName("C&W may pay invoices for Seed");
bpr.setC_BPartner_ID(DictionaryIDs.C_BPartner.SEED_FARM.id);
bpr.setC_BPartnerRelation_ID(DictionaryIDs.C_BPartner.C_AND_W.id);
bpr.setC_BPartnerRelation_Location_ID(DictionaryIDs.C_BPartner_Location.C_AND_W_STAMFORD.id);
bpr.setIsBillTo(true);
bpr.setIsRemitTo(true);
bpr.saveEx();
MOrder order1 = new MOrder(Env.getCtx(), 0, getTrxName());
order1.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id));
order1.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
order1.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
order1.setInvoiceRule(MOrder.INVOICERULE_Immediate);
order1.setDocStatus(DocAction.STATUS_Drafted);
order1.setDocAction(DocAction.ACTION_Complete);
order1.setBill_BPartner_ID(DictionaryIDs.C_BPartner.C_AND_W.id);
order1.setBill_Location_ID(DictionaryIDs.C_BPartner_Location.C_AND_W_STAMFORD.id);
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
order1.setDateOrdered(today);
order1.setDatePromised(today);
order1.saveEx();
MOrderLine line1 = new MOrderLine(order1);
line1.setLine(10);
line1.setProduct(MProduct.get(Env.getCtx(), DictionaryIDs.M_Product.SEEDER.id));
line1.setQty(new BigDecimal("1"));
line1.setDatePromised(today);
line1.saveEx();
ProcessInfo info1 = MWorkflow.runDocumentActionWorkflow(order1, DocAction.ACTION_Complete);
assertFalse(info1.isError(), info1.getSummary());
order1.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, order1.getDocStatus());
line1.load(getTrxName());
assertEquals(1, line1.getQtyReserved().intValue());
assertEquals(0, line1.getQtyInvoiced().intValue());
MOrder order2 = new MOrder(Env.getCtx(), 0, getTrxName());
order2.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.SEED_FARM.id));
order2.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
order2.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
order2.setInvoiceRule(MOrder.INVOICERULE_Immediate);
order2.setDocStatus(DocAction.STATUS_Drafted);
order2.setDocAction(DocAction.ACTION_Complete);
order2.setBill_BPartner_ID(DictionaryIDs.C_BPartner.C_AND_W.id);
order2.setBill_Location_ID(DictionaryIDs.C_BPartner_Location.C_AND_W_STAMFORD.id);
order2.setDateOrdered(today);
order2.setDatePromised(today);
order2.saveEx();
MOrderLine line2 = new MOrderLine(order2);
line2.setLine(10);
line2.setProduct(MProduct.get(Env.getCtx(), DictionaryIDs.M_Product.WEEDER.id));
line2.setQty(new BigDecimal("1"));
line2.setDatePromised(today);
line2.saveEx();
ProcessInfo info2 = MWorkflow.runDocumentActionWorkflow(order2, DocAction.ACTION_Complete);
assertFalse(info2.isError(), info2.getSummary());
order2.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, order2.getDocStatus());
line2.load(getTrxName());
assertEquals(1, line2.getQtyReserved().intValue());
assertEquals(0, line2.getQtyInvoiced().intValue());
int AD_Process_ID = SystemIDs.PROCESS_C_INVOICE_GENERATE;
MPInstance instance = new MPInstance(Env.getCtx(), AD_Process_ID, 0);
instance.saveEx();
//call process
ProcessInfo pi = new ProcessInfo ("InvoiceGenerate", AD_Process_ID);
pi.setAD_PInstance_ID (instance.getAD_PInstance_ID());
// Add Parameter - Selection=Y
MPInstancePara ip = new MPInstancePara(instance, 10);
ip.setParameter("DateInvoiced",today);
ip.saveEx();
// Org
ip = new MPInstancePara(instance, 20);
ip.setParameter("AD_Org_ID", DictionaryIDs.AD_Org.HQ.id);
ip.saveEx();
//Add Document action parameter
ip = new MPInstancePara(instance, 50);
ip.setParameter("DocAction", MOrder.DOCACTION_Prepare);
ip.saveEx();
// Consolidate
ip = new MPInstancePara(instance, 60);
ip.setParameter("ConsolidateDocument", true);
ip.saveEx();
ServerProcessCtl processCtl = new ServerProcessCtl(pi, getTrx());
processCtl.setManagedTrxForJavaProcess(false);
processCtl.run();
assertFalse(pi.isError(), pi.getSummary());
// It must create two invoices because they are for different ship BP
// even if they have the same Bill BP and Location
assertEquals(pi.getSummary(), "Created = 2");
}
} }