diff --git a/base/src/org/compiere/model/MOrder.java b/base/src/org/compiere/model/MOrder.java index 27923ae999..949272b771 100644 --- a/base/src/org/compiere/model/MOrder.java +++ b/base/src/org/compiere/model/MOrder.java @@ -1,2401 +1,2383 @@ -/****************************************************************************** - * 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.io.File; -import java.math.BigDecimal; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Properties; -import java.util.logging.Level; -import java.util.regex.Pattern; - -import org.compiere.print.ReportEngine; -import org.compiere.process.DocAction; -import org.compiere.process.DocumentEngine; -import org.compiere.util.DB; -import org.compiere.util.Env; -import org.compiere.util.Msg; -import org.eevolution.model.MPPProductBOMLine; -import org.eevolution.model.MPPProductBOM; - - -/** - * Order Model. - * Please do not set DocStatus and C_DocType_ID directly. - * They are set in the process() method. - * Use DocAction and C_DocTypeTarget_ID instead. - * - * @author Jorg Janke - * @version $Id: MOrder.java,v 1.5 2006/10/06 00:42:24 jjanke Exp $ - */ -public class MOrder extends X_C_Order implements DocAction -{ - /** - * Create new Order by copying - * @param from order - * @param dateDoc date of the document date - * @param C_DocTypeTarget_ID target document type - * @param isSOTrx sales order - * @param counter create counter links - * @param copyASI copy line attributes Attribute Set Instance, Resaouce Assignment - * @param trxName trx - * @return Order - */ - public static MOrder copyFrom (MOrder from, Timestamp dateDoc, - int C_DocTypeTarget_ID, boolean isSOTrx, boolean counter, boolean copyASI, - String trxName) - { - MOrder to = new MOrder (from.getCtx(), 0, trxName); - to.set_TrxName(trxName); - PO.copyValues(from, to, from.getAD_Client_ID(), from.getAD_Org_ID()); - to.set_ValueNoCheck ("C_Order_ID", I_ZERO); - to.set_ValueNoCheck ("DocumentNo", null); - // - to.setDocStatus (DOCSTATUS_Drafted); // Draft - to.setDocAction(DOCACTION_Complete); - // - to.setC_DocType_ID(0); - to.setC_DocTypeTarget_ID (C_DocTypeTarget_ID); - to.setIsSOTrx(isSOTrx); - // - to.setIsSelected (false); - to.setDateOrdered (dateDoc); - to.setDateAcct (dateDoc); - to.setDatePromised (dateDoc); // assumption - to.setDatePrinted(null); - to.setIsPrinted (false); - // - to.setIsApproved (false); - to.setIsCreditApproved(false); - to.setC_Payment_ID(0); - to.setC_CashLine_ID(0); - // Amounts are updated when adding lines - to.setGrandTotal(Env.ZERO); - to.setTotalLines(Env.ZERO); - // - to.setIsDelivered(false); - to.setIsInvoiced(false); - to.setIsSelfService(false); - to.setIsTransferred (false); - to.setPosted (false); - to.setProcessed (false); - if (counter) - to.setRef_Order_ID(from.getC_Order_ID()); - else - to.setRef_Order_ID(0); - // - if (!to.save(trxName)) - throw new IllegalStateException("Could not create Order"); - if (counter) - from.setRef_Order_ID(to.getC_Order_ID()); - - if (to.copyLinesFrom(from, counter, copyASI) == 0) - throw new IllegalStateException("Could not create Order Lines"); - - return to; - } // copyFrom - - - /************************************************************************** - * Default Constructor - * @param ctx context - * @param C_Order_ID order to load, (0 create new order) - * @param trxName trx name - */ - public MOrder(Properties ctx, int C_Order_ID, String trxName) - { - super (ctx, C_Order_ID, trxName); - // New - if (C_Order_ID == 0) - { - setDocStatus(DOCSTATUS_Drafted); - setDocAction (DOCACTION_Prepare); - // - setDeliveryRule (DELIVERYRULE_Availability); - setFreightCostRule (FREIGHTCOSTRULE_FreightIncluded); - setInvoiceRule (INVOICERULE_Immediate); - setPaymentRule(PAYMENTRULE_OnCredit); - setPriorityRule (PRIORITYRULE_Medium); - setDeliveryViaRule (DELIVERYVIARULE_Pickup); - // - setIsDiscountPrinted (false); - setIsSelected (false); - setIsTaxIncluded (false); - setIsSOTrx (true); - setIsDropShip(false); - setSendEMail (false); - // - setIsApproved(false); - setIsPrinted(false); - setIsCreditApproved(false); - setIsDelivered(false); - setIsInvoiced(false); - setIsTransferred(false); - setIsSelfService(false); - // - super.setProcessed(false); - setProcessing(false); - setPosted(false); - - setDateAcct (new Timestamp(System.currentTimeMillis())); - setDatePromised (new Timestamp(System.currentTimeMillis())); - setDateOrdered (new Timestamp(System.currentTimeMillis())); - - setFreightAmt (Env.ZERO); - setChargeAmt (Env.ZERO); - setTotalLines (Env.ZERO); - setGrandTotal (Env.ZERO); - } - } // MOrder - - /************************************************************************** - * Project Constructor - * @param project Project to create Order from - * @param IsSOTrx sales order - * @param DocSubTypeSO if SO DocType Target (default DocSubTypeSO_OnCredit) - */ - public MOrder (MProject project, boolean IsSOTrx, String DocSubTypeSO) - { - this (project.getCtx(), 0, project.get_TrxName()); - setAD_Client_ID(project.getAD_Client_ID()); - setAD_Org_ID(project.getAD_Org_ID()); - setC_Campaign_ID(project.getC_Campaign_ID()); - setSalesRep_ID(project.getSalesRep_ID()); - // - setC_Project_ID(project.getC_Project_ID()); - setDescription(project.getName()); - Timestamp ts = project.getDateContract(); - if (ts != null) - setDateOrdered (ts); - ts = project.getDateFinish(); - if (ts != null) - setDatePromised (ts); - // - setC_BPartner_ID(project.getC_BPartner_ID()); - setC_BPartner_Location_ID(project.getC_BPartner_Location_ID()); - setAD_User_ID(project.getAD_User_ID()); - // - setM_Warehouse_ID(project.getM_Warehouse_ID()); - setM_PriceList_ID(project.getM_PriceList_ID()); - setC_PaymentTerm_ID(project.getC_PaymentTerm_ID()); - // - setIsSOTrx(IsSOTrx); - if (IsSOTrx) - { - if (DocSubTypeSO == null || DocSubTypeSO.length() == 0) - setC_DocTypeTarget_ID(DocSubTypeSO_OnCredit); - else - setC_DocTypeTarget_ID(DocSubTypeSO); - } - else - setC_DocTypeTarget_ID(); - } // MOrder - - /** - * Load Constructor - * @param ctx context - * @param rs result set record - * @param trxName transaction - */ - public MOrder (Properties ctx, ResultSet rs, String trxName) - { - super(ctx, rs, trxName); - } // MOrder - - /** Order Lines */ - private MOrderLine[] m_lines = null; - /** Tax Lines */ - private MOrderTax[] m_taxes = null; - /** Force Creation of order */ - private boolean m_forceCreation = false; - - /** - * Overwrite Client/Org if required - * @param AD_Client_ID client - * @param AD_Org_ID org - */ - public void setClientOrg (int AD_Client_ID, int AD_Org_ID) - { - super.setClientOrg(AD_Client_ID, AD_Org_ID); - } // setClientOrg - - - /** - * Add to Description - * @param description text - */ - public void addDescription (String description) - { - String desc = getDescription(); - if (desc == null) - setDescription(description); - else - setDescription(desc + " | " + description); - } // addDescription - - /** - * Set Business Partner (Ship+Bill) - * @param C_BPartner_ID bpartner - */ - public void setC_BPartner_ID (int C_BPartner_ID) - { - super.setC_BPartner_ID (C_BPartner_ID); - super.setBill_BPartner_ID (C_BPartner_ID); - } // setC_BPartner_ID - - /** - * Set Business Partner Location (Ship+Bill) - * @param C_BPartner_Location_ID bp location - */ - public void setC_BPartner_Location_ID (int C_BPartner_Location_ID) - { - super.setC_BPartner_Location_ID (C_BPartner_Location_ID); - super.setBill_Location_ID(C_BPartner_Location_ID); - } // setC_BPartner_Location_ID - - /** - * Set Business Partner Contact (Ship+Bill) - * @param AD_User_ID user - */ - public void setAD_User_ID (int AD_User_ID) - { - super.setAD_User_ID (AD_User_ID); - super.setBill_User_ID (AD_User_ID); - } // setAD_User_ID - - /** - * Set Ship Business Partner - * @param C_BPartner_ID bpartner - */ - public void setShip_BPartner_ID (int C_BPartner_ID) - { - super.setC_BPartner_ID (C_BPartner_ID); - } // setShip_BPartner_ID - - /** - * Set Ship Business Partner Location - * @param C_BPartner_Location_ID bp location - */ - public void setShip_Location_ID (int C_BPartner_Location_ID) - { - super.setC_BPartner_Location_ID (C_BPartner_Location_ID); - } // setShip_Location_ID - - /** - * Set Ship Business Partner Contact - * @param AD_User_ID user - */ - public void setShip_User_ID (int AD_User_ID) - { - super.setAD_User_ID (AD_User_ID); - } // setShip_User_ID - - - /** - * Set Warehouse - * @param M_Warehouse_ID warehouse - */ - public void setM_Warehouse_ID (int M_Warehouse_ID) - { - super.setM_Warehouse_ID (M_Warehouse_ID); - } // setM_Warehouse_ID - - /** - * Set Drop Ship - * @param IsDropShip drop ship - */ - public void setIsDropShip (boolean IsDropShip) - { - super.setIsDropShip (IsDropShip); - } // setIsDropShip - - /*************************************************************************/ - - /** Sales Order Sub Type - SO */ - public static final String DocSubTypeSO_Standard = "SO"; - /** Sales Order Sub Type - OB */ - public static final String DocSubTypeSO_Quotation = "OB"; - /** Sales Order Sub Type - ON */ - public static final String DocSubTypeSO_Proposal = "ON"; - /** Sales Order Sub Type - PR */ - public static final String DocSubTypeSO_Prepay = "PR"; - /** Sales Order Sub Type - WR */ - public static final String DocSubTypeSO_POS = "WR"; - /** Sales Order Sub Type - WP */ - public static final String DocSubTypeSO_Warehouse = "WP"; - /** Sales Order Sub Type - WI */ - public static final String DocSubTypeSO_OnCredit = "WI"; - /** Sales Order Sub Type - RM */ - public static final String DocSubTypeSO_RMA = "RM"; - - /** - * Set Target Sales Document Type - * @param DocSubTypeSO_x SO sub type - see DocSubTypeSO_* - */ - public void setC_DocTypeTarget_ID (String DocSubTypeSO_x) - { - String sql = "SELECT C_DocType_ID FROM C_DocType " - + "WHERE AD_Client_ID=? AND AD_Org_ID IN (0," + getAD_Org_ID() - + ") AND DocSubTypeSO=? " - + "ORDER BY AD_Org_ID DESC, IsDefault DESC"; - int C_DocType_ID = DB.getSQLValue(null, sql, getAD_Client_ID(), DocSubTypeSO_x); - if (C_DocType_ID <= 0) - log.severe ("Not found for AD_Client_ID=" + getAD_Client_ID () + ", SubType=" + DocSubTypeSO_x); - else - { - log.fine("(SO) - " + DocSubTypeSO_x); - setC_DocTypeTarget_ID (C_DocType_ID); - setIsSOTrx(true); - } - } // setC_DocTypeTarget_ID - - /** - * Set Target Document Type. - * Standard Order or PO - */ - public void setC_DocTypeTarget_ID () - { - if (isSOTrx()) // SO = Std Order - { - setC_DocTypeTarget_ID(DocSubTypeSO_Standard); - return; - } - // PO - String sql = "SELECT C_DocType_ID FROM C_DocType " - + "WHERE AD_Client_ID=? AND AD_Org_ID IN (0," + getAD_Org_ID() - + ") AND DocBaseType='POO' " - + "ORDER BY AD_Org_ID DESC, IsDefault DESC"; - int C_DocType_ID = DB.getSQLValue(null, sql, getAD_Client_ID()); - if (C_DocType_ID <= 0) - log.severe ("No POO found for AD_Client_ID=" + getAD_Client_ID ()); - else - { - log.fine("(PO) - " + C_DocType_ID); - setC_DocTypeTarget_ID (C_DocType_ID); - } - } // setC_DocTypeTarget_ID - - - /** - * Set Business Partner Defaults & Details. - * SOTrx should be set. - * @param bp business partner - */ - public void setBPartner (MBPartner bp) - { - if (bp == null) - return; - - setC_BPartner_ID(bp.getC_BPartner_ID()); - // Defaults Payment Term - int ii = 0; - if (isSOTrx()) - ii = bp.getC_PaymentTerm_ID(); - else - ii = bp.getPO_PaymentTerm_ID(); - if (ii != 0) - setC_PaymentTerm_ID(ii); - // Default Price List - if (isSOTrx()) - ii = bp.getM_PriceList_ID(); - else - ii = bp.getPO_PriceList_ID(); - if (ii != 0) - setM_PriceList_ID(ii); - // Default Delivery/Via Rule - String ss = bp.getDeliveryRule(); - if (ss != null) - setDeliveryRule(ss); - ss = bp.getDeliveryViaRule(); - if (ss != null) - setDeliveryViaRule(ss); - // Default Invoice/Payment Rule - ss = bp.getInvoiceRule(); - if (ss != null) - setInvoiceRule(ss); - ss = bp.getPaymentRule(); - if (ss != null) - setPaymentRule(ss); - // Sales Rep - ii = bp.getSalesRep_ID(); - if (ii != 0) - setSalesRep_ID(ii); - - - // Set Locations - MBPartnerLocation[] locs = bp.getLocations(false); - if (locs != null) - { - for (int i = 0; i < locs.length; i++) - { - if (locs[i].isShipTo()) - super.setC_BPartner_Location_ID(locs[i].getC_BPartner_Location_ID()); - if (locs[i].isBillTo()) - setBill_Location_ID(locs[i].getC_BPartner_Location_ID()); - } - // set to first - if (getC_BPartner_Location_ID() == 0 && locs.length > 0) - super.setC_BPartner_Location_ID(locs[0].getC_BPartner_Location_ID()); - if (getBill_Location_ID() == 0 && locs.length > 0) - setBill_Location_ID(locs[0].getC_BPartner_Location_ID()); - } - if (getC_BPartner_Location_ID() == 0) - log.log(Level.SEVERE, "MOrder.setBPartner - Has no Ship To Address: " + bp); - if (getBill_Location_ID() == 0) - log.log(Level.SEVERE, "MOrder.setBPartner - Has no Bill To Address: " + bp); - - // Set Contact - MUser[] contacts = bp.getContacts(false); - if (contacts != null && contacts.length == 1) - setAD_User_ID(contacts[0].getAD_User_ID()); - } // setBPartner - - - /** - * Copy Lines From other Order - * @param otherOrder order - * @param counter set counter info - * @param copyASI copy line attributes Attribute Set Instance, Resaouce Assignment - * @return number of lines copied - */ - public int copyLinesFrom (MOrder otherOrder, boolean counter, boolean copyASI) - { - if (isProcessed() || isPosted() || otherOrder == null) - return 0; - MOrderLine[] fromLines = otherOrder.getLines(false, null); - int count = 0; - for (int i = 0; i < fromLines.length; i++) - { - MOrderLine line = new MOrderLine (this); - PO.copyValues(fromLines[i], line, getAD_Client_ID(), getAD_Org_ID()); - line.setC_Order_ID(getC_Order_ID()); - line.setOrder(this); - line.set_ValueNoCheck ("C_OrderLine_ID", I_ZERO); // new - // References - if (!copyASI) - { - line.setM_AttributeSetInstance_ID(0); - line.setS_ResourceAssignment_ID(0); - } - if (counter) - line.setRef_OrderLine_ID(fromLines[i].getC_OrderLine_ID()); - else - line.setRef_OrderLine_ID(0); - // - line.setQtyDelivered(Env.ZERO); - line.setQtyInvoiced(Env.ZERO); - line.setQtyReserved(Env.ZERO); - line.setDateDelivered(null); - line.setDateInvoiced(null); - // Tax - if (getC_BPartner_ID() != otherOrder.getC_BPartner_ID()) - line.setTax(); // recalculate - // - // - line.setProcessed(false); - if (line.save(get_TrxName())) - count++; - // Cross Link - if (counter) - { - fromLines[i].setRef_OrderLine_ID(line.getC_OrderLine_ID()); - fromLines[i].save(get_TrxName()); - } - } - if (fromLines.length != count) - log.log(Level.SEVERE, "Line difference - From=" + fromLines.length + " <> Saved=" + count); - return count; - } // copyLinesFrom - - - /************************************************************************** - * String Representation - * @return info - */ - public String toString () - { - StringBuffer sb = new StringBuffer ("MOrder[") - .append(get_ID()).append("-").append(getDocumentNo()) - .append(",IsSOTrx=").append(isSOTrx()) - .append(",C_DocType_ID=").append(getC_DocType_ID()) - .append(", GrandTotal=").append(getGrandTotal()) - .append ("]"); - return sb.toString (); - } // toString - - /** - * Get Document Info - * @return document info (untranslated) - */ - public String getDocumentInfo() - { - MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); - return dt.getName() + " " + getDocumentNo(); - } // getDocumentInfo - - /** - * Create PDF - * @return File or null - */ - public File createPDF () - { - try - { - File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); - return createPDF (temp); - } - catch (Exception e) - { - log.severe("Could not create PDF - " + e.getMessage()); - } - return null; - } // getPDF - - /** - * Create PDF file - * @param file output file - * @return file if success - */ - public File createPDF (File file) - { - ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.ORDER, getC_Order_ID(), get_TrxName()); - if (re == null) - return null; - return re.getPDF(file); - } // createPDF - - /** - * Set Price List (and Currency, TaxIncluded) when valid - * @param M_PriceList_ID price list - */ - public void setM_PriceList_ID (int M_PriceList_ID) - { - MPriceList pl = MPriceList.get(getCtx(), M_PriceList_ID, null); - if (pl.get_ID() == M_PriceList_ID) - { - super.setM_PriceList_ID(M_PriceList_ID); - setC_Currency_ID(pl.getC_Currency_ID()); - setIsTaxIncluded(pl.isTaxIncluded()); - } - } // setM_PriceList_ID - - - /************************************************************************** - * Get Lines of Order - * @param whereClause where clause or null (starting with AND) - * @param orderClause order clause - * @return lines - */ - public MOrderLine[] getLines (String whereClause, String orderClause) - { - ArrayList list = new ArrayList (); - StringBuffer sql = new StringBuffer("SELECT * FROM C_OrderLine WHERE C_Order_ID=? "); - if (whereClause != null) - sql.append(whereClause); - if (orderClause != null) - sql.append(" ").append(orderClause); - PreparedStatement pstmt = null; - ResultSet rs = null; - try - { - pstmt = DB.prepareStatement(sql.toString(), get_TrxName()); - pstmt.setInt(1, getC_Order_ID()); - rs = pstmt.executeQuery(); - while (rs.next()) - { - MOrderLine ol = new MOrderLine(getCtx(), rs, get_TrxName()); - ol.setHeaderInfo (this); - list.add(ol); - } - } - catch (Exception e) - { - log.log(Level.SEVERE, sql.toString(), e); - } - finally - { - DB.close(rs, pstmt); - rs = null; pstmt = null; - } - // - MOrderLine[] lines = new MOrderLine[list.size ()]; - list.toArray (lines); - return lines; - } // getLines - - /** - * Get Lines of Order - * @param requery requery - * @param orderBy optional order by column - * @return lines - */ - public MOrderLine[] getLines (boolean requery, String orderBy) - { - if (m_lines != null && !requery) { - set_TrxName(m_lines, get_TrxName()); - return m_lines; - } - // - String orderClause = "ORDER BY "; - if (orderBy != null && orderBy.length() > 0) - orderClause += orderBy; - else - orderClause += "Line"; - m_lines = getLines(null, orderClause); - return m_lines; - } // getLines - - /** - * Get Lines of Order. - * (useb by web store) - * @return lines - */ - public MOrderLine[] getLines() - { - return getLines(false, null); - } // getLines - - /** - * Renumber Lines - * @param step start and step - */ - public void renumberLines (int step) - { - int number = step; - MOrderLine[] lines = getLines(true, null); // Line is default - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - line.setLine(number); - line.save(get_TrxName()); - number += step; - } - m_lines = null; - } // renumberLines - - /** - * Does the Order Line belong to this Order - * @param C_OrderLine_ID line - * @return true if part of the order - */ - public boolean isOrderLine(int C_OrderLine_ID) - { - if (m_lines == null) - getLines(); - for (int i = 0; i < m_lines.length; i++) - if (m_lines[i].getC_OrderLine_ID() == C_OrderLine_ID) - return true; - return false; - } // isOrderLine - - /** - * Get Taxes of Order - * @param requery requery - * @return array of taxes - */ - public MOrderTax[] getTaxes(boolean requery) - { - if (m_taxes != null && !requery) - return m_taxes; - // - ArrayList list = new ArrayList(); - String sql = "SELECT * FROM C_OrderTax WHERE C_Order_ID=?"; - PreparedStatement pstmt = null; - try - { - pstmt = DB.prepareStatement(sql, get_TrxName()); - pstmt.setInt(1, getC_Order_ID()); - ResultSet rs = pstmt.executeQuery(); - while (rs.next()) - list.add(new MOrderTax(getCtx(), rs, get_TrxName())); - rs.close(); - pstmt.close(); - pstmt = null; - } - catch (Exception e) - { - log.log(Level.SEVERE, "getTaxes", e); - } - finally - { - try - { - if (pstmt != null) - pstmt.close (); - } - catch (Exception e) - {} - pstmt = null; - } - // - m_taxes = new MOrderTax[list.size ()]; - list.toArray (m_taxes); - return m_taxes; - } // getTaxes - - - /** - * Get Invoices of Order - * @return invoices - */ - public MInvoice[] getInvoices() - { - ArrayList list = new ArrayList(); - String sql = " SELECT DISTINCT i.* FROM C_InvoiceLine il " + - "INNER JOIN C_OrderLine ol ON (ol.C_OrderLine_ID = il.C_OrderLine_ID) " + - "INNER JOIN C_Order o ON (o.C_Order_ID = ol.C_Order_ID) " + - "INNER JOIN C_Invoice i ON (i.C_Invoice_ID = il.C_Invoice_ID) " + - "WHERE o.C_Order_ID=? " + - "ORDER BY i.Created DESC"; - PreparedStatement pstmt = null; - ResultSet rs = null; - try - { - pstmt = DB.prepareStatement(sql, get_TrxName()); - pstmt.setInt(1, getC_Order_ID()); - rs = pstmt.executeQuery(); - while (rs.next()) - list.add(new MInvoice(getCtx(), rs, get_TrxName())); - } - catch (Exception e) - { - log.log(Level.SEVERE, sql, e); - } - finally - { - DB.close(rs, pstmt); - rs = null; pstmt = null; - } - // - MInvoice[] retValue = new MInvoice[list.size()]; - list.toArray(retValue); - return retValue; - } // getInvoices - - /** - * Get latest Invoice of Order - * @return invoice id or 0 - */ - public int getC_Invoice_ID() - { - int C_Invoice_ID = 0; - String sql = "SELECT C_Invoice_ID FROM C_Invoice " - + "WHERE C_Order_ID=? AND DocStatus IN ('CO','CL') " - + "ORDER BY Created DESC"; - PreparedStatement pstmt = null; - ResultSet rs = null; - try - { - pstmt = DB.prepareStatement(sql, get_TrxName()); - pstmt.setInt(1, getC_Order_ID()); - rs = pstmt.executeQuery(); - if (rs.next()) - C_Invoice_ID = rs.getInt(1); - } - catch (Exception e) - { - log.log(Level.SEVERE, "getC_Invoice_ID", e); - } - finally - { - DB.close(rs, pstmt); - rs = null; pstmt = null; - } - return C_Invoice_ID; - } // getC_Invoice_ID - - - /** - * Get Shipments of Order - * @return shipments - */ - public MInOut[] getShipments() - { - ArrayList list = new ArrayList(); - String sql = "SELECT DISTINCT io.* FROM M_InOutLine iol " + - "INNER JOIN M_InOut io ON (io.M_InOut_ID = iol.M_InOut_ID) " + - "INNER JOIN C_ORDERLINE ol ON (ol.C_ORDERLINE_ID=iol.C_ORDERLINE_ID) " + - "INNER JOIN C_ORDER o ON (o.C_ORDER_ID=ol.C_ORDER_ID) " + - "WHERE o.C_ORDER_ID=? " + - "ORDER BY io.Created DESC"; - - PreparedStatement pstmt = null; - ResultSet rs = null; - try - { - pstmt = DB.prepareStatement(sql, get_TrxName()); - pstmt.setInt(1, getC_Order_ID()); - rs = pstmt.executeQuery(); - while (rs.next()) - list.add(new MInOut(getCtx(), rs, get_TrxName())); - } - catch (Exception e) - { - log.log(Level.SEVERE, sql, e); - } - finally - { - DB.close(rs, pstmt); - rs = null; pstmt = null; - } - // - MInOut[] retValue = new MInOut[list.size()]; - list.toArray(retValue); - return retValue; - } // getShipments - - /** - * Get ISO Code of Currency - * @return Currency ISO - */ - public String getCurrencyISO() - { - return MCurrency.getISO_Code (getCtx(), getC_Currency_ID()); - } // getCurrencyISO - - /** - * Get Currency Precision - * @return precision - */ - public int getPrecision() - { - return MCurrency.getStdPrecision(getCtx(), getC_Currency_ID()); - } // getPrecision - - /** - * Get Document Status - * @return Document Status Clear Text - */ - public String getDocStatusName() - { - return MRefList.getListName(getCtx(), 131, getDocStatus()); - } // getDocStatusName - - /** - * Set DocAction - * @param DocAction doc action - */ - public void setDocAction (String DocAction) - { - setDocAction (DocAction, false); - } // setDocAction - - /** - * Set DocAction - * @param DocAction doc oction - * @param forceCreation force creation - */ - public void setDocAction (String DocAction, boolean forceCreation) - { - super.setDocAction (DocAction); - m_forceCreation = forceCreation; - } // setDocAction - - /** - * Set Processed. - * Propergate to Lines/Taxes - * @param processed processed - */ - public void setProcessed (boolean processed) - { - super.setProcessed (processed); - if (get_ID() == 0) - return; - String set = "SET Processed='" - + (processed ? "Y" : "N") - + "' WHERE C_Order_ID=" + getC_Order_ID(); - int noLine = DB.executeUpdate("UPDATE C_OrderLine " + set, get_TrxName()); - int noTax = DB.executeUpdate("UPDATE C_OrderTax " + set, get_TrxName()); - m_lines = null; - m_taxes = null; - log.fine("setProcessed - " + processed + " - Lines=" + noLine + ", Tax=" + noTax); - } // setProcessed - - - - /************************************************************************** - * Before Save - * @param newRecord new - * @return save - */ - protected boolean beforeSave (boolean newRecord) - { - // Client/Org Check - if (getAD_Org_ID() == 0) - { - int context_AD_Org_ID = Env.getAD_Org_ID(getCtx()); - if (context_AD_Org_ID != 0) - { - setAD_Org_ID(context_AD_Org_ID); - log.warning("Changed Org to Context=" + context_AD_Org_ID); - } - } - if (getAD_Client_ID() == 0) - { - m_processMsg = "AD_Client_ID = 0"; - return false; - } - - // New Record Doc Type - make sure DocType set to 0 - if (newRecord && getC_DocType_ID() == 0) - setC_DocType_ID (0); - - // Default Warehouse - if (getM_Warehouse_ID() == 0) - { - int ii = Env.getContextAsInt(getCtx(), "#M_Warehouse_ID"); - if (ii != 0) - setM_Warehouse_ID(ii); - else - { - log.saveError("FillMandatory", Msg.getElement(getCtx(), "M_Warehouse_ID")); - return false; - } - } - // Warehouse Org - if (newRecord - || is_ValueChanged("AD_Org_ID") || is_ValueChanged("M_Warehouse_ID")) - { - MWarehouse wh = MWarehouse.get(getCtx(), getM_Warehouse_ID()); - if (wh.getAD_Org_ID() != getAD_Org_ID()) - log.saveWarning("WarehouseOrgConflict", ""); - } - // Reservations in Warehouse - if (!newRecord && is_ValueChanged("M_Warehouse_ID")) - { - MOrderLine[] lines = getLines(false,null); - for (int i = 0; i < lines.length; i++) - { - if (!lines[i].canChangeWarehouse()) - return false; - } - } - - // No Partner Info - set Template - if (getC_BPartner_ID() == 0) - setBPartner(MBPartner.getTemplate(getCtx(), getAD_Client_ID())); - if (getC_BPartner_Location_ID() == 0) - setBPartner(new MBPartner(getCtx(), getC_BPartner_ID(), null)); - // No Bill - get from Ship - if (getBill_BPartner_ID() == 0) - { - setBill_BPartner_ID(getC_BPartner_ID()); - setBill_Location_ID(getC_BPartner_Location_ID()); - } - if (getBill_Location_ID() == 0) - setBill_Location_ID(getC_BPartner_Location_ID()); - - // Default Price List - if (getM_PriceList_ID() == 0) - { - int ii = DB.getSQLValue(null, - "SELECT M_PriceList_ID FROM M_PriceList " - + "WHERE AD_Client_ID=? AND IsSOPriceList=? " - + "ORDER BY IsDefault DESC", getAD_Client_ID(), isSOTrx() ? "Y" : "N"); - if (ii != 0) - setM_PriceList_ID (ii); - } - // Default Currency - if (getC_Currency_ID() == 0) - { - String sql = "SELECT C_Currency_ID FROM M_PriceList WHERE M_PriceList_ID=?"; - int ii = DB.getSQLValue (null, sql, getM_PriceList_ID()); - if (ii != 0) - setC_Currency_ID (ii); - else - setC_Currency_ID(Env.getContextAsInt(getCtx(), "#C_Currency_ID")); - } - - // Default Sales Rep - if (getSalesRep_ID() == 0) - { - int ii = Env.getContextAsInt(getCtx(), "#SalesRep_ID"); - if (ii != 0) - setSalesRep_ID (ii); - } - - // Default Document Type - if (getC_DocTypeTarget_ID() == 0) - setC_DocTypeTarget_ID(DocSubTypeSO_Standard); - - // Default Payment Term - if (getC_PaymentTerm_ID() == 0) - { - int ii = Env.getContextAsInt(getCtx(), "#C_PaymentTerm_ID"); - if (ii != 0) - setC_PaymentTerm_ID(ii); - else - { - String sql = "SELECT C_PaymentTerm_ID FROM C_PaymentTerm WHERE AD_Client_ID=? AND IsDefault='Y'"; - ii = DB.getSQLValue(null, sql, getAD_Client_ID()); - if (ii != 0) - setC_PaymentTerm_ID (ii); - } - } - - return true; - } // beforeSave - - - /** - * After Save - * @param newRecord new - * @param success success - * @return true if can be saved - */ - protected boolean afterSave (boolean newRecord, boolean success) - { - if (!success || newRecord) - return success; - - // Propagate Description changes - if (is_ValueChanged("Description") || is_ValueChanged("POReference")) - { - String sql = "UPDATE C_Invoice i" - + " SET (Description,POReference)=" - + "(SELECT Description,POReference " - + "FROM C_Order o WHERE i.C_Order_ID=o.C_Order_ID) " - + "WHERE DocStatus NOT IN ('RE','CL') AND C_Order_ID=" + getC_Order_ID(); - int no = DB.executeUpdate(sql, get_TrxName()); - log.fine("Description -> #" + no); - } - - // Propagate Changes of Payment Info to existing (not reversed/closed) invoices - if (is_ValueChanged("PaymentRule") || is_ValueChanged("C_PaymentTerm_ID") - || is_ValueChanged("DateAcct") || is_ValueChanged("C_Payment_ID") - || is_ValueChanged("C_CashLine_ID")) - { - String sql = "UPDATE C_Invoice i " - + "SET (PaymentRule,C_PaymentTerm_ID,DateAcct,C_Payment_ID,C_CashLine_ID)=" - + "(SELECT PaymentRule,C_PaymentTerm_ID,DateAcct,C_Payment_ID,C_CashLine_ID " - + "FROM C_Order o WHERE i.C_Order_ID=o.C_Order_ID)" - + "WHERE DocStatus NOT IN ('RE','CL') AND C_Order_ID=" + getC_Order_ID(); - // Don't touch Closed/Reversed entries - int no = DB.executeUpdate(sql, get_TrxName()); - log.fine("Payment -> #" + no); - } - - // Sync Lines - MOrderLine[] lines = getLines(); - for (MOrderLine line : lines) { - line.setAD_Org_ID(getAD_Org_ID()); - line.setC_BPartner_ID(getC_BPartner_ID()); - line.setC_BPartner_Location_ID(getC_BPartner_Location_ID()); - line.setDateOrdered(getDateOrdered()); - line.setDatePromised(getDatePromised()); - line.setM_Warehouse_ID(getM_Warehouse_ID()); - line.setM_Shipper_ID(getM_Shipper_ID()); - line.setC_Currency_ID(getC_Currency_ID()); - line.saveEx(); - } - // - return true; - } // afterSave - - /** - * Before Delete - * @return true of it can be deleted - */ - protected boolean beforeDelete () - { - if (isProcessed()) - return false; - - for (MOrderLine line : getLines()) { - line.deleteEx(true); - } - return true; - } // beforeDelete - - /************************************************************************** - * Process document - * @param processAction document action - * @return true if performed - */ - public boolean processIt (String processAction) - { - m_processMsg = null; - DocumentEngine engine = new DocumentEngine (this, getDocStatus()); - return engine.processIt (processAction, getDocAction()); - } // processIt - - /** Process Message */ - private String m_processMsg = null; - /** Just Prepared Flag */ - private boolean m_justPrepared = false; - - /** - * Unlock Document. - * @return true if success - */ - public boolean unlockIt() - { - log.info("unlockIt - " + toString()); - setProcessing(false); - return true; - } // unlockIt - - /** - * Invalidate Document - * @return true if success - */ - public boolean invalidateIt() - { - log.info(toString()); - setDocAction(DOCACTION_Prepare); - return true; - } // invalidateIt - - - /************************************************************************** - * Prepare Document - * @return new status (In Progress or Invalid) - */ - public String prepareIt() - { - log.info(toString()); - m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); - if (m_processMsg != null) - return DocAction.STATUS_Invalid; - MDocType dt = MDocType.get(getCtx(), getC_DocTypeTarget_ID()); - - // Std Period open? - if (!MPeriod.isOpen(getCtx(), getDateAcct(), dt.getDocBaseType())) - { - m_processMsg = "@PeriodClosed@"; - return DocAction.STATUS_Invalid; - } - - // Lines - MOrderLine[] lines = getLines(true, "M_Product_ID"); - if (lines.length == 0) - { - m_processMsg = "@NoLines@"; - return DocAction.STATUS_Invalid; - } - - // Bug 1564431 - if (getDeliveryRule() != null && getDeliveryRule().equals(MOrder.DELIVERYRULE_CompleteOrder)) - { - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - MProduct product = line.getProduct(); - if (product != null && product.isExcludeAutoDelivery()) - { - m_processMsg = "@M_Product_ID@ "+product.getValue()+" @IsExcludeAutoDelivery@"; - return DocAction.STATUS_Invalid; - } - } - } - - // Convert DocType to Target - if (getC_DocType_ID() != getC_DocTypeTarget_ID() ) - { - // Cannot change Std to anything else if different warehouses - if (getC_DocType_ID() != 0) - { - MDocType dtOld = MDocType.get(getCtx(), getC_DocType_ID()); - if (MDocType.DOCSUBTYPESO_StandardOrder.equals(dtOld.getDocSubTypeSO()) // From SO - && !MDocType.DOCSUBTYPESO_StandardOrder.equals(dt.getDocSubTypeSO())) // To !SO - { - for (int i = 0; i < lines.length; i++) - { - if (lines[i].getM_Warehouse_ID() != getM_Warehouse_ID()) - { - log.warning("different Warehouse " + lines[i]); - m_processMsg = "@CannotChangeDocType@"; - return DocAction.STATUS_Invalid; - } - } - } - } - - // New or in Progress/Invalid - if (DOCSTATUS_Drafted.equals(getDocStatus()) - || DOCSTATUS_InProgress.equals(getDocStatus()) - || DOCSTATUS_Invalid.equals(getDocStatus()) - || getC_DocType_ID() == 0) - { - setC_DocType_ID(getC_DocTypeTarget_ID()); - } - else // convert only if offer - { - if (dt.isOffer()) - setC_DocType_ID(getC_DocTypeTarget_ID()); - else - { - m_processMsg = "@CannotChangeDocType@"; - return DocAction.STATUS_Invalid; - } - } - } // convert DocType - - // Mandatory Product Attribute Set Instance - String mandatoryType = "='Y'"; // IN ('Y','S') - String sql = "SELECT COUNT(*) " - + "FROM C_OrderLine ol" - + " INNER JOIN M_Product p ON (ol.M_Product_ID=p.M_Product_ID)" - + " INNER JOIN M_AttributeSet pas ON (p.M_AttributeSet_ID=pas.M_AttributeSet_ID) " - + "WHERE pas.MandatoryType" + mandatoryType - + " AND ol.M_AttributeSetInstance_ID IS NULL" - + " AND ol.C_Order_ID=?"; - int no = DB.getSQLValue(get_TrxName(), sql, getC_Order_ID()); - if (no != 0) - { - m_processMsg = "@LinesWithoutProductAttribute@ (" + no + ")"; - return DocAction.STATUS_Invalid; - } - - // Lines - if (explodeBOM()) - lines = getLines(true, "M_Product_ID"); - if (!reserveStock(dt, lines)) - { - m_processMsg = "Cannot reserve Stock"; - return DocAction.STATUS_Invalid; - } - if (!calculateTaxTotal()) - { - m_processMsg = "Error calculating tax"; - return DocAction.STATUS_Invalid; - } - - // Credit Check - if (isSOTrx()) - { - MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); - if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus())) - { - m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" - + bp.getTotalOpenBalance() - + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); - return DocAction.STATUS_Invalid; - } - if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus())) - { - m_processMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@=" - + bp.getTotalOpenBalance() - + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); - return DocAction.STATUS_Invalid; - } - BigDecimal grandTotal = MConversionRate.convertBase(getCtx(), - getGrandTotal(), getC_Currency_ID(), getDateOrdered(), - getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); - if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus(grandTotal))) - { - m_processMsg = "@BPartnerOverOCreditHold@ - @TotalOpenBalance@=" - + bp.getTotalOpenBalance() + ", @GrandTotal@=" + grandTotal - + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); - return DocAction.STATUS_Invalid; - } - } - - m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); - if (m_processMsg != null) - return DocAction.STATUS_Invalid; - - m_justPrepared = true; - // if (!DOCACTION_Complete.equals(getDocAction())) don't set for just prepare - // setDocAction(DOCACTION_Complete); - return DocAction.STATUS_InProgress; - } // prepareIt - - /** - * Explode non stocked BOM. - * @return true if bom exploded - */ - private boolean explodeBOM() - { - boolean retValue = false; - String where = "AND IsActive='Y' AND EXISTS " - + "(SELECT * FROM M_Product p WHERE C_OrderLine.M_Product_ID=p.M_Product_ID" - + " AND p.IsBOM='Y' AND p.IsVerified='Y' AND p.IsStocked='N')"; - // - String sql = "SELECT COUNT(*) FROM C_OrderLine " - + "WHERE C_Order_ID=? " + where; - int count = DB.getSQLValue(get_TrxName(), sql, getC_Order_ID()); - while (count != 0) - { - retValue = true; - renumberLines (1000); // max 999 bom items - - // Order Lines with non-stocked BOMs - MOrderLine[] lines = getLines (where, "ORDER BY Line"); - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - MProduct product = MProduct.get (getCtx(), line.getM_Product_ID()); - log.fine(product.getName()); - // New Lines - int lineNo = line.getLine (); - //find default BOM with valid dates and to this product - MPPProductBOM bom = MPPProductBOM.get(product, getAD_Org_ID(),getDatePromised(), get_TrxName()); - if(bom != null) - { - MPPProductBOMLine[] bomlines = bom.getLines(getDatePromised()); - for (int j = 0; j < bomlines.length; j++) - { - MPPProductBOMLine bomline = bomlines[j]; - MOrderLine newLine = new MOrderLine (this); - newLine.setLine (++lineNo); - newLine.setM_Product_ID (bomline.getM_Product_ID ()); - newLine.setC_UOM_ID (bomline.getC_UOM_ID ()); - newLine.setQty (line.getQtyOrdered ().multiply ( - bomline.getQtyBOM())); - if (bomline.getDescription () != null) - newLine.setDescription (bomline.getDescription ()); - // - newLine.setPrice (); - newLine.save (get_TrxName()); - } - } - - /*MProductBOM[] boms = MProductBOM.getBOMLines (product); - for (int j = 0; j < boms.length; j++) - { - //MProductBOM bom = boms[j]; - MPPProductBOMLine bom = boms[j]; - MOrderLine newLine = new MOrderLine (this); - newLine.setLine (++lineNo); - //newLine.setM_Product_ID (bom.getProduct () - // .getM_Product_ID ()); - newLine.setM_Product_ID (bom.getM_Product_ID ()); - //newLine.setC_UOM_ID (bom.getProduct ().getC_UOM_ID ()); - newLine.setC_UOM_ID (bom.getC_UOM_ID ()); - //newLine.setQty (line.getQtyOrdered ().multiply ( - // bom.getBOMQty ())); - newLine.setQty (line.getQtyOrdered ().multiply ( - bom.getQtyBOM())); - if (bom.getDescription () != null) - newLine.setDescription (bom.getDescription ()); - // - newLine.setPrice (); - newLine.save (get_TrxName()); - }*/ - - // Convert into Comment Line - line.setM_Product_ID (0); - line.setM_AttributeSetInstance_ID (0); - line.setPrice (Env.ZERO); - line.setPriceLimit (Env.ZERO); - line.setPriceList (Env.ZERO); - line.setLineNetAmt (Env.ZERO); - line.setFreightAmt (Env.ZERO); - // - String description = product.getName (); - if (product.getDescription () != null) - description += " " + product.getDescription (); - if (line.getDescription () != null) - description += " " + line.getDescription (); - line.setDescription (description); - line.save (get_TrxName()); - } // for all lines with BOM - - m_lines = null; // force requery - count = DB.getSQLValue (get_TrxName(), sql, getC_Invoice_ID ()); - renumberLines (10); - } // while count != 0 - return retValue; - } // explodeBOM - - - /** - * Reserve Inventory. - * Counterpart: MInOut.completeIt() - * @param dt document type or null - * @param lines order lines (ordered by M_Product_ID for deadlock prevention) - * @return true if (un) reserved - */ - private boolean reserveStock (MDocType dt, MOrderLine[] lines) - { - if (dt == null) - dt = MDocType.get(getCtx(), getC_DocType_ID()); - - // Binding - boolean binding = !dt.isProposal(); - // Not binding - i.e. Target=0 - if (DOCACTION_Void.equals(getDocAction()) - // Closing Binding Quotation - || (MDocType.DOCSUBTYPESO_Quotation.equals(dt.getDocSubTypeSO()) - && DOCACTION_Close.equals(getDocAction())) - || isDropShip() ) - binding = false; - boolean isSOTrx = isSOTrx(); - log.fine("Binding=" + binding + " - IsSOTrx=" + isSOTrx); - // Force same WH for all but SO/PO - int header_M_Warehouse_ID = getM_Warehouse_ID(); - if (MDocType.DOCSUBTYPESO_StandardOrder.equals(dt.getDocSubTypeSO()) - || MDocType.DOCBASETYPE_PurchaseOrder.equals(dt.getDocBaseType())) - header_M_Warehouse_ID = 0; // don't enforce - - BigDecimal Volume = Env.ZERO; - BigDecimal Weight = Env.ZERO; - - // Always check and (un) Reserve Inventory - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - // Check/set WH/Org - if (header_M_Warehouse_ID != 0) // enforce WH - { - if (header_M_Warehouse_ID != line.getM_Warehouse_ID()) - line.setM_Warehouse_ID(header_M_Warehouse_ID); - if (getAD_Org_ID() != line.getAD_Org_ID()) - line.setAD_Org_ID(getAD_Org_ID()); - } - // Binding - BigDecimal target = binding ? line.getQtyOrdered() : Env.ZERO; - BigDecimal difference = target - .subtract(line.getQtyReserved()) - .subtract(line.getQtyDelivered()); - if (difference.signum() == 0) - { - MProduct product = line.getProduct(); - if (product != null) - { - Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); - Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); - } - continue; - } - - log.fine("Line=" + line.getLine() - + " - Target=" + target + ",Difference=" + difference - + " - Ordered=" + line.getQtyOrdered() - + ",Reserved=" + line.getQtyReserved() + ",Delivered=" + line.getQtyDelivered()); - - // Check Product - Stocked and Item - MProduct product = line.getProduct(); - if (product != null) - { - if (product.isStocked()) - { - BigDecimal ordered = isSOTrx ? Env.ZERO : difference; - BigDecimal reserved = isSOTrx ? difference : Env.ZERO; - int M_Locator_ID = 0; - // Get Locator to reserve - if (line.getM_AttributeSetInstance_ID() != 0) // Get existing Location - M_Locator_ID = MStorage.getM_Locator_ID (line.getM_Warehouse_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - ordered, get_TrxName()); - // Get default Location - if (M_Locator_ID == 0) - { - // try to take default locator for product first - // if it is from the selected warehouse - MWarehouse wh = MWarehouse.get(getCtx(), line.getM_Warehouse_ID()); - M_Locator_ID = product.getM_Locator_ID(); - if (M_Locator_ID!=0) { - MLocator locator = new MLocator(getCtx(), product.getM_Locator_ID(), get_TrxName()); - //product has default locator defined but is not from the order warehouse - if(locator.getM_Warehouse_ID()!=wh.get_ID()) { - M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); - } - } else { - M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); - } - } - // Update Storage - if (!MStorage.add(getCtx(), line.getM_Warehouse_ID(), M_Locator_ID, - line.getM_Product_ID(), - line.getM_AttributeSetInstance_ID(), line.getM_AttributeSetInstance_ID(), - Env.ZERO, reserved, ordered, get_TrxName())) - return false; - } // stockec - // update line - line.setQtyReserved(line.getQtyReserved().add(difference)); - if (!line.save(get_TrxName())) - return false; - // - Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); - Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); - } // product - } // reverse inventory - - setVolume(Volume); - setWeight(Weight); - return true; - } // reserveStock - - /** - * Calculate Tax and Total - * @return true if tax total calculated - */ - public boolean calculateTaxTotal() - { - log.fine(""); - // Delete Taxes - DB.executeUpdate("DELETE C_OrderTax WHERE C_Order_ID=" + getC_Order_ID(), get_TrxName()); - m_taxes = null; - - // Lines - BigDecimal totalLines = Env.ZERO; - ArrayList taxList = new ArrayList(); - MOrderLine[] lines = getLines(); - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - Integer taxID = new Integer(line.getC_Tax_ID()); - if (!taxList.contains(taxID)) - { - MOrderTax oTax = MOrderTax.get (line, getPrecision(), - false, get_TrxName()); // current Tax - oTax.setIsTaxIncluded(isTaxIncluded()); - if (!oTax.calculateTaxFromLines()) - return false; - if (!oTax.save(get_TrxName())) - return false; - taxList.add(taxID); - } - totalLines = totalLines.add(line.getLineNetAmt()); - } - - // Taxes - BigDecimal grandTotal = totalLines; - MOrderTax[] taxes = getTaxes(true); - for (int i = 0; i < taxes.length; i++) - { - MOrderTax oTax = taxes[i]; - MTax tax = oTax.getTax(); - if (tax.isSummary()) - { - MTax[] cTaxes = tax.getChildTaxes(false); - for (int j = 0; j < cTaxes.length; j++) - { - MTax cTax = cTaxes[j]; - BigDecimal taxAmt = cTax.calculateTax(oTax.getTaxBaseAmt(), isTaxIncluded(), getPrecision()); - // - MOrderTax newOTax = new MOrderTax(getCtx(), 0, get_TrxName()); - newOTax.setClientOrg(this); - newOTax.setC_Order_ID(getC_Order_ID()); - newOTax.setC_Tax_ID(cTax.getC_Tax_ID()); - newOTax.setPrecision(getPrecision()); - newOTax.setIsTaxIncluded(isTaxIncluded()); - newOTax.setTaxBaseAmt(oTax.getTaxBaseAmt()); - newOTax.setTaxAmt(taxAmt); - if (!newOTax.save(get_TrxName())) - return false; - // - if (!isTaxIncluded()) - grandTotal = grandTotal.add(taxAmt); - } - if (!oTax.delete(true, get_TrxName())) - return false; - if (!oTax.save(get_TrxName())) - return false; - } - else - { - if (!isTaxIncluded()) - grandTotal = grandTotal.add(oTax.getTaxAmt()); - } - } - // - setTotalLines(totalLines); - setGrandTotal(grandTotal); - return true; - } // calculateTaxTotal - - - /** - * Approve Document - * @return true if success - */ - public boolean approveIt() - { - log.info("approveIt - " + toString()); - setIsApproved(true); - return true; - } // approveIt - - /** - * Reject Approval - * @return true if success - */ - public boolean rejectIt() - { - log.info("rejectIt - " + toString()); - setIsApproved(false); - return true; - } // rejectIt - - - /************************************************************************** - * Complete Document - * @return new status (Complete, In Progress, Invalid, Waiting ..) - */ - public String completeIt() - { - MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); - String DocSubTypeSO = dt.getDocSubTypeSO(); - - // Just prepare - if (DOCACTION_Prepare.equals(getDocAction())) - { - setProcessed(false); - return DocAction.STATUS_InProgress; - } - // Offers - if (MDocType.DOCSUBTYPESO_Proposal.equals(DocSubTypeSO) - || MDocType.DOCSUBTYPESO_Quotation.equals(DocSubTypeSO)) - { - // Binding - if (MDocType.DOCSUBTYPESO_Quotation.equals(DocSubTypeSO)) - reserveStock(dt, getLines(true, "M_Product_ID")); - m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); - if (m_processMsg != null) - return DocAction.STATUS_Invalid; - m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); - if (m_processMsg != null) - return DocAction.STATUS_Invalid; - // Set the definite document number after completed (if needed) - setDefiniteDocumentNo(); - setProcessed(true); - return DocAction.STATUS_Completed; - } - // Waiting Payment - until we have a payment - if (!m_forceCreation - && MDocType.DOCSUBTYPESO_PrepayOrder.equals(DocSubTypeSO) - && getC_Payment_ID() == 0 && getC_CashLine_ID() == 0) - { - setProcessed(true); - return DocAction.STATUS_WaitingPayment; - } - - // Re-Check - if (!m_justPrepared) - { - String status = prepareIt(); - if (!DocAction.STATUS_InProgress.equals(status)) - return status; - } - - m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); - if (m_processMsg != null) - return DocAction.STATUS_Invalid; - - // Implicit Approval - if (!isApproved()) - approveIt(); - getLines(true,null); - log.info(toString()); - StringBuffer info = new StringBuffer(); - - boolean realTimePOS = false; - - // Create SO Shipment - Force Shipment - MInOut shipment = null; - if (MDocType.DOCSUBTYPESO_OnCreditOrder.equals(DocSubTypeSO) // (W)illCall(I)nvoice - || MDocType.DOCSUBTYPESO_WarehouseOrder.equals(DocSubTypeSO) // (W)illCall(P)ickup - || MDocType.DOCSUBTYPESO_POSOrder.equals(DocSubTypeSO) // (W)alkIn(R)eceipt - || MDocType.DOCSUBTYPESO_PrepayOrder.equals(DocSubTypeSO)) - { - if (!DELIVERYRULE_Force.equals(getDeliveryRule())) - setDeliveryRule(DELIVERYRULE_Force); - // - shipment = createShipment (dt, realTimePOS ? null : getDateOrdered()); - if (shipment == null) - return DocAction.STATUS_Invalid; - info.append("@M_InOut_ID@: ").append(shipment.getDocumentNo()); - String msg = shipment.getProcessMsg(); - if (msg != null && msg.length() > 0) - info.append(" (").append(msg).append(")"); - } // Shipment - - - // Create SO Invoice - Always invoice complete Order - if ( MDocType.DOCSUBTYPESO_POSOrder.equals(DocSubTypeSO) - || MDocType.DOCSUBTYPESO_OnCreditOrder.equals(DocSubTypeSO) - || MDocType.DOCSUBTYPESO_PrepayOrder.equals(DocSubTypeSO)) - { - MInvoice invoice = createInvoice (dt, shipment, realTimePOS ? null : getDateOrdered()); - if (invoice == null) - return DocAction.STATUS_Invalid; - info.append(" - @C_Invoice_ID@: ").append(invoice.getDocumentNo()); - String msg = invoice.getProcessMsg(); - if (msg != null && msg.length() > 0) - info.append(" (").append(msg).append(")"); - } // Invoice - - // Counter Documents - MOrder counter = createCounterDoc(); - if (counter != null) - info.append(" - @CounterDoc@: @Order@=").append(counter.getDocumentNo()); - // User Validation - String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); - if (valid != null) - { - if (info.length() > 0) - info.append(" - "); - info.append(valid); - m_processMsg = info.toString(); - return DocAction.STATUS_Invalid; - } - - // Set the definite document number after completed (if needed) - setDefiniteDocumentNo(); - - setProcessed(true); - m_processMsg = info.toString(); - // - setDocAction(DOCACTION_Close); - return DocAction.STATUS_Completed; - } // completeIt - - /** - * Set the definite document number after completed - */ - private void setDefiniteDocumentNo() { - MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); - if (dt.isOverwriteDateOnComplete()) { - setDateOrdered(new Timestamp (System.currentTimeMillis())); - } - if (dt.isOverwriteSeqOnComplete()) { - String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); - if (value != null) - setDocumentNo(value); - } - } - - /** - * Create Shipment - * @param dt order document type - * @param movementDate optional movement date (default today) - * @return shipment or null - */ - private MInOut createShipment(MDocType dt, Timestamp movementDate) - { - log.info("For " + dt); - MInOut shipment = new MInOut (this, dt.getC_DocTypeShipment_ID(), movementDate); - // shipment.setDateAcct(getDateAcct()); - if (!shipment.save(get_TrxName())) - { - m_processMsg = "Could not create Shipment"; - return null; - } - // - MOrderLine[] oLines = getLines(true, null); - for (int i = 0; i < oLines.length; i++) - { - MOrderLine oLine = oLines[i]; - // - MInOutLine ioLine = new MInOutLine(shipment); - // Qty = Ordered - Delivered - BigDecimal MovementQty = oLine.getQtyOrdered().subtract(oLine.getQtyDelivered()); - // Location - int M_Locator_ID = MStorage.getM_Locator_ID (oLine.getM_Warehouse_ID(), - oLine.getM_Product_ID(), oLine.getM_AttributeSetInstance_ID(), - MovementQty, get_TrxName()); - if (M_Locator_ID == 0) // Get default Location - { - MWarehouse wh = MWarehouse.get(getCtx(), oLine.getM_Warehouse_ID()); - M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); - } - // - ioLine.setOrderLine(oLine, M_Locator_ID, MovementQty); - ioLine.setQty(MovementQty); - if (oLine.getQtyEntered().compareTo(oLine.getQtyOrdered()) != 0) - ioLine.setQtyEntered(MovementQty - .multiply(oLine.getQtyEntered()) - .divide(oLine.getQtyOrdered(), 6, BigDecimal.ROUND_HALF_UP)); - if (!ioLine.save(get_TrxName())) - { - m_processMsg = "Could not create Shipment Line"; - return null; - } - } - // Manually Process Shipment - String status = shipment.completeIt(); - shipment.setDocStatus(status); - shipment.save(get_TrxName()); - if (!DOCSTATUS_Completed.equals(status)) - { - m_processMsg = "@M_InOut_ID@: " + shipment.getProcessMsg(); - return null; - } - return shipment; - } // createShipment - - /** - * Create Invoice - * @param dt order document type - * @param shipment optional shipment - * @param invoiceDate invoice date - * @return invoice or null - */ - private MInvoice createInvoice (MDocType dt, MInOut shipment, Timestamp invoiceDate) - { - log.info(dt.toString()); - MInvoice invoice = new MInvoice (this, dt.getC_DocTypeInvoice_ID(), invoiceDate); - if (!invoice.save(get_TrxName())) - { - m_processMsg = "Could not create Invoice"; - return null; - } - - // If we have a Shipment - use that as a base - if (shipment != null) - { - if (!INVOICERULE_AfterDelivery.equals(getInvoiceRule())) - setInvoiceRule(INVOICERULE_AfterDelivery); - // - MInOutLine[] sLines = shipment.getLines(false); - for (int i = 0; i < sLines.length; i++) - { - MInOutLine sLine = sLines[i]; - // - MInvoiceLine iLine = new MInvoiceLine(invoice); - iLine.setShipLine(sLine); - // Qty = Delivered - iLine.setQtyEntered(sLine.getQtyEntered()); - iLine.setQtyInvoiced(sLine.getMovementQty()); - if (!iLine.save(get_TrxName())) - { - m_processMsg = "Could not create Invoice Line from Shipment Line"; - return null; - } - // - sLine.setIsInvoiced(true); - if (!sLine.save(get_TrxName())) - { - log.warning("Could not update Shipment line: " + sLine); - } - } - } - else // Create Invoice from Order - { - if (!INVOICERULE_Immediate.equals(getInvoiceRule())) - setInvoiceRule(INVOICERULE_Immediate); - // - MOrderLine[] oLines = getLines(); - for (int i = 0; i < oLines.length; i++) - { - MOrderLine oLine = oLines[i]; - // - MInvoiceLine iLine = new MInvoiceLine(invoice); - iLine.setOrderLine(oLine); - // Qty = Ordered - Invoiced - iLine.setQtyInvoiced(oLine.getQtyOrdered().subtract(oLine.getQtyInvoiced())); - if (oLine.getQtyOrdered().compareTo(oLine.getQtyEntered()) == 0) - iLine.setQtyEntered(iLine.getQtyInvoiced()); - else - iLine.setQtyEntered(iLine.getQtyInvoiced().multiply(oLine.getQtyEntered()) - .divide(oLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); - if (!iLine.save(get_TrxName())) - { - m_processMsg = "Could not create Invoice Line from Order Line"; - return null; - } - } - } - // Manually Process Invoice - String status = invoice.completeIt(); - invoice.setDocStatus(status); - invoice.save(get_TrxName()); - setC_CashLine_ID(invoice.getC_CashLine_ID()); - if (!DOCSTATUS_Completed.equals(status)) - { - m_processMsg = "@C_Invoice_ID@: " + invoice.getProcessMsg(); - return null; - } - return invoice; - } // createInvoice - - /** - * Create Counter Document - * @return counter order - */ - private MOrder createCounterDoc() - { - // Is this itself a counter doc ? - if (getRef_Order_ID() != 0) - return null; - - // Org Must be linked to BPartner - MOrg org = MOrg.get(getCtx(), getAD_Org_ID()); - int counterC_BPartner_ID = org.getLinkedC_BPartner_ID(get_TrxName()); - if (counterC_BPartner_ID == 0) - return null; - // Business Partner needs to be linked to Org - MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); - int counterAD_Org_ID = bp.getAD_OrgBP_ID_Int(); - if (counterAD_Org_ID == 0) - return null; - - MBPartner counterBP = new MBPartner (getCtx(), counterC_BPartner_ID, null); - MOrgInfo counterOrgInfo = MOrgInfo.get(getCtx(), counterAD_Org_ID); - log.info("Counter BP=" + counterBP.getName()); - - // Document Type - int C_DocTypeTarget_ID = 0; - MDocTypeCounter counterDT = MDocTypeCounter.getCounterDocType(getCtx(), getC_DocType_ID()); - if (counterDT != null) - { - log.fine(counterDT.toString()); - if (!counterDT.isCreateCounter() || !counterDT.isValid()) - return null; - C_DocTypeTarget_ID = counterDT.getCounter_C_DocType_ID(); - } - else // indirect - { - C_DocTypeTarget_ID = MDocTypeCounter.getCounterDocType_ID(getCtx(), getC_DocType_ID()); - log.fine("Indirect C_DocTypeTarget_ID=" + C_DocTypeTarget_ID); - if (C_DocTypeTarget_ID <= 0) - return null; - } - // Deep Copy - MOrder counter = copyFrom (this, getDateOrdered(), - C_DocTypeTarget_ID, !isSOTrx(), true, false, get_TrxName()); - // - counter.setAD_Org_ID(counterAD_Org_ID); - counter.setM_Warehouse_ID(counterOrgInfo.getM_Warehouse_ID()); - // - counter.setBPartner(counterBP); - counter.setDatePromised(getDatePromised()); // default is date ordered - // Refernces (Should not be required - counter.setSalesRep_ID(getSalesRep_ID()); - counter.save(get_TrxName()); - - // Update copied lines - MOrderLine[] counterLines = counter.getLines(true, null); - for (int i = 0; i < counterLines.length; i++) - { - MOrderLine counterLine = counterLines[i]; - counterLine.setOrder(counter); // copies header values (BP, etc.) - counterLine.setPrice(); - counterLine.setTax(); - counterLine.save(get_TrxName()); - } - log.fine(counter.toString()); - - // Document Action - if (counterDT != null) - { - if (counterDT.getDocAction() != null) - { - counter.setDocAction(counterDT.getDocAction()); - counter.processIt(counterDT.getDocAction()); - counter.save(get_TrxName()); - } - } - return counter; - } // createCounterDoc - - /** - * Void Document. - * Set Qtys to 0 - Sales: reverse all documents - * @return true if success - */ - public boolean voidIt() - { - log.info(toString()); - // Before Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); - if (m_processMsg != null) - return false; - - MOrderLine[] lines = getLines(true, "M_Product_ID"); - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - BigDecimal old = line.getQtyOrdered(); - if (old.signum() != 0) - { - line.addDescription(Msg.getMsg(getCtx(), "Voided") + " (" + old + ")"); - line.setQty(Env.ZERO); - line.setLineNetAmt(Env.ZERO); - line.save(get_TrxName()); - } - } - addDescription(Msg.getMsg(getCtx(), "Voided")); - // Clear Reservations - if (!reserveStock(null, lines)) - { - m_processMsg = "Cannot unreserve Stock (void)"; - return false; - } - - if (!createReversals()) - return false; - - //MZ Goodwill - if (!isSOTrx()) - { - // delete Matched PO Cost Detail - MOrderLine[] linesMZ = getLines(); - for (int i = 0; i < lines.length; i++) - { - MMatchPO[] mPO = MMatchPO.getOrderLine(getCtx(), linesMZ[i].getC_OrderLine_ID(), get_TrxName()); - // delete Cost Detail if the Matched PO has been deleted - if (mPO.length == 0) - { - MCostDetail cd = MCostDetail.get(getCtx(), "C_OrderLine_ID=? AND M_AttributeSetInstance_ID=?", - linesMZ[i].getC_OrderLine_ID(), linesMZ[i].getM_AttributeSetInstance_ID(), get_TrxName()); - if (cd != null) - { - cd.setProcessed(false); - cd.delete(true); - } - } - } - } - //End MZ - - // After Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); - if (m_processMsg != null) - return false; - - setProcessed(true); - setDocAction(DOCACTION_None); - return true; - } // voidIt - - /** - * Create Shipment/Invoice Reversals - * @return true if success - */ - private boolean createReversals() - { - // Cancel only Sales - if (!isSOTrx()) - return true; - - log.info("createReversals"); - StringBuffer info = new StringBuffer(); - - // Reverse All *Shipments* - info.append("@M_InOut_ID@:"); - MInOut[] shipments = getShipments(); - for (int i = 0; i < shipments.length; i++) - { - MInOut ship = shipments[i]; - // if closed - ignore - if (MInOut.DOCSTATUS_Closed.equals(ship.getDocStatus()) - || MInOut.DOCSTATUS_Reversed.equals(ship.getDocStatus()) - || MInOut.DOCSTATUS_Voided.equals(ship.getDocStatus()) ) - continue; - ship.set_TrxName(get_TrxName()); - - // If not completed - void - otherwise reverse it - if (!MInOut.DOCSTATUS_Completed.equals(ship.getDocStatus())) - { - if (ship.voidIt()) - ship.setDocStatus(MInOut.DOCSTATUS_Voided); - } - else if (ship.reverseCorrectIt()) // completed shipment - { - ship.setDocStatus(MInOut.DOCSTATUS_Reversed); - info.append(" ").append(ship.getDocumentNo()); - } - else - { - m_processMsg = "Could not reverse Shipment " + ship; - return false; - } - ship.setDocAction(MInOut.DOCACTION_None); - ship.save(get_TrxName()); - } // for all shipments - - // Reverse All *Invoices* - info.append(" - @C_Invoice_ID@:"); - MInvoice[] invoices = getInvoices(); - for (int i = 0; i < invoices.length; i++) - { - MInvoice invoice = invoices[i]; - // if closed - ignore - if (MInvoice.DOCSTATUS_Closed.equals(invoice.getDocStatus()) - || MInvoice.DOCSTATUS_Reversed.equals(invoice.getDocStatus()) - || MInvoice.DOCSTATUS_Voided.equals(invoice.getDocStatus()) ) - continue; - invoice.set_TrxName(get_TrxName()); - - // If not completed - void - otherwise reverse it - if (!MInvoice.DOCSTATUS_Completed.equals(invoice.getDocStatus())) - { - if (invoice.voidIt()) - invoice.setDocStatus(MInvoice.DOCSTATUS_Voided); - } - else if (invoice.reverseCorrectIt()) // completed invoice - { - invoice.setDocStatus(MInvoice.DOCSTATUS_Reversed); - info.append(" ").append(invoice.getDocumentNo()); - } - else - { - m_processMsg = "Could not reverse Invoice " + invoice; - return false; - } - invoice.setDocAction(MInvoice.DOCACTION_None); - invoice.save(get_TrxName()); - } // for all shipments - - m_processMsg = info.toString(); - return true; - } // createReversals - - - /** - * Close Document. - * Cancel not delivered Qunatities - * @return true if success - */ - public boolean closeIt() - { - log.info(toString()); - // Before Close - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); - if (m_processMsg != null) - return false; - - // Close Not delivered Qty - SO/PO - MOrderLine[] lines = getLines(true, "M_Product_ID"); - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - BigDecimal old = line.getQtyOrdered(); - if (old.compareTo(line.getQtyDelivered()) != 0) - { - line.setQtyLostSales(line.getQtyOrdered().subtract(line.getQtyDelivered())); - line.setQtyOrdered(line.getQtyDelivered()); - // QtyEntered unchanged - line.addDescription("Close (" + old + ")"); - line.save(get_TrxName()); - } - } - // Clear Reservations - if (!reserveStock(null, lines)) - { - m_processMsg = "Cannot unreserve Stock (close)"; - return false; - } - // After Close - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); - if (m_processMsg != null) - return false; - - setProcessed(true); - setDocAction(DOCACTION_None); - return true; - } // closeIt - - /** - * @author: phib - * re-open a closed order - * (reverse steps of close()) - */ - public String reopenIt() { - log.info(toString()); - if (!MOrder.DOCSTATUS_Closed.equals(getDocStatus())) - { - return "Not closed - can't reopen"; - } - - // - MOrderLine[] lines = getLines(true, "M_Product_ID"); - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - if (Env.ZERO.compareTo(line.getQtyLostSales()) != 0) - { - line.setQtyOrdered(line.getQtyLostSales().add(line.getQtyDelivered())); - line.setQtyLostSales(Env.ZERO); - // QtyEntered unchanged - - // Strip Close() tags from description - String desc = line.getDescription(); - if (desc == null) - desc = ""; - Pattern pattern = Pattern.compile("( \\| )?Close \\(.*\\)"); - String[] parts = pattern.split(desc); - desc = ""; - for (String s : parts) { - desc = desc.concat(s); - } - line.setDescription(desc); - if (!line.save(get_TrxName())) - return "Couldn't save orderline"; - } - } - // Clear Reservations - if (!reserveStock(null, lines)) - { - m_processMsg = "Cannot unreserve Stock (close)"; - return "Failed to update reservations"; - } - - setDocStatus(MOrder.DOCSTATUS_Completed); - setDocAction(DOCACTION_Close); - if (!this.save(get_TrxName())) - return "Couldn't save reopened order"; - else - return ""; - } // reopenIt - /** - * Reverse Correction - same void - * @return true if success - */ - public boolean reverseCorrectIt() - { - log.info(toString()); - // Before reverseCorrect - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); - if (m_processMsg != null) - return false; - - // After reverseCorrect - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); - if (m_processMsg != null) - return false; - - return voidIt(); - } // reverseCorrectionIt - - /** - * Reverse Accrual - none - * @return false - */ - public boolean reverseAccrualIt() - { - log.info(toString()); - // Before reverseAccrual - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); - if (m_processMsg != null) - return false; - - // After reverseAccrual - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); - if (m_processMsg != null) - return false; - - return false; - } // reverseAccrualIt - - /** - * Re-activate. - * @return true if success - */ - public boolean reActivateIt() - { - log.info(toString()); - // Before reActivate - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); - if (m_processMsg != null) - return false; - - - - MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); - String DocSubTypeSO = dt.getDocSubTypeSO(); - - // Replace Prepay with POS to revert all doc - if (MDocType.DOCSUBTYPESO_PrepayOrder.equals (DocSubTypeSO)) - { - MDocType newDT = null; - MDocType[] dts = MDocType.getOfClient (getCtx()); - for (int i = 0; i < dts.length; i++) - { - MDocType type = dts[i]; - if (MDocType.DOCSUBTYPESO_PrepayOrder.equals(type.getDocSubTypeSO())) - { - if (type.isDefault() || newDT == null) - newDT = type; - } - } - if (newDT == null) - return false; - else - setC_DocType_ID (newDT.getC_DocType_ID()); - } - - // PO - just re-open - if (!isSOTrx()) - log.info("Existing documents not modified - " + dt); - // Reverse Direct Documents - else if (MDocType.DOCSUBTYPESO_OnCreditOrder.equals(DocSubTypeSO) // (W)illCall(I)nvoice - || MDocType.DOCSUBTYPESO_WarehouseOrder.equals(DocSubTypeSO) // (W)illCall(P)ickup - || MDocType.DOCSUBTYPESO_POSOrder.equals(DocSubTypeSO)) // (W)alkIn(R)eceipt - { - if (!createReversals()) - return false; - } - else - { - log.info("Existing documents not modified - SubType=" + DocSubTypeSO); - } - // After reActivate - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); - if (m_processMsg != null) - return false; - - setDocAction(DOCACTION_Complete); - setProcessed(false); - return true; - } // reActivateIt - - - /************************************************************************* - * Get Summary - * @return Summary of Document - */ - public String getSummary() - { - StringBuffer sb = new StringBuffer(); - sb.append(getDocumentNo()); - // : Grand Total = 123.00 (#1) - sb.append(": "). - append(Msg.translate(getCtx(),"GrandTotal")).append("=").append(getGrandTotal()); - if (m_lines != null) - sb.append(" (#").append(m_lines.length).append(")"); - // - Description - if (getDescription() != null && getDescription().length() > 0) - sb.append(" - ").append(getDescription()); - return sb.toString(); - } // getSummary - - /** - * Get Process Message - * @return clear text error message - */ - public String getProcessMsg() - { - return m_processMsg; - } // getProcessMsg - - /** - * Get Document Owner (Responsible) - * @return AD_User_ID - */ - public int getDoc_User_ID() - { - return getSalesRep_ID(); - } // getDoc_User_ID - - /** - * Get Document Approval Amount - * @return amount - */ - public BigDecimal getApprovalAmt() - { - return getGrandTotal(); - } // getApprovalAmt - -} // MOrder +/****************************************************************************** + * 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.io.File; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.logging.Level; +import java.util.regex.Pattern; + +import org.compiere.print.ReportEngine; +import org.compiere.process.DocAction; +import org.compiere.process.DocumentEngine; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.compiere.util.Util; +import org.eevolution.model.MDDOrderLine; +import org.eevolution.model.MPPProductBOMLine; +import org.eevolution.model.MPPProductBOM; + + +/** + * Order Model. + * Please do not set DocStatus and C_DocType_ID directly. + * They are set in the process() method. + * Use DocAction and C_DocTypeTarget_ID instead. + * + * @author Jorg Janke + * @version $Id: MOrder.java,v 1.5 2006/10/06 00:42:24 jjanke Exp $ + */ +public class MOrder extends X_C_Order implements DocAction +{ + /** + * Create new Order by copying + * @param from order + * @param dateDoc date of the document date + * @param C_DocTypeTarget_ID target document type + * @param isSOTrx sales order + * @param counter create counter links + * @param copyASI copy line attributes Attribute Set Instance, Resaouce Assignment + * @param trxName trx + * @return Order + */ + public static MOrder copyFrom (MOrder from, Timestamp dateDoc, + int C_DocTypeTarget_ID, boolean isSOTrx, boolean counter, boolean copyASI, + String trxName) + { + MOrder to = new MOrder (from.getCtx(), 0, trxName); + to.set_TrxName(trxName); + PO.copyValues(from, to, from.getAD_Client_ID(), from.getAD_Org_ID()); + to.set_ValueNoCheck ("C_Order_ID", I_ZERO); + to.set_ValueNoCheck ("DocumentNo", null); + // + to.setDocStatus (DOCSTATUS_Drafted); // Draft + to.setDocAction(DOCACTION_Complete); + // + to.setC_DocType_ID(0); + to.setC_DocTypeTarget_ID (C_DocTypeTarget_ID); + to.setIsSOTrx(isSOTrx); + // + to.setIsSelected (false); + to.setDateOrdered (dateDoc); + to.setDateAcct (dateDoc); + to.setDatePromised (dateDoc); // assumption + to.setDatePrinted(null); + to.setIsPrinted (false); + // + to.setIsApproved (false); + to.setIsCreditApproved(false); + to.setC_Payment_ID(0); + to.setC_CashLine_ID(0); + // Amounts are updated when adding lines + to.setGrandTotal(Env.ZERO); + to.setTotalLines(Env.ZERO); + // + to.setIsDelivered(false); + to.setIsInvoiced(false); + to.setIsSelfService(false); + to.setIsTransferred (false); + to.setPosted (false); + to.setProcessed (false); + if (counter) + to.setRef_Order_ID(from.getC_Order_ID()); + else + to.setRef_Order_ID(0); + // + if (!to.save(trxName)) + throw new IllegalStateException("Could not create Order"); + if (counter) + from.setRef_Order_ID(to.getC_Order_ID()); + + if (to.copyLinesFrom(from, counter, copyASI) == 0) + throw new IllegalStateException("Could not create Order Lines"); + + return to; + } // copyFrom + + + /************************************************************************** + * Default Constructor + * @param ctx context + * @param C_Order_ID order to load, (0 create new order) + * @param trxName trx name + */ + public MOrder(Properties ctx, int C_Order_ID, String trxName) + { + super (ctx, C_Order_ID, trxName); + // New + if (C_Order_ID == 0) + { + setDocStatus(DOCSTATUS_Drafted); + setDocAction (DOCACTION_Prepare); + // + setDeliveryRule (DELIVERYRULE_Availability); + setFreightCostRule (FREIGHTCOSTRULE_FreightIncluded); + setInvoiceRule (INVOICERULE_Immediate); + setPaymentRule(PAYMENTRULE_OnCredit); + setPriorityRule (PRIORITYRULE_Medium); + setDeliveryViaRule (DELIVERYVIARULE_Pickup); + // + setIsDiscountPrinted (false); + setIsSelected (false); + setIsTaxIncluded (false); + setIsSOTrx (true); + setIsDropShip(false); + setSendEMail (false); + // + setIsApproved(false); + setIsPrinted(false); + setIsCreditApproved(false); + setIsDelivered(false); + setIsInvoiced(false); + setIsTransferred(false); + setIsSelfService(false); + // + super.setProcessed(false); + setProcessing(false); + setPosted(false); + + setDateAcct (new Timestamp(System.currentTimeMillis())); + setDatePromised (new Timestamp(System.currentTimeMillis())); + setDateOrdered (new Timestamp(System.currentTimeMillis())); + + setFreightAmt (Env.ZERO); + setChargeAmt (Env.ZERO); + setTotalLines (Env.ZERO); + setGrandTotal (Env.ZERO); + } + } // MOrder + + /************************************************************************** + * Project Constructor + * @param project Project to create Order from + * @param IsSOTrx sales order + * @param DocSubTypeSO if SO DocType Target (default DocSubTypeSO_OnCredit) + */ + public MOrder (MProject project, boolean IsSOTrx, String DocSubTypeSO) + { + this (project.getCtx(), 0, project.get_TrxName()); + setAD_Client_ID(project.getAD_Client_ID()); + setAD_Org_ID(project.getAD_Org_ID()); + setC_Campaign_ID(project.getC_Campaign_ID()); + setSalesRep_ID(project.getSalesRep_ID()); + // + setC_Project_ID(project.getC_Project_ID()); + setDescription(project.getName()); + Timestamp ts = project.getDateContract(); + if (ts != null) + setDateOrdered (ts); + ts = project.getDateFinish(); + if (ts != null) + setDatePromised (ts); + // + setC_BPartner_ID(project.getC_BPartner_ID()); + setC_BPartner_Location_ID(project.getC_BPartner_Location_ID()); + setAD_User_ID(project.getAD_User_ID()); + // + setM_Warehouse_ID(project.getM_Warehouse_ID()); + setM_PriceList_ID(project.getM_PriceList_ID()); + setC_PaymentTerm_ID(project.getC_PaymentTerm_ID()); + // + setIsSOTrx(IsSOTrx); + if (IsSOTrx) + { + if (DocSubTypeSO == null || DocSubTypeSO.length() == 0) + setC_DocTypeTarget_ID(DocSubTypeSO_OnCredit); + else + setC_DocTypeTarget_ID(DocSubTypeSO); + } + else + setC_DocTypeTarget_ID(); + } // MOrder + + /** + * Load Constructor + * @param ctx context + * @param rs result set record + * @param trxName transaction + */ + public MOrder (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MOrder + + /** Order Lines */ + private MOrderLine[] m_lines = null; + /** Tax Lines */ + private MOrderTax[] m_taxes = null; + /** Force Creation of order */ + private boolean m_forceCreation = false; + + /** + * Overwrite Client/Org if required + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + public void setClientOrg (int AD_Client_ID, int AD_Org_ID) + { + super.setClientOrg(AD_Client_ID, AD_Org_ID); + } // setClientOrg + + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + /** + * Set Business Partner (Ship+Bill) + * @param C_BPartner_ID bpartner + */ + public void setC_BPartner_ID (int C_BPartner_ID) + { + super.setC_BPartner_ID (C_BPartner_ID); + super.setBill_BPartner_ID (C_BPartner_ID); + } // setC_BPartner_ID + + /** + * Set Business Partner Location (Ship+Bill) + * @param C_BPartner_Location_ID bp location + */ + public void setC_BPartner_Location_ID (int C_BPartner_Location_ID) + { + super.setC_BPartner_Location_ID (C_BPartner_Location_ID); + super.setBill_Location_ID(C_BPartner_Location_ID); + } // setC_BPartner_Location_ID + + /** + * Set Business Partner Contact (Ship+Bill) + * @param AD_User_ID user + */ + public void setAD_User_ID (int AD_User_ID) + { + super.setAD_User_ID (AD_User_ID); + super.setBill_User_ID (AD_User_ID); + } // setAD_User_ID + + /** + * Set Ship Business Partner + * @param C_BPartner_ID bpartner + */ + public void setShip_BPartner_ID (int C_BPartner_ID) + { + super.setC_BPartner_ID (C_BPartner_ID); + } // setShip_BPartner_ID + + /** + * Set Ship Business Partner Location + * @param C_BPartner_Location_ID bp location + */ + public void setShip_Location_ID (int C_BPartner_Location_ID) + { + super.setC_BPartner_Location_ID (C_BPartner_Location_ID); + } // setShip_Location_ID + + /** + * Set Ship Business Partner Contact + * @param AD_User_ID user + */ + public void setShip_User_ID (int AD_User_ID) + { + super.setAD_User_ID (AD_User_ID); + } // setShip_User_ID + + + /** + * Set Warehouse + * @param M_Warehouse_ID warehouse + */ + public void setM_Warehouse_ID (int M_Warehouse_ID) + { + super.setM_Warehouse_ID (M_Warehouse_ID); + } // setM_Warehouse_ID + + /** + * Set Drop Ship + * @param IsDropShip drop ship + */ + public void setIsDropShip (boolean IsDropShip) + { + super.setIsDropShip (IsDropShip); + } // setIsDropShip + + /*************************************************************************/ + + /** Sales Order Sub Type - SO */ + public static final String DocSubTypeSO_Standard = "SO"; + /** Sales Order Sub Type - OB */ + public static final String DocSubTypeSO_Quotation = "OB"; + /** Sales Order Sub Type - ON */ + public static final String DocSubTypeSO_Proposal = "ON"; + /** Sales Order Sub Type - PR */ + public static final String DocSubTypeSO_Prepay = "PR"; + /** Sales Order Sub Type - WR */ + public static final String DocSubTypeSO_POS = "WR"; + /** Sales Order Sub Type - WP */ + public static final String DocSubTypeSO_Warehouse = "WP"; + /** Sales Order Sub Type - WI */ + public static final String DocSubTypeSO_OnCredit = "WI"; + /** Sales Order Sub Type - RM */ + public static final String DocSubTypeSO_RMA = "RM"; + + /** + * Set Target Sales Document Type + * @param DocSubTypeSO_x SO sub type - see DocSubTypeSO_* + */ + public void setC_DocTypeTarget_ID (String DocSubTypeSO_x) + { + String sql = "SELECT C_DocType_ID FROM C_DocType " + + "WHERE AD_Client_ID=? AND AD_Org_ID IN (0," + getAD_Org_ID() + + ") AND DocSubTypeSO=? " + + "ORDER BY AD_Org_ID DESC, IsDefault DESC"; + int C_DocType_ID = DB.getSQLValue(null, sql, getAD_Client_ID(), DocSubTypeSO_x); + if (C_DocType_ID <= 0) + log.severe ("Not found for AD_Client_ID=" + getAD_Client_ID () + ", SubType=" + DocSubTypeSO_x); + else + { + log.fine("(SO) - " + DocSubTypeSO_x); + setC_DocTypeTarget_ID (C_DocType_ID); + setIsSOTrx(true); + } + } // setC_DocTypeTarget_ID + + /** + * Set Target Document Type. + * Standard Order or PO + */ + public void setC_DocTypeTarget_ID () + { + if (isSOTrx()) // SO = Std Order + { + setC_DocTypeTarget_ID(DocSubTypeSO_Standard); + return; + } + // PO + String sql = "SELECT C_DocType_ID FROM C_DocType " + + "WHERE AD_Client_ID=? AND AD_Org_ID IN (0," + getAD_Org_ID() + + ") AND DocBaseType='POO' " + + "ORDER BY AD_Org_ID DESC, IsDefault DESC"; + int C_DocType_ID = DB.getSQLValue(null, sql, getAD_Client_ID()); + if (C_DocType_ID <= 0) + log.severe ("No POO found for AD_Client_ID=" + getAD_Client_ID ()); + else + { + log.fine("(PO) - " + C_DocType_ID); + setC_DocTypeTarget_ID (C_DocType_ID); + } + } // setC_DocTypeTarget_ID + + + /** + * Set Business Partner Defaults & Details. + * SOTrx should be set. + * @param bp business partner + */ + public void setBPartner (MBPartner bp) + { + if (bp == null) + return; + + setC_BPartner_ID(bp.getC_BPartner_ID()); + // Defaults Payment Term + int ii = 0; + if (isSOTrx()) + ii = bp.getC_PaymentTerm_ID(); + else + ii = bp.getPO_PaymentTerm_ID(); + if (ii != 0) + setC_PaymentTerm_ID(ii); + // Default Price List + if (isSOTrx()) + ii = bp.getM_PriceList_ID(); + else + ii = bp.getPO_PriceList_ID(); + if (ii != 0) + setM_PriceList_ID(ii); + // Default Delivery/Via Rule + String ss = bp.getDeliveryRule(); + if (ss != null) + setDeliveryRule(ss); + ss = bp.getDeliveryViaRule(); + if (ss != null) + setDeliveryViaRule(ss); + // Default Invoice/Payment Rule + ss = bp.getInvoiceRule(); + if (ss != null) + setInvoiceRule(ss); + ss = bp.getPaymentRule(); + if (ss != null) + setPaymentRule(ss); + // Sales Rep + ii = bp.getSalesRep_ID(); + if (ii != 0) + setSalesRep_ID(ii); + + + // Set Locations + MBPartnerLocation[] locs = bp.getLocations(false); + if (locs != null) + { + for (int i = 0; i < locs.length; i++) + { + if (locs[i].isShipTo()) + super.setC_BPartner_Location_ID(locs[i].getC_BPartner_Location_ID()); + if (locs[i].isBillTo()) + setBill_Location_ID(locs[i].getC_BPartner_Location_ID()); + } + // set to first + if (getC_BPartner_Location_ID() == 0 && locs.length > 0) + super.setC_BPartner_Location_ID(locs[0].getC_BPartner_Location_ID()); + if (getBill_Location_ID() == 0 && locs.length > 0) + setBill_Location_ID(locs[0].getC_BPartner_Location_ID()); + } + if (getC_BPartner_Location_ID() == 0) + log.log(Level.SEVERE, "MOrder.setBPartner - Has no Ship To Address: " + bp); + if (getBill_Location_ID() == 0) + log.log(Level.SEVERE, "MOrder.setBPartner - Has no Bill To Address: " + bp); + + // Set Contact + MUser[] contacts = bp.getContacts(false); + if (contacts != null && contacts.length == 1) + setAD_User_ID(contacts[0].getAD_User_ID()); + } // setBPartner + + + /** + * Copy Lines From other Order + * @param otherOrder order + * @param counter set counter info + * @param copyASI copy line attributes Attribute Set Instance, Resaouce Assignment + * @return number of lines copied + */ + public int copyLinesFrom (MOrder otherOrder, boolean counter, boolean copyASI) + { + if (isProcessed() || isPosted() || otherOrder == null) + return 0; + MOrderLine[] fromLines = otherOrder.getLines(false, null); + int count = 0; + for (int i = 0; i < fromLines.length; i++) + { + MOrderLine line = new MOrderLine (this); + PO.copyValues(fromLines[i], line, getAD_Client_ID(), getAD_Org_ID()); + line.setC_Order_ID(getC_Order_ID()); + line.setOrder(this); + line.set_ValueNoCheck ("C_OrderLine_ID", I_ZERO); // new + // References + if (!copyASI) + { + line.setM_AttributeSetInstance_ID(0); + line.setS_ResourceAssignment_ID(0); + } + if (counter) + line.setRef_OrderLine_ID(fromLines[i].getC_OrderLine_ID()); + else + line.setRef_OrderLine_ID(0); + // + line.setQtyDelivered(Env.ZERO); + line.setQtyInvoiced(Env.ZERO); + line.setQtyReserved(Env.ZERO); + line.setDateDelivered(null); + line.setDateInvoiced(null); + // Tax + if (getC_BPartner_ID() != otherOrder.getC_BPartner_ID()) + line.setTax(); // recalculate + // + // + line.setProcessed(false); + if (line.save(get_TrxName())) + count++; + // Cross Link + if (counter) + { + fromLines[i].setRef_OrderLine_ID(line.getC_OrderLine_ID()); + fromLines[i].save(get_TrxName()); + } + } + if (fromLines.length != count) + log.log(Level.SEVERE, "Line difference - From=" + fromLines.length + " <> Saved=" + count); + return count; + } // copyLinesFrom + + + /************************************************************************** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MOrder[") + .append(get_ID()).append("-").append(getDocumentNo()) + .append(",IsSOTrx=").append(isSOTrx()) + .append(",C_DocType_ID=").append(getC_DocType_ID()) + .append(", GrandTotal=").append(getGrandTotal()) + .append ("]"); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.ORDER, getC_Order_ID(), get_TrxName()); + if (re == null) + return null; + return re.getPDF(file); + } // createPDF + + /** + * Set Price List (and Currency, TaxIncluded) when valid + * @param M_PriceList_ID price list + */ + public void setM_PriceList_ID (int M_PriceList_ID) + { + MPriceList pl = MPriceList.get(getCtx(), M_PriceList_ID, null); + if (pl.get_ID() == M_PriceList_ID) + { + super.setM_PriceList_ID(M_PriceList_ID); + setC_Currency_ID(pl.getC_Currency_ID()); + setIsTaxIncluded(pl.isTaxIncluded()); + } + } // setM_PriceList_ID + + + /************************************************************************** + * Get Lines of Order + * @param whereClause where clause or null (starting with AND) + * @param orderClause order clause + * @return lines + */ + public MOrderLine[] getLines (String whereClause, String orderClause) + { + //red1 - using new Query class from Teo / Victor's MDDOrder.java implementation + StringBuffer whereClauseFinal = new StringBuffer("C_Order_ID =?"); + if (!Util.isEmpty(whereClause, true)) + whereClauseFinal.append(whereClause); + if (orderClause.length() == 0) + orderClause = "Line"; + // + List list = new Query(getCtx(), MOrderLine.Table_Name, whereClauseFinal.toString(), get_TrxName()) + .setParameters(new Object[]{getC_Order_ID()}) + .setOrderBy(orderClause) + .list(); + return list.toArray(new MOrderLine[list.size()]); + } // getLines + + /** + * Get Lines of Order + * @param requery requery + * @param orderBy optional order by column + * @return lines + */ + public MOrderLine[] getLines (boolean requery, String orderBy) + { + if (m_lines != null && !requery) { + set_TrxName(m_lines, get_TrxName()); + return m_lines; + } + // + String orderClause = ""; + if (orderBy != null && orderBy.length() > 0) + orderClause += orderBy; + else + orderClause += "Line"; + m_lines = getLines(null, orderClause); + return m_lines; + } // getLines + + /** + * Get Lines of Order. + * (useb by web store) + * @return lines + */ + public MOrderLine[] getLines() + { + return getLines(false, null); + } // getLines + + /** + * Renumber Lines + * @param step start and step + */ + public void renumberLines (int step) + { + int number = step; + MOrderLine[] lines = getLines(true, null); // Line is default + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + line.setLine(number); + line.save(get_TrxName()); + number += step; + } + m_lines = null; + } // renumberLines + + /** + * Does the Order Line belong to this Order + * @param C_OrderLine_ID line + * @return true if part of the order + */ + public boolean isOrderLine(int C_OrderLine_ID) + { + if (m_lines == null) + getLines(); + for (int i = 0; i < m_lines.length; i++) + if (m_lines[i].getC_OrderLine_ID() == C_OrderLine_ID) + return true; + return false; + } // isOrderLine + + /** + * Get Taxes of Order + * @param requery requery + * @return array of taxes + */ + public MOrderTax[] getTaxes(boolean requery) + { + if (m_taxes != null && !requery) + return m_taxes; + // + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM C_OrderTax WHERE C_Order_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Order_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MOrderTax(getCtx(), rs, get_TrxName())); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "getTaxes", e); + } + finally + { + try + { + if (pstmt != null) + pstmt.close (); + } + catch (Exception e) + {} + pstmt = null; + } + // + m_taxes = new MOrderTax[list.size ()]; + list.toArray (m_taxes); + return m_taxes; + } // getTaxes + + + /** + * Get Invoices of Order + * @return invoices + */ + public MInvoice[] getInvoices() + { + ArrayList list = new ArrayList(); + String sql = " SELECT DISTINCT i.* FROM C_InvoiceLine il " + + "INNER JOIN C_OrderLine ol ON (ol.C_OrderLine_ID = il.C_OrderLine_ID) " + + "INNER JOIN C_Order o ON (o.C_Order_ID = ol.C_Order_ID) " + + "INNER JOIN C_Invoice i ON (i.C_Invoice_ID = il.C_Invoice_ID) " + + "WHERE o.C_Order_ID=? " + + "ORDER BY i.Created DESC"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Order_ID()); + rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MInvoice(getCtx(), rs, get_TrxName())); + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // + MInvoice[] retValue = new MInvoice[list.size()]; + list.toArray(retValue); + return retValue; + } // getInvoices + + /** + * Get latest Invoice of Order + * @return invoice id or 0 + */ + public int getC_Invoice_ID() + { + int C_Invoice_ID = 0; + String sql = "SELECT C_Invoice_ID FROM C_Invoice " + + "WHERE C_Order_ID=? AND DocStatus IN ('CO','CL') " + + "ORDER BY Created DESC"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Order_ID()); + rs = pstmt.executeQuery(); + if (rs.next()) + C_Invoice_ID = rs.getInt(1); + } + catch (Exception e) + { + log.log(Level.SEVERE, "getC_Invoice_ID", e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + return C_Invoice_ID; + } // getC_Invoice_ID + + + /** + * Get Shipments of Order + * @return shipments + */ + public MInOut[] getShipments() + { + ArrayList list = new ArrayList(); + String sql = "SELECT DISTINCT io.* FROM M_InOutLine iol " + + "INNER JOIN M_InOut io ON (io.M_InOut_ID = iol.M_InOut_ID) " + + "INNER JOIN C_ORDERLINE ol ON (ol.C_ORDERLINE_ID=iol.C_ORDERLINE_ID) " + + "INNER JOIN C_ORDER o ON (o.C_ORDER_ID=ol.C_ORDER_ID) " + + "WHERE o.C_ORDER_ID=? " + + "ORDER BY io.Created DESC"; + + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Order_ID()); + rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MInOut(getCtx(), rs, get_TrxName())); + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // + MInOut[] retValue = new MInOut[list.size()]; + list.toArray(retValue); + return retValue; + } // getShipments + + /** + * Get ISO Code of Currency + * @return Currency ISO + */ + public String getCurrencyISO() + { + return MCurrency.getISO_Code (getCtx(), getC_Currency_ID()); + } // getCurrencyISO + + /** + * Get Currency Precision + * @return precision + */ + public int getPrecision() + { + return MCurrency.getStdPrecision(getCtx(), getC_Currency_ID()); + } // getPrecision + + /** + * Get Document Status + * @return Document Status Clear Text + */ + public String getDocStatusName() + { + return MRefList.getListName(getCtx(), 131, getDocStatus()); + } // getDocStatusName + + /** + * Set DocAction + * @param DocAction doc action + */ + public void setDocAction (String DocAction) + { + setDocAction (DocAction, false); + } // setDocAction + + /** + * Set DocAction + * @param DocAction doc oction + * @param forceCreation force creation + */ + public void setDocAction (String DocAction, boolean forceCreation) + { + super.setDocAction (DocAction); + m_forceCreation = forceCreation; + } // setDocAction + + /** + * Set Processed. + * Propergate to Lines/Taxes + * @param processed processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + if (get_ID() == 0) + return; + String set = "SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE C_Order_ID=" + getC_Order_ID(); + int noLine = DB.executeUpdate("UPDATE C_OrderLine " + set, get_TrxName()); + int noTax = DB.executeUpdate("UPDATE C_OrderTax " + set, get_TrxName()); + m_lines = null; + m_taxes = null; + log.fine("setProcessed - " + processed + " - Lines=" + noLine + ", Tax=" + noTax); + } // setProcessed + + + + /************************************************************************** + * Before Save + * @param newRecord new + * @return save + */ + protected boolean beforeSave (boolean newRecord) + { + // Client/Org Check + if (getAD_Org_ID() == 0) + { + int context_AD_Org_ID = Env.getAD_Org_ID(getCtx()); + if (context_AD_Org_ID != 0) + { + setAD_Org_ID(context_AD_Org_ID); + log.warning("Changed Org to Context=" + context_AD_Org_ID); + } + } + if (getAD_Client_ID() == 0) + { + m_processMsg = "AD_Client_ID = 0"; + return false; + } + + // New Record Doc Type - make sure DocType set to 0 + if (newRecord && getC_DocType_ID() == 0) + setC_DocType_ID (0); + + // Default Warehouse + if (getM_Warehouse_ID() == 0) + { + int ii = Env.getContextAsInt(getCtx(), "#M_Warehouse_ID"); + if (ii != 0) + setM_Warehouse_ID(ii); + else + { + log.saveError("FillMandatory", Msg.getElement(getCtx(), "M_Warehouse_ID")); + return false; + } + } + // Warehouse Org + if (newRecord + || is_ValueChanged("AD_Org_ID") || is_ValueChanged("M_Warehouse_ID")) + { + MWarehouse wh = MWarehouse.get(getCtx(), getM_Warehouse_ID()); + if (wh.getAD_Org_ID() != getAD_Org_ID()) + log.saveWarning("WarehouseOrgConflict", ""); + } + // Reservations in Warehouse + if (!newRecord && is_ValueChanged("M_Warehouse_ID")) + { + MOrderLine[] lines = getLines(false,null); + for (int i = 0; i < lines.length; i++) + { + if (!lines[i].canChangeWarehouse()) + return false; + } + } + + // No Partner Info - set Template + if (getC_BPartner_ID() == 0) + setBPartner(MBPartner.getTemplate(getCtx(), getAD_Client_ID())); + if (getC_BPartner_Location_ID() == 0) + setBPartner(new MBPartner(getCtx(), getC_BPartner_ID(), null)); + // No Bill - get from Ship + if (getBill_BPartner_ID() == 0) + { + setBill_BPartner_ID(getC_BPartner_ID()); + setBill_Location_ID(getC_BPartner_Location_ID()); + } + if (getBill_Location_ID() == 0) + setBill_Location_ID(getC_BPartner_Location_ID()); + + // Default Price List + if (getM_PriceList_ID() == 0) + { + int ii = DB.getSQLValue(null, + "SELECT M_PriceList_ID FROM M_PriceList " + + "WHERE AD_Client_ID=? AND IsSOPriceList=? " + + "ORDER BY IsDefault DESC", getAD_Client_ID(), isSOTrx() ? "Y" : "N"); + if (ii != 0) + setM_PriceList_ID (ii); + } + // Default Currency + if (getC_Currency_ID() == 0) + { + String sql = "SELECT C_Currency_ID FROM M_PriceList WHERE M_PriceList_ID=?"; + int ii = DB.getSQLValue (null, sql, getM_PriceList_ID()); + if (ii != 0) + setC_Currency_ID (ii); + else + setC_Currency_ID(Env.getContextAsInt(getCtx(), "#C_Currency_ID")); + } + + // Default Sales Rep + if (getSalesRep_ID() == 0) + { + int ii = Env.getContextAsInt(getCtx(), "#SalesRep_ID"); + if (ii != 0) + setSalesRep_ID (ii); + } + + // Default Document Type + if (getC_DocTypeTarget_ID() == 0) + setC_DocTypeTarget_ID(DocSubTypeSO_Standard); + + // Default Payment Term + if (getC_PaymentTerm_ID() == 0) + { + int ii = Env.getContextAsInt(getCtx(), "#C_PaymentTerm_ID"); + if (ii != 0) + setC_PaymentTerm_ID(ii); + else + { + String sql = "SELECT C_PaymentTerm_ID FROM C_PaymentTerm WHERE AD_Client_ID=? AND IsDefault='Y'"; + ii = DB.getSQLValue(null, sql, getAD_Client_ID()); + if (ii != 0) + setC_PaymentTerm_ID (ii); + } + } + + return true; + } // beforeSave + + + /** + * After Save + * @param newRecord new + * @param success success + * @return true if can be saved + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + if (!success || newRecord) + return success; + + // Propagate Description changes + if (is_ValueChanged("Description") || is_ValueChanged("POReference")) + { + String sql = "UPDATE C_Invoice i" + + " SET (Description,POReference)=" + + "(SELECT Description,POReference " + + "FROM C_Order o WHERE i.C_Order_ID=o.C_Order_ID) " + + "WHERE DocStatus NOT IN ('RE','CL') AND C_Order_ID=" + getC_Order_ID(); + int no = DB.executeUpdate(sql, get_TrxName()); + log.fine("Description -> #" + no); + } + + // Propagate Changes of Payment Info to existing (not reversed/closed) invoices + if (is_ValueChanged("PaymentRule") || is_ValueChanged("C_PaymentTerm_ID") + || is_ValueChanged("DateAcct") || is_ValueChanged("C_Payment_ID") + || is_ValueChanged("C_CashLine_ID")) + { + String sql = "UPDATE C_Invoice i " + + "SET (PaymentRule,C_PaymentTerm_ID,DateAcct,C_Payment_ID,C_CashLine_ID)=" + + "(SELECT PaymentRule,C_PaymentTerm_ID,DateAcct,C_Payment_ID,C_CashLine_ID " + + "FROM C_Order o WHERE i.C_Order_ID=o.C_Order_ID)" + + "WHERE DocStatus NOT IN ('RE','CL') AND C_Order_ID=" + getC_Order_ID(); + // Don't touch Closed/Reversed entries + int no = DB.executeUpdate(sql, get_TrxName()); + log.fine("Payment -> #" + no); + } + + // Sync Lines + MOrderLine[] lines = getLines(); + for (MOrderLine line : lines) { + line.setAD_Org_ID(getAD_Org_ID()); + line.setC_BPartner_ID(getC_BPartner_ID()); + line.setC_BPartner_Location_ID(getC_BPartner_Location_ID()); + line.setDateOrdered(getDateOrdered()); + line.setDatePromised(getDatePromised()); + line.setM_Warehouse_ID(getM_Warehouse_ID()); + line.setM_Shipper_ID(getM_Shipper_ID()); + line.setC_Currency_ID(getC_Currency_ID()); + line.saveEx(); + } + // + return true; + } // afterSave + + /** + * Before Delete + * @return true of it can be deleted + */ + protected boolean beforeDelete () + { + if (isProcessed()) + return false; + + for (MOrderLine line : getLines()) { + line.deleteEx(true); + } + return true; + } // beforeDelete + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // processIt + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info("unlockIt - " + toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + + /************************************************************************** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + MDocType dt = MDocType.get(getCtx(), getC_DocTypeTarget_ID()); + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getDateAcct(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + + // Lines + MOrderLine[] lines = getLines(true, "M_Product_ID"); + if (lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + + // Bug 1564431 + if (getDeliveryRule() != null && getDeliveryRule().equals(MOrder.DELIVERYRULE_CompleteOrder)) + { + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + MProduct product = line.getProduct(); + if (product != null && product.isExcludeAutoDelivery()) + { + m_processMsg = "@M_Product_ID@ "+product.getValue()+" @IsExcludeAutoDelivery@"; + return DocAction.STATUS_Invalid; + } + } + } + + // Convert DocType to Target + if (getC_DocType_ID() != getC_DocTypeTarget_ID() ) + { + // Cannot change Std to anything else if different warehouses + if (getC_DocType_ID() != 0) + { + MDocType dtOld = MDocType.get(getCtx(), getC_DocType_ID()); + if (MDocType.DOCSUBTYPESO_StandardOrder.equals(dtOld.getDocSubTypeSO()) // From SO + && !MDocType.DOCSUBTYPESO_StandardOrder.equals(dt.getDocSubTypeSO())) // To !SO + { + for (int i = 0; i < lines.length; i++) + { + if (lines[i].getM_Warehouse_ID() != getM_Warehouse_ID()) + { + log.warning("different Warehouse " + lines[i]); + m_processMsg = "@CannotChangeDocType@"; + return DocAction.STATUS_Invalid; + } + } + } + } + + // New or in Progress/Invalid + if (DOCSTATUS_Drafted.equals(getDocStatus()) + || DOCSTATUS_InProgress.equals(getDocStatus()) + || DOCSTATUS_Invalid.equals(getDocStatus()) + || getC_DocType_ID() == 0) + { + setC_DocType_ID(getC_DocTypeTarget_ID()); + } + else // convert only if offer + { + if (dt.isOffer()) + setC_DocType_ID(getC_DocTypeTarget_ID()); + else + { + m_processMsg = "@CannotChangeDocType@"; + return DocAction.STATUS_Invalid; + } + } + } // convert DocType + + // Mandatory Product Attribute Set Instance + String mandatoryType = "='Y'"; // IN ('Y','S') + String sql = "SELECT COUNT(*) " + + "FROM C_OrderLine ol" + + " INNER JOIN M_Product p ON (ol.M_Product_ID=p.M_Product_ID)" + + " INNER JOIN M_AttributeSet pas ON (p.M_AttributeSet_ID=pas.M_AttributeSet_ID) " + + "WHERE pas.MandatoryType" + mandatoryType + + " AND ol.M_AttributeSetInstance_ID IS NULL" + + " AND ol.C_Order_ID=?"; + int no = DB.getSQLValue(get_TrxName(), sql, getC_Order_ID()); + if (no != 0) + { + m_processMsg = "@LinesWithoutProductAttribute@ (" + no + ")"; + return DocAction.STATUS_Invalid; + } + + // Lines + if (explodeBOM()) + lines = getLines(true, "M_Product_ID"); + if (!reserveStock(dt, lines)) + { + m_processMsg = "Cannot reserve Stock"; + return DocAction.STATUS_Invalid; + } + if (!calculateTaxTotal()) + { + m_processMsg = "Error calculating tax"; + return DocAction.STATUS_Invalid; + } + + // Credit Check + if (isSOTrx()) + { + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus())) + { + m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus())) + { + m_processMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + BigDecimal grandTotal = MConversionRate.convertBase(getCtx(), + getGrandTotal(), getC_Currency_ID(), getDateOrdered(), + getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); + if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus(grandTotal))) + { + m_processMsg = "@BPartnerOverOCreditHold@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + ", @GrandTotal@=" + grandTotal + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + // if (!DOCACTION_Complete.equals(getDocAction())) don't set for just prepare + // setDocAction(DOCACTION_Complete); + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Explode non stocked BOM. + * @return true if bom exploded + */ + private boolean explodeBOM() + { + boolean retValue = false; + String where = "AND IsActive='Y' AND EXISTS " + + "(SELECT * FROM M_Product p WHERE C_OrderLine.M_Product_ID=p.M_Product_ID" + + " AND p.IsBOM='Y' AND p.IsVerified='Y' AND p.IsStocked='N')"; + // + String sql = "SELECT COUNT(*) FROM C_OrderLine " + + "WHERE C_Order_ID=? " + where; + int count = DB.getSQLValue(get_TrxName(), sql, getC_Order_ID()); + while (count != 0) + { + retValue = true; + renumberLines (1000); // max 999 bom items + + // Order Lines with non-stocked BOMs + MOrderLine[] lines = getLines (where, " Line"); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + MProduct product = MProduct.get (getCtx(), line.getM_Product_ID()); + log.fine(product.getName()); + // New Lines + int lineNo = line.getLine (); + //find default BOM with valid dates and to this product + MPPProductBOM bom = MPPProductBOM.get(product, getAD_Org_ID(),getDatePromised(), get_TrxName()); + if(bom != null) + { + MPPProductBOMLine[] bomlines = bom.getLines(getDatePromised()); + for (int j = 0; j < bomlines.length; j++) + { + MPPProductBOMLine bomline = bomlines[j]; + MOrderLine newLine = new MOrderLine (this); + newLine.setLine (++lineNo); + newLine.setM_Product_ID (bomline.getM_Product_ID ()); + newLine.setC_UOM_ID (bomline.getC_UOM_ID ()); + newLine.setQty (line.getQtyOrdered ().multiply ( + bomline.getQtyBOM())); + if (bomline.getDescription () != null) + newLine.setDescription (bomline.getDescription ()); + // + newLine.setPrice (); + newLine.save (get_TrxName()); + } + } + + /*MProductBOM[] boms = MProductBOM.getBOMLines (product); + for (int j = 0; j < boms.length; j++) + { + //MProductBOM bom = boms[j]; + MPPProductBOMLine bom = boms[j]; + MOrderLine newLine = new MOrderLine (this); + newLine.setLine (++lineNo); + //newLine.setM_Product_ID (bom.getProduct () + // .getM_Product_ID ()); + newLine.setM_Product_ID (bom.getM_Product_ID ()); + //newLine.setC_UOM_ID (bom.getProduct ().getC_UOM_ID ()); + newLine.setC_UOM_ID (bom.getC_UOM_ID ()); + //newLine.setQty (line.getQtyOrdered ().multiply ( + // bom.getBOMQty ())); + newLine.setQty (line.getQtyOrdered ().multiply ( + bom.getQtyBOM())); + if (bom.getDescription () != null) + newLine.setDescription (bom.getDescription ()); + // + newLine.setPrice (); + newLine.save (get_TrxName()); + }*/ + + // Convert into Comment Line + line.setM_Product_ID (0); + line.setM_AttributeSetInstance_ID (0); + line.setPrice (Env.ZERO); + line.setPriceLimit (Env.ZERO); + line.setPriceList (Env.ZERO); + line.setLineNetAmt (Env.ZERO); + line.setFreightAmt (Env.ZERO); + // + String description = product.getName (); + if (product.getDescription () != null) + description += " " + product.getDescription (); + if (line.getDescription () != null) + description += " " + line.getDescription (); + line.setDescription (description); + line.save (get_TrxName()); + } // for all lines with BOM + + m_lines = null; // force requery + count = DB.getSQLValue (get_TrxName(), sql, getC_Invoice_ID ()); + renumberLines (10); + } // while count != 0 + return retValue; + } // explodeBOM + + + /** + * Reserve Inventory. + * Counterpart: MInOut.completeIt() + * @param dt document type or null + * @param lines order lines (ordered by M_Product_ID for deadlock prevention) + * @return true if (un) reserved + */ + private boolean reserveStock (MDocType dt, MOrderLine[] lines) + { + if (dt == null) + dt = MDocType.get(getCtx(), getC_DocType_ID()); + + // Binding + boolean binding = !dt.isProposal(); + // Not binding - i.e. Target=0 + if (DOCACTION_Void.equals(getDocAction()) + // Closing Binding Quotation + || (MDocType.DOCSUBTYPESO_Quotation.equals(dt.getDocSubTypeSO()) + && DOCACTION_Close.equals(getDocAction())) + || isDropShip() ) + binding = false; + boolean isSOTrx = isSOTrx(); + log.fine("Binding=" + binding + " - IsSOTrx=" + isSOTrx); + // Force same WH for all but SO/PO + int header_M_Warehouse_ID = getM_Warehouse_ID(); + if (MDocType.DOCSUBTYPESO_StandardOrder.equals(dt.getDocSubTypeSO()) + || MDocType.DOCBASETYPE_PurchaseOrder.equals(dt.getDocBaseType())) + header_M_Warehouse_ID = 0; // don't enforce + + BigDecimal Volume = Env.ZERO; + BigDecimal Weight = Env.ZERO; + + // Always check and (un) Reserve Inventory + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + // Check/set WH/Org + if (header_M_Warehouse_ID != 0) // enforce WH + { + if (header_M_Warehouse_ID != line.getM_Warehouse_ID()) + line.setM_Warehouse_ID(header_M_Warehouse_ID); + if (getAD_Org_ID() != line.getAD_Org_ID()) + line.setAD_Org_ID(getAD_Org_ID()); + } + // Binding + BigDecimal target = binding ? line.getQtyOrdered() : Env.ZERO; + BigDecimal difference = target + .subtract(line.getQtyReserved()) + .subtract(line.getQtyDelivered()); + if (difference.signum() == 0) + { + MProduct product = line.getProduct(); + if (product != null) + { + Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); + Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); + } + continue; + } + + log.fine("Line=" + line.getLine() + + " - Target=" + target + ",Difference=" + difference + + " - Ordered=" + line.getQtyOrdered() + + ",Reserved=" + line.getQtyReserved() + ",Delivered=" + line.getQtyDelivered()); + + // Check Product - Stocked and Item + MProduct product = line.getProduct(); + if (product != null) + { + if (product.isStocked()) + { + BigDecimal ordered = isSOTrx ? Env.ZERO : difference; + BigDecimal reserved = isSOTrx ? difference : Env.ZERO; + int M_Locator_ID = 0; + // Get Locator to reserve + if (line.getM_AttributeSetInstance_ID() != 0) // Get existing Location + M_Locator_ID = MStorage.getM_Locator_ID (line.getM_Warehouse_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + ordered, get_TrxName()); + // Get default Location + if (M_Locator_ID == 0) + { + // try to take default locator for product first + // if it is from the selected warehouse + MWarehouse wh = MWarehouse.get(getCtx(), line.getM_Warehouse_ID()); + M_Locator_ID = product.getM_Locator_ID(); + if (M_Locator_ID!=0) { + MLocator locator = new MLocator(getCtx(), product.getM_Locator_ID(), get_TrxName()); + //product has default locator defined but is not from the order warehouse + if(locator.getM_Warehouse_ID()!=wh.get_ID()) { + M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); + } + } else { + M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); + } + } + // Update Storage + if (!MStorage.add(getCtx(), line.getM_Warehouse_ID(), M_Locator_ID, + line.getM_Product_ID(), + line.getM_AttributeSetInstance_ID(), line.getM_AttributeSetInstance_ID(), + Env.ZERO, reserved, ordered, get_TrxName())) + return false; + } // stockec + // update line + line.setQtyReserved(line.getQtyReserved().add(difference)); + if (!line.save(get_TrxName())) + return false; + // + Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); + Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); + } // product + } // reverse inventory + + setVolume(Volume); + setWeight(Weight); + return true; + } // reserveStock + + /** + * Calculate Tax and Total + * @return true if tax total calculated + */ + public boolean calculateTaxTotal() + { + log.fine(""); + // Delete Taxes + DB.executeUpdate("DELETE C_OrderTax WHERE C_Order_ID=" + getC_Order_ID(), get_TrxName()); + m_taxes = null; + + // Lines + BigDecimal totalLines = Env.ZERO; + ArrayList taxList = new ArrayList(); + MOrderLine[] lines = getLines(); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + Integer taxID = new Integer(line.getC_Tax_ID()); + if (!taxList.contains(taxID)) + { + MOrderTax oTax = MOrderTax.get (line, getPrecision(), + false, get_TrxName()); // current Tax + oTax.setIsTaxIncluded(isTaxIncluded()); + if (!oTax.calculateTaxFromLines()) + return false; + if (!oTax.save(get_TrxName())) + return false; + taxList.add(taxID); + } + totalLines = totalLines.add(line.getLineNetAmt()); + } + + // Taxes + BigDecimal grandTotal = totalLines; + MOrderTax[] taxes = getTaxes(true); + for (int i = 0; i < taxes.length; i++) + { + MOrderTax oTax = taxes[i]; + MTax tax = oTax.getTax(); + if (tax.isSummary()) + { + MTax[] cTaxes = tax.getChildTaxes(false); + for (int j = 0; j < cTaxes.length; j++) + { + MTax cTax = cTaxes[j]; + BigDecimal taxAmt = cTax.calculateTax(oTax.getTaxBaseAmt(), isTaxIncluded(), getPrecision()); + // + MOrderTax newOTax = new MOrderTax(getCtx(), 0, get_TrxName()); + newOTax.setClientOrg(this); + newOTax.setC_Order_ID(getC_Order_ID()); + newOTax.setC_Tax_ID(cTax.getC_Tax_ID()); + newOTax.setPrecision(getPrecision()); + newOTax.setIsTaxIncluded(isTaxIncluded()); + newOTax.setTaxBaseAmt(oTax.getTaxBaseAmt()); + newOTax.setTaxAmt(taxAmt); + if (!newOTax.save(get_TrxName())) + return false; + // + if (!isTaxIncluded()) + grandTotal = grandTotal.add(taxAmt); + } + if (!oTax.delete(true, get_TrxName())) + return false; + if (!oTax.save(get_TrxName())) + return false; + } + else + { + if (!isTaxIncluded()) + grandTotal = grandTotal.add(oTax.getTaxAmt()); + } + } + // + setTotalLines(totalLines); + setGrandTotal(grandTotal); + return true; + } // calculateTaxTotal + + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info("approveIt - " + toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info("rejectIt - " + toString()); + setIsApproved(false); + return true; + } // rejectIt + + + /************************************************************************** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + String DocSubTypeSO = dt.getDocSubTypeSO(); + + // Just prepare + if (DOCACTION_Prepare.equals(getDocAction())) + { + setProcessed(false); + return DocAction.STATUS_InProgress; + } + // Offers + if (MDocType.DOCSUBTYPESO_Proposal.equals(DocSubTypeSO) + || MDocType.DOCSUBTYPESO_Quotation.equals(DocSubTypeSO)) + { + // Binding + if (MDocType.DOCSUBTYPESO_Quotation.equals(DocSubTypeSO)) + reserveStock(dt, getLines(true, "M_Product_ID")); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + setProcessed(true); + return DocAction.STATUS_Completed; + } + // Waiting Payment - until we have a payment + if (!m_forceCreation + && MDocType.DOCSUBTYPESO_PrepayOrder.equals(DocSubTypeSO) + && getC_Payment_ID() == 0 && getC_CashLine_ID() == 0) + { + setProcessed(true); + return DocAction.STATUS_WaitingPayment; + } + + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Implicit Approval + if (!isApproved()) + approveIt(); + getLines(true,null); + log.info(toString()); + StringBuffer info = new StringBuffer(); + + boolean realTimePOS = false; + + // Create SO Shipment - Force Shipment + MInOut shipment = null; + if (MDocType.DOCSUBTYPESO_OnCreditOrder.equals(DocSubTypeSO) // (W)illCall(I)nvoice + || MDocType.DOCSUBTYPESO_WarehouseOrder.equals(DocSubTypeSO) // (W)illCall(P)ickup + || MDocType.DOCSUBTYPESO_POSOrder.equals(DocSubTypeSO) // (W)alkIn(R)eceipt + || MDocType.DOCSUBTYPESO_PrepayOrder.equals(DocSubTypeSO)) + { + if (!DELIVERYRULE_Force.equals(getDeliveryRule())) + setDeliveryRule(DELIVERYRULE_Force); + // + shipment = createShipment (dt, realTimePOS ? null : getDateOrdered()); + if (shipment == null) + return DocAction.STATUS_Invalid; + info.append("@M_InOut_ID@: ").append(shipment.getDocumentNo()); + String msg = shipment.getProcessMsg(); + if (msg != null && msg.length() > 0) + info.append(" (").append(msg).append(")"); + } // Shipment + + + // Create SO Invoice - Always invoice complete Order + if ( MDocType.DOCSUBTYPESO_POSOrder.equals(DocSubTypeSO) + || MDocType.DOCSUBTYPESO_OnCreditOrder.equals(DocSubTypeSO) + || MDocType.DOCSUBTYPESO_PrepayOrder.equals(DocSubTypeSO)) + { + MInvoice invoice = createInvoice (dt, shipment, realTimePOS ? null : getDateOrdered()); + if (invoice == null) + return DocAction.STATUS_Invalid; + info.append(" - @C_Invoice_ID@: ").append(invoice.getDocumentNo()); + String msg = invoice.getProcessMsg(); + if (msg != null && msg.length() > 0) + info.append(" (").append(msg).append(")"); + } // Invoice + + // Counter Documents + MOrder counter = createCounterDoc(); + if (counter != null) + info.append(" - @CounterDoc@: @Order@=").append(counter.getDocumentNo()); + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + if (info.length() > 0) + info.append(" - "); + info.append(valid); + m_processMsg = info.toString(); + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + setProcessed(true); + m_processMsg = info.toString(); + // + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setDateOrdered(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Create Shipment + * @param dt order document type + * @param movementDate optional movement date (default today) + * @return shipment or null + */ + private MInOut createShipment(MDocType dt, Timestamp movementDate) + { + log.info("For " + dt); + MInOut shipment = new MInOut (this, dt.getC_DocTypeShipment_ID(), movementDate); + // shipment.setDateAcct(getDateAcct()); + if (!shipment.save(get_TrxName())) + { + m_processMsg = "Could not create Shipment"; + return null; + } + // + MOrderLine[] oLines = getLines(true, null); + for (int i = 0; i < oLines.length; i++) + { + MOrderLine oLine = oLines[i]; + // + MInOutLine ioLine = new MInOutLine(shipment); + // Qty = Ordered - Delivered + BigDecimal MovementQty = oLine.getQtyOrdered().subtract(oLine.getQtyDelivered()); + // Location + int M_Locator_ID = MStorage.getM_Locator_ID (oLine.getM_Warehouse_ID(), + oLine.getM_Product_ID(), oLine.getM_AttributeSetInstance_ID(), + MovementQty, get_TrxName()); + if (M_Locator_ID == 0) // Get default Location + { + MWarehouse wh = MWarehouse.get(getCtx(), oLine.getM_Warehouse_ID()); + M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); + } + // + ioLine.setOrderLine(oLine, M_Locator_ID, MovementQty); + ioLine.setQty(MovementQty); + if (oLine.getQtyEntered().compareTo(oLine.getQtyOrdered()) != 0) + ioLine.setQtyEntered(MovementQty + .multiply(oLine.getQtyEntered()) + .divide(oLine.getQtyOrdered(), 6, BigDecimal.ROUND_HALF_UP)); + if (!ioLine.save(get_TrxName())) + { + m_processMsg = "Could not create Shipment Line"; + return null; + } + } + // Manually Process Shipment + String status = shipment.completeIt(); + shipment.setDocStatus(status); + shipment.save(get_TrxName()); + if (!DOCSTATUS_Completed.equals(status)) + { + m_processMsg = "@M_InOut_ID@: " + shipment.getProcessMsg(); + return null; + } + return shipment; + } // createShipment + + /** + * Create Invoice + * @param dt order document type + * @param shipment optional shipment + * @param invoiceDate invoice date + * @return invoice or null + */ + private MInvoice createInvoice (MDocType dt, MInOut shipment, Timestamp invoiceDate) + { + log.info(dt.toString()); + MInvoice invoice = new MInvoice (this, dt.getC_DocTypeInvoice_ID(), invoiceDate); + if (!invoice.save(get_TrxName())) + { + m_processMsg = "Could not create Invoice"; + return null; + } + + // If we have a Shipment - use that as a base + if (shipment != null) + { + if (!INVOICERULE_AfterDelivery.equals(getInvoiceRule())) + setInvoiceRule(INVOICERULE_AfterDelivery); + // + MInOutLine[] sLines = shipment.getLines(false); + for (int i = 0; i < sLines.length; i++) + { + MInOutLine sLine = sLines[i]; + // + MInvoiceLine iLine = new MInvoiceLine(invoice); + iLine.setShipLine(sLine); + // Qty = Delivered + iLine.setQtyEntered(sLine.getQtyEntered()); + iLine.setQtyInvoiced(sLine.getMovementQty()); + if (!iLine.save(get_TrxName())) + { + m_processMsg = "Could not create Invoice Line from Shipment Line"; + return null; + } + // + sLine.setIsInvoiced(true); + if (!sLine.save(get_TrxName())) + { + log.warning("Could not update Shipment line: " + sLine); + } + } + } + else // Create Invoice from Order + { + if (!INVOICERULE_Immediate.equals(getInvoiceRule())) + setInvoiceRule(INVOICERULE_Immediate); + // + MOrderLine[] oLines = getLines(); + for (int i = 0; i < oLines.length; i++) + { + MOrderLine oLine = oLines[i]; + // + MInvoiceLine iLine = new MInvoiceLine(invoice); + iLine.setOrderLine(oLine); + // Qty = Ordered - Invoiced + iLine.setQtyInvoiced(oLine.getQtyOrdered().subtract(oLine.getQtyInvoiced())); + if (oLine.getQtyOrdered().compareTo(oLine.getQtyEntered()) == 0) + iLine.setQtyEntered(iLine.getQtyInvoiced()); + else + iLine.setQtyEntered(iLine.getQtyInvoiced().multiply(oLine.getQtyEntered()) + .divide(oLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + if (!iLine.save(get_TrxName())) + { + m_processMsg = "Could not create Invoice Line from Order Line"; + return null; + } + } + } + // Manually Process Invoice + String status = invoice.completeIt(); + invoice.setDocStatus(status); + invoice.save(get_TrxName()); + setC_CashLine_ID(invoice.getC_CashLine_ID()); + if (!DOCSTATUS_Completed.equals(status)) + { + m_processMsg = "@C_Invoice_ID@: " + invoice.getProcessMsg(); + return null; + } + return invoice; + } // createInvoice + + /** + * Create Counter Document + * @return counter order + */ + private MOrder createCounterDoc() + { + // Is this itself a counter doc ? + if (getRef_Order_ID() != 0) + return null; + + // Org Must be linked to BPartner + MOrg org = MOrg.get(getCtx(), getAD_Org_ID()); + int counterC_BPartner_ID = org.getLinkedC_BPartner_ID(get_TrxName()); + if (counterC_BPartner_ID == 0) + return null; + // Business Partner needs to be linked to Org + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + int counterAD_Org_ID = bp.getAD_OrgBP_ID_Int(); + if (counterAD_Org_ID == 0) + return null; + + MBPartner counterBP = new MBPartner (getCtx(), counterC_BPartner_ID, null); + MOrgInfo counterOrgInfo = MOrgInfo.get(getCtx(), counterAD_Org_ID); + log.info("Counter BP=" + counterBP.getName()); + + // Document Type + int C_DocTypeTarget_ID = 0; + MDocTypeCounter counterDT = MDocTypeCounter.getCounterDocType(getCtx(), getC_DocType_ID()); + if (counterDT != null) + { + log.fine(counterDT.toString()); + if (!counterDT.isCreateCounter() || !counterDT.isValid()) + return null; + C_DocTypeTarget_ID = counterDT.getCounter_C_DocType_ID(); + } + else // indirect + { + C_DocTypeTarget_ID = MDocTypeCounter.getCounterDocType_ID(getCtx(), getC_DocType_ID()); + log.fine("Indirect C_DocTypeTarget_ID=" + C_DocTypeTarget_ID); + if (C_DocTypeTarget_ID <= 0) + return null; + } + // Deep Copy + MOrder counter = copyFrom (this, getDateOrdered(), + C_DocTypeTarget_ID, !isSOTrx(), true, false, get_TrxName()); + // + counter.setAD_Org_ID(counterAD_Org_ID); + counter.setM_Warehouse_ID(counterOrgInfo.getM_Warehouse_ID()); + // + counter.setBPartner(counterBP); + counter.setDatePromised(getDatePromised()); // default is date ordered + // Refernces (Should not be required + counter.setSalesRep_ID(getSalesRep_ID()); + counter.save(get_TrxName()); + + // Update copied lines + MOrderLine[] counterLines = counter.getLines(true, null); + for (int i = 0; i < counterLines.length; i++) + { + MOrderLine counterLine = counterLines[i]; + counterLine.setOrder(counter); // copies header values (BP, etc.) + counterLine.setPrice(); + counterLine.setTax(); + counterLine.save(get_TrxName()); + } + log.fine(counter.toString()); + + // Document Action + if (counterDT != null) + { + if (counterDT.getDocAction() != null) + { + counter.setDocAction(counterDT.getDocAction()); + counter.processIt(counterDT.getDocAction()); + counter.save(get_TrxName()); + } + } + return counter; + } // createCounterDoc + + /** + * Void Document. + * Set Qtys to 0 - Sales: reverse all documents + * @return true if success + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + MOrderLine[] lines = getLines(true, "M_Product_ID"); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + BigDecimal old = line.getQtyOrdered(); + if (old.signum() != 0) + { + line.addDescription(Msg.getMsg(getCtx(), "Voided") + " (" + old + ")"); + line.setQty(Env.ZERO); + line.setLineNetAmt(Env.ZERO); + line.save(get_TrxName()); + } + } + addDescription(Msg.getMsg(getCtx(), "Voided")); + // Clear Reservations + if (!reserveStock(null, lines)) + { + m_processMsg = "Cannot unreserve Stock (void)"; + return false; + } + + if (!createReversals()) + return false; + + //MZ Goodwill + if (!isSOTrx()) + { + // delete Matched PO Cost Detail + MOrderLine[] linesMZ = getLines(); + for (int i = 0; i < lines.length; i++) + { + MMatchPO[] mPO = MMatchPO.getOrderLine(getCtx(), linesMZ[i].getC_OrderLine_ID(), get_TrxName()); + // delete Cost Detail if the Matched PO has been deleted + if (mPO.length == 0) + { + MCostDetail cd = MCostDetail.get(getCtx(), "C_OrderLine_ID=? AND M_AttributeSetInstance_ID=?", + linesMZ[i].getC_OrderLine_ID(), linesMZ[i].getM_AttributeSetInstance_ID(), get_TrxName()); + if (cd != null) + { + cd.setProcessed(false); + cd.delete(true); + } + } + } + } + //End MZ + + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // voidIt + + /** + * Create Shipment/Invoice Reversals + * @return true if success + */ + private boolean createReversals() + { + // Cancel only Sales + if (!isSOTrx()) + return true; + + log.info("createReversals"); + StringBuffer info = new StringBuffer(); + + // Reverse All *Shipments* + info.append("@M_InOut_ID@:"); + MInOut[] shipments = getShipments(); + for (int i = 0; i < shipments.length; i++) + { + MInOut ship = shipments[i]; + // if closed - ignore + if (MInOut.DOCSTATUS_Closed.equals(ship.getDocStatus()) + || MInOut.DOCSTATUS_Reversed.equals(ship.getDocStatus()) + || MInOut.DOCSTATUS_Voided.equals(ship.getDocStatus()) ) + continue; + ship.set_TrxName(get_TrxName()); + + // If not completed - void - otherwise reverse it + if (!MInOut.DOCSTATUS_Completed.equals(ship.getDocStatus())) + { + if (ship.voidIt()) + ship.setDocStatus(MInOut.DOCSTATUS_Voided); + } + else if (ship.reverseCorrectIt()) // completed shipment + { + ship.setDocStatus(MInOut.DOCSTATUS_Reversed); + info.append(" ").append(ship.getDocumentNo()); + } + else + { + m_processMsg = "Could not reverse Shipment " + ship; + return false; + } + ship.setDocAction(MInOut.DOCACTION_None); + ship.save(get_TrxName()); + } // for all shipments + + // Reverse All *Invoices* + info.append(" - @C_Invoice_ID@:"); + MInvoice[] invoices = getInvoices(); + for (int i = 0; i < invoices.length; i++) + { + MInvoice invoice = invoices[i]; + // if closed - ignore + if (MInvoice.DOCSTATUS_Closed.equals(invoice.getDocStatus()) + || MInvoice.DOCSTATUS_Reversed.equals(invoice.getDocStatus()) + || MInvoice.DOCSTATUS_Voided.equals(invoice.getDocStatus()) ) + continue; + invoice.set_TrxName(get_TrxName()); + + // If not completed - void - otherwise reverse it + if (!MInvoice.DOCSTATUS_Completed.equals(invoice.getDocStatus())) + { + if (invoice.voidIt()) + invoice.setDocStatus(MInvoice.DOCSTATUS_Voided); + } + else if (invoice.reverseCorrectIt()) // completed invoice + { + invoice.setDocStatus(MInvoice.DOCSTATUS_Reversed); + info.append(" ").append(invoice.getDocumentNo()); + } + else + { + m_processMsg = "Could not reverse Invoice " + invoice; + return false; + } + invoice.setDocAction(MInvoice.DOCACTION_None); + invoice.save(get_TrxName()); + } // for all shipments + + m_processMsg = info.toString(); + return true; + } // createReversals + + + /** + * Close Document. + * Cancel not delivered Qunatities + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + + // Close Not delivered Qty - SO/PO + MOrderLine[] lines = getLines(true, "M_Product_ID"); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + BigDecimal old = line.getQtyOrdered(); + if (old.compareTo(line.getQtyDelivered()) != 0) + { + line.setQtyLostSales(line.getQtyOrdered().subtract(line.getQtyDelivered())); + line.setQtyOrdered(line.getQtyDelivered()); + // QtyEntered unchanged + line.addDescription("Close (" + old + ")"); + line.save(get_TrxName()); + } + } + // Clear Reservations + if (!reserveStock(null, lines)) + { + m_processMsg = "Cannot unreserve Stock (close)"; + return false; + } + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // closeIt + + /** + * @author: phib + * re-open a closed order + * (reverse steps of close()) + */ + public String reopenIt() { + log.info(toString()); + if (!MOrder.DOCSTATUS_Closed.equals(getDocStatus())) + { + return "Not closed - can't reopen"; + } + + // + MOrderLine[] lines = getLines(true, "M_Product_ID"); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + if (Env.ZERO.compareTo(line.getQtyLostSales()) != 0) + { + line.setQtyOrdered(line.getQtyLostSales().add(line.getQtyDelivered())); + line.setQtyLostSales(Env.ZERO); + // QtyEntered unchanged + + // Strip Close() tags from description + String desc = line.getDescription(); + if (desc == null) + desc = ""; + Pattern pattern = Pattern.compile("( \\| )?Close \\(.*\\)"); + String[] parts = pattern.split(desc); + desc = ""; + for (String s : parts) { + desc = desc.concat(s); + } + line.setDescription(desc); + if (!line.save(get_TrxName())) + return "Couldn't save orderline"; + } + } + // Clear Reservations + if (!reserveStock(null, lines)) + { + m_processMsg = "Cannot unreserve Stock (close)"; + return "Failed to update reservations"; + } + + setDocStatus(MOrder.DOCSTATUS_Completed); + setDocAction(DOCACTION_Close); + if (!this.save(get_TrxName())) + return "Couldn't save reopened order"; + else + return ""; + } // reopenIt + /** + * Reverse Correction - same void + * @return true if success + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + return voidIt(); + } // reverseCorrectionIt + + /** + * Reverse Accrual - none + * @return false + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return false; + } // reverseAccrualIt + + /** + * Re-activate. + * @return true if success + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + + + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + String DocSubTypeSO = dt.getDocSubTypeSO(); + + // Replace Prepay with POS to revert all doc + if (MDocType.DOCSUBTYPESO_PrepayOrder.equals (DocSubTypeSO)) + { + MDocType newDT = null; + MDocType[] dts = MDocType.getOfClient (getCtx()); + for (int i = 0; i < dts.length; i++) + { + MDocType type = dts[i]; + if (MDocType.DOCSUBTYPESO_PrepayOrder.equals(type.getDocSubTypeSO())) + { + if (type.isDefault() || newDT == null) + newDT = type; + } + } + if (newDT == null) + return false; + else + setC_DocType_ID (newDT.getC_DocType_ID()); + } + + // PO - just re-open + if (!isSOTrx()) + log.info("Existing documents not modified - " + dt); + // Reverse Direct Documents + else if (MDocType.DOCSUBTYPESO_OnCreditOrder.equals(DocSubTypeSO) // (W)illCall(I)nvoice + || MDocType.DOCSUBTYPESO_WarehouseOrder.equals(DocSubTypeSO) // (W)illCall(P)ickup + || MDocType.DOCSUBTYPESO_POSOrder.equals(DocSubTypeSO)) // (W)alkIn(R)eceipt + { + if (!createReversals()) + return false; + } + else + { + log.info("Existing documents not modified - SubType=" + DocSubTypeSO); + } + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + setDocAction(DOCACTION_Complete); + setProcessed(false); + return true; + } // reActivateIt + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Grand Total = 123.00 (#1) + sb.append(": "). + append(Msg.translate(getCtx(),"GrandTotal")).append("=").append(getGrandTotal()); + if (m_lines != null) + sb.append(" (#").append(m_lines.length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getSalesRep_ID(); + } // getDoc_User_ID + + /** + * Get Document Approval Amount + * @return amount + */ + public BigDecimal getApprovalAmt() + { + return getGrandTotal(); + } // getApprovalAmt + +} // MOrder diff --git a/base/src/org/compiere/process/InOutGenerate.java b/base/src/org/compiere/process/InOutGenerate.java index 1827cef410..d49e5352fe 100644 --- a/base/src/org/compiere/process/InOutGenerate.java +++ b/base/src/org/compiere/process/InOutGenerate.java @@ -1,684 +1,684 @@ -/****************************************************************************** - * 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.process; - -import java.math.*; -import java.sql.*; -import java.util.*; -import java.util.logging.*; - -import org.compiere.model.*; -import org.compiere.util.*; - -/** - * Generate Shipments. - * Manual or Automatic - * - * @author Jorg Janke - * @version $Id: InOutGenerate.java,v 1.2 2006/07/30 00:51:01 jjanke Exp $ - */ -public class InOutGenerate extends SvrProcess -{ - /** Manual Selection */ - private boolean p_Selection = false; - /** Warehouse */ - private int p_M_Warehouse_ID = 0; - /** BPartner */ - private int p_C_BPartner_ID = 0; - /** Promise Date */ - private Timestamp p_DatePromised = null; - /** Include Orders w. unconfirmed Shipments */ - private boolean p_IsUnconfirmedInOut = false; - /** DocAction */ - private String p_docAction = DocAction.ACTION_Complete; - /** Consolidate */ - private boolean p_ConsolidateDocument = true; - /** Shipment Date */ - private Timestamp p_DateShipped = null; - - /** The current Shipment */ - private MInOut m_shipment = null; - /** Numner of Shipments */ - private int m_created = 0; - /** Line Number */ - private int m_line = 0; - /** Movement Date */ - private Timestamp m_movementDate = null; - /** Last BP Location */ - private int m_lastC_BPartner_Location_ID = -1; - - /** The Query sql */ - private String m_sql = null; - - - /** Storages temp space */ - private HashMap m_map = new HashMap(); - /** Last Parameter */ - private SParameter m_lastPP = null; - /** Last Storage */ - private MStorage[] m_lastStorages = null; - - - /** - * Prepare - e.g., get Parameters. - */ - protected void prepare() - { - ProcessInfoParameter[] para = getParameter(); - for (int i = 0; i < para.length; i++) - { - String name = para[i].getParameterName(); - if (para[i].getParameter() == null) - ; - else if (name.equals("M_Warehouse_ID")) - p_M_Warehouse_ID = para[i].getParameterAsInt(); - else if (name.equals("C_BPartner_ID")) - p_C_BPartner_ID = para[i].getParameterAsInt(); - else if (name.equals("DatePromised")) - p_DatePromised = (Timestamp)para[i].getParameter(); - else if (name.equals("Selection")) - p_Selection = "Y".equals(para[i].getParameter()); - else if (name.equals("IsUnconfirmedInOut")) - p_IsUnconfirmedInOut = "Y".equals(para[i].getParameter()); - else if (name.equals("ConsolidateDocument")) - p_ConsolidateDocument = "Y".equals(para[i].getParameter()); - else if (name.equals("DocAction")) - p_docAction = (String)para[i].getParameter(); - else if (name.equals("MovementDate")) - p_DateShipped = (Timestamp)para[i].getParameter(); - else - log.log(Level.SEVERE, "Unknown Parameter: " + name); - - // juddm - added ability to specify a shipment date from Generate Shipments - if (p_DateShipped == null) { - m_movementDate = Env.getContextAsDate(getCtx(), "#Date"); - if (m_movementDate == null) - m_movementDate = new Timestamp(System.currentTimeMillis()); - } else - m_movementDate = p_DateShipped; - // DocAction check - if (!DocAction.ACTION_Complete.equals(p_docAction)) - p_docAction = DocAction.ACTION_Prepare; - } - } // prepare - - /** - * Generate Shipments - * @return info - * @throws Exception - */ - protected String doIt () throws Exception - { - log.info("Selection=" + p_Selection - + ", M_Warehouse_ID=" + p_M_Warehouse_ID - + ", C_BPartner_ID=" + p_C_BPartner_ID - + ", Consolidate=" + p_ConsolidateDocument - + ", IsUnconfirmed=" + p_IsUnconfirmedInOut - + ", Movement=" + m_movementDate); - - if (p_M_Warehouse_ID == 0) - throw new AdempiereUserError("@NotFound@ @M_Warehouse_ID@"); - - if (p_Selection) // VInOutGen - { - m_sql = "SELECT C_Order.* FROM C_Order, T_Selection " - + "WHERE C_Order.DocStatus='CO' AND C_Order.IsSOTrx='Y' AND C_Order.AD_Client_ID=? " - + "AND C_Order.C_Order_ID = T_Selection.T_Selection_ID " - + "AND T_Selection.AD_PInstance_ID=? "; - } - else - { - m_sql = "SELECT * FROM C_Order o " - + "WHERE DocStatus='CO' AND IsSOTrx='Y'" - // No Offer,POS - + " AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType " - + "WHERE DocBaseType='SOO' AND DocSubTypeSO NOT IN ('ON','OB','WR'))" - + " AND o.IsDropShip='N'" - // No Manual - + " AND o.DeliveryRule<>'M'" - // Open Order Lines with Warehouse - + " AND EXISTS (SELECT * FROM C_OrderLine ol " - + "WHERE ol.M_Warehouse_ID=?"; // #1 - if (p_DatePromised != null) - m_sql += " AND TRUNC(ol.DatePromised)<=?"; // #2 - m_sql += " AND o.C_Order_ID=ol.C_Order_ID AND ol.QtyOrdered<>ol.QtyDelivered)"; - // - if (p_C_BPartner_ID != 0) - m_sql += " AND o.C_BPartner_ID=?"; // #3 - } - m_sql += " ORDER BY M_Warehouse_ID, PriorityRule, M_Shipper_ID, C_BPartner_ID, C_BPartner_Location_ID, C_Order_ID"; - // m_sql += " FOR UPDATE"; - - PreparedStatement pstmt = null; - try - { - pstmt = DB.prepareStatement (m_sql, get_TrxName()); - int index = 1; - if (p_Selection) - { - pstmt.setInt(index++, Env.getAD_Client_ID(getCtx())); - pstmt.setInt(index++, getAD_PInstance_ID()); - } - else - { - pstmt.setInt(index++, p_M_Warehouse_ID); - if (p_DatePromised != null) - pstmt.setTimestamp(index++, p_DatePromised); - if (p_C_BPartner_ID != 0) - pstmt.setInt(index++, p_C_BPartner_ID); - } - } - catch (Exception e) - { - log.log(Level.SEVERE, m_sql, e); - } - return generate(pstmt); - } // doIt - - /** - * Generate Shipments - * @param pstmt Order Query - * @return info - */ - private String generate (PreparedStatement pstmt) - { - MClient client = MClient.get(getCtx()); - try - { - ResultSet rs = pstmt.executeQuery (); - while (rs.next ()) // Order - { - MOrder order = new MOrder (getCtx(), rs, get_TrxName()); - // New Header different Shipper, Shipment Location - if (!p_ConsolidateDocument - || (m_shipment != null - && (m_shipment.getC_BPartner_Location_ID() != order.getC_BPartner_Location_ID() - || m_shipment.getM_Shipper_ID() != order.getM_Shipper_ID() ))) - completeShipment(); - log.fine("check: " + order + " - DeliveryRule=" + order.getDeliveryRule()); - // - Timestamp minGuaranteeDate = m_movementDate; - boolean completeOrder = MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule()); - // OrderLine WHERE - String where = " AND M_Warehouse_ID=" + p_M_Warehouse_ID; - if (p_DatePromised != null) - where += " AND (TRUNC(DatePromised)<=" + DB.TO_DATE(p_DatePromised, true) - + " OR DatePromised IS NULL)"; - // Exclude Auto Delivery if not Force - if (!MOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) - where += " AND (C_OrderLine.M_Product_ID IS NULL" - + " OR EXISTS (SELECT * FROM M_Product p " - + "WHERE C_OrderLine.M_Product_ID=p.M_Product_ID" - + " AND IsExcludeAutoDelivery='N'))"; - // Exclude Unconfirmed - if (!p_IsUnconfirmedInOut) - where += " AND NOT EXISTS (SELECT * FROM M_InOutLine iol" - + " INNER JOIN M_InOut io ON (iol.M_InOut_ID=io.M_InOut_ID) " - + "WHERE iol.C_OrderLine_ID=C_OrderLine.C_OrderLine_ID AND io.DocStatus IN ('IP','WC'))"; - // Deadlock Prevention - Order by M_Product_ID - MOrderLine[] lines = order.getLines (where, "ORDER BY C_BPartner_Location_ID, M_Product_ID"); - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - if (line.getM_Warehouse_ID() != p_M_Warehouse_ID) - continue; - log.fine("check: " + line); - BigDecimal onHand = Env.ZERO; - BigDecimal toDeliver = line.getQtyOrdered() - .subtract(line.getQtyDelivered()); - MProduct product = line.getProduct(); - // Nothing to Deliver - if (product != null && toDeliver.signum() == 0) - continue; - - // or it's a charge - Bug#: 1603966 - if (line.getC_Charge_ID()!=0 && toDeliver.signum() == 0) - continue; - - // Check / adjust for confirmations - BigDecimal unconfirmedShippedQty = Env.ZERO; - if (p_IsUnconfirmedInOut && product != null && toDeliver.signum() != 0) - { - String where2 = "EXISTS (SELECT * FROM M_InOut io WHERE io.M_InOut_ID=M_InOutLine.M_InOut_ID AND io.DocStatus IN ('IP','WC'))"; - MInOutLine[] iols = MInOutLine.getOfOrderLine(getCtx(), - line.getC_OrderLine_ID(), where2, null); - for (int j = 0; j < iols.length; j++) - unconfirmedShippedQty = unconfirmedShippedQty.add(iols[j].getMovementQty()); - String logInfo = "Unconfirmed Qty=" + unconfirmedShippedQty - + " - ToDeliver=" + toDeliver + "->"; - toDeliver = toDeliver.subtract(unconfirmedShippedQty); - logInfo += toDeliver; - if (toDeliver.signum() < 0) - { - toDeliver = Env.ZERO; - logInfo += " (set to 0)"; - } - // Adjust On Hand - onHand = onHand.subtract(unconfirmedShippedQty); - log.fine(logInfo); - } - - // Comments & lines w/o product & services - if ((product == null || !product.isStocked()) - && (line.getQtyOrdered().signum() == 0 // comments - || toDeliver.signum() != 0)) // lines w/o product - { - if (!MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) // printed later - createLine (order, line, toDeliver, null, false); - continue; - } - - // Stored Product - String MMPolicy = product.getMMPolicy(); - MStorage[] storages = getStorages(line.getM_Warehouse_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - product.getM_AttributeSet_ID(), - line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, - MClient.MMPOLICY_FiFo.equals(MMPolicy)); - - for (int j = 0; j < storages.length; j++) - { - MStorage storage = storages[j]; - onHand = onHand.add(storage.getQtyOnHand()); - } - boolean fullLine = onHand.compareTo(toDeliver) >= 0 - || toDeliver.signum() < 0; - - // Complete Order - if (completeOrder && !fullLine) - { - log.fine("Failed CompleteOrder - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver + " - " + line); - completeOrder = false; - break; - } - // Complete Line - else if (fullLine && MOrder.DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule())) - { - log.fine("CompleteLine - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + ", ToDeliver=" + toDeliver + " - " + line); - // - createLine (order, line, toDeliver, storages, false); - } - // Availability - else if (MOrder.DELIVERYRULE_Availability.equals(order.getDeliveryRule()) - && (onHand.signum() > 0 - || toDeliver.signum() < 0)) - { - BigDecimal deliver = toDeliver; - if (deliver.compareTo(onHand) > 0) - deliver = onHand; - log.fine("Available - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver - + ", Delivering=" + deliver + " - " + line); - // - createLine (order, line, deliver, storages, false); - } - // Force - else if (MOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) - { - BigDecimal deliver = toDeliver; - log.fine("Force - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver - + ", Delivering=" + deliver + " - " + line); - // - createLine (order, line, deliver, storages, true); - } - // Manual - else if (MOrder.DELIVERYRULE_Manual.equals(order.getDeliveryRule())) - log.fine("Manual - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + ") - " + line); - else - log.fine("Failed: " + order.getDeliveryRule() + " - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver + " - " + line); - } // for all order lines - - // Complete Order successful - if (completeOrder && MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) - { - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - if (line.getM_Warehouse_ID() != p_M_Warehouse_ID) - continue; - MProduct product = line.getProduct(); - BigDecimal toDeliver = line.getQtyOrdered().subtract(line.getQtyDelivered()); - // - MStorage[] storages = null; - if (product != null && product.isStocked()) - { - String MMPolicy = product.getMMPolicy(); - storages = getStorages(line.getM_Warehouse_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - product.getM_AttributeSet_ID(), - line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, - MClient.MMPOLICY_FiFo.equals(MMPolicy)); - } - // - createLine (order, line, toDeliver, storages, false); - } - } - m_line += 1000; - } // while order - rs.close (); - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - log.log(Level.SEVERE, m_sql, e); - } - try - { - if (pstmt != null) - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - pstmt = null; - } - completeShipment(); - return "@Created@ = " + m_created; - } // generate - - - - /************************************************************************** - * Create Line - * @param order order - * @param orderLine line - * @param qty qty - * @param storages storage info - * @param force force delivery - */ - private void createLine (MOrder order, MOrderLine orderLine, BigDecimal qty, - MStorage[] storages, boolean force) - { - // Complete last Shipment - can have multiple shipments - if (m_lastC_BPartner_Location_ID != orderLine.getC_BPartner_Location_ID() ) - completeShipment(); - m_lastC_BPartner_Location_ID = orderLine.getC_BPartner_Location_ID(); - // Create New Shipment - if (m_shipment == null) - { - m_shipment = new MInOut (order, 0, m_movementDate); - m_shipment.setM_Warehouse_ID(orderLine.getM_Warehouse_ID()); // sets Org too - if (order.getC_BPartner_ID() != orderLine.getC_BPartner_ID()) - m_shipment.setC_BPartner_ID(orderLine.getC_BPartner_ID()); - if (order.getC_BPartner_Location_ID() != orderLine.getC_BPartner_Location_ID()) - m_shipment.setC_BPartner_Location_ID(orderLine.getC_BPartner_Location_ID()); - if (!m_shipment.save()) - throw new IllegalStateException("Could not create Shipment"); - } - // Non Inventory Lines - if (storages == null) - { - MInOutLine line = new MInOutLine (m_shipment); - line.setOrderLine(orderLine, 0, Env.ZERO); - line.setQty(qty); // Correct UOM - if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) - line.setQtyEntered(qty - .multiply(orderLine.getQtyEntered()) - .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); - line.setLine(m_line + orderLine.getLine()); - if (!line.save()) - throw new IllegalStateException("Could not create Shipment Line"); - log.fine(line.toString()); - return; - } - - // Product - MProduct product = orderLine.getProduct(); - boolean linePerASI = false; - if (product.getM_AttributeSet_ID() != 0) - { - MAttributeSet mas = MAttributeSet.get(getCtx(), product.getM_AttributeSet_ID()); - linePerASI = mas.isInstanceAttribute(); - } - - // Inventory Lines - ArrayList list = new ArrayList(); - BigDecimal toDeliver = qty; - for (int i = 0; i < storages.length; i++) - { - MStorage storage = storages[i]; - BigDecimal deliver = toDeliver; - // Not enough On Hand - if (deliver.compareTo(storage.getQtyOnHand()) > 0 - && storage.getQtyOnHand().signum() >= 0) // positive storage - { - if (!force // Adjust to OnHand Qty - || (force && i+1 != storages.length)) // if force not on last location - deliver = storage.getQtyOnHand(); - } - if (deliver.signum() == 0) // zero deliver - continue; - int M_Locator_ID = storage.getM_Locator_ID(); - // - MInOutLine line = null; - if (!linePerASI) // find line with Locator - { - for (int ll = 0; ll < list.size(); ll++) - { - MInOutLine test = (MInOutLine)list.get(ll); - if (test.getM_Locator_ID() == M_Locator_ID) - { - line = test; - break; - } - } - } - if (line == null) // new line - { - line = new MInOutLine (m_shipment); - line.setOrderLine(orderLine, M_Locator_ID, order.isSOTrx() ? deliver : Env.ZERO); - line.setQty(deliver); - list.add(line); - } - else // existing line - line.setQty(line.getMovementQty().add(deliver)); - if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) - line.setQtyEntered(line.getMovementQty().multiply(orderLine.getQtyEntered()) - .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); - line.setLine(m_line + orderLine.getLine()); - if (linePerASI) - line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); - if (!line.save()) - throw new IllegalStateException("Could not create Shipment Line"); - log.fine("ToDeliver=" + qty + "/" + deliver + " - " + line); - toDeliver = toDeliver.subtract(deliver); - // Temp adjustment - storage.setQtyOnHand(storage.getQtyOnHand().subtract(deliver)); - // - if (toDeliver.signum() == 0) - break; - } - if (toDeliver.signum() != 0) - throw new IllegalStateException("Not All Delivered - Remainder=" + toDeliver); - } // createLine - - - /** - * Get Storages - * @param M_Warehouse_ID - * @param M_Product_ID - * @param M_AttributeSetInstance_ID - * @param M_AttributeSet_ID - * @param allAttributeInstances - * @param minGuaranteeDate - * @param FiFo - * @return storages - */ - private MStorage[] getStorages(int M_Warehouse_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, int M_AttributeSet_ID, - boolean allAttributeInstances, Timestamp minGuaranteeDate, - boolean FiFo) - { - m_lastPP = new SParameter(M_Warehouse_ID, - M_Product_ID, M_AttributeSetInstance_ID, M_AttributeSet_ID, - allAttributeInstances, minGuaranteeDate, FiFo); - // - m_lastStorages = m_map.get(m_lastPP); - - if (m_lastStorages == null) - { - m_lastStorages = MStorage.getWarehouse(getCtx(), - M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, - M_AttributeSet_ID, allAttributeInstances, minGuaranteeDate, - FiFo, get_TrxName()); - m_map.put(m_lastPP, m_lastStorages); - } - return m_lastStorages; - } // getStorages - - - /** - * Complete Shipment - */ - private void completeShipment() - { - if (m_shipment != null) - { - // Fails if there is a confirmation - if (!m_shipment.processIt(p_docAction)) - log.warning("Failed: " + m_shipment); - m_shipment.save(); - // - addLog(m_shipment.getM_InOut_ID(), m_shipment.getMovementDate(), null, m_shipment.getDocumentNo()); - m_created++; - m_map = new HashMap(); - if (m_lastPP != null && m_lastStorages != null) - m_map.put(m_lastPP, m_lastStorages); - } - m_shipment = null; - m_line = 0; - } // completeOrder - - /** - * InOutGenerate Parameter - */ - class SParameter - { - /** - * Parameter - * @param p_Warehouse_ID warehouse - * @param p_Product_ID - * @param p_AttributeSetInstance_ID - * @param p_AttributeSet_ID - * @param p_allAttributeInstances - * @param p_minGuaranteeDate - * @param p_FiFo - */ - protected SParameter (int p_Warehouse_ID, - int p_Product_ID, int p_AttributeSetInstance_ID, int p_AttributeSet_ID, - boolean p_allAttributeInstances, Timestamp p_minGuaranteeDate, - boolean p_FiFo) - { - this.M_Warehouse_ID = p_Warehouse_ID; - this.M_Product_ID = p_Product_ID; - this.M_AttributeSetInstance_ID = p_AttributeSetInstance_ID; - this.M_AttributeSet_ID = p_AttributeSet_ID; - this.allAttributeInstances = p_allAttributeInstances; - this.minGuaranteeDate = p_minGuaranteeDate; - this.FiFo = p_FiFo; - } - /** Warehouse */ - public int M_Warehouse_ID; - /** Product */ - public int M_Product_ID; - /** ASI */ - public int M_AttributeSetInstance_ID; - /** AS */ - public int M_AttributeSet_ID; - /** All instances */ - public boolean allAttributeInstances; - /** Mon Guarantee Date */ - public Timestamp minGuaranteeDate; - /** FiFo */ - public boolean FiFo; - - /** - * Equals - * @param obj - * @return true if equal - */ - public boolean equals (Object obj) - { - if (obj != null && obj instanceof SParameter) - { - SParameter cmp = (SParameter)obj; - boolean eq = cmp.M_Warehouse_ID == M_Warehouse_ID - && cmp.M_Product_ID == M_Product_ID - && cmp.M_AttributeSetInstance_ID == M_AttributeSetInstance_ID - && cmp.M_AttributeSet_ID == M_AttributeSet_ID - && cmp.allAttributeInstances == allAttributeInstances - && cmp.FiFo == FiFo; - if (eq) - { - if (cmp.minGuaranteeDate == null && minGuaranteeDate == null) - ; - else if (cmp.minGuaranteeDate != null && minGuaranteeDate != null - && cmp.minGuaranteeDate.equals(minGuaranteeDate)) - ; - else - eq = false; - } - return eq; - } - return false; - } // equals - - /** - * hashCode - * @return hash code - */ - public int hashCode () - { - long hash = M_Warehouse_ID - + (M_Product_ID * 2) - + (M_AttributeSetInstance_ID * 3) - + (M_AttributeSet_ID * 4); - - if (allAttributeInstances) - hash *= -1; - if (FiFo); - hash *= -2; - if (hash < 0) - hash = -hash + 7; - while (hash > Integer.MAX_VALUE) - hash -= Integer.MAX_VALUE; - // - if (minGuaranteeDate != null) - { - hash += minGuaranteeDate.hashCode(); - while (hash > Integer.MAX_VALUE) - hash -= Integer.MAX_VALUE; - } - - return (int)hash; - } // hashCode - - } // Parameter - -} // InOutGenerate +/****************************************************************************** + * 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.process; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Generate Shipments. + * Manual or Automatic + * + * @author Jorg Janke + * @version $Id: InOutGenerate.java,v 1.2 2006/07/30 00:51:01 jjanke Exp $ + */ +public class InOutGenerate extends SvrProcess +{ + /** Manual Selection */ + private boolean p_Selection = false; + /** Warehouse */ + private int p_M_Warehouse_ID = 0; + /** BPartner */ + private int p_C_BPartner_ID = 0; + /** Promise Date */ + private Timestamp p_DatePromised = null; + /** Include Orders w. unconfirmed Shipments */ + private boolean p_IsUnconfirmedInOut = false; + /** DocAction */ + private String p_docAction = DocAction.ACTION_Complete; + /** Consolidate */ + private boolean p_ConsolidateDocument = true; + /** Shipment Date */ + private Timestamp p_DateShipped = null; + + /** The current Shipment */ + private MInOut m_shipment = null; + /** Numner of Shipments */ + private int m_created = 0; + /** Line Number */ + private int m_line = 0; + /** Movement Date */ + private Timestamp m_movementDate = null; + /** Last BP Location */ + private int m_lastC_BPartner_Location_ID = -1; + + /** The Query sql */ + private String m_sql = null; + + + /** Storages temp space */ + private HashMap m_map = new HashMap(); + /** Last Parameter */ + private SParameter m_lastPP = null; + /** Last Storage */ + private MStorage[] m_lastStorages = null; + + + /** + * Prepare - e.g., get Parameters. + */ + protected void prepare() + { + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) + { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null) + ; + else if (name.equals("M_Warehouse_ID")) + p_M_Warehouse_ID = para[i].getParameterAsInt(); + else if (name.equals("C_BPartner_ID")) + p_C_BPartner_ID = para[i].getParameterAsInt(); + else if (name.equals("DatePromised")) + p_DatePromised = (Timestamp)para[i].getParameter(); + else if (name.equals("Selection")) + p_Selection = "Y".equals(para[i].getParameter()); + else if (name.equals("IsUnconfirmedInOut")) + p_IsUnconfirmedInOut = "Y".equals(para[i].getParameter()); + else if (name.equals("ConsolidateDocument")) + p_ConsolidateDocument = "Y".equals(para[i].getParameter()); + else if (name.equals("DocAction")) + p_docAction = (String)para[i].getParameter(); + else if (name.equals("MovementDate")) + p_DateShipped = (Timestamp)para[i].getParameter(); + else + log.log(Level.SEVERE, "Unknown Parameter: " + name); + + // juddm - added ability to specify a shipment date from Generate Shipments + if (p_DateShipped == null) { + m_movementDate = Env.getContextAsDate(getCtx(), "#Date"); + if (m_movementDate == null) + m_movementDate = new Timestamp(System.currentTimeMillis()); + } else + m_movementDate = p_DateShipped; + // DocAction check + if (!DocAction.ACTION_Complete.equals(p_docAction)) + p_docAction = DocAction.ACTION_Prepare; + } + } // prepare + + /** + * Generate Shipments + * @return info + * @throws Exception + */ + protected String doIt () throws Exception + { + log.info("Selection=" + p_Selection + + ", M_Warehouse_ID=" + p_M_Warehouse_ID + + ", C_BPartner_ID=" + p_C_BPartner_ID + + ", Consolidate=" + p_ConsolidateDocument + + ", IsUnconfirmed=" + p_IsUnconfirmedInOut + + ", Movement=" + m_movementDate); + + if (p_M_Warehouse_ID == 0) + throw new AdempiereUserError("@NotFound@ @M_Warehouse_ID@"); + + if (p_Selection) // VInOutGen + { + m_sql = "SELECT C_Order.* FROM C_Order, T_Selection " + + "WHERE C_Order.DocStatus='CO' AND C_Order.IsSOTrx='Y' AND C_Order.AD_Client_ID=? " + + "AND C_Order.C_Order_ID = T_Selection.T_Selection_ID " + + "AND T_Selection.AD_PInstance_ID=? "; + } + else + { + m_sql = "SELECT * FROM C_Order o " + + "WHERE DocStatus='CO' AND IsSOTrx='Y'" + // No Offer,POS + + " AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType " + + "WHERE DocBaseType='SOO' AND DocSubTypeSO NOT IN ('ON','OB','WR'))" + + " AND o.IsDropShip='N'" + // No Manual + + " AND o.DeliveryRule<>'M'" + // Open Order Lines with Warehouse + + " AND EXISTS (SELECT * FROM C_OrderLine ol " + + "WHERE ol.M_Warehouse_ID=?"; // #1 + if (p_DatePromised != null) + m_sql += " AND TRUNC(ol.DatePromised)<=?"; // #2 + m_sql += " AND o.C_Order_ID=ol.C_Order_ID AND ol.QtyOrdered<>ol.QtyDelivered)"; + // + if (p_C_BPartner_ID != 0) + m_sql += " AND o.C_BPartner_ID=?"; // #3 + } + m_sql += " ORDER BY M_Warehouse_ID, PriorityRule, M_Shipper_ID, C_BPartner_ID, C_BPartner_Location_ID, C_Order_ID"; + // m_sql += " FOR UPDATE"; + + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (m_sql, get_TrxName()); + int index = 1; + if (p_Selection) + { + pstmt.setInt(index++, Env.getAD_Client_ID(getCtx())); + pstmt.setInt(index++, getAD_PInstance_ID()); + } + else + { + pstmt.setInt(index++, p_M_Warehouse_ID); + if (p_DatePromised != null) + pstmt.setTimestamp(index++, p_DatePromised); + if (p_C_BPartner_ID != 0) + pstmt.setInt(index++, p_C_BPartner_ID); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, m_sql, e); + } + return generate(pstmt); + } // doIt + + /** + * Generate Shipments + * @param pstmt Order Query + * @return info + */ + private String generate (PreparedStatement pstmt) + { + MClient client = MClient.get(getCtx()); + try + { + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) // Order + { + MOrder order = new MOrder (getCtx(), rs, get_TrxName()); + // New Header different Shipper, Shipment Location + if (!p_ConsolidateDocument + || (m_shipment != null + && (m_shipment.getC_BPartner_Location_ID() != order.getC_BPartner_Location_ID() + || m_shipment.getM_Shipper_ID() != order.getM_Shipper_ID() ))) + completeShipment(); + log.fine("check: " + order + " - DeliveryRule=" + order.getDeliveryRule()); + // + Timestamp minGuaranteeDate = m_movementDate; + boolean completeOrder = MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule()); + // OrderLine WHERE + String where = " AND M_Warehouse_ID=" + p_M_Warehouse_ID; + if (p_DatePromised != null) + where += " AND (TRUNC(DatePromised)<=" + DB.TO_DATE(p_DatePromised, true) + + " OR DatePromised IS NULL)"; + // Exclude Auto Delivery if not Force + if (!MOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) + where += " AND (C_OrderLine.M_Product_ID IS NULL" + + " OR EXISTS (SELECT * FROM M_Product p " + + "WHERE C_OrderLine.M_Product_ID=p.M_Product_ID" + + " AND IsExcludeAutoDelivery='N'))"; + // Exclude Unconfirmed + if (!p_IsUnconfirmedInOut) + where += " AND NOT EXISTS (SELECT * FROM M_InOutLine iol" + + " INNER JOIN M_InOut io ON (iol.M_InOut_ID=io.M_InOut_ID) " + + "WHERE iol.C_OrderLine_ID=C_OrderLine.C_OrderLine_ID AND io.DocStatus IN ('IP','WC'))"; + // Deadlock Prevention - Order by M_Product_ID + MOrderLine[] lines = order.getLines (where, "C_BPartner_Location_ID, M_Product_ID"); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + if (line.getM_Warehouse_ID() != p_M_Warehouse_ID) + continue; + log.fine("check: " + line); + BigDecimal onHand = Env.ZERO; + BigDecimal toDeliver = line.getQtyOrdered() + .subtract(line.getQtyDelivered()); + MProduct product = line.getProduct(); + // Nothing to Deliver + if (product != null && toDeliver.signum() == 0) + continue; + + // or it's a charge - Bug#: 1603966 + if (line.getC_Charge_ID()!=0 && toDeliver.signum() == 0) + continue; + + // Check / adjust for confirmations + BigDecimal unconfirmedShippedQty = Env.ZERO; + if (p_IsUnconfirmedInOut && product != null && toDeliver.signum() != 0) + { + String where2 = "EXISTS (SELECT * FROM M_InOut io WHERE io.M_InOut_ID=M_InOutLine.M_InOut_ID AND io.DocStatus IN ('IP','WC'))"; + MInOutLine[] iols = MInOutLine.getOfOrderLine(getCtx(), + line.getC_OrderLine_ID(), where2, null); + for (int j = 0; j < iols.length; j++) + unconfirmedShippedQty = unconfirmedShippedQty.add(iols[j].getMovementQty()); + String logInfo = "Unconfirmed Qty=" + unconfirmedShippedQty + + " - ToDeliver=" + toDeliver + "->"; + toDeliver = toDeliver.subtract(unconfirmedShippedQty); + logInfo += toDeliver; + if (toDeliver.signum() < 0) + { + toDeliver = Env.ZERO; + logInfo += " (set to 0)"; + } + // Adjust On Hand + onHand = onHand.subtract(unconfirmedShippedQty); + log.fine(logInfo); + } + + // Comments & lines w/o product & services + if ((product == null || !product.isStocked()) + && (line.getQtyOrdered().signum() == 0 // comments + || toDeliver.signum() != 0)) // lines w/o product + { + if (!MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) // printed later + createLine (order, line, toDeliver, null, false); + continue; + } + + // Stored Product + String MMPolicy = product.getMMPolicy(); + MStorage[] storages = getStorages(line.getM_Warehouse_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + product.getM_AttributeSet_ID(), + line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, + MClient.MMPOLICY_FiFo.equals(MMPolicy)); + + for (int j = 0; j < storages.length; j++) + { + MStorage storage = storages[j]; + onHand = onHand.add(storage.getQtyOnHand()); + } + boolean fullLine = onHand.compareTo(toDeliver) >= 0 + || toDeliver.signum() < 0; + + // Complete Order + if (completeOrder && !fullLine) + { + log.fine("Failed CompleteOrder - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + " - " + line); + completeOrder = false; + break; + } + // Complete Line + else if (fullLine && MOrder.DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule())) + { + log.fine("CompleteLine - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + ", ToDeliver=" + toDeliver + " - " + line); + // + createLine (order, line, toDeliver, storages, false); + } + // Availability + else if (MOrder.DELIVERYRULE_Availability.equals(order.getDeliveryRule()) + && (onHand.signum() > 0 + || toDeliver.signum() < 0)) + { + BigDecimal deliver = toDeliver; + if (deliver.compareTo(onHand) > 0) + deliver = onHand; + log.fine("Available - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + + ", Delivering=" + deliver + " - " + line); + // + createLine (order, line, deliver, storages, false); + } + // Force + else if (MOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) + { + BigDecimal deliver = toDeliver; + log.fine("Force - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + + ", Delivering=" + deliver + " - " + line); + // + createLine (order, line, deliver, storages, true); + } + // Manual + else if (MOrder.DELIVERYRULE_Manual.equals(order.getDeliveryRule())) + log.fine("Manual - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + ") - " + line); + else + log.fine("Failed: " + order.getDeliveryRule() + " - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + " - " + line); + } // for all order lines + + // Complete Order successful + if (completeOrder && MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) + { + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + if (line.getM_Warehouse_ID() != p_M_Warehouse_ID) + continue; + MProduct product = line.getProduct(); + BigDecimal toDeliver = line.getQtyOrdered().subtract(line.getQtyDelivered()); + // + MStorage[] storages = null; + if (product != null && product.isStocked()) + { + String MMPolicy = product.getMMPolicy(); + storages = getStorages(line.getM_Warehouse_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + product.getM_AttributeSet_ID(), + line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, + MClient.MMPOLICY_FiFo.equals(MMPolicy)); + } + // + createLine (order, line, toDeliver, storages, false); + } + } + m_line += 1000; + } // while order + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, m_sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + completeShipment(); + return "@Created@ = " + m_created; + } // generate + + + + /************************************************************************** + * Create Line + * @param order order + * @param orderLine line + * @param qty qty + * @param storages storage info + * @param force force delivery + */ + private void createLine (MOrder order, MOrderLine orderLine, BigDecimal qty, + MStorage[] storages, boolean force) + { + // Complete last Shipment - can have multiple shipments + if (m_lastC_BPartner_Location_ID != orderLine.getC_BPartner_Location_ID() ) + completeShipment(); + m_lastC_BPartner_Location_ID = orderLine.getC_BPartner_Location_ID(); + // Create New Shipment + if (m_shipment == null) + { + m_shipment = new MInOut (order, 0, m_movementDate); + m_shipment.setM_Warehouse_ID(orderLine.getM_Warehouse_ID()); // sets Org too + if (order.getC_BPartner_ID() != orderLine.getC_BPartner_ID()) + m_shipment.setC_BPartner_ID(orderLine.getC_BPartner_ID()); + if (order.getC_BPartner_Location_ID() != orderLine.getC_BPartner_Location_ID()) + m_shipment.setC_BPartner_Location_ID(orderLine.getC_BPartner_Location_ID()); + if (!m_shipment.save()) + throw new IllegalStateException("Could not create Shipment"); + } + // Non Inventory Lines + if (storages == null) + { + MInOutLine line = new MInOutLine (m_shipment); + line.setOrderLine(orderLine, 0, Env.ZERO); + line.setQty(qty); // Correct UOM + if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) + line.setQtyEntered(qty + .multiply(orderLine.getQtyEntered()) + .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + line.setLine(m_line + orderLine.getLine()); + if (!line.save()) + throw new IllegalStateException("Could not create Shipment Line"); + log.fine(line.toString()); + return; + } + + // Product + MProduct product = orderLine.getProduct(); + boolean linePerASI = false; + if (product.getM_AttributeSet_ID() != 0) + { + MAttributeSet mas = MAttributeSet.get(getCtx(), product.getM_AttributeSet_ID()); + linePerASI = mas.isInstanceAttribute(); + } + + // Inventory Lines + ArrayList list = new ArrayList(); + BigDecimal toDeliver = qty; + for (int i = 0; i < storages.length; i++) + { + MStorage storage = storages[i]; + BigDecimal deliver = toDeliver; + // Not enough On Hand + if (deliver.compareTo(storage.getQtyOnHand()) > 0 + && storage.getQtyOnHand().signum() >= 0) // positive storage + { + if (!force // Adjust to OnHand Qty + || (force && i+1 != storages.length)) // if force not on last location + deliver = storage.getQtyOnHand(); + } + if (deliver.signum() == 0) // zero deliver + continue; + int M_Locator_ID = storage.getM_Locator_ID(); + // + MInOutLine line = null; + if (!linePerASI) // find line with Locator + { + for (int ll = 0; ll < list.size(); ll++) + { + MInOutLine test = (MInOutLine)list.get(ll); + if (test.getM_Locator_ID() == M_Locator_ID) + { + line = test; + break; + } + } + } + if (line == null) // new line + { + line = new MInOutLine (m_shipment); + line.setOrderLine(orderLine, M_Locator_ID, order.isSOTrx() ? deliver : Env.ZERO); + line.setQty(deliver); + list.add(line); + } + else // existing line + line.setQty(line.getMovementQty().add(deliver)); + if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) + line.setQtyEntered(line.getMovementQty().multiply(orderLine.getQtyEntered()) + .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + line.setLine(m_line + orderLine.getLine()); + if (linePerASI) + line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); + if (!line.save()) + throw new IllegalStateException("Could not create Shipment Line"); + log.fine("ToDeliver=" + qty + "/" + deliver + " - " + line); + toDeliver = toDeliver.subtract(deliver); + // Temp adjustment + storage.setQtyOnHand(storage.getQtyOnHand().subtract(deliver)); + // + if (toDeliver.signum() == 0) + break; + } + if (toDeliver.signum() != 0) + throw new IllegalStateException("Not All Delivered - Remainder=" + toDeliver); + } // createLine + + + /** + * Get Storages + * @param M_Warehouse_ID + * @param M_Product_ID + * @param M_AttributeSetInstance_ID + * @param M_AttributeSet_ID + * @param allAttributeInstances + * @param minGuaranteeDate + * @param FiFo + * @return storages + */ + private MStorage[] getStorages(int M_Warehouse_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, int M_AttributeSet_ID, + boolean allAttributeInstances, Timestamp minGuaranteeDate, + boolean FiFo) + { + m_lastPP = new SParameter(M_Warehouse_ID, + M_Product_ID, M_AttributeSetInstance_ID, M_AttributeSet_ID, + allAttributeInstances, minGuaranteeDate, FiFo); + // + m_lastStorages = m_map.get(m_lastPP); + + if (m_lastStorages == null) + { + m_lastStorages = MStorage.getWarehouse(getCtx(), + M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, + M_AttributeSet_ID, allAttributeInstances, minGuaranteeDate, + FiFo, get_TrxName()); + m_map.put(m_lastPP, m_lastStorages); + } + return m_lastStorages; + } // getStorages + + + /** + * Complete Shipment + */ + private void completeShipment() + { + if (m_shipment != null) + { + // Fails if there is a confirmation + if (!m_shipment.processIt(p_docAction)) + log.warning("Failed: " + m_shipment); + m_shipment.save(); + // + addLog(m_shipment.getM_InOut_ID(), m_shipment.getMovementDate(), null, m_shipment.getDocumentNo()); + m_created++; + m_map = new HashMap(); + if (m_lastPP != null && m_lastStorages != null) + m_map.put(m_lastPP, m_lastStorages); + } + m_shipment = null; + m_line = 0; + } // completeOrder + + /** + * InOutGenerate Parameter + */ + class SParameter + { + /** + * Parameter + * @param p_Warehouse_ID warehouse + * @param p_Product_ID + * @param p_AttributeSetInstance_ID + * @param p_AttributeSet_ID + * @param p_allAttributeInstances + * @param p_minGuaranteeDate + * @param p_FiFo + */ + protected SParameter (int p_Warehouse_ID, + int p_Product_ID, int p_AttributeSetInstance_ID, int p_AttributeSet_ID, + boolean p_allAttributeInstances, Timestamp p_minGuaranteeDate, + boolean p_FiFo) + { + this.M_Warehouse_ID = p_Warehouse_ID; + this.M_Product_ID = p_Product_ID; + this.M_AttributeSetInstance_ID = p_AttributeSetInstance_ID; + this.M_AttributeSet_ID = p_AttributeSet_ID; + this.allAttributeInstances = p_allAttributeInstances; + this.minGuaranteeDate = p_minGuaranteeDate; + this.FiFo = p_FiFo; + } + /** Warehouse */ + public int M_Warehouse_ID; + /** Product */ + public int M_Product_ID; + /** ASI */ + public int M_AttributeSetInstance_ID; + /** AS */ + public int M_AttributeSet_ID; + /** All instances */ + public boolean allAttributeInstances; + /** Mon Guarantee Date */ + public Timestamp minGuaranteeDate; + /** FiFo */ + public boolean FiFo; + + /** + * Equals + * @param obj + * @return true if equal + */ + public boolean equals (Object obj) + { + if (obj != null && obj instanceof SParameter) + { + SParameter cmp = (SParameter)obj; + boolean eq = cmp.M_Warehouse_ID == M_Warehouse_ID + && cmp.M_Product_ID == M_Product_ID + && cmp.M_AttributeSetInstance_ID == M_AttributeSetInstance_ID + && cmp.M_AttributeSet_ID == M_AttributeSet_ID + && cmp.allAttributeInstances == allAttributeInstances + && cmp.FiFo == FiFo; + if (eq) + { + if (cmp.minGuaranteeDate == null && minGuaranteeDate == null) + ; + else if (cmp.minGuaranteeDate != null && minGuaranteeDate != null + && cmp.minGuaranteeDate.equals(minGuaranteeDate)) + ; + else + eq = false; + } + return eq; + } + return false; + } // equals + + /** + * hashCode + * @return hash code + */ + public int hashCode () + { + long hash = M_Warehouse_ID + + (M_Product_ID * 2) + + (M_AttributeSetInstance_ID * 3) + + (M_AttributeSet_ID * 4); + + if (allAttributeInstances) + hash *= -1; + if (FiFo); + hash *= -2; + if (hash < 0) + hash = -hash + 7; + while (hash > Integer.MAX_VALUE) + hash -= Integer.MAX_VALUE; + // + if (minGuaranteeDate != null) + { + hash += minGuaranteeDate.hashCode(); + while (hash > Integer.MAX_VALUE) + hash -= Integer.MAX_VALUE; + } + + return (int)hash; + } // hashCode + + } // Parameter + +} // InOutGenerate diff --git a/base/src/org/eevolution/process/MovementGenerate.java b/base/src/org/eevolution/process/MovementGenerate.java index c5ace6fef7..cb35dc233f 100644 --- a/base/src/org/eevolution/process/MovementGenerate.java +++ b/base/src/org/eevolution/process/MovementGenerate.java @@ -1,700 +1,700 @@ -/****************************************************************************** - * 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-2007 e-Evolution,SC. All Rights Reserved. * - * Contributor(s): Victor Perez www.e-evolution.com * - *****************************************************************************/ -package org.eevolution.process; - -import java.math.*; -import java.sql.*; -import java.util.*; -import java.util.logging.*; - -import org.compiere.model.*; -import org.compiere.util.*; -import org.compiere.process.*; - -import org.eevolution.model.*; - - - -/** - * Generate Movement - * Manual or Automatic - * - * @author Victor Perez www.e-evolution.com - * @version $Id: MovementGenerate.java,v 1.0 - */ -public class MovementGenerate extends SvrProcess -{ - /** Manual Selection */ - private boolean p_Selection = false; - /** Warehouse */ - private int p_M_Warehouse_ID = 0; - /** BPartner */ - private int p_C_BPartner_ID = 0; - /** Promise Date */ - private Timestamp p_DatePromised = null; - /** Include Orders w. unconfirmed Shipments */ - private boolean p_IsUnconfirmedInOut = false; - /** DocAction */ - private String p_docAction = DocAction.ACTION_Complete; - /** Consolidate */ - private boolean p_ConsolidateDocument = true; - /** Shipment Date */ - private Timestamp p_DateShipped = null; - - /** The current Shipment */ - private MMovement m_movement = null; - /** Numner of Shipments */ - private int m_created = 0; - /** Line Number */ - private int m_line = 0; - /** Movement Date */ - private Timestamp m_movementDate = null; - /** Last BP Location */ - private int m_lastC_BPartner_Location_ID = -1; - - /** The Query sql */ - private String m_sql = null; - - - /** Storages temp space */ - private HashMap m_map = new HashMap(); - /** Last Parameter */ - private SParameter m_lastPP = null; - /** Last Storage */ - private MStorage[] m_lastStorages = null; - - /** - * Prepare - e.g., get Parameters. - */ - protected void prepare() - { - ProcessInfoParameter[] para = getParameter(); - for (int i = 0; i < para.length; i++) - { - String name = para[i].getParameterName(); - if (para[i].getParameter() == null) - ; - else if (name.equals("M_Warehouse_ID")) - p_M_Warehouse_ID = para[i].getParameterAsInt(); - else if (name.equals("C_BPartner_ID")) - p_C_BPartner_ID = para[i].getParameterAsInt(); - else if (name.equals("DatePromised")) - p_DatePromised = (Timestamp)para[i].getParameter(); - else if (name.equals("Selection")) - p_Selection = "Y".equals(para[i].getParameter()); - else if (name.equals("IsUnconfirmedInOut")) - p_IsUnconfirmedInOut = "Y".equals(para[i].getParameter()); - else if (name.equals("ConsolidateDocument")) - p_ConsolidateDocument = "Y".equals(para[i].getParameter()); - else if (name.equals("DocAction")) - p_docAction = (String)para[i].getParameter(); - else if (name.equals("MovementDate")) - p_DateShipped = (Timestamp)para[i].getParameter(); - else - log.log(Level.SEVERE, "Unknown Parameter: " + name); - - // juddm - added ability to specify a shipment date from Generate Shipments - if (p_DateShipped == null) { - m_movementDate = Env.getContextAsDate(getCtx(), "#Date"); - if (m_movementDate == null) - m_movementDate = new Timestamp(System.currentTimeMillis()); - } else - m_movementDate = p_DateShipped; - // DocAction check - if (!DocAction.ACTION_Complete.equals(p_docAction)) - p_docAction = DocAction.ACTION_Prepare; - } - } // prepare - - /** - * Generate Movemements - * @return info - * @throws Exception - */ - protected String doIt () throws Exception - { - log.info("Selection=" + p_Selection - + ", M_Warehouse_ID=" + p_M_Warehouse_ID - + ", C_BPartner_ID=" + p_C_BPartner_ID - + ", Consolidate=" + p_ConsolidateDocument - + ", IsUnconfirmed=" + p_IsUnconfirmedInOut - + ", Movement=" + m_movementDate); - - if (p_M_Warehouse_ID == 0) - throw new AdempiereUserError("@NotFound@ @M_Warehouse_ID@"); - - if (p_Selection) // VInOutGen - { - m_sql = "SELECT DD_Order.* FROM DD_Order, T_Selection " - + "WHERE DD_Order.DocStatus='CO' AND DD_Order.IsSOTrx='Y' AND DD_Order.AD_Client_ID=? " - + "AND DD_Order.DD_Order_ID = T_Selection.T_Selection_ID " - + "AND T_Selection.AD_PInstance_ID=? "; - } - else - { - m_sql = "SELECT * FROM DD_Order o " - + "WHERE DocStatus='CO' AND IsSOTrx='Y'" - // No Offer,POS - + " AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType " - + "WHERE DocBaseType='DOO')" - + " AND o.IsDropShip='N'" - // No Manual - + " AND o.DeliveryRule<>'M'" - // Open Order Lines with Warehouse - + " AND EXISTS (SELECT * FROM DD_OrderLine ol " - + "WHERE ol.M_Warehouse_ID=?"; // #1 - if (p_DatePromised != null) - m_sql += " AND TRUNC(ol.DatePromised)<=?"; // #2 - m_sql += " AND o.DD_Order_ID=ol.DD_Order_ID AND ol.QtyOrdered<>ol.QtyIntransit)"; - // - if (p_C_BPartner_ID != 0) - m_sql += " AND o.C_BPartner_ID=?"; // #3 - } - m_sql += " ORDER BY M_Warehouse_ID, PriorityRule, M_Shipper_ID, C_BPartner_ID, C_BPartner_Location_ID, DD_Order_ID"; - // m_sql += " FOR UPDATE"; - - PreparedStatement pstmt = null; - try - { - pstmt = DB.prepareStatement (m_sql, get_TrxName()); - int index = 1; - if (p_Selection) - { - pstmt.setInt(index++, Env.getAD_Client_ID(getCtx())); - pstmt.setInt(index++, getAD_PInstance_ID()); - } - else - { - pstmt.setInt(index++, p_M_Warehouse_ID); - if (p_DatePromised != null) - pstmt.setTimestamp(index++, p_DatePromised); - if (p_C_BPartner_ID != 0) - pstmt.setInt(index++, p_C_BPartner_ID); - } - } - catch (Exception e) - { - log.log(Level.SEVERE, m_sql, e); - } - return generate(pstmt); - } // doIt - - /** - * Generate Shipments - * @param pstmt Order Query - * @return info - */ - private String generate (PreparedStatement pstmt) - { - MClient client = MClient.get(getCtx()); - try - { - ResultSet rs = pstmt.executeQuery (); - while (rs.next ()) // Order - { - MDDOrder order = new MDDOrder (getCtx(), rs, get_TrxName()); - // New Header different Shipper, Shipment Location - if (!p_ConsolidateDocument - || (m_movement != null - && (m_movement.getC_BPartner_Location_ID() != order.getC_BPartner_Location_ID() - || m_movement.getM_Shipper_ID() != order.getM_Shipper_ID() ))) - completeMovement(); - log.fine("check: " + order + " - DeliveryRule=" + order.getDeliveryRule()); - // - Timestamp minGuaranteeDate = m_movementDate; - boolean completeOrder = MDDOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule()); - // OrderLine WHERE - String where = " AND " + p_M_Warehouse_ID + " IN (SELECT l.M_Warehouse_ID FROM M_Locator l WHERE l.M_Locator_ID=M_Locator_ID) "; - if (p_DatePromised != null) - where += " AND (TRUNC(DatePromised)<=" + DB.TO_DATE(p_DatePromised, true) - + " OR DatePromised IS NULL)"; - // Exclude Auto Delivery if not Force - if (!MDDOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) - where += " AND (DD_OrderLine.M_Product_ID IS NULL" - + " OR EXISTS (SELECT * FROM M_Product p " - + "WHERE DD_OrderLine.M_Product_ID=p.M_Product_ID" - + " AND IsExcludeAutoDelivery='N'))"; - // Exclude Unconfirmed - if (!p_IsUnconfirmedInOut) - where += " AND NOT EXISTS (SELECT * FROM M_MovementLine iol" - + " INNER JOIN M_Movement io ON (iol.M_Movement_ID=io.M_Movement_ID) " - + "WHERE iol.DD_OrderLine_ID=DD_OrderLine.DD_OrderLine_ID AND io.DocStatus IN ('IP','WC'))"; - // Deadlock Prevention - Order by M_Product_ID - MDDOrderLine[] lines = order.getLines (where, "ORDER BY M_Product_ID"); - for (int i = 0; i < lines.length; i++) - { - MDDOrderLine line = lines[i]; - MLocator l = new MLocator(getCtx(),line.getM_Locator_ID(), get_TrxName()); - if (l.getM_Warehouse_ID() != p_M_Warehouse_ID) - continue; - log.fine("check: " + line); - BigDecimal onHand = Env.ZERO; - //BigDecimal toDeliver = line.getQtyOrdered() - BigDecimal toDeliver = line.getConfirmedQty() - .subtract(line.getQtyDelivered()); - MProduct product = line.getProduct(); - // Nothing to Deliver - if (product != null && toDeliver.signum() == 0) - continue; - - // or it's a charge - Bug#: 1603966 - if (line.getC_Charge_ID()!=0 && toDeliver.signum() == 0) - continue; - - // Check / adjust for confirmations - BigDecimal unconfirmedShippedQty = Env.ZERO; - if (p_IsUnconfirmedInOut && product != null && toDeliver.signum() != 0) - { - String where2 = "EXISTS (SELECT * FROM M_Movement io WHERE io.M_Movement_ID=M_MovementLine.M_Movement_ID AND io.DocStatus IN ('IP','WC'))"; - MMovementLine[] iols = MMovementLine.getOfOrderLine(getCtx(), - line.getDD_OrderLine_ID(), where2, null); - for (int j = 0; j < iols.length; j++) - unconfirmedShippedQty = unconfirmedShippedQty.add(iols[j].getMovementQty()); - String logInfo = "Unconfirmed Qty=" + unconfirmedShippedQty - + " - ToDeliver=" + toDeliver + "->"; - toDeliver = toDeliver.subtract(unconfirmedShippedQty); - logInfo += toDeliver; - if (toDeliver.signum() < 0) - { - toDeliver = Env.ZERO; - logInfo += " (set to 0)"; - } - // Adjust On Hand - onHand = onHand.subtract(unconfirmedShippedQty); - log.fine(logInfo); - } - - // Comments & lines w/o product & services - if ((product == null || !product.isStocked()) - && (line.getQtyOrdered().signum() == 0 // comments - || toDeliver.signum() != 0)) // lines w/o product - { - if (!MDDOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) // printed later - createLine (order, line, toDeliver, null, false); - continue; - } - - // Stored Product - MProductCategory pc = MProductCategory.get(order.getCtx(), product.getM_Product_Category_ID()); - String MMPolicy = pc.getMMPolicy(); - if (MMPolicy == null || MMPolicy.length() == 0) - MMPolicy = client.getMMPolicy(); - // - MStorage[] storages = getStorages(l.getM_Warehouse_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - product.getM_AttributeSet_ID(), - line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, - MClient.MMPOLICY_FiFo.equals(MMPolicy)); - - for (int j = 0; j < storages.length; j++) - { - MStorage storage = storages[j]; - onHand = onHand.add(storage.getQtyOnHand()); - } - boolean fullLine = onHand.compareTo(toDeliver) >= 0 - || toDeliver.signum() < 0; - - // Complete Order - if (completeOrder && !fullLine) - { - log.fine("Failed CompleteOrder - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver + " - " + line); - completeOrder = false; - break; - } - // Complete Line - else if (fullLine && MDDOrder.DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule())) - { - log.fine("CompleteLine - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + ", ToDeliver=" + toDeliver + " - " + line); - // - createLine (order, line, toDeliver, storages, false); - } - // Availability - else if (MDDOrder.DELIVERYRULE_Availability.equals(order.getDeliveryRule()) - && (onHand.signum() > 0 - || toDeliver.signum() < 0)) - { - BigDecimal deliver = toDeliver; - if (deliver.compareTo(onHand) > 0) - deliver = onHand; - log.fine("Available - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver - + ", Delivering=" + deliver + " - " + line); - // - createLine (order, line, deliver, storages, false); - } - // Force - else if (MDDOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) - { - BigDecimal deliver = toDeliver; - log.fine("Force - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver - + ", Delivering=" + deliver + " - " + line); - // - createLine (order, line, deliver, storages, true); - } - // Manual - else if (MDDOrder.DELIVERYRULE_Manual.equals(order.getDeliveryRule())) - log.fine("Manual - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + ") - " + line); - else - log.fine("Failed: " + order.getDeliveryRule() + " - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver + " - " + line); - } // for all order lines - - // Complete Order successful - if (completeOrder && MDDOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) - { - for (int i = 0; i < lines.length; i++) - { - MDDOrderLine line = lines[i]; - MLocator l = new MLocator(getCtx(),line.getM_Locator_ID(), get_TrxName()); - if (l.getM_Warehouse_ID() != p_M_Warehouse_ID) - continue; - MProduct product = line.getProduct(); - BigDecimal toDeliver = line.getQtyOrdered().subtract(line.getQtyDelivered()); - // - MStorage[] storages = null; - if (product != null && product.isStocked()) - { - MProductCategory pc = MProductCategory.get(order.getCtx(), product.getM_Product_Category_ID()); - String MMPolicy = pc.getMMPolicy(); - if (MMPolicy == null || MMPolicy.length() == 0) - MMPolicy = client.getMMPolicy(); - // - storages = getStorages(l.getM_Warehouse_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - product.getM_AttributeSet_ID(), - line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, - MClient.MMPOLICY_FiFo.equals(MMPolicy)); - } - // - createLine (order, line, toDeliver, storages, false); - } - } - m_line += 1000; - } // while order - rs.close (); - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - log.log(Level.SEVERE, m_sql, e); - } - try - { - if (pstmt != null) - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - pstmt = null; - } - completeMovement(); - return "@Created@ = " + m_created; - } // generate - - - - /************************************************************************** - * Create Line - * @param Distribution order order - * @param orderLine line - * @param qty qty - * @param storages storage info - * @param force force delivery - */ - private void createLine (MDDOrder order, MDDOrderLine orderLine, BigDecimal qty, - MStorage[] storages, boolean force) - { - // Complete last Shipment - can have multiple shipments - if (m_lastC_BPartner_Location_ID != order.getC_BPartner_Location_ID() ) - completeMovement(); - m_lastC_BPartner_Location_ID = order.getC_BPartner_Location_ID(); - // Create New Shipment - if (m_movement == null) - { - m_movement = new MMovement (order, 0, m_movementDate); - //m_movement.setM_Warehouse_ID(orderLine.getM_Warehouse_ID()); // sets Org too - m_movement.setIsInTransit(true); - m_movement.setDD_Order_ID(order.getDD_Order_ID()); - if (order.getC_BPartner_ID() != order.getC_BPartner_ID()) - m_movement.setC_BPartner_ID(order.getC_BPartner_ID()); - if (order.getC_BPartner_Location_ID() != order.getC_BPartner_Location_ID()) - m_movement.setC_BPartner_Location_ID(order.getC_BPartner_Location_ID()); - if (!m_movement.save()) - throw new IllegalStateException("Could not create Movement"); - } - // Non Inventory Lines - if (storages == null) - { - MMovementLine line = new MMovementLine (m_movement); - line.setOrderLine(orderLine, Env.ZERO, false); - line.setMovementQty(qty); // Correct UOM - if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) - line.setMovementQty(qty - .multiply(orderLine.getQtyEntered()) - .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); - line.setLine(m_line + orderLine.getLine()); - if (!line.save()) - throw new IllegalStateException("Could not create Shipment Line"); - log.fine(line.toString()); - return; - } - - // Product - MProduct product = orderLine.getProduct(); - boolean linePerASI = false; - if (product.getM_AttributeSet_ID() != 0) - { - MAttributeSet mas = MAttributeSet.get(getCtx(), product.getM_AttributeSet_ID()); - linePerASI = mas.isInstanceAttribute(); - } - - // Inventory Lines - ArrayList list = new ArrayList(); - BigDecimal toDeliver = qty; - for (int i = 0; i < storages.length; i++) - { - MStorage storage = storages[i]; - BigDecimal deliver = toDeliver; - // Not enough On Hand - if (deliver.compareTo(storage.getQtyOnHand()) > 0 - && storage.getQtyOnHand().signum() >= 0) // positive storage - { - if (!force // Adjust to OnHand Qty - || (force && i+1 != storages.length)) // if force not on last location - deliver = storage.getQtyOnHand(); - } - if (deliver.signum() == 0) // zero deliver - continue; - int M_Locator_ID = storage.getM_Locator_ID(); - // - MMovementLine line = null; - if (!linePerASI) // find line with Locator - { - for (int ll = 0; ll < list.size(); ll++) - { - MMovementLine test = (MMovementLine)list.get(ll); - if (test.getM_Locator_ID() == M_Locator_ID) - { - line = test; - break; - } - } - } - if (line == null) // new line - { - line = new MMovementLine (m_movement); - line.setOrderLine(orderLine, deliver , false); - line.setMovementQty(deliver); - list.add(line); - } - else // existing line - line.setMovementQty(line.getMovementQty().add(deliver)); - if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) - line.setMovementQty(line.getMovementQty().multiply(orderLine.getQtyEntered()) - .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); - line.setLine(m_line + orderLine.getLine()); - if (linePerASI) - line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); - if (!line.save()) - throw new IllegalStateException("Could not create Shipment Line"); - log.fine("ToDeliver=" + qty + "/" + deliver + " - " + line); - toDeliver = toDeliver.subtract(deliver); - // Temp adjustment - storage.setQtyOnHand(storage.getQtyOnHand().subtract(deliver)); - // - if (toDeliver.signum() == 0) - break; - } - if (toDeliver.signum() != 0) - throw new IllegalStateException("Not All Delivered - Remainder=" + toDeliver); - } // createLine - - - /** - * Get Storages - * @param M_Warehouse_ID - * @param M_Product_ID - * @param M_AttributeSetInstance_ID - * @param M_AttributeSet_ID - * @param allAttributeInstances - * @param minGuaranteeDate - * @param FiFo - * @return storages - */ - private MStorage[] getStorages(int M_Warehouse_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, int M_AttributeSet_ID, - boolean allAttributeInstances, Timestamp minGuaranteeDate, - boolean FiFo) - { - m_lastPP = new SParameter(M_Warehouse_ID, - M_Product_ID, M_AttributeSetInstance_ID, M_AttributeSet_ID, - allAttributeInstances, minGuaranteeDate, FiFo); - // - m_lastStorages = m_map.get(m_lastPP); - - if (m_lastStorages == null) - { - m_lastStorages = MStorage.getWarehouse(getCtx(), - M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, - M_AttributeSet_ID, allAttributeInstances, minGuaranteeDate, - FiFo, get_TrxName()); - m_map.put(m_lastPP, m_lastStorages); - } - return m_lastStorages; - } // getStorages - - - /** - * Complete Shipment - */ - private void completeMovement() - { - if (m_movement != null) - { - // Fails if there is a confirmation - if (!m_movement.processIt(p_docAction)) - log.warning("Failed: " + m_movement); - m_movement.save(); - // - addLog(m_movement.getM_Movement_ID(), m_movement.getMovementDate(), null, m_movement.getDocumentNo()); - m_created++; - m_map = new HashMap(); - if (m_lastPP != null && m_lastStorages != null) - m_map.put(m_lastPP, m_lastStorages); - } - m_movement = null; - m_line = 0; - } // completeOrder - - /** - * MovementGenerate Parameter - */ - class SParameter - { - /** - * Parameter - * @param p_Warehouse_ID warehouse - * @param p_Product_ID - * @param p_AttributeSetInstance_ID - * @param p_AttributeSet_ID - * @param p_allAttributeInstances - * @param p_minGuaranteeDate - * @param p_FiFo - */ - protected SParameter (int p_Warehouse_ID, - int p_Product_ID, int p_AttributeSetInstance_ID, int p_AttributeSet_ID, - boolean p_allAttributeInstances, Timestamp p_minGuaranteeDate, - boolean p_FiFo) - { - this.M_Warehouse_ID = p_Warehouse_ID; - this.M_Product_ID = p_Product_ID; - this.M_AttributeSetInstance_ID = p_AttributeSetInstance_ID; - this.M_AttributeSet_ID = p_AttributeSet_ID; - this.allAttributeInstances = p_allAttributeInstances; - this.minGuaranteeDate = p_minGuaranteeDate; - this.FiFo = p_FiFo; - } - /** Warehouse */ - public int M_Warehouse_ID; - /** Product */ - public int M_Product_ID; - /** ASI */ - public int M_AttributeSetInstance_ID; - /** AS */ - public int M_AttributeSet_ID; - /** All instances */ - public boolean allAttributeInstances; - /** Mon Guarantee Date */ - public Timestamp minGuaranteeDate; - /** FiFo */ - public boolean FiFo; - - /** - * Equals - * @param obj - * @return true if equal - */ - public boolean equals (Object obj) - { - if (obj != null && obj instanceof SParameter) - { - SParameter cmp = (SParameter)obj; - boolean eq = cmp.M_Warehouse_ID == M_Warehouse_ID - && cmp.M_Product_ID == M_Product_ID - && cmp.M_AttributeSetInstance_ID == M_AttributeSetInstance_ID - && cmp.M_AttributeSet_ID == M_AttributeSet_ID - && cmp.allAttributeInstances == allAttributeInstances - && cmp.FiFo == FiFo; - if (eq) - { - if (cmp.minGuaranteeDate == null && minGuaranteeDate == null) - ; - else if (cmp.minGuaranteeDate != null && minGuaranteeDate != null - && cmp.minGuaranteeDate.equals(minGuaranteeDate)) - ; - else - eq = false; - } - return eq; - } - return false; - } // equals - - /** - * hashCode - * @return hash code - */ - public int hashCode () - { - long hash = M_Warehouse_ID - + (M_Product_ID * 2) - + (M_AttributeSetInstance_ID * 3) - + (M_AttributeSet_ID * 4); - - if (allAttributeInstances) - hash *= -1; - if (FiFo); - hash *= -2; - if (hash < 0) - hash = -hash + 7; - while (hash > Integer.MAX_VALUE) - hash -= Integer.MAX_VALUE; - // - if (minGuaranteeDate != null) - { - hash += minGuaranteeDate.hashCode(); - while (hash > Integer.MAX_VALUE) - hash -= Integer.MAX_VALUE; - } - - return (int)hash; - } // hashCode - - } // Parameter - -} // MovementGenerate +/****************************************************************************** + * 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-2007 e-Evolution,SC. All Rights Reserved. * + * Contributor(s): Victor Perez www.e-evolution.com * + *****************************************************************************/ +package org.eevolution.process; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; + +import org.compiere.model.*; +import org.compiere.util.*; +import org.compiere.process.*; + +import org.eevolution.model.*; + + + +/** + * Generate Movement + * Manual or Automatic + * + * @author Victor Perez www.e-evolution.com + * @version $Id: MovementGenerate.java,v 1.0 + */ +public class MovementGenerate extends SvrProcess +{ + /** Manual Selection */ + private boolean p_Selection = false; + /** Warehouse */ + private int p_M_Warehouse_ID = 0; + /** BPartner */ + private int p_C_BPartner_ID = 0; + /** Promise Date */ + private Timestamp p_DatePromised = null; + /** Include Orders w. unconfirmed Shipments */ + private boolean p_IsUnconfirmedInOut = false; + /** DocAction */ + private String p_docAction = DocAction.ACTION_Complete; + /** Consolidate */ + private boolean p_ConsolidateDocument = true; + /** Shipment Date */ + private Timestamp p_DateShipped = null; + + /** The current Shipment */ + private MMovement m_movement = null; + /** Numner of Shipments */ + private int m_created = 0; + /** Line Number */ + private int m_line = 0; + /** Movement Date */ + private Timestamp m_movementDate = null; + /** Last BP Location */ + private int m_lastC_BPartner_Location_ID = -1; + + /** The Query sql */ + private String m_sql = null; + + + /** Storages temp space */ + private HashMap m_map = new HashMap(); + /** Last Parameter */ + private SParameter m_lastPP = null; + /** Last Storage */ + private MStorage[] m_lastStorages = null; + + /** + * Prepare - e.g., get Parameters. + */ + protected void prepare() + { + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) + { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null) + ; + else if (name.equals("M_Warehouse_ID")) + p_M_Warehouse_ID = para[i].getParameterAsInt(); + else if (name.equals("C_BPartner_ID")) + p_C_BPartner_ID = para[i].getParameterAsInt(); + else if (name.equals("DatePromised")) + p_DatePromised = (Timestamp)para[i].getParameter(); + else if (name.equals("Selection")) + p_Selection = "Y".equals(para[i].getParameter()); + else if (name.equals("IsUnconfirmedInOut")) + p_IsUnconfirmedInOut = "Y".equals(para[i].getParameter()); + else if (name.equals("ConsolidateDocument")) + p_ConsolidateDocument = "Y".equals(para[i].getParameter()); + else if (name.equals("DocAction")) + p_docAction = (String)para[i].getParameter(); + else if (name.equals("MovementDate")) + p_DateShipped = (Timestamp)para[i].getParameter(); + else + log.log(Level.SEVERE, "Unknown Parameter: " + name); + + // juddm - added ability to specify a shipment date from Generate Shipments + if (p_DateShipped == null) { + m_movementDate = Env.getContextAsDate(getCtx(), "#Date"); + if (m_movementDate == null) + m_movementDate = new Timestamp(System.currentTimeMillis()); + } else + m_movementDate = p_DateShipped; + // DocAction check + if (!DocAction.ACTION_Complete.equals(p_docAction)) + p_docAction = DocAction.ACTION_Prepare; + } + } // prepare + + /** + * Generate Movemements + * @return info + * @throws Exception + */ + protected String doIt () throws Exception + { + log.info("Selection=" + p_Selection + + ", M_Warehouse_ID=" + p_M_Warehouse_ID + + ", C_BPartner_ID=" + p_C_BPartner_ID + + ", Consolidate=" + p_ConsolidateDocument + + ", IsUnconfirmed=" + p_IsUnconfirmedInOut + + ", Movement=" + m_movementDate); + + if (p_M_Warehouse_ID == 0) + throw new AdempiereUserError("@NotFound@ @M_Warehouse_ID@"); + + if (p_Selection) // VInOutGen + { + m_sql = "SELECT DD_Order.* FROM DD_Order, T_Selection " + + "WHERE DD_Order.DocStatus='CO' AND DD_Order.IsSOTrx='Y' AND DD_Order.AD_Client_ID=? " + + "AND DD_Order.DD_Order_ID = T_Selection.T_Selection_ID " + + "AND T_Selection.AD_PInstance_ID=? "; + } + else + { + m_sql = "SELECT * FROM DD_Order o " + + "WHERE DocStatus='CO' AND IsSOTrx='Y'" + // No Offer,POS + + " AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType " + + "WHERE DocBaseType='DOO')" + + " AND o.IsDropShip='N'" + // No Manual + + " AND o.DeliveryRule<>'M'" + // Open Order Lines with Warehouse + + " AND EXISTS (SELECT * FROM DD_OrderLine ol " + + "WHERE ol.M_Warehouse_ID=?"; // #1 + if (p_DatePromised != null) + m_sql += " AND TRUNC(ol.DatePromised)<=?"; // #2 + m_sql += " AND o.DD_Order_ID=ol.DD_Order_ID AND ol.QtyOrdered<>ol.QtyIntransit)"; + // + if (p_C_BPartner_ID != 0) + m_sql += " AND o.C_BPartner_ID=?"; // #3 + } + m_sql += " ORDER BY M_Warehouse_ID, PriorityRule, M_Shipper_ID, C_BPartner_ID, C_BPartner_Location_ID, DD_Order_ID"; + // m_sql += " FOR UPDATE"; + + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (m_sql, get_TrxName()); + int index = 1; + if (p_Selection) + { + pstmt.setInt(index++, Env.getAD_Client_ID(getCtx())); + pstmt.setInt(index++, getAD_PInstance_ID()); + } + else + { + pstmt.setInt(index++, p_M_Warehouse_ID); + if (p_DatePromised != null) + pstmt.setTimestamp(index++, p_DatePromised); + if (p_C_BPartner_ID != 0) + pstmt.setInt(index++, p_C_BPartner_ID); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, m_sql, e); + } + return generate(pstmt); + } // doIt + + /** + * Generate Shipments + * @param pstmt Order Query + * @return info + */ + private String generate (PreparedStatement pstmt) + { + MClient client = MClient.get(getCtx()); + try + { + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) // Order + { + MDDOrder order = new MDDOrder (getCtx(), rs, get_TrxName()); + // New Header different Shipper, Shipment Location + if (!p_ConsolidateDocument + || (m_movement != null + && (m_movement.getC_BPartner_Location_ID() != order.getC_BPartner_Location_ID() + || m_movement.getM_Shipper_ID() != order.getM_Shipper_ID() ))) + completeMovement(); + log.fine("check: " + order + " - DeliveryRule=" + order.getDeliveryRule()); + // + Timestamp minGuaranteeDate = m_movementDate; + boolean completeOrder = MDDOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule()); + // OrderLine WHERE + String where = " AND " + p_M_Warehouse_ID + " IN (SELECT l.M_Warehouse_ID FROM M_Locator l WHERE l.M_Locator_ID=M_Locator_ID) "; + if (p_DatePromised != null) + where += " AND (TRUNC(DatePromised)<=" + DB.TO_DATE(p_DatePromised, true) + + " OR DatePromised IS NULL)"; + // Exclude Auto Delivery if not Force + if (!MDDOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) + where += " AND (DD_OrderLine.M_Product_ID IS NULL" + + " OR EXISTS (SELECT * FROM M_Product p " + + "WHERE DD_OrderLine.M_Product_ID=p.M_Product_ID" + + " AND IsExcludeAutoDelivery='N'))"; + // Exclude Unconfirmed + if (!p_IsUnconfirmedInOut) + where += " AND NOT EXISTS (SELECT * FROM M_MovementLine iol" + + " INNER JOIN M_Movement io ON (iol.M_Movement_ID=io.M_Movement_ID) " + + "WHERE iol.DD_OrderLine_ID=DD_OrderLine.DD_OrderLine_ID AND io.DocStatus IN ('IP','WC'))"; + // Deadlock Prevention - Order by M_Product_ID + MDDOrderLine[] lines = order.getLines (where, "M_Product_ID"); + for (int i = 0; i < lines.length; i++) + { + MDDOrderLine line = lines[i]; + MLocator l = new MLocator(getCtx(),line.getM_Locator_ID(), get_TrxName()); + if (l.getM_Warehouse_ID() != p_M_Warehouse_ID) + continue; + log.fine("check: " + line); + BigDecimal onHand = Env.ZERO; + //BigDecimal toDeliver = line.getQtyOrdered() + BigDecimal toDeliver = line.getConfirmedQty() + .subtract(line.getQtyDelivered()); + MProduct product = line.getProduct(); + // Nothing to Deliver + if (product != null && toDeliver.signum() == 0) + continue; + + // or it's a charge - Bug#: 1603966 + if (line.getC_Charge_ID()!=0 && toDeliver.signum() == 0) + continue; + + // Check / adjust for confirmations + BigDecimal unconfirmedShippedQty = Env.ZERO; + if (p_IsUnconfirmedInOut && product != null && toDeliver.signum() != 0) + { + String where2 = "EXISTS (SELECT * FROM M_Movement io WHERE io.M_Movement_ID=M_MovementLine.M_Movement_ID AND io.DocStatus IN ('IP','WC'))"; + MMovementLine[] iols = MMovementLine.getOfOrderLine(getCtx(), + line.getDD_OrderLine_ID(), where2, null); + for (int j = 0; j < iols.length; j++) + unconfirmedShippedQty = unconfirmedShippedQty.add(iols[j].getMovementQty()); + String logInfo = "Unconfirmed Qty=" + unconfirmedShippedQty + + " - ToDeliver=" + toDeliver + "->"; + toDeliver = toDeliver.subtract(unconfirmedShippedQty); + logInfo += toDeliver; + if (toDeliver.signum() < 0) + { + toDeliver = Env.ZERO; + logInfo += " (set to 0)"; + } + // Adjust On Hand + onHand = onHand.subtract(unconfirmedShippedQty); + log.fine(logInfo); + } + + // Comments & lines w/o product & services + if ((product == null || !product.isStocked()) + && (line.getQtyOrdered().signum() == 0 // comments + || toDeliver.signum() != 0)) // lines w/o product + { + if (!MDDOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) // printed later + createLine (order, line, toDeliver, null, false); + continue; + } + + // Stored Product + MProductCategory pc = MProductCategory.get(order.getCtx(), product.getM_Product_Category_ID()); + String MMPolicy = pc.getMMPolicy(); + if (MMPolicy == null || MMPolicy.length() == 0) + MMPolicy = client.getMMPolicy(); + // + MStorage[] storages = getStorages(l.getM_Warehouse_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + product.getM_AttributeSet_ID(), + line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, + MClient.MMPOLICY_FiFo.equals(MMPolicy)); + + for (int j = 0; j < storages.length; j++) + { + MStorage storage = storages[j]; + onHand = onHand.add(storage.getQtyOnHand()); + } + boolean fullLine = onHand.compareTo(toDeliver) >= 0 + || toDeliver.signum() < 0; + + // Complete Order + if (completeOrder && !fullLine) + { + log.fine("Failed CompleteOrder - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + " - " + line); + completeOrder = false; + break; + } + // Complete Line + else if (fullLine && MDDOrder.DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule())) + { + log.fine("CompleteLine - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + ", ToDeliver=" + toDeliver + " - " + line); + // + createLine (order, line, toDeliver, storages, false); + } + // Availability + else if (MDDOrder.DELIVERYRULE_Availability.equals(order.getDeliveryRule()) + && (onHand.signum() > 0 + || toDeliver.signum() < 0)) + { + BigDecimal deliver = toDeliver; + if (deliver.compareTo(onHand) > 0) + deliver = onHand; + log.fine("Available - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + + ", Delivering=" + deliver + " - " + line); + // + createLine (order, line, deliver, storages, false); + } + // Force + else if (MDDOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) + { + BigDecimal deliver = toDeliver; + log.fine("Force - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + + ", Delivering=" + deliver + " - " + line); + // + createLine (order, line, deliver, storages, true); + } + // Manual + else if (MDDOrder.DELIVERYRULE_Manual.equals(order.getDeliveryRule())) + log.fine("Manual - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + ") - " + line); + else + log.fine("Failed: " + order.getDeliveryRule() + " - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + " - " + line); + } // for all order lines + + // Complete Order successful + if (completeOrder && MDDOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) + { + for (int i = 0; i < lines.length; i++) + { + MDDOrderLine line = lines[i]; + MLocator l = new MLocator(getCtx(),line.getM_Locator_ID(), get_TrxName()); + if (l.getM_Warehouse_ID() != p_M_Warehouse_ID) + continue; + MProduct product = line.getProduct(); + BigDecimal toDeliver = line.getQtyOrdered().subtract(line.getQtyDelivered()); + // + MStorage[] storages = null; + if (product != null && product.isStocked()) + { + MProductCategory pc = MProductCategory.get(order.getCtx(), product.getM_Product_Category_ID()); + String MMPolicy = pc.getMMPolicy(); + if (MMPolicy == null || MMPolicy.length() == 0) + MMPolicy = client.getMMPolicy(); + // + storages = getStorages(l.getM_Warehouse_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + product.getM_AttributeSet_ID(), + line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, + MClient.MMPOLICY_FiFo.equals(MMPolicy)); + } + // + createLine (order, line, toDeliver, storages, false); + } + } + m_line += 1000; + } // while order + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, m_sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + completeMovement(); + return "@Created@ = " + m_created; + } // generate + + + + /************************************************************************** + * Create Line + * @param Distribution order order + * @param orderLine line + * @param qty qty + * @param storages storage info + * @param force force delivery + */ + private void createLine (MDDOrder order, MDDOrderLine orderLine, BigDecimal qty, + MStorage[] storages, boolean force) + { + // Complete last Shipment - can have multiple shipments + if (m_lastC_BPartner_Location_ID != order.getC_BPartner_Location_ID() ) + completeMovement(); + m_lastC_BPartner_Location_ID = order.getC_BPartner_Location_ID(); + // Create New Shipment + if (m_movement == null) + { + m_movement = new MMovement (order, 0, m_movementDate); + //m_movement.setM_Warehouse_ID(orderLine.getM_Warehouse_ID()); // sets Org too + m_movement.setIsInTransit(true); + m_movement.setDD_Order_ID(order.getDD_Order_ID()); + if (order.getC_BPartner_ID() != order.getC_BPartner_ID()) + m_movement.setC_BPartner_ID(order.getC_BPartner_ID()); + if (order.getC_BPartner_Location_ID() != order.getC_BPartner_Location_ID()) + m_movement.setC_BPartner_Location_ID(order.getC_BPartner_Location_ID()); + if (!m_movement.save()) + throw new IllegalStateException("Could not create Movement"); + } + // Non Inventory Lines + if (storages == null) + { + MMovementLine line = new MMovementLine (m_movement); + line.setOrderLine(orderLine, Env.ZERO, false); + line.setMovementQty(qty); // Correct UOM + if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) + line.setMovementQty(qty + .multiply(orderLine.getQtyEntered()) + .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + line.setLine(m_line + orderLine.getLine()); + if (!line.save()) + throw new IllegalStateException("Could not create Shipment Line"); + log.fine(line.toString()); + return; + } + + // Product + MProduct product = orderLine.getProduct(); + boolean linePerASI = false; + if (product.getM_AttributeSet_ID() != 0) + { + MAttributeSet mas = MAttributeSet.get(getCtx(), product.getM_AttributeSet_ID()); + linePerASI = mas.isInstanceAttribute(); + } + + // Inventory Lines + ArrayList list = new ArrayList(); + BigDecimal toDeliver = qty; + for (int i = 0; i < storages.length; i++) + { + MStorage storage = storages[i]; + BigDecimal deliver = toDeliver; + // Not enough On Hand + if (deliver.compareTo(storage.getQtyOnHand()) > 0 + && storage.getQtyOnHand().signum() >= 0) // positive storage + { + if (!force // Adjust to OnHand Qty + || (force && i+1 != storages.length)) // if force not on last location + deliver = storage.getQtyOnHand(); + } + if (deliver.signum() == 0) // zero deliver + continue; + int M_Locator_ID = storage.getM_Locator_ID(); + // + MMovementLine line = null; + if (!linePerASI) // find line with Locator + { + for (int ll = 0; ll < list.size(); ll++) + { + MMovementLine test = (MMovementLine)list.get(ll); + if (test.getM_Locator_ID() == M_Locator_ID) + { + line = test; + break; + } + } + } + if (line == null) // new line + { + line = new MMovementLine (m_movement); + line.setOrderLine(orderLine, deliver , false); + line.setMovementQty(deliver); + list.add(line); + } + else // existing line + line.setMovementQty(line.getMovementQty().add(deliver)); + if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) + line.setMovementQty(line.getMovementQty().multiply(orderLine.getQtyEntered()) + .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + line.setLine(m_line + orderLine.getLine()); + if (linePerASI) + line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); + if (!line.save()) + throw new IllegalStateException("Could not create Shipment Line"); + log.fine("ToDeliver=" + qty + "/" + deliver + " - " + line); + toDeliver = toDeliver.subtract(deliver); + // Temp adjustment + storage.setQtyOnHand(storage.getQtyOnHand().subtract(deliver)); + // + if (toDeliver.signum() == 0) + break; + } + if (toDeliver.signum() != 0) + throw new IllegalStateException("Not All Delivered - Remainder=" + toDeliver); + } // createLine + + + /** + * Get Storages + * @param M_Warehouse_ID + * @param M_Product_ID + * @param M_AttributeSetInstance_ID + * @param M_AttributeSet_ID + * @param allAttributeInstances + * @param minGuaranteeDate + * @param FiFo + * @return storages + */ + private MStorage[] getStorages(int M_Warehouse_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, int M_AttributeSet_ID, + boolean allAttributeInstances, Timestamp minGuaranteeDate, + boolean FiFo) + { + m_lastPP = new SParameter(M_Warehouse_ID, + M_Product_ID, M_AttributeSetInstance_ID, M_AttributeSet_ID, + allAttributeInstances, minGuaranteeDate, FiFo); + // + m_lastStorages = m_map.get(m_lastPP); + + if (m_lastStorages == null) + { + m_lastStorages = MStorage.getWarehouse(getCtx(), + M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, + M_AttributeSet_ID, allAttributeInstances, minGuaranteeDate, + FiFo, get_TrxName()); + m_map.put(m_lastPP, m_lastStorages); + } + return m_lastStorages; + } // getStorages + + + /** + * Complete Shipment + */ + private void completeMovement() + { + if (m_movement != null) + { + // Fails if there is a confirmation + if (!m_movement.processIt(p_docAction)) + log.warning("Failed: " + m_movement); + m_movement.save(); + // + addLog(m_movement.getM_Movement_ID(), m_movement.getMovementDate(), null, m_movement.getDocumentNo()); + m_created++; + m_map = new HashMap(); + if (m_lastPP != null && m_lastStorages != null) + m_map.put(m_lastPP, m_lastStorages); + } + m_movement = null; + m_line = 0; + } // completeOrder + + /** + * MovementGenerate Parameter + */ + class SParameter + { + /** + * Parameter + * @param p_Warehouse_ID warehouse + * @param p_Product_ID + * @param p_AttributeSetInstance_ID + * @param p_AttributeSet_ID + * @param p_allAttributeInstances + * @param p_minGuaranteeDate + * @param p_FiFo + */ + protected SParameter (int p_Warehouse_ID, + int p_Product_ID, int p_AttributeSetInstance_ID, int p_AttributeSet_ID, + boolean p_allAttributeInstances, Timestamp p_minGuaranteeDate, + boolean p_FiFo) + { + this.M_Warehouse_ID = p_Warehouse_ID; + this.M_Product_ID = p_Product_ID; + this.M_AttributeSetInstance_ID = p_AttributeSetInstance_ID; + this.M_AttributeSet_ID = p_AttributeSet_ID; + this.allAttributeInstances = p_allAttributeInstances; + this.minGuaranteeDate = p_minGuaranteeDate; + this.FiFo = p_FiFo; + } + /** Warehouse */ + public int M_Warehouse_ID; + /** Product */ + public int M_Product_ID; + /** ASI */ + public int M_AttributeSetInstance_ID; + /** AS */ + public int M_AttributeSet_ID; + /** All instances */ + public boolean allAttributeInstances; + /** Mon Guarantee Date */ + public Timestamp minGuaranteeDate; + /** FiFo */ + public boolean FiFo; + + /** + * Equals + * @param obj + * @return true if equal + */ + public boolean equals (Object obj) + { + if (obj != null && obj instanceof SParameter) + { + SParameter cmp = (SParameter)obj; + boolean eq = cmp.M_Warehouse_ID == M_Warehouse_ID + && cmp.M_Product_ID == M_Product_ID + && cmp.M_AttributeSetInstance_ID == M_AttributeSetInstance_ID + && cmp.M_AttributeSet_ID == M_AttributeSet_ID + && cmp.allAttributeInstances == allAttributeInstances + && cmp.FiFo == FiFo; + if (eq) + { + if (cmp.minGuaranteeDate == null && minGuaranteeDate == null) + ; + else if (cmp.minGuaranteeDate != null && minGuaranteeDate != null + && cmp.minGuaranteeDate.equals(minGuaranteeDate)) + ; + else + eq = false; + } + return eq; + } + return false; + } // equals + + /** + * hashCode + * @return hash code + */ + public int hashCode () + { + long hash = M_Warehouse_ID + + (M_Product_ID * 2) + + (M_AttributeSetInstance_ID * 3) + + (M_AttributeSet_ID * 4); + + if (allAttributeInstances) + hash *= -1; + if (FiFo); + hash *= -2; + if (hash < 0) + hash = -hash + 7; + while (hash > Integer.MAX_VALUE) + hash -= Integer.MAX_VALUE; + // + if (minGuaranteeDate != null) + { + hash += minGuaranteeDate.hashCode(); + while (hash > Integer.MAX_VALUE) + hash -= Integer.MAX_VALUE; + } + + return (int)hash; + } // hashCode + + } // Parameter + +} // MovementGenerate