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];
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 fullLine = onHand.compareTo(toDeliver) >= 0
@ -426,6 +430,9 @@ public class InOutGenerate extends SvrProcess
// Complete Order successful
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++)
{
MOrderLine line = lines[i];
@ -638,6 +645,15 @@ public class InOutGenerate extends SvrProcess
return m_lastStorages;
} // 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
@ -660,9 +676,7 @@ public class InOutGenerate extends SvrProcess
m_created++;
//reset storage cache as MInOut.completeIt will update m_storage
m_map = new HashMap<SParameter,MStorageOnHand[]>();
m_lastPP = null;
m_lastStorages = null;
resetStorageCache();
}
m_shipment = null;
m_line = 0;

View File

@ -406,34 +406,38 @@ public class MStorageOnHand extends X_M_StorageOnHand
allAttributeInstances = true;
ArrayList<MStorageOnHand> list = new ArrayList<MStorageOnHand>();
// Specific Attribute Set Instance
String 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.QtyOnHand,s.DateLastInventory,s.M_StorageOnHand_UU,s.DateMaterialPolicy "
+ "FROM M_StorageOnHand s"
+ " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID) ";
if (M_Locator_ID > 0)
sql += "WHERE l.M_Locator_ID = ?";
else
sql += "WHERE l.M_Warehouse_ID=?";
sql += " AND s.M_Product_ID=?"
+ " AND COALESCE(s.M_AttributeSetInstance_ID,0)=? ";
if (positiveOnly)
String sql;
if (! allAttributeInstances)
{
sql += " AND s.QtyOnHand > 0 ";
// Specific Attribute Set Instance
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.QtyOnHand,s.DateLastInventory,s.M_StorageOnHand_UU,s.DateMaterialPolicy "
+ "FROM M_StorageOnHand s"
+ " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID) ";
if (M_Locator_ID > 0)
sql += "WHERE l.M_Locator_ID = ?";
else
sql += "WHERE l.M_Warehouse_ID=?";
sql += " AND s.M_Product_ID=?"
+ " AND COALESCE(s.M_AttributeSetInstance_ID,0)=? ";
if (positiveOnly)
{
sql += " AND s.QtyOnHand > 0 ";
}
else
{
sql += " AND s.QtyOnHand <> 0 ";
}
sql += " ORDER BY l.PriorityNo DESC, DateMaterialPolicy ";
if (!FiFo)
sql += " DESC, s.M_AttributeSetInstance_ID DESC ";
else
sql += ", s.M_AttributeSetInstance_ID ";
}
else
{
sql += " AND s.QtyOnHand <> 0 ";
}
sql += " ORDER BY l.PriorityNo DESC, DateMaterialPolicy ";
if (!FiFo)
sql += " DESC, s.M_AttributeSetInstance_ID DESC ";
else
sql += ", s.M_AttributeSetInstance_ID ";
// All Attribute Set Instances
if (allAttributeInstances)
{
// All Attribute Set Instances
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.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 ";
}
if (minGuaranteeDate != null)
{
sql += " AND (asi.GuaranteeDate IS NULL OR asi.GuaranteeDate>?) ";
}
MProduct product = MProduct.get(Env.getCtx(), M_Product_ID);
if(product.isUseGuaranteeDateForMPolicy()){
sql += " ORDER BY l.PriorityNo DESC, COALESCE(asi.GuaranteeDate,s.DateMaterialPolicy)";
if (!FiFo)
@ -476,7 +477,6 @@ public class MStorageOnHand extends X_M_StorageOnHand
else
sql += ", s.M_AttributeSetInstance_ID ";
}
sql += ", s.QtyOnHand DESC";
}
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");
}
@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());
}
}