From 93ebe43cbd1b83d3247408c58c092e2f941db74d Mon Sep 17 00:00:00 2001 From: hengsin Date: Thu, 23 Dec 2021 17:14:22 +0800 Subject: [PATCH] IDEMPIERE-5121 MStorageOnHand API enhancements (#1079) --- .../src/org/compiere/model/CalloutInOut.java | 1 - .../src/org/compiere/model/MInOut.java | 14 +- .../src/org/compiere/model/MInventory.java | 4 +- .../src/org/compiere/model/MMovement.java | 12 +- .../src/org/compiere/model/MProjectIssue.java | 6 +- .../org/compiere/model/MStorageOnHand.java | 105 ++++++++-- .../src/org/eevolution/model/MDDOrder.java | 4 +- .../test/model/MStorageOnHandTest.java | 196 ++++++++++++++++++ .../idempiere/test/model/SalesOrderTest.java | 4 +- 9 files changed, 301 insertions(+), 45 deletions(-) create mode 100644 org.idempiere.test/src/org/idempiere/test/model/MStorageOnHandTest.java diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutInOut.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutInOut.java index 91c3d2008e..90e3afbcb2 100644 --- a/org.adempiere.base.callout/src/org/compiere/model/CalloutInOut.java +++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutInOut.java @@ -191,7 +191,6 @@ public class CalloutInOut extends CalloutEngine if (rs.next()) { // Set Movement Type - String DocBaseType = rs.getString("DocBaseType"); // BF [2708789] Read IsSOTrx from C_DocType String trxFlag = rs.getString("IsSOTrx"); Object isSOTrxValue = mTab.getValue("IsSOTrx"); diff --git a/org.adempiere.base/src/org/compiere/model/MInOut.java b/org.adempiere.base/src/org/compiere/model/MInOut.java index 980245b301..5d62b16e50 100644 --- a/org.adempiere.base/src/org/compiere/model/MInOut.java +++ b/org.adempiere.base/src/org/compiere/model/MInOut.java @@ -1456,7 +1456,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess } // Update Storage - see also VMatch.createMatchRecord - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), + if (!MStorageOnHand.add(getCtx(), sLine.getM_Locator_ID(), sLine.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), @@ -1545,7 +1545,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess } else if (storage.getQtyOnHand().signum() > 0) { BigDecimal onHand = storage.getQtyOnHand(); // this locator has less qty than required, ship all qtyonhand and iterate to next locator - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), + if (!MStorageOnHand.add(getCtx(), sLine.getM_Locator_ID(), sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), @@ -1578,7 +1578,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess // Fallback: Update Storage - see also VMatch.createMatchRecord if (pendingQty.signum() != 0 && - !MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), + !MStorageOnHand.add(getCtx(), sLine.getM_Locator_ID(), sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), @@ -2671,7 +2671,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess return null; if (reversal) { - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), M_Locator_ID, product.getM_Product_ID(), 0, qty.negate(), dateMaterialPolicy, trxName)) { + if (!MStorageOnHand.add(getCtx(), M_Locator_ID, product.getM_Product_ID(), 0, qty.negate(), dateMaterialPolicy, trxName)) { String lastError = CLogger.retrieveErrorString(""); m_processMsg = "Cannot move Inventory OnHand to Non ASI [" + product.getValue() + "] - " + lastError; return DocAction.STATUS_Invalid; @@ -2683,7 +2683,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess m_processMsg = "Transaction From not inserted (MA) [" + product.getValue() + "] - "; return DocAction.STATUS_Invalid; } - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), M_Locator_ID, product.getM_Product_ID(), M_AttributeSetInstance_ID, qty, dateMaterialPolicy, trxName)) { + if (!MStorageOnHand.add(getCtx(), M_Locator_ID, product.getM_Product_ID(), M_AttributeSetInstance_ID, qty, dateMaterialPolicy, trxName)) { String lastError = CLogger.retrieveErrorString(""); m_processMsg = "Cannot move Inventory OnHand to Shipment ASI [" + product.getValue() + "] - " + lastError; return DocAction.STATUS_Invalid; @@ -2741,7 +2741,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess if (!reversal && toMove.compareTo(onhand.getQtyOnHand()) >= 0) { toMove = onhand.getQtyOnHand(); } - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), M_Locator_ID, product.getM_Product_ID(), 0, toMove.negate(), onhand.getDateMaterialPolicy(), trxName)) { + if (!MStorageOnHand.add(getCtx(), M_Locator_ID, product.getM_Product_ID(), 0, toMove.negate(), onhand.getDateMaterialPolicy(), trxName)) { String lastError = CLogger.retrieveErrorString(""); m_processMsg = "Cannot move Inventory OnHand to Non ASI [" + product.getValue() + "] - " + lastError; return DocAction.STATUS_Invalid; @@ -2758,7 +2758,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess if ((!reversal && totalToMove.signum() <= 0) || (reversal && totalToMove.signum() >= 0)) break; } - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), M_Locator_ID, product.getM_Product_ID(), M_AttributeSetInstance_ID, qty, + if (!MStorageOnHand.add(getCtx(), M_Locator_ID, product.getM_Product_ID(), M_AttributeSetInstance_ID, qty, (dateMaterialPolicy != null ? dateMaterialPolicy : onHandDateMaterialPolicy), trxName)) { String lastError = CLogger.retrieveErrorString(""); m_processMsg = "Cannot move Inventory OnHand to Shipment ASI [" + product.getValue() + "] - " + lastError; diff --git a/org.adempiere.base/src/org/compiere/model/MInventory.java b/org.adempiere.base/src/org/compiere/model/MInventory.java index 34e84b4a0b..4b0aa95489 100644 --- a/org.adempiere.base/src/org/compiere/model/MInventory.java +++ b/org.adempiere.base/src/org/compiere/model/MInventory.java @@ -559,7 +559,7 @@ public class MInventory extends X_M_Inventory implements DocAction if (log.isLoggable(Level.FINE)) log.fine("Diff=" + qtyDiff + " - Instance OnHand=" + QtyMA + "->" + QtyNew); - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), + if (!MStorageOnHand.add(getCtx(), line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), @@ -617,7 +617,7 @@ public class MInventory extends X_M_Inventory implements DocAction } //Fallback: Update Storage - see also VMatch.createMatchRecord - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), + if (!MStorageOnHand.add(getCtx(), line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), diff --git a/org.adempiere.base/src/org/compiere/model/MMovement.java b/org.adempiere.base/src/org/compiere/model/MMovement.java index 146252f871..77e97b93bc 100644 --- a/org.adempiere.base/src/org/compiere/model/MMovement.java +++ b/org.adempiere.base/src/org/compiere/model/MMovement.java @@ -444,9 +444,8 @@ public class MMovement extends X_M_Movement implements DocAction { MMovementLineMA ma = mas[j]; // - MLocator locator = new MLocator (getCtx(), line.getM_Locator_ID(), get_TrxName()); //Update Storage - if (!MStorageOnHand.add(getCtx(),locator.getM_Warehouse_ID(), + if (!MStorageOnHand.add(getCtx(), line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), @@ -464,8 +463,7 @@ public class MMovement extends X_M_Movement implements DocAction M_AttributeSetInstanceTo_ID = ma.getM_AttributeSetInstance_ID(); } //Update Storage - MLocator locatorTo = new MLocator (getCtx(), line.getM_LocatorTo_ID(), get_TrxName()); - if (!MStorageOnHand.add(getCtx(),locatorTo.getM_Warehouse_ID(), + if (!MStorageOnHand.add(getCtx(), line.getM_LocatorTo_ID(), line.getM_Product_ID(), M_AttributeSetInstanceTo_ID, @@ -528,12 +526,11 @@ public class MMovement extends X_M_Movement implements DocAction if (dateMPolicy == null && storages.length > 0) dateMPolicy = storages[0].getDateMaterialPolicy(); - MLocator locator = new MLocator (getCtx(), line.getM_Locator_ID(), get_TrxName()); //Update Storage Timestamp effDateMPolicy = dateMPolicy; if (dateMPolicy == null && line.getMovementQty().negate().signum() > 0) effDateMPolicy = getMovementDate(); - if (!MStorageOnHand.add(getCtx(),locator.getM_Warehouse_ID(), + if (!MStorageOnHand.add(getCtx(), line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), @@ -548,8 +545,7 @@ public class MMovement extends X_M_Movement implements DocAction effDateMPolicy = dateMPolicy; if (dateMPolicy == null && line.getMovementQty().signum() > 0) effDateMPolicy = getMovementDate(); - MLocator locatorTo = new MLocator (getCtx(), line.getM_LocatorTo_ID(), get_TrxName()); - if (!MStorageOnHand.add(getCtx(),locatorTo.getM_Warehouse_ID(), + if (!MStorageOnHand.add(getCtx(), line.getM_LocatorTo_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), diff --git a/org.adempiere.base/src/org/compiere/model/MProjectIssue.java b/org.adempiere.base/src/org/compiere/model/MProjectIssue.java index 3dadfdd197..7ae3a471d1 100644 --- a/org.adempiere.base/src/org/compiere/model/MProjectIssue.java +++ b/org.adempiere.base/src/org/compiere/model/MProjectIssue.java @@ -178,8 +178,6 @@ public class MProjectIssue extends X_C_ProjectIssue implements DocAction, DocOpt getMovementQty().negate(), getMovementDate(), get_TrxName()); mTrx.setC_ProjectIssue_ID(getC_ProjectIssue_ID()); // - MLocator loc = MLocator.get(getCtx(), getM_Locator_ID()); - Timestamp dateMPolicy = getMovementDate(); if(getM_AttributeSetInstance_ID()>0){ @@ -217,14 +215,14 @@ public class MProjectIssue extends X_C_ProjectIssue implements DocAction, DocOpt } if (qtyToIssue.signum() > 0) { - ok = MStorageOnHand.add(getCtx(), loc.getM_Warehouse_ID(), getM_Locator_ID(), + ok = MStorageOnHand.add(getCtx(), getM_Locator_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(), qtyToIssue.negate(),dateMPolicy, get_TrxName()); } } else { - ok = MStorageOnHand.add(getCtx(), loc.getM_Warehouse_ID(), getM_Locator_ID(), + ok = MStorageOnHand.add(getCtx(), getM_Locator_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(), getMovementQty().negate(),dateMPolicy, get_TrxName()); } diff --git a/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java b/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java index e50def8927..5023d69645 100644 --- a/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java +++ b/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java @@ -149,7 +149,7 @@ public class MStorageOnHand extends X_M_StorageOnHand * Get all Storages for Product where QtyOnHand <> 0 * @param ctx context * @param M_Product_ID product - * @param M_Locator_ID locator + * @param M_Locator_ID locator, 0 to match all locator * @param trxName transaction * @return existing or null */ @@ -163,25 +163,68 @@ public class MStorageOnHand extends X_M_StorageOnHand * Get all Storages for Product where QtyOnHand <> 0 * @param ctx context * @param M_Product_ID product - * @param M_Locator_ID locator + * @param M_Locator_ID locator, 0 to match all locator * @param trxName transaction * @return existing or null */ public static MStorageOnHand[] getAll (Properties ctx, int M_Product_ID, int M_Locator_ID, String trxName, boolean forUpdate, int timeout) { - String sqlWhere = "M_Product_ID=? AND M_Locator_ID=? AND QtyOnHand <> 0"; - Query query = new Query(ctx, MStorageOnHand.Table_Name, sqlWhere, trxName) - .setParameters(M_Product_ID, M_Locator_ID); + return getAll(ctx, M_Product_ID, M_Locator_ID, false, true, trxName, forUpdate, timeout); + } + + /** + * Get all Storages for Product where QtyOnHand <> 0 + * @param ctx context + * @param M_Product_ID product + * @param M_Locator_ID locator, 0 to match all locator + * @param locatorPriority If true, sort descending by locator Priority No + * @param fifo Sort ascending(fifo) or descending(lifo) by date material policy, m_attributesetinstance_id + * @param trxName transaction + * @param forUpdate If true, acquire db lock for update + * @param timeout timeout for the acquisition of db update lock + * @return existing or null + */ + public static MStorageOnHand[] getAll (Properties ctx, + int M_Product_ID, int M_Locator_ID, boolean locatorPriority, boolean fifo, String trxName, boolean forUpdate, int timeout) + { + String sqlWhere = "M_Product_ID=? AND QtyOnHand <> 0"; + if (M_Locator_ID > 0) + sqlWhere = sqlWhere + " AND M_Locator_ID=? "; + Query query = new Query(ctx, MStorageOnHand.Table_Name, sqlWhere, trxName); + if (M_Locator_ID > 0) + query.setParameters(M_Product_ID, M_Locator_ID); + else + query.setParameters(M_Product_ID); MProduct product = MProduct.get(ctx, M_Product_ID); + StringBuilder orderBy = new StringBuilder(); + if (locatorPriority) + { + query.addJoinClause("JOIN M_Locator locator ON (M_StorageOnHand.M_Locator_ID=locator.M_Locator_ID) "); + orderBy.append("locator.PriorityNo DESC, "); + } if (product.isUseGuaranteeDateForMPolicy()) { - query.addJoinClause(" LEFT OUTER JOIN M_AttributeSetInstance asi ON (M_StorageOnHand.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) ") - .setOrderBy("asi."+I_M_AttributeSetInstance.COLUMNNAME_GuaranteeDate); + query.addJoinClause(" LEFT OUTER JOIN M_AttributeSetInstance asi ON (M_StorageOnHand.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) "); + orderBy.append("asi.").append(I_M_AttributeSetInstance.COLUMNNAME_GuaranteeDate); + if (!fifo) + orderBy.append(" DESC"); + orderBy.append(", "); + orderBy.append(MStorageOnHand.Table_Name).append(".").append(MStorageOnHand.COLUMNNAME_M_AttributeSetInstance_ID); + if (!fifo) + orderBy.append(" DESC"); + query.setOrderBy(orderBy.toString()); } else { - query.setOrderBy(MStorageOnHand.COLUMNNAME_DateMaterialPolicy+","+ MStorageOnHand.COLUMNNAME_M_AttributeSetInstance_ID); + orderBy.append(MStorageOnHand.Table_Name).append(".").append(MStorageOnHand.COLUMNNAME_DateMaterialPolicy); + if (!fifo) + orderBy.append(" DESC"); + orderBy.append(", "); + orderBy.append(MStorageOnHand.Table_Name).append(".").append(MStorageOnHand.COLUMNNAME_M_AttributeSetInstance_ID); + if (!fifo) + orderBy.append(" DESC"); + query.setOrderBy(orderBy.toString()); } if (forUpdate) { @@ -680,7 +723,28 @@ public class MStorageOnHand extends X_M_StorageOnHand * Update Storage Info add. * Called from MProjectIssue * @param ctx context - * @param M_Warehouse_ID warehouse + * @param M_Warehouse_ID warehouse, not use + * @param M_Locator_ID locator + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID AS Instance + * @param reservationAttributeSetInstance_ID reservation AS Instance + * @param diffQtyOnHand add on hand + * @param dateMPolicy + * @param trxName transaction + * @return true if updated + * @deprecated + */ + public static boolean add (Properties ctx, int M_Warehouse_ID, int M_Locator_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + BigDecimal diffQtyOnHand,Timestamp dateMPolicy, String trxName) + { + return add(ctx, M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID, diffQtyOnHand, dateMPolicy, trxName); + } + + /** + * Update Storage Info add. + * Called from MProjectIssue + * @param ctx context * @param M_Locator_ID locator * @param M_Product_ID product * @param M_AttributeSetInstance_ID AS Instance @@ -690,7 +754,7 @@ public class MStorageOnHand extends X_M_StorageOnHand * @param trxName transaction * @return true if updated */ - public static boolean add (Properties ctx, int M_Warehouse_ID, int M_Locator_ID, + public static boolean add (Properties ctx, int M_Locator_ID, int M_Product_ID, int M_AttributeSetInstance_ID, BigDecimal diffQtyOnHand,Timestamp dateMPolicy, String trxName) { @@ -744,10 +808,10 @@ public class MStorageOnHand extends X_M_StorageOnHand * Get Location with highest Locator Priority and a sufficient OnHand Qty * @param M_Warehouse_ID warehouse * @param M_Product_ID product - * @param M_AttributeSetInstance_ID asi + * @param M_AttributeSetInstance_ID asi id, use negative value (for e.g -1) to match all asi including 0 * @param Qty qty * @param trxName transaction - * @return id + * @return locator id (0 if no match found) */ public static int getM_Locator_ID (int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, BigDecimal Qty, @@ -758,12 +822,14 @@ public class MStorageOnHand extends X_M_StorageOnHand String sql = "SELECT s.M_Locator_ID, s.QtyOnHand " + "FROM M_StorageOnHand s" + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)" - + " INNER JOIN M_Product p ON (s.M_Product_ID=p.M_Product_ID)" - + " LEFT OUTER JOIN M_AttributeSet mas ON (p.M_AttributeSet_ID=mas.M_AttributeSet_ID) " - + "WHERE l.M_Warehouse_ID=?" - + " AND s.M_Product_ID=?" - + " AND (mas.IsInstanceAttribute IS NULL OR mas.IsInstanceAttribute='N' OR s.M_AttributeSetInstance_ID=?)" - + " AND l.IsActive='Y' " + + " INNER JOIN M_Product p ON (s.M_Product_ID=p.M_Product_ID) "; + if (M_AttributeSetInstance_ID >= 0) + sql = sql + " LEFT OUTER JOIN M_AttributeSet mas ON (p.M_AttributeSet_ID=mas.M_AttributeSet_ID) "; + sql = sql + "WHERE l.M_Warehouse_ID=? " + + " AND s.M_Product_ID=? "; + if (M_AttributeSetInstance_ID >= 0) + sql = sql + " AND (mas.IsInstanceAttribute IS NULL OR mas.IsInstanceAttribute='N' OR s.M_AttributeSetInstance_ID=?) "; + sql = sql + " AND l.IsActive='Y' " + "ORDER BY l.PriorityNo DESC, s.QtyOnHand DESC"; PreparedStatement pstmt = null; @@ -773,7 +839,8 @@ public class MStorageOnHand extends X_M_StorageOnHand pstmt = DB.prepareStatement(sql, trxName); pstmt.setInt(1, M_Warehouse_ID); pstmt.setInt(2, M_Product_ID); - pstmt.setInt(3, M_AttributeSetInstance_ID); + if (M_AttributeSetInstance_ID >= 0) + pstmt.setInt(3, M_AttributeSetInstance_ID); rs = pstmt.executeQuery(); while (rs.next()) { diff --git a/org.adempiere.base/src/org/eevolution/model/MDDOrder.java b/org.adempiere.base/src/org/eevolution/model/MDDOrder.java index 3f894242a4..c3512bd615 100644 --- a/org.adempiere.base/src/org/eevolution/model/MDDOrder.java +++ b/org.adempiere.base/src/org/eevolution/model/MDDOrder.java @@ -881,7 +881,7 @@ public class MDDOrder extends X_DD_Order implements DocAction if (product.isStocked()) { // Update Storage - if (!MStorageOnHand.add(getCtx(), locator_to.getM_Warehouse_ID(), locator_to.getM_Locator_ID(), + if (!MStorageOnHand.add(getCtx(), locator_to.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), Env.ZERO,null, get_TrxName())) @@ -889,7 +889,7 @@ public class MDDOrder extends X_DD_Order implements DocAction throw new AdempiereException(); } - if (!MStorageOnHand.add(getCtx(), locator_from.getM_Warehouse_ID(), locator_from.getM_Locator_ID(), + if (!MStorageOnHand.add(getCtx(), locator_from.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), Env.ZERO,null, get_TrxName())) diff --git a/org.idempiere.test/src/org/idempiere/test/model/MStorageOnHandTest.java b/org.idempiere.test/src/org/idempiere/test/model/MStorageOnHandTest.java new file mode 100644 index 0000000000..070925bc0f --- /dev/null +++ b/org.idempiere.test/src/org/idempiere/test/model/MStorageOnHandTest.java @@ -0,0 +1,196 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.idempiere.test.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +import org.compiere.model.MAttributeSet; +import org.compiere.model.MAttributeSetInstance; +import org.compiere.model.MLocator; +import org.compiere.model.MProduct; +import org.compiere.model.MStorageOnHand; +import org.compiere.util.CacheMgt; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.TimeUtil; +import org.idempiere.test.AbstractTestCase; +import org.junit.jupiter.api.Test; + +/** + * + * @author hengsin + * + */ +public class MStorageOnHandTest extends AbstractTestCase { + + private final static int FERTILIZER_LOT_ATTRIBUTESET_ID = 101; + private final static int HQ_LOCATOR_ID = 101; + private final static int STORE_LOCATOR_ID = 102; + private static final int TAX_CATEGORY_STANDARD_ID = 107; + private static final int CHEMICALS_CATEGORY_ID = 109; + private static final int UOM_EACH_ID = 100; + + public MStorageOnHandTest() { + } + + @Test + public void testGetAll() { + //storageonhand api doesn't use trx to retrieve product + MProduct product = new MProduct(Env.getCtx(), 0, null); + product.setName("testGetAll"); + product.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID); + product.setIsStocked(true); + product.setProductType(MProduct.PRODUCTTYPE_Item); + product.setC_UOM_ID(UOM_EACH_ID); + product.setM_Product_Category_ID(CHEMICALS_CATEGORY_ID); + product.setC_TaxCategory_ID(TAX_CATEGORY_STANDARD_ID); + product.saveEx(); + + try { + Timestamp today = TimeUtil.getDay(null); + Timestamp tomorrow = TimeUtil.addDays(today, 1); + MStorageOnHand.add(Env.getCtx(), HQ_LOCATOR_ID, product.get_ID(), 0, new BigDecimal("1"), today, getTrxName()); + MStorageOnHand.add(Env.getCtx(), STORE_LOCATOR_ID, product.get_ID(), 0, new BigDecimal("2"), tomorrow, getTrxName()); + + MStorageOnHand[] onhands = MStorageOnHand.getAll(Env.getCtx(), product.get_ID(), HQ_LOCATOR_ID, getTrxName(), false, 0); + assertNotNull(onhands); + assertEquals(1, onhands.length); + assertEquals(HQ_LOCATOR_ID, onhands[0].getM_Locator_ID()); + assertEquals(1, onhands[0].getQtyOnHand().intValue()); + + onhands = MStorageOnHand.getAll(Env.getCtx(), product.get_ID(), STORE_LOCATOR_ID, getTrxName(), false, 0); + assertNotNull(onhands); + assertEquals(1, onhands.length); + assertEquals(STORE_LOCATOR_ID, onhands[0].getM_Locator_ID()); + assertEquals(2, onhands[0].getQtyOnHand().intValue()); + + onhands = MStorageOnHand.getAll(Env.getCtx(), product.get_ID(), 0, getTrxName(), false, 0); + assertNotNull(onhands); + assertEquals(2, onhands.length); + + //test locator priority + MLocator locator = new MLocator(Env.getCtx(), STORE_LOCATOR_ID, getTrxName()); + locator.setPriorityNo(Integer.MAX_VALUE); + locator.saveEx(); + onhands = MStorageOnHand.getAll(Env.getCtx(), product.get_ID(), 0, true, true, getTrxName(), false, 0); + assertNotNull(onhands); + assertEquals(2, onhands.length); + assertEquals(STORE_LOCATOR_ID, onhands[0].getM_Locator_ID()); + assertEquals(2, onhands[0].getQtyOnHand().intValue()); + + //test fifo + onhands = MStorageOnHand.getAll(Env.getCtx(), product.get_ID(), 0, false, true, getTrxName(), false, 0); + assertNotNull(onhands); + assertEquals(2, onhands.length); + assertEquals(HQ_LOCATOR_ID, onhands[0].getM_Locator_ID()); + assertEquals(1, onhands[0].getQtyOnHand().intValue()); + + //test lifo + onhands = MStorageOnHand.getAll(Env.getCtx(), product.get_ID(), 0, false, false, getTrxName(), false, 0); + assertNotNull(onhands); + assertEquals(2, onhands.length); + assertEquals(STORE_LOCATOR_ID, onhands[0].getM_Locator_ID()); + assertEquals(2, onhands[0].getQtyOnHand().intValue()); + + //test UseGuaranteeDateForMPolicy + onhands = MStorageOnHand.getAll(Env.getCtx(), product.get_ID(), 0, false, true, getTrxName(), false, 0); + assertEquals(HQ_LOCATOR_ID, onhands[0].getM_Locator_ID()); + assertEquals(1, onhands[0].getQtyOnHand().intValue()); + MAttributeSet as = new MAttributeSet(Env.getCtx(), FERTILIZER_LOT_ATTRIBUTESET_ID, null); + try { + as.setUseGuaranteeDateForMPolicy(true); + as.saveEx(); + MAttributeSetInstance asi1 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName()); + asi1.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID); + asi1.setGuaranteeDate(tomorrow); + asi1.saveEx(); + DB.executeUpdateEx("UPDATE M_StorageOnHand SET M_AttributeSetInstance_ID=? WHERE M_StorageOnHand_UU=?", new Object[] {asi1.get_ID(), onhands[0].getM_StorageOnHand_UU()}, getTrxName()); + MAttributeSetInstance asi2 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName()); + asi2.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID); + asi2.setGuaranteeDate(today); + asi2.saveEx(); + DB.executeUpdateEx("UPDATE M_StorageOnHand SET M_AttributeSetInstance_ID=? WHERE M_StorageOnHand_UU=?", new Object[] {asi2.get_ID(), onhands[1].getM_StorageOnHand_UU()}, getTrxName()); + CacheMgt.get().reset(MProduct.Table_Name, product.get_ID()); + onhands = MStorageOnHand.getAll(Env.getCtx(), product.get_ID(), 0, false, true, getTrxName(), false, 0); + assertEquals(asi2.get_ID(), onhands[0].getM_AttributeSetInstance_ID()); + assertEquals(STORE_LOCATOR_ID, onhands[0].getM_Locator_ID()); + assertEquals(2, onhands[0].getQtyOnHand().intValue()); + } finally { + as.setUseGuaranteeDateForMPolicy(false); + as.saveEx(); + } + } finally { + product.deleteEx(true); + } + } + + @Test + public void testGetM_Locator_ID() { + MLocator hqLocator = new MLocator(Env.getCtx(), HQ_LOCATOR_ID, getTrxName()); + MLocator hqLocator1 = new MLocator(Env.getCtx(), 0, getTrxName()); + hqLocator1.setM_Warehouse_ID(hqLocator.getM_Warehouse_ID()); + hqLocator1.setValue("HQ Locator 1"); + hqLocator1.setPriorityNo(hqLocator.getPriorityNo()); + hqLocator1.setX("x"); + hqLocator1.setY("y"); + hqLocator1.setZ("z"); + hqLocator1.saveEx(); + + MProduct product = new MProduct(Env.getCtx(), 0, getTrxName()); + product.setName("testGetM_Locator_ID"); + product.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID); + product.setIsStocked(true); + product.setProductType(MProduct.PRODUCTTYPE_Item); + product.setC_UOM_ID(UOM_EACH_ID); + product.setM_Product_Category_ID(CHEMICALS_CATEGORY_ID); + product.setC_TaxCategory_ID(TAX_CATEGORY_STANDARD_ID); + product.saveEx(); + + Timestamp today = TimeUtil.getDay(null); + MStorageOnHand.add(Env.getCtx(), HQ_LOCATOR_ID, product.get_ID(), 0, new BigDecimal("2"), today, getTrxName()); + MAttributeSetInstance asi1 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName()); + asi1.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID); + asi1.setLot("Lot1"); + asi1.saveEx(); + MStorageOnHand.add(Env.getCtx(), hqLocator1.get_ID(), product.get_ID(), asi1.get_ID(), new BigDecimal("1"), today, getTrxName()); + + //get asi=0 + int M_Locator_ID = MStorageOnHand.getM_Locator_ID(hqLocator.getM_Warehouse_ID(), product.get_ID(), 0, new BigDecimal("1"), getTrxName()); + assertEquals(HQ_LOCATOR_ID, M_Locator_ID); + //get asi>0 + M_Locator_ID = MStorageOnHand.getM_Locator_ID(hqLocator.getM_Warehouse_ID(), product.get_ID(), asi1.get_ID(), new BigDecimal("1"), getTrxName()); + assertEquals(hqLocator1.get_ID(), M_Locator_ID); + //check all asi and get locator with highest onhand + M_Locator_ID = MStorageOnHand.getM_Locator_ID(hqLocator.getM_Warehouse_ID(), product.get_ID(), -1, new BigDecimal("1"), getTrxName()); + assertEquals(HQ_LOCATOR_ID, M_Locator_ID); + MStorageOnHand.add(Env.getCtx(), hqLocator1.get_ID(), product.get_ID(), asi1.get_ID(), new BigDecimal("2"), today, getTrxName()); + M_Locator_ID = MStorageOnHand.getM_Locator_ID(hqLocator.getM_Warehouse_ID(), product.get_ID(), -1, new BigDecimal("1"), getTrxName()); + assertEquals(hqLocator1.get_ID(), M_Locator_ID); + } +} diff --git a/org.idempiere.test/src/org/idempiere/test/model/SalesOrderTest.java b/org.idempiere.test/src/org/idempiere/test/model/SalesOrderTest.java index d43e8cfaf3..cc244bf995 100644 --- a/org.idempiere.test/src/org/idempiere/test/model/SalesOrderTest.java +++ b/org.idempiere.test/src/org/idempiere/test/model/SalesOrderTest.java @@ -698,8 +698,8 @@ public class SalesOrderTest extends AbstractTestCase { asi.setLot("1010"); asi.saveEx(); - MStorageOnHand.add(ctx, WAREHOUSE_FERTILIZER, LOCATOR_FERTILIZER, PRODUCT_FERT50, asi.getM_AttributeSetInstance_ID(), Env.ONE, past_month, trxName); - MStorageOnHand.add(ctx, WAREHOUSE_FERTILIZER, LOCATOR_FERTILIZER, PRODUCT_FERT50, asi.getM_AttributeSetInstance_ID(), Env.ONE, today, trxName); + MStorageOnHand.add(ctx, LOCATOR_FERTILIZER, PRODUCT_FERT50, asi.getM_AttributeSetInstance_ID(), Env.ONE, past_month, trxName); + MStorageOnHand.add(ctx, LOCATOR_FERTILIZER, PRODUCT_FERT50, asi.getM_AttributeSetInstance_ID(), Env.ONE, today, trxName); // Expected to create two entries in storage because of the different dates MStorageOnHand[] storages = MStorageOnHand.getWarehouse(ctx, WAREHOUSE_FERTILIZER,