IDEMPIERE-5155 Inventory Document: Auto picking of storage record should ignore record with Serial (#1141)
* IDEMPIERE-5155 Inventory Document: Auto picking of storage record should ignore record with Serial * IDEMPIERE-5155 Inventory Document: Auto picking of storage record should ignore record with Serial add unit test
This commit is contained in:
parent
06e9071a82
commit
f934d62efc
|
@ -722,6 +722,7 @@ public class MInventory extends X_M_Inventory implements DocAction
|
|||
if (line.getM_AttributeSetInstance_ID() == 0)
|
||||
{
|
||||
MProduct product = MProduct.get(getCtx(), line.getM_Product_ID(), get_TrxName());
|
||||
boolean serial = product.isSerial();
|
||||
if (qtyDiff.signum() > 0) // Incoming Trx
|
||||
{
|
||||
//auto balance negative on hand
|
||||
|
@ -729,6 +730,12 @@ public class MInventory extends X_M_Inventory implements DocAction
|
|||
null, MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), line.getM_Locator_ID(), get_TrxName(), false);
|
||||
for (MStorageOnHand storage : storages)
|
||||
{
|
||||
if (storage.getM_AttributeSetInstance_ID() > 0 && serial)
|
||||
{
|
||||
MAttributeSetInstance asi = new MAttributeSetInstance(Env.getCtx(), storage.getM_AttributeSetInstance_ID(), get_TrxName());
|
||||
if (!Util.isEmpty(asi.getSerNo(), true))
|
||||
continue;
|
||||
}
|
||||
if (storage.getQtyOnHand().signum() < 0)
|
||||
{
|
||||
BigDecimal maQty = qtyDiff;
|
||||
|
@ -758,6 +765,17 @@ public class MInventory extends X_M_Inventory implements DocAction
|
|||
false, true, 0, get_TrxName());
|
||||
for (MStorageOnHand storage : storages)
|
||||
{
|
||||
if (storage.getM_AttributeSetInstance_ID() == 0)
|
||||
continue;
|
||||
|
||||
if (serial)
|
||||
{
|
||||
MAttributeSetInstance asi = new MAttributeSetInstance(Env.getCtx(), storage.getM_AttributeSetInstance_ID(), get_TrxName());
|
||||
if (!Util.isEmpty(asi.getSerNo(), true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BigDecimal maQty = qtyDiff;
|
||||
//backward compatibility: -ve in MA is incoming trx, +ve in MA is outgoing trx
|
||||
MInventoryLineMA lineMA = new MInventoryLineMA(line, storage.getM_AttributeSetInstance_ID(), maQty.negate(), storage.getDateMaterialPolicy(),true);
|
||||
|
@ -827,6 +845,12 @@ public class MInventory extends X_M_Inventory implements DocAction
|
|||
BigDecimal qtyToDeliver = qtyDiff.negate();
|
||||
for (MStorageOnHand storage: storages)
|
||||
{
|
||||
if (serial && storage.getM_AttributeSetInstance_ID() > 0)
|
||||
{
|
||||
MAttributeSetInstance asi = new MAttributeSetInstance(Env.getCtx(), storage.getM_AttributeSetInstance_ID(), get_TrxName());
|
||||
if (!Util.isEmpty(asi.getSerNo(), true))
|
||||
continue;
|
||||
}
|
||||
if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0)
|
||||
{
|
||||
MInventoryLineMA ma = new MInventoryLineMA (line,
|
||||
|
|
|
@ -29,12 +29,15 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
|||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.compiere.model.MAcctSchema;
|
||||
import org.compiere.model.MAttributeSet;
|
||||
import org.compiere.model.MAttributeSetInstance;
|
||||
import org.compiere.model.MBPartner;
|
||||
import org.compiere.model.MClient;
|
||||
import org.compiere.model.MCost;
|
||||
|
@ -44,10 +47,16 @@ import org.compiere.model.MInventory;
|
|||
import org.compiere.model.MInventoryLine;
|
||||
import org.compiere.model.MOrder;
|
||||
import org.compiere.model.MOrderLine;
|
||||
import org.compiere.model.MPriceList;
|
||||
import org.compiere.model.MPriceListVersion;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.MProductPrice;
|
||||
import org.compiere.model.MStorageOnHand;
|
||||
import org.compiere.model.MWarehouse;
|
||||
import org.compiere.process.DocAction;
|
||||
import org.compiere.process.DocumentEngine;
|
||||
import org.compiere.process.ProcessInfo;
|
||||
import org.compiere.util.CacheMgt;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.TimeUtil;
|
||||
import org.compiere.wf.MWorkflow;
|
||||
|
@ -74,6 +83,9 @@ public class InventoryTest extends AbstractTestCase {
|
|||
private static final int BP_PATIO = 121;
|
||||
private static final int USER_GARDENADMIN = 101;
|
||||
private static final int MULCH_PRODUCT_ID = 137;
|
||||
private final static int FERTILIZER_LOT_ATTRIBUTESET_ID = 101;
|
||||
private static final int CHEMICALS_CATEGORY_ID = 109;
|
||||
private static final int PURCHASE_PRICE_LIST_ID = 102;
|
||||
|
||||
/**
|
||||
* https://idempiere.atlassian.net/browse/IDEMPIERE-4596
|
||||
|
@ -142,6 +154,10 @@ public class InventoryTest extends AbstractTestCase {
|
|||
}
|
||||
|
||||
private void createPOAndMRForProduct(int productId) {
|
||||
createPOAndMRForProduct(productId, null);
|
||||
}
|
||||
|
||||
private void createPOAndMRForProduct(int productId, MAttributeSetInstance asi) {
|
||||
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||
order.setBPartner(MBPartner.get(Env.getCtx(), BP_PATIO));
|
||||
order.setC_DocTypeTarget_ID(DOCTYPE_PO);
|
||||
|
@ -156,7 +172,7 @@ public class InventoryTest extends AbstractTestCase {
|
|||
|
||||
MOrderLine line1 = new MOrderLine(order);
|
||||
line1.setLine(10);
|
||||
line1.setProduct(MProduct.get(Env.getCtx(), productId));
|
||||
line1.setProduct(new MProduct(Env.getCtx(), productId, getTrxName()));
|
||||
line1.setQty(new BigDecimal("1"));
|
||||
line1.setDatePromised(today);
|
||||
line1.saveEx();
|
||||
|
@ -174,6 +190,8 @@ public class InventoryTest extends AbstractTestCase {
|
|||
MInOutLine receiptLine1 = new MInOutLine(receipt1);
|
||||
receiptLine1.setOrderLine(line1, 0, new BigDecimal("1"));
|
||||
receiptLine1.setQty(new BigDecimal("1"));
|
||||
if (asi != null)
|
||||
receiptLine1.setM_AttributeSetInstance_ID(asi.get_ID());
|
||||
receiptLine1.saveEx();
|
||||
|
||||
info = MWorkflow.runDocumentActionWorkflow(receipt1, DocAction.ACTION_Complete);
|
||||
|
@ -185,4 +203,192 @@ public class InventoryTest extends AbstractTestCase {
|
|||
assertNull(error, error);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipProductWithSerial() {
|
||||
Properties ctx = Env.getCtx();
|
||||
String trxName = getTrxName();
|
||||
|
||||
MAttributeSet set = new MAttributeSet(Env.getCtx(), FERTILIZER_LOT_ATTRIBUTESET_ID, null);
|
||||
set.setIsSerNo(true);
|
||||
set.saveEx();
|
||||
|
||||
MWarehouse wh = new MWarehouse(Env.getCtx(), WAREHOUSE_HQ, null);
|
||||
boolean disallow = wh.isDisallowNegativeInv();
|
||||
MProduct product = null;
|
||||
try {
|
||||
|
||||
if (!disallow) {
|
||||
wh.setIsDisallowNegativeInv(true);
|
||||
wh.saveEx();
|
||||
CacheMgt.get().reset(MWarehouse.Table_Name, wh.get_ID());
|
||||
}
|
||||
|
||||
product = new MProduct(ctx, 0, null);
|
||||
product.setM_Product_Category_ID(CHEMICALS_CATEGORY_ID);
|
||||
product.setName("testSkipProductWithSerial");
|
||||
product.setValue("testSkipProductWithSerial");
|
||||
product.setProductType(MProduct.PRODUCTTYPE_Item);
|
||||
product.setIsStocked(true);
|
||||
product.setIsSold(true);
|
||||
product.setIsPurchased(true);
|
||||
product.setC_UOM_ID(UOM_EACH);
|
||||
product.setC_TaxCategory_ID(TAXCAT_STANDARD);
|
||||
product.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||
product.saveEx();
|
||||
|
||||
MPriceListVersion plv = MPriceList.get(PURCHASE_PRICE_LIST_ID).getPriceListVersion(null);
|
||||
MProductPrice pp = new MProductPrice(Env.getCtx(), 0, getTrxName());
|
||||
pp.setM_PriceList_Version_ID(plv.getM_PriceList_Version_ID());
|
||||
pp.setM_Product_ID(product.get_ID());
|
||||
pp.setPriceStd(new BigDecimal("2"));
|
||||
pp.setPriceList(new BigDecimal("2"));
|
||||
pp.saveEx();
|
||||
|
||||
MAttributeSetInstance asi = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||
asi.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||
asi.setSerNo("testSkipProductWithSerial #1");
|
||||
asi.saveEx();
|
||||
|
||||
createPOAndMRForProduct(product.get_ID(), asi);
|
||||
|
||||
MStorageOnHand[] onhands = MStorageOnHand.getOfProduct(Env.getCtx(), product.get_ID(), getTrxName());
|
||||
assertEquals(1, onhands.length, "Unexpected number of on hand records");
|
||||
assertEquals(onhands[0].getM_AttributeSetInstance_ID(), asi.get_ID(), "Unexpected M_AttributeSetInstance_ID for on hand record");
|
||||
|
||||
MInventory inventory = new MInventory(ctx, 0, trxName);
|
||||
inventory.setM_Warehouse_ID(WAREHOUSE_HQ);
|
||||
inventory.setC_DocType_ID(DOCTYPE_PHYSICAL_INV);
|
||||
inventory.saveEx();
|
||||
|
||||
MInventoryLine iline = new MInventoryLine(inventory,
|
||||
LOCATOR_HQ,
|
||||
product.getM_Product_ID(),
|
||||
0, // M_AttributeSetInstance_ID
|
||||
Env.ONE, // QtyBook
|
||||
Env.ZERO);
|
||||
iline.saveEx();
|
||||
|
||||
//show error out with negative on hand (skip the only asi record with serno)
|
||||
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(inventory, DocAction.ACTION_Complete);
|
||||
assertTrue(info.isError(), info.getSummary());
|
||||
} finally {
|
||||
rollback();
|
||||
set.setIsSerNo(false);
|
||||
set.saveEx();
|
||||
|
||||
if (product != null)
|
||||
product.deleteEx(true);
|
||||
|
||||
if (!disallow) {
|
||||
wh.setIsDisallowNegativeInv(false);
|
||||
wh.saveEx();
|
||||
CacheMgt.get().reset(MWarehouse.Table_Name, wh.get_ID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipProductWithSerial2() {
|
||||
Properties ctx = Env.getCtx();
|
||||
String trxName = getTrxName();
|
||||
|
||||
MAttributeSet set = new MAttributeSet(Env.getCtx(), FERTILIZER_LOT_ATTRIBUTESET_ID, null);
|
||||
set.setIsSerNo(true);
|
||||
set.saveEx();
|
||||
|
||||
MWarehouse wh = new MWarehouse(Env.getCtx(), WAREHOUSE_HQ, null);
|
||||
boolean disallow = wh.isDisallowNegativeInv();
|
||||
MProduct product = null;
|
||||
try {
|
||||
|
||||
if (!disallow) {
|
||||
wh.setIsDisallowNegativeInv(true);
|
||||
wh.saveEx();
|
||||
CacheMgt.get().reset(MWarehouse.Table_Name, wh.get_ID());
|
||||
}
|
||||
|
||||
product = new MProduct(ctx, 0, null);
|
||||
product.setM_Product_Category_ID(CHEMICALS_CATEGORY_ID);
|
||||
product.setName("testSkipProductWithSerial");
|
||||
product.setValue("testSkipProductWithSerial");
|
||||
product.setProductType(MProduct.PRODUCTTYPE_Item);
|
||||
product.setIsStocked(true);
|
||||
product.setIsSold(true);
|
||||
product.setIsPurchased(true);
|
||||
product.setC_UOM_ID(UOM_EACH);
|
||||
product.setC_TaxCategory_ID(TAXCAT_STANDARD);
|
||||
product.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||
product.saveEx();
|
||||
|
||||
MPriceListVersion plv = MPriceList.get(PURCHASE_PRICE_LIST_ID).getPriceListVersion(null);
|
||||
MProductPrice pp = new MProductPrice(Env.getCtx(), 0, getTrxName());
|
||||
pp.setM_PriceList_Version_ID(plv.getM_PriceList_Version_ID());
|
||||
pp.setM_Product_ID(product.get_ID());
|
||||
pp.setPriceStd(new BigDecimal("2"));
|
||||
pp.setPriceList(new BigDecimal("2"));
|
||||
pp.saveEx();
|
||||
|
||||
MAttributeSetInstance asi = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||
asi.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||
asi.setSerNo("testSkipProductWithSerial #1");
|
||||
asi.saveEx();
|
||||
|
||||
createPOAndMRForProduct(product.get_ID(), asi);
|
||||
|
||||
MStorageOnHand[] onhands = MStorageOnHand.getOfProduct(Env.getCtx(), product.get_ID(), getTrxName());
|
||||
assertEquals(1, onhands.length, "Unexpected number of on hand records");
|
||||
assertEquals(onhands[0].getM_AttributeSetInstance_ID(), asi.get_ID(), "Unexpected M_AttributeSetInstance_ID for on hand record");
|
||||
|
||||
MAttributeSetInstance asi1 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||
asi1.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||
asi1.saveEx();
|
||||
|
||||
createPOAndMRForProduct(product.get_ID(), asi1);
|
||||
|
||||
onhands = MStorageOnHand.getOfProduct(Env.getCtx(), product.get_ID(), getTrxName());
|
||||
assertEquals(2, onhands.length, "Unexpected number of on hand records");
|
||||
assertEquals(onhands[0].getM_AttributeSetInstance_ID(), asi.get_ID(), "Unexpected M_AttributeSetInstance_ID for first on hand record");
|
||||
assertEquals(onhands[1].getM_AttributeSetInstance_ID(), asi1.get_ID(), "Unexpected M_AttributeSetInstance_ID for second on hand record");
|
||||
|
||||
MInventory inventory = new MInventory(ctx, 0, trxName);
|
||||
inventory.setM_Warehouse_ID(WAREHOUSE_HQ);
|
||||
inventory.setC_DocType_ID(DOCTYPE_PHYSICAL_INV);
|
||||
inventory.saveEx();
|
||||
|
||||
MInventoryLine iline = new MInventoryLine(inventory,
|
||||
LOCATOR_HQ,
|
||||
product.getM_Product_ID(),
|
||||
0, // M_AttributeSetInstance_ID
|
||||
new BigDecimal("2"), // QtyBook
|
||||
new BigDecimal("1"));
|
||||
iline.saveEx();
|
||||
|
||||
//should success with qty difference being applied to the asi with null serno record
|
||||
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(inventory, DocAction.ACTION_Complete);
|
||||
assertFalse(info.isError(), info.getSummary());
|
||||
inventory.load(getTrxName());
|
||||
assertEquals(DocAction.STATUS_Completed, inventory.getDocStatus());
|
||||
|
||||
onhands = MStorageOnHand.getOfProduct(Env.getCtx(), product.get_ID(), getTrxName());
|
||||
assertEquals(2, onhands.length, "Unexpected number of on hand records");
|
||||
assertEquals(onhands[0].getM_AttributeSetInstance_ID(), asi.get_ID(), "Unexpected M_AttributeSetInstance_ID for first on hand record");
|
||||
assertEquals(onhands[1].getM_AttributeSetInstance_ID(), asi1.get_ID(), "Unexpected M_AttributeSetInstance_ID for second on hand record");
|
||||
assertEquals(1, onhands[0].getQtyOnHand().intValue());
|
||||
assertEquals(0, onhands[1].getQtyOnHand().intValue());
|
||||
} finally {
|
||||
rollback();
|
||||
set.setIsSerNo(false);
|
||||
set.saveEx();
|
||||
|
||||
if (product != null)
|
||||
product.deleteEx(true);
|
||||
|
||||
if (!disallow) {
|
||||
wh.setIsDisallowNegativeInv(false);
|
||||
wh.saveEx();
|
||||
CacheMgt.get().reset(MWarehouse.Table_Name, wh.get_ID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue