/****************************************************************************** * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * * under the terms version 2 of the GNU General Public License as published * * by the Free Software Foundation. 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., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * For the text or an alternative of this public license, you may reach us * * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * * or via info@compiere.org or http://www.compiere.org/license.html * *****************************************************************************/ package org.compiere.model; import java.math.BigDecimal; import java.math.RoundingMode; import java.sql.ResultSet; import java.util.Properties; import org.compiere.process.DocAction; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.Util; /** * Inventory Document Line Model * * @author Jorg Janke * @version $Id: MInventoryLine.java,v 1.3 2006/07/30 00:51:02 jjanke Exp $ * * @author Teo Sarca, SC ARHIPAC SERVICE SRL *
  • BF [ 1817757 ] Error on saving MInventoryLine in a custom environment *
  • BF [ 1722982 ] Error with inventory when you enter count qty in negative */ public class MInventoryLine extends X_M_InventoryLine { /** * generated serial id */ private static final long serialVersionUID = 3973418005721380194L; /** * Get Inventory Line with parameters * @param inventory inventory * @param M_Locator_ID locator * @param M_Product_ID product * @param M_AttributeSetInstance_ID asi * @return line or null */ public static MInventoryLine get (MInventory inventory, int M_Locator_ID, int M_Product_ID, int M_AttributeSetInstance_ID) { final String whereClause = "M_Inventory_ID=? AND M_Locator_ID=?" +" AND M_Product_ID=? AND M_AttributeSetInstance_ID=?"; return new Query(inventory.getCtx(), I_M_InventoryLine.Table_Name, whereClause, inventory.get_TrxName()) .setParameters(inventory.get_ID(), M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID) .firstOnly(); } // get /** * UUID based Constructor * @param ctx Context * @param M_InventoryLine_UU UUID key * @param trxName Transaction */ public MInventoryLine(Properties ctx, String M_InventoryLine_UU, String trxName) { super(ctx, M_InventoryLine_UU, trxName); if (Util.isEmpty(M_InventoryLine_UU)) setInitialDefaults(); } /** * Default Constructor * @param ctx context * @param M_InventoryLine_ID line * @param trxName transaction */ public MInventoryLine (Properties ctx, int M_InventoryLine_ID, String trxName) { this (ctx, M_InventoryLine_ID, trxName, (String[]) null); } // MInventoryLine /** * @param ctx * @param M_InventoryLine_ID * @param trxName * @param virtualColumns */ public MInventoryLine(Properties ctx, int M_InventoryLine_ID, String trxName, String... virtualColumns) { super(ctx, M_InventoryLine_ID, trxName, virtualColumns); if (M_InventoryLine_ID == 0) setInitialDefaults(); } /** * Set the initial defaults for a new record */ private void setInitialDefaults() { setLine(0); setM_AttributeSetInstance_ID(0); // FK setInventoryType (INVENTORYTYPE_InventoryDifference); setQtyBook (Env.ZERO); setQtyCount (Env.ZERO); setProcessed(false); } /** * Load Constructor * @param ctx context * @param rs result set * @param trxName transaction */ public MInventoryLine (Properties ctx, ResultSet rs, String trxName) { super(ctx, rs, trxName); } // MInventoryLine /** * Detail Constructor. * Locator/Product/AttributeSetInstance must be unique. * @param inventory parent * @param M_Locator_ID locator * @param M_Product_ID product * @param M_AttributeSetInstance_ID instance * @param QtyBook book value * @param QtyCount count value * @param QtyInternalUse internal use value */ public MInventoryLine (MInventory inventory, int M_Locator_ID, int M_Product_ID, int M_AttributeSetInstance_ID, BigDecimal QtyBook, BigDecimal QtyCount, BigDecimal QtyInternalUse) { this (inventory.getCtx(), 0, inventory.get_TrxName()); if (inventory.get_ID() == 0) throw new IllegalArgumentException("Header not saved"); m_parent = inventory; setM_Inventory_ID (inventory.getM_Inventory_ID()); // Parent setClientOrg (inventory.getAD_Client_ID(), inventory.getAD_Org_ID()); setM_Locator_ID (M_Locator_ID); // FK setM_Product_ID (M_Product_ID); // FK setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID); // if (QtyBook != null) setQtyBook (QtyBook); if (QtyCount != null && QtyCount.signum() != 0) setQtyCount (QtyCount); if (QtyInternalUse != null && QtyInternalUse.signum() != 0) setQtyInternalUse (QtyInternalUse); } // MInventoryLine /** * @param inventory * @param M_Locator_ID * @param M_Product_ID * @param M_AttributeSetInstance_ID * @param QtyBook * @param QtyCount */ public MInventoryLine (MInventory inventory, int M_Locator_ID, int M_Product_ID, int M_AttributeSetInstance_ID, BigDecimal QtyBook, BigDecimal QtyCount) { this(inventory, M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID, QtyBook, QtyCount, null); } /** * Copy constructor * @param copy */ public MInventoryLine(MInventoryLine copy) { this(Env.getCtx(), copy); } /** * Copy constructor * @param ctx * @param copy */ public MInventoryLine(Properties ctx, MInventoryLine copy) { this(ctx, copy, (String) null); } /** * Copy constructor * @param ctx * @param copy * @param trxName */ public MInventoryLine(Properties ctx, MInventoryLine copy, String trxName) { this(ctx, 0, trxName); copyPO(copy); this.m_parent = null; this.m_product = copy.m_product != null ? new MProduct(ctx, copy.m_product, trxName) : null; } /** Parent */ protected MInventory m_parent = null; /** Product */ protected MProduct m_product = null; /** * Get Product * @return product or null if not defined */ public MProduct getProduct() { int M_Product_ID = getM_Product_ID(); if (M_Product_ID == 0) return null; if (m_product != null && m_product.getM_Product_ID() != M_Product_ID) m_product = null; // reset if (m_product == null) { m_product = MProduct.get(getCtx(), M_Product_ID, get_TrxName()); } return m_product; } // getProduct /** * Set Count Qty - enforce product UOM precision * @param QtyCount qty */ @Override public void setQtyCount (BigDecimal QtyCount) { if (QtyCount != null) { MProduct product = getProduct(); if (product != null) { int precision = product.getUOMPrecision(); QtyCount = QtyCount.setScale(precision, RoundingMode.HALF_UP); } } super.setQtyCount(QtyCount); } // setQtyCount /** * Set Internal Use Qty - enforce product UOM precision * @param QtyInternalUse qty */ @Override public void setQtyInternalUse (BigDecimal QtyInternalUse) { if (QtyInternalUse != null) { MProduct product = getProduct(); if (product != null) { int precision = product.getUOMPrecision(); QtyInternalUse = QtyInternalUse.setScale(precision, RoundingMode.HALF_UP); } } super.setQtyInternalUse(QtyInternalUse); } // setQtyInternalUse /** * Add to Description * @param description text */ public void addDescription (String description) { String desc = getDescription(); if (desc == null) setDescription(description); else{ StringBuilder msgd = new StringBuilder(desc).append(" | ").append(description); setDescription(msgd.toString()); } } // addDescription /** * Set Parent * @param parent parent */ protected void setParent(MInventory parent) { m_parent = parent; } // setParent /** * Get Parent * @return parent */ public MInventory getParent() { if (m_parent == null) m_parent = new MInventory (getCtx(), getM_Inventory_ID(), get_TrxName()); return m_parent; } // getParent /** * String Representation * @return info */ @Override public String toString () { StringBuilder sb = new StringBuilder ("MInventoryLine["); sb.append (get_ID()) .append("-M_Product_ID=").append (getM_Product_ID()) .append(",QtyCount=").append(getQtyCount()) .append(",QtyInternalUse=").append(getQtyInternalUse()) .append(",QtyBook=").append(getQtyBook()) .append(",M_AttributeSetInstance_ID=").append(getM_AttributeSetInstance_ID()) .append("]"); return sb.toString (); } // toString /** * Set Line (if not set yet).
    * Check mandatory fields by document type.
    * Cost adjustment document: set current cost price. * @param newRecord new * @return true if can be saved */ @Override protected boolean beforeSave (boolean newRecord) { if (newRecord && getParent().isProcessed()) { log.saveError("ParentComplete", Msg.translate(getCtx(), "M_Inventory_ID")); return false; } // Set Line No if (getLine() == 0) { String sql = "SELECT COALESCE(MAX(Line),0)+10 AS DefaultValue FROM M_InventoryLine WHERE M_Inventory_ID=?"; int ii = DB.getSQLValue (get_TrxName(), sql, getM_Inventory_ID()); setLine (ii); } // Enforce Qty UOM precision if (newRecord || is_ValueChanged("QtyCount")) setQtyCount(getQtyCount()); if (newRecord || is_ValueChanged("QtyInternalUse")) setQtyInternalUse(getQtyInternalUse()); MDocType dt = MDocType.get(getCtx(), getParent().getC_DocType_ID()); String docSubTypeInv = dt.getDocSubTypeInv(); if (MDocType.DOCSUBTYPEINV_InternalUseInventory.equals(docSubTypeInv)) { // Internal Use Inventory validations if (!INVENTORYTYPE_ChargeAccount.equals(getInventoryType())) setInventoryType(INVENTORYTYPE_ChargeAccount); // Charge is mandatory for internal use if (getC_Charge_ID() == 0) { log.saveError("InternalUseNeedsCharge", ""); return false; } // Error if book or count are filled on an internal use inventory document // i.e. coming from import or web services if (getQtyBook().signum() != 0) { log.saveError("Quantity", Msg.getElement(getCtx(), COLUMNNAME_QtyBook)); return false; } if (getQtyCount().signum() != 0) { log.saveError("Quantity", Msg.getElement(getCtx(), COLUMNNAME_QtyCount)); return false; } // QtyInternalUse is mandatory for internal use if (getQtyInternalUse().signum() == 0 && !getParent().getDocAction().equals(DocAction.ACTION_Void)) { log.saveError("FillMandatory", Msg.getElement(getCtx(), COLUMNNAME_QtyInternalUse)); return false; } } else if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) { // Physical Inventory validations if (INVENTORYTYPE_ChargeAccount.equals(getInventoryType())) { if (getC_Charge_ID() == 0) { log.saveError("FillMandatory", Msg.getElement(getCtx(), "C_Charge_ID")); return false; } } else if (getC_Charge_ID() != 0) { setC_Charge_ID(0); } // Error is QtyInternalUse is fill for physical inventory document if (getQtyInternalUse().signum() != 0) { log.saveError("Quantity", Msg.getElement(getCtx(), COLUMNNAME_QtyInternalUse)); return false; } } else if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(docSubTypeInv)) { int M_ASI_ID = getM_AttributeSetInstance_ID(); MProduct product = new MProduct(getCtx(), getM_Product_ID(), get_TrxName()); MClient client = MClient.get(getCtx()); MAcctSchema as = client.getAcctSchema(); String costingLevel = product.getCostingLevel(as); // M_AttributeSetInstance_ID is mandatory for COSTINGLEVEL_BatchLot if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(costingLevel)) { if (M_ASI_ID == 0) { log.saveError("FillMandatory", Msg.getElement(getCtx(), COLUMNNAME_M_AttributeSetInstance_ID)); return false; } } // Find accounting schema via currency int C_Currency_ID = getParent().getC_Currency_ID(); if (as.getC_Currency_ID() != C_Currency_ID) { MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(getCtx(), client.get_ID()); for (int i = 0; i < ass.length ; i ++) { MAcctSchema a = ass[i]; if (a.getC_Currency_ID() == C_Currency_ID) as = a ; } } // Set current cost price String costingMethod = getParent().getCostingMethod(); int AD_Org_ID = getAD_Org_ID(); MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod); if (cost == null) { // Error if no costing record (except standard costing) if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) { log.saveError("NoCostingRecord", ""); return false; } } else { if (is_new() || is_ValueChanged(COLUMNNAME_M_Product_ID) || is_ValueChanged(COLUMNNAME_M_AttributeSetInstance_ID)) setCurrentCostPrice(cost.getCurrentCostPrice()); } setM_Locator_ID(0); } else { //unknown subtype, should never reach here log.saveError("Error", "Document inventory subtype not configured, cannot complete"); return false; } // Set AD_Org to parent if not charge if (getC_Charge_ID() == 0) setAD_Org_ID(getParent().getAD_Org_ID()); return true; } // beforeSave /** * Is Internal Use Inventory * @return true if this is an internal use inventory document */ public boolean isInternalUseInventory() { // IDEMPIERE-675 MDocType dt = MDocType.get(getCtx(), getParent().getC_DocType_ID()); String docSubTypeInv = dt.getDocSubTypeInv(); return (MDocType.DOCSUBTYPEINV_InternalUseInventory.equals(docSubTypeInv)); } /** * Get Movement Qty *
  • negative value means outgoing trx *
  • positive value means incoming trx * @return movement qty */ public BigDecimal getMovementQty() { if(isInternalUseInventory()) { return getQtyInternalUse().negate(); } else { return getQtyCount().subtract(getQtyBook()); } } /** * @return true if is an outgoing transaction (movement qty < 0) */ public boolean isSOTrx() { return getMovementQty().signum() < 0; } } // MInventoryLine