/****************************************************************************** * Product: Adempiere ERP & CRM Smart Business Solution * * 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 * * Copyright (C) 2003-2010 e-Evolution,SC. All Rights Reserved. * * Contributor(s): Victor Perez www.e-evolution.com * * Teo Sarca, www.arhipac.ro * *****************************************************************************/ package org.eevolution.model; import java.math.BigDecimal; import java.math.RoundingMode; import java.sql.ResultSet; import java.sql.Timestamp; import java.util.List; import java.util.Properties; import org.adempiere.exceptions.AdempiereException; import org.compiere.model.I_M_Product; import org.compiere.model.MProduct; import org.compiere.model.MUOM; import org.compiere.model.Query; import org.compiere.util.DB; import org.compiere.util.Env; import org.idempiere.cache.ImmutablePOSupport; /** * PP Product BOM Line Model. * * MPPProductBOMLine l = new MPPProductBOMLine(bom); * l.setM_Product_ID(wbl.getM_Product_ID()); * l.setQty(wbl.getQuantity()); * l.saveEx(); * * * @author Victor Perez www.e-evolution.com * @author Teo Sarca, www.arhipac.ro */ public class MPPProductBOMLine extends X_PP_Product_BOMLine implements ImmutablePOSupport { /** * generated serial id */ private static final long serialVersionUID = 5942313871247489972L; protected MPPProductBOM m_bom = null; /** * Get all the Product BOM line for a Component Product * @param product Component Product * @return list of MPPProductBOMLine */ public static List getByProduct(MProduct product) { final String whereClause = MPPProductBOMLine.COLUMNNAME_M_Product_ID+"=?"; return new Query(product.getCtx(), MPPProductBOMLine.Table_Name, whereClause, product.get_TrxName()) .setParameters(product.getM_Product_ID()) .setOnlyActiveRecords(true) .setClient_ID() .list(); } /** * Get all the BOM lines for a Product * @param product Product * @return list of MPPProductBOMLine */ public static MPPProductBOMLine[] getBOMLines(MProduct product) { final String whereClause = MPPProductBOMLine.COLUMNNAME_PP_Product_BOM_ID + " IN ( SELECT PP_PRODUCT_BOM_ID FROM PP_PRODUCT_BOM WHERE M_PRODUCT_ID = " + product.getM_Product_ID() + " AND IsActive='Y' AND BOMType='A' AND BOMUse='A' " + " AND AD_Client_ID=" + product.getAD_Client_ID() + ")"; List list = new Query(product.getCtx(), MPPProductBOMLine.Table_Name, whereClause, product.get_TrxName()) .setClient_ID() .setOnlyActiveRecords(true) .setOrderBy(MPPProductBOMLine.COLUMNNAME_Line) .list(); MPPProductBOMLine[] retValue = new MPPProductBOMLine[list.size()]; list.toArray(retValue); return retValue; } /** * UUID based Constructor * @param ctx Context * @param PP_Product_BOMLine_UU UUID key * @param trxName Transaction */ public MPPProductBOMLine(Properties ctx, String PP_Product_BOMLine_UU, String trxName) { super(ctx, PP_Product_BOMLine_UU, trxName); } /** * Default Constructor * @param ctx context * @param PP_Product_BOMLine BOM line to load * @param trxName */ public MPPProductBOMLine(Properties ctx, int PP_Product_BOMLine, String trxName) { super(ctx, PP_Product_BOMLine, trxName); } // MPPProductBOMLine /** * Parent Constructor. * @param bom parent BOM */ public MPPProductBOMLine(MPPProductBOM bom) { super(bom.getCtx(), 0, bom.get_TrxName()); if (bom.get_ID() <= 0) throw new IllegalArgumentException("Header not saved"); setPP_Product_BOM_ID(bom.getPP_Product_BOM_ID()); // parent } /** * Load Constructor * @param ctx context * @param rs result set record */ public MPPProductBOMLine(Properties ctx, ResultSet rs, String trxName) { super(ctx, rs, trxName); } // MPPProductBOMLine /** * Copy constructor * @param copy */ public MPPProductBOMLine(MPPProductBOMLine copy) { this(Env.getCtx(), copy); } /** * Copy constructor * @param ctx * @param copy */ public MPPProductBOMLine(Properties ctx, MPPProductBOMLine copy) { this(ctx, copy, (String) null); } /** * Copy constructor * @param ctx * @param copy * @param trxName */ public MPPProductBOMLine(Properties ctx, MPPProductBOMLine copy, String trxName) { this(ctx, 0, trxName); copyPO(copy); this.m_bom = copy.m_bom != null ? new MPPProductBOM(ctx, copy.m_bom, trxName) : null; } /** * @param ctx * @param PP_Product_BOMLine_ID * @param trxName * @param virtualColumns */ public MPPProductBOMLine(Properties ctx, int PP_Product_BOMLine_ID, String trxName, String... virtualColumns) { super(ctx, PP_Product_BOMLine_ID, trxName, virtualColumns); } /** * Get Low Level of parent Product * @return low level */ public int getLowLevel() { I_M_Product parent = getPP_Product_BOM().getM_Product(); if (parent.getLowLevel() > 0) return parent.getLowLevel()+1; else return 1; } /** * get Parent BOM * @return parent bom */ public MPPProductBOM getParent() { if(m_bom == null) { m_bom = new MPPProductBOM (getCtx(), this.getPP_Product_BOM_ID(), get_TrxName()); } return m_bom; } /** * Get updateable copy of component product * @return */ public MProduct getProduct() { return MProduct.getCopy(getCtx(), getM_Product_ID(), get_TrxName()); } /** * Calculate Low Level of a Product * @param ctx * @param M_Product_ID Product ID * @param trxName * @return int low level */ public static int getLowLevel(Properties ctx, int M_Product_ID, String trxName) { return MProduct.get(ctx, M_Product_ID, trxName).getLowLevel(); } @Override protected boolean beforeSave(boolean newRecord) { // For Co/By Products, Qty should be always negative: if (isCoProduct() && getQty(false).signum() >= 0) { throw new AdempiereException("@Qty@ > 0"); } // Update Line# if (getLine() <= 0) { final String sql = "SELECT COALESCE(MAX("+COLUMNNAME_Line+"),0) + 10 FROM "+Table_Name +" WHERE "+COLUMNNAME_PP_Product_BOM_ID+"=?"; int line = DB.getSQLValueEx(get_TrxName(), sql, getPP_Product_BOM_ID()); setLine(line); } return true; } @Override protected boolean afterSave(boolean newRecord, boolean success) { if (!success) return false; // Update LowLevel of product int lowlevel = getLowLevel(); MProduct product = new MProduct(getCtx(), getM_Product_ID(), get_TrxName()); if (lowlevel > product.getLowLevel()) { product.setLowLevel(lowlevel); product.saveEx(); } // Reset IsVerified flag of parent product MPPProductBOM bom = getParent(); MProduct parentProduct = (MProduct) bom.getM_Product(); if (parentProduct.isVerified()) { MPPProductBOM defaultBOM = MPPProductBOM.getDefault(parentProduct, get_TrxName()); if (defaultBOM != null && defaultBOM.getPP_Product_BOM_ID()==bom.getPP_Product_BOM_ID()) { if ( newRecord || is_ValueChanged("M_Product_ID") // Product Line was changed || (is_ValueChanged("IsActive") && isActive())) // line was activated { // Invalidate BOM parentProduct.setIsVerified(false); parentProduct.saveEx(get_TrxName()); } if (parentProduct.isVerified() && is_ValueChanged("IsActive") && !isActive()) // line was inactivated { MPPProductBOMLine[] lines = bom.getLines(true); int count = 0; for (MPPProductBOMLine line : lines) { if (line.getPP_Product_BOMLine_ID() == getPP_Product_BOMLine_ID()) continue; if (line.isActive()) count++; } if (count == 0) { parentProduct.setIsVerified(false); parentProduct.saveEx(get_TrxName()); } } } } return true; } /** * Is BOM line valid for date * @param date * @return true if BOM line is valid for date */ public boolean isValidFromTo(Timestamp date) { Timestamp validFrom = getValidFrom(); Timestamp validTo = getValidTo(); if (validFrom != null && date.before(validFrom)) return false; if (validTo != null && date.after(validTo)) return false; return true; } /** * Is COMPONENTTYPE_By_Product * @return true if it is COMPONENTTYPE_By_Product */ public boolean isByProduct() { String componentType = getComponentType(); return COMPONENTTYPE_By_Product.equals(componentType); } /** * Is COMPONENTTYPE_Co_Product * @return true if it is COMPONENTTYPE_Co_Product */ public boolean isCoProduct() { String componentType = getComponentType(); return COMPONENTTYPE_Co_Product.equals(componentType); } /** * Return absolute (unified) quantity value.
* If IsQtyPercentage then QtyBatch / 100 will be returned. * Else QtyBOM will be returned. * @param includeScrapQty if true, scrap qty will be used for calculating qty * @return qty */ public BigDecimal getQty(boolean includeScrapQty) { int precision = getPrecision(); BigDecimal qty; if (isQtyPercentage()) { precision += 2; qty = getQtyBatch().divide(Env.ONEHUNDRED, precision, RoundingMode.HALF_UP); } else { qty = getQtyBOM(); } // if (includeScrapQty) { BigDecimal scrapDec = getScrap().divide(Env.ONEHUNDRED, 12, RoundingMode.UP); qty = qty.divide(Env.ONE.subtract(scrapDec), precision, RoundingMode.HALF_UP); } // if (qty.scale() > precision) { qty = qty.setScale(precision, RoundingMode.HALF_UP); } // return qty; } /** * Like {@link #getQty(boolean)}, includeScrapQty = false */ public BigDecimal getQty() { return getQty(false); } /** * Get UOM precision * @return UOM precision */ public int getPrecision() { return MUOM.getPrecision(getCtx(), getC_UOM_ID()); } /** * Get cost allocation percentage * @param fallback use QtyBOM/QtyPercentage if CostAllocationPerc is zero * @return co-product cost allocation percent (i.e. -1/qty) */ public BigDecimal getCostAllocationPerc(boolean fallback) { BigDecimal allocationPercent = super.getCostAllocationPerc(); if (allocationPercent.signum() != 0) return allocationPercent; // // Fallback and try to calculate it from Qty if (fallback) { BigDecimal qty = getQty(false).negate(); if (qty.signum() != 0) { allocationPercent = Env.ONE.divide(qty, 4, RoundingMode.HALF_UP); } } return allocationPercent; } @Override public MPPProductBOMLine markImmutable() { if (is_Immutable()) return this; makeImmutable(); return this; } }