IDEMPIERE-5776 Cannot generate shipment for an order with multiple lines for same product and Complete Order delivery rule (#1900)

This commit is contained in:
Carlos Ruiz 2023-06-22 08:00:27 +02:00 committed by GitHub
parent 86207d12ab
commit 3641e398ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 118 additions and 34 deletions

View File

@ -362,6 +362,10 @@ public class InOutGenerate extends SvrProcess
{ {
MStorageOnHand storage = storages[j]; MStorageOnHand storage = storages[j];
onHand = onHand.add(storage.getQtyOnHand()); onHand = onHand.add(storage.getQtyOnHand());
if (completeOrder && j == 0) {
// CompleteOrder is created at the end, so we need to subtract here to keep track of what is "consumed"
storage.setQtyOnHand(storage.getQtyOnHand().subtract(toDeliver));
}
} }
boolean autoProduce = product.isBOM() && product.isVerified() && product.isAutoProduce(); boolean autoProduce = product.isBOM() && product.isVerified() && product.isAutoProduce();
boolean fullLine = onHand.compareTo(toDeliver) >= 0 boolean fullLine = onHand.compareTo(toDeliver) >= 0
@ -426,6 +430,9 @@ public class InOutGenerate extends SvrProcess
// Complete Order successful // Complete Order successful
if (completeOrder && MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) if (completeOrder && MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule()))
{ {
// reset storage cache - it was updated in memory above
resetStorageCache();
for (int i = 0; i < lines.length; i++) for (int i = 0; i < lines.length; i++)
{ {
MOrderLine line = lines[i]; MOrderLine line = lines[i];
@ -638,6 +645,15 @@ public class InOutGenerate extends SvrProcess
return m_lastStorages; return m_lastStorages;
} // getStorages } // getStorages
/**
* Reset in memory map, array and parameters
*/
public void resetStorageCache()
{
m_map = new HashMap<SParameter,MStorageOnHand[]>();
m_lastPP = null;
m_lastStorages = null;
}
/** /**
* Complete Shipment * Complete Shipment
@ -660,9 +676,7 @@ public class InOutGenerate extends SvrProcess
m_created++; m_created++;
//reset storage cache as MInOut.completeIt will update m_storage //reset storage cache as MInOut.completeIt will update m_storage
m_map = new HashMap<SParameter,MStorageOnHand[]>(); resetStorageCache();
m_lastPP = null;
m_lastStorages = null;
} }
m_shipment = null; m_shipment = null;
m_line = 0; m_line = 0;

View File

@ -406,8 +406,11 @@ public class MStorageOnHand extends X_M_StorageOnHand
allAttributeInstances = true; allAttributeInstances = true;
ArrayList<MStorageOnHand> list = new ArrayList<MStorageOnHand>(); ArrayList<MStorageOnHand> list = new ArrayList<MStorageOnHand>();
String sql;
if (! allAttributeInstances)
{
// Specific Attribute Set Instance // Specific Attribute Set Instance
String sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID," sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID,"
+ "s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy," + "s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy,"
+ "s.QtyOnHand,s.DateLastInventory,s.M_StorageOnHand_UU,s.DateMaterialPolicy " + "s.QtyOnHand,s.DateLastInventory,s.M_StorageOnHand_UU,s.DateMaterialPolicy "
+ "FROM M_StorageOnHand s" + "FROM M_StorageOnHand s"
@ -431,9 +434,10 @@ public class MStorageOnHand extends X_M_StorageOnHand
sql += " DESC, s.M_AttributeSetInstance_ID DESC "; sql += " DESC, s.M_AttributeSetInstance_ID DESC ";
else else
sql += ", s.M_AttributeSetInstance_ID "; sql += ", s.M_AttributeSetInstance_ID ";
// All Attribute Set Instances }
if (allAttributeInstances) else
{ {
// All Attribute Set Instances
sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID," sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID,"
+ " s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy," + " s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy,"
+ " s.QtyOnHand,s.DateLastInventory,s.M_StorageOnHand_UU,s.DateMaterialPolicy " + " s.QtyOnHand,s.DateLastInventory,s.M_StorageOnHand_UU,s.DateMaterialPolicy "
@ -453,14 +457,11 @@ public class MStorageOnHand extends X_M_StorageOnHand
{ {
sql += " AND s.QtyOnHand <> 0 "; sql += " AND s.QtyOnHand <> 0 ";
} }
if (minGuaranteeDate != null) if (minGuaranteeDate != null)
{ {
sql += " AND (asi.GuaranteeDate IS NULL OR asi.GuaranteeDate>?) "; sql += " AND (asi.GuaranteeDate IS NULL OR asi.GuaranteeDate>?) ";
} }
MProduct product = MProduct.get(Env.getCtx(), M_Product_ID); MProduct product = MProduct.get(Env.getCtx(), M_Product_ID);
if(product.isUseGuaranteeDateForMPolicy()){ if(product.isUseGuaranteeDateForMPolicy()){
sql += " ORDER BY l.PriorityNo DESC, COALESCE(asi.GuaranteeDate,s.DateMaterialPolicy)"; sql += " ORDER BY l.PriorityNo DESC, COALESCE(asi.GuaranteeDate,s.DateMaterialPolicy)";
if (!FiFo) if (!FiFo)
@ -476,7 +477,6 @@ public class MStorageOnHand extends X_M_StorageOnHand
else else
sql += ", s.M_AttributeSetInstance_ID "; sql += ", s.M_AttributeSetInstance_ID ";
} }
sql += ", s.QtyOnHand DESC"; sql += ", s.QtyOnHand DESC";
} }
PreparedStatement pstmt = null; PreparedStatement pstmt = null;

View File

@ -1526,4 +1526,74 @@ public class SalesOrderTest extends AbstractTestCase {
} }
assertEquals(orderTaxes.length, match, "MOrdexTax record doesn't match child tax records"); assertEquals(orderTaxes.length, match, "MOrdexTax record doesn't match child tax records");
} }
@Test
public void testGenerateShipmentCompleteMultiSameLine() {
// CompleteOrder with 2 lines with almost the available on hand
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
//Joe Block
order.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id));
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
order.setDocStatus(DocAction.STATUS_Drafted);
order.setDocAction(DocAction.ACTION_Complete);
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
order.setDateOrdered(today);
order.setDatePromised(today);
order.saveEx();
BigDecimal qtyOnHandMinusOne = MStorageOnHand.getQtyOnHandForShipping(DictionaryIDs.M_Product.AZALEA_BUSH.id, order.getM_Warehouse_ID(), 0, getTrxName());
if (qtyOnHandMinusOne.signum() > 0)
qtyOnHandMinusOne = qtyOnHandMinusOne.subtract(Env.ONE);
MOrderLine line1 = new MOrderLine(order);
line1.setLine(10);
line1.setProduct(MProduct.get(Env.getCtx(), DictionaryIDs.M_Product.AZALEA_BUSH.id));
line1.setQty(qtyOnHandMinusOne);
line1.setDatePromised(today);
line1.saveEx();
MOrderLine line2 = new MOrderLine(order);
line2.setLine(20);
line2.setProduct(MProduct.get(Env.getCtx(), DictionaryIDs.M_Product.AZALEA_BUSH.id));
line2.setQty(qtyOnHandMinusOne);
line2.setDatePromised(today);
line2.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
order.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
int AD_Process_ID = PROCESS_M_INOUT_GENERATE_MANUAL;
MPInstance instance = new MPInstance(Env.getCtx(), AD_Process_ID, 0);
instance.saveEx();
String insert = "INSERT INTO T_SELECTION(AD_PINSTANCE_ID, T_SELECTION_ID) Values (?, ?)";
DB.executeUpdateEx(insert, new Object[] {instance.getAD_PInstance_ID(), order.getC_Order_ID()}, null);
//call process
ProcessInfo pi = new ProcessInfo ("InOutGen", AD_Process_ID);
pi.setAD_PInstance_ID (instance.getAD_PInstance_ID());
// Add Parameter - Selection=Y
MPInstancePara ip = new MPInstancePara(instance, 10);
ip.setParameter("Selection","Y");
ip.saveEx();
//Add Document action parameter
ip = new MPInstancePara(instance, 20);
ip.setParameter("DocAction", "PR");
ip.saveEx();
// Add Parameter - M_Warehouse_ID=x
ip = new MPInstancePara(instance, 30);
ip.setParameter("M_Warehouse_ID", getM_Warehouse_ID());
ip.saveEx();
ServerProcessCtl processCtl = new ServerProcessCtl(pi, getTrx());
processCtl.setManagedTrxForJavaProcess(false);
processCtl.run();
assertFalse(pi.isError(), pi.getSummary());
}
} }