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:
parent
86207d12ab
commit
3641e398ca
|
@ -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;
|
||||||
|
|
|
@ -406,34 +406,38 @@ public class MStorageOnHand extends X_M_StorageOnHand
|
||||||
allAttributeInstances = true;
|
allAttributeInstances = true;
|
||||||
|
|
||||||
ArrayList<MStorageOnHand> list = new ArrayList<MStorageOnHand>();
|
ArrayList<MStorageOnHand> list = new ArrayList<MStorageOnHand>();
|
||||||
// Specific Attribute Set Instance
|
String sql;
|
||||||
String sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID,"
|
if (! allAttributeInstances)
|
||||||
+ "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 ";
|
// 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
|
else
|
||||||
{
|
{
|
||||||
sql += " AND s.QtyOnHand <> 0 ";
|
// All Attribute Set Instances
|
||||||
}
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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;
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue