/**
 * Contains the code from the former jboss-aop aspect <code>de.metas.commission.aop.PriceListCreate
 * </code>
 *
 * @author ts
 * @task http://dewiki908/mediawiki/index.php/07286_get_rid_of_jboss-aop_for_good_%28104432455599%29
 */
public class CommissionPlvCreationListener implements IPlvCreationListener {

  private static final Logger logger = LogManager.getLogger(CommissionPlvCreationListener.class);

  @Override
  public void onPlvCreation(
      final IContextAware ctxAware,
      final I_M_PriceList_Version targetPriceListVersion,
      final Iterator<I_M_ProductPrice> oldProductPrices,
      final org.compiere.model.I_M_DiscountSchemaLine dsl,
      final int adPinstanceId) {
    final I_M_DiscountSchemaLine dslToUse =
        InterfaceWrapperHelper.create(dsl, I_M_DiscountSchemaLine.class);

    if (targetPriceListVersion.getM_Pricelist_Version_Base_ID() == 0) {
      logger.info(targetPriceListVersion + " has M_Pricelist_Version_Base_ID=0; nothing to do");
      // process.addLog("Only working with base price list version");
      return;
    }

    final IPriceListBL plBL = Services.get(IPriceListBL.class);

    final int plCountryId =
        InterfaceWrapperHelper.create(targetPriceListVersion.getM_PriceList(), I_M_PriceList.class)
            .getC_Country_ID();
    if (plCountryId <= 0) {
      if (dslToUse.isCommissionPoints_SubtractVAT()) {
        logger.info(
            "Ignoriere '@"
                + I_M_DiscountSchemaLine.COLUMNNAME_CommissionPoints_SubtractVAT
                + "@' fuer Produktpreise, da in der Preisliste "
                + targetPriceListVersion.getM_PriceList().getName()
                + " kein Land vermerkt ist.\n");
      }
    }

    final String trxName = ctxAware.getTrxName();
    plBL.updateCommissionPoints(targetPriceListVersion, dslToUse, adPinstanceId, trxName);
    plBL.updateSalcePriceCommissionPoints(targetPriceListVersion, dslToUse, adPinstanceId, trxName);

    return;
  }

  /**
   * Returns <code>10</code>.
   *
   * <p>From the orginal jboss-aop.xml file.
   *
   * <pre>
   *  Make sure that the commission aspect is called before the swat aspect.
   * 	The this means that the commission aspect will be called around the swat aspect (which in turn will be called around the joinpoint).
   * 	This further means that the commission aspect can work with the results created by the swat aspect.
   * </pre>
   */
  @Override
  public int getExecutionOrderSeqNo() {
    return 10;
  }
}
Esempio n. 2
0
/**
 * Issue Project (and Asset Link)
 *
 * @author Jorg Janke
 * @version $Id: MIssueProject.java,v 1.2 2006/07/30 00:58:18 jjanke Exp $
 */
public class MIssueProject extends X_R_IssueProject {
  /** */
  private static final long serialVersionUID = -9115162283984109370L;

  /**
   * Get/Set Project
   *
   * @param issue issue
   * @return project
   */
  public static MIssueProject get(MIssue issue) {
    if (issue.getName() == null) return null;
    MIssueProject pj = null;
    String sql = "SELECT * FROM R_IssueProject WHERE Name=?";
    PreparedStatement pstmt = null;
    try {
      pstmt = DB.prepareStatement(sql, null);
      pstmt.setString(1, issue.getName());
      ResultSet rs = pstmt.executeQuery();
      if (rs.next()) pj = new MIssueProject(issue.getCtx(), rs, null);
      rs.close();
      pstmt.close();
      pstmt = null;
    } catch (Exception e) {
      s_log.error(sql, e);
    }
    try {
      if (pstmt != null) pstmt.close();
      pstmt = null;
    } catch (Exception e) {
      pstmt = null;
    }
    //	New
    if (pj == null) {
      pj = new MIssueProject(issue.getCtx(), 0, null);
      pj.setName(issue.getName());
      pj.setA_Asset_ID(issue);
    }
    pj.setSystemStatus(issue.getSystemStatus());
    pj.setStatisticsInfo(issue.getStatisticsInfo());
    pj.setProfileInfo(issue.getProfileInfo());
    if (!pj.save()) return null;

    //	Set
    issue.setR_IssueProject_ID(pj.getR_IssueProject_ID());
    if (pj.getA_Asset_ID() != 0) issue.setA_Asset_ID(pj.getA_Asset_ID());
    return pj;
  } //	get

  /** Logger */
  private static Logger s_log = LogManager.getLogger(MIssueProject.class);

  /**
   * ************************************************************************ Standard Constructor
   *
   * @param ctx context
   * @param R_IssueProject_ID id
   * @param trxName trx
   */
  public MIssueProject(Properties ctx, int R_IssueProject_ID, String trxName) {
    super(ctx, R_IssueProject_ID, trxName);
  } //	MIssueProject

  /**
   * Load Constructor
   *
   * @param ctx context
   * @param rs result set
   * @param trxName trx
   */
  public MIssueProject(Properties ctx, ResultSet rs, String trxName) {
    super(ctx, rs, trxName);
  } //	MIssueProject

  /**
   * Set A_Asset_ID
   *
   * @param issue issue
   */
  public void setA_Asset_ID(MIssue issue) {
    int A_Asset_ID = 0;
    String sql =
        "SELECT * FROM A_Asset a "
            + "WHERE EXISTS (SELECT * FROM A_Asset_Group ag " //	Tracking Assets
            + "WHERE a.A_Asset_Group_ID=ag.A_Asset_Group_ID AND ag.IsTrackIssues='Y')"
            + " AND EXISTS (SELECT * FROM AD_User u "
            + "WHERE (a.C_BPartner_ID=u.C_BPartner_ID OR a.C_BPartnerSR_ID=u.C_BPartner_ID)"
            + " AND u.EMail=?)" //	#1 EMail
            + " AND (SerNo IS NULL OR SerNo=?)"; //	#2 Name

    super.setA_Asset_ID(A_Asset_ID);
  } //	setA_Asset_ID

  /**
   * String Representation
   *
   * @return info
   */
  public String toString() {
    StringBuffer sb = new StringBuffer("MIssueProject[");
    sb.append(get_ID())
        .append("-")
        .append(getName())
        .append(",A_Asset_ID=")
        .append(getA_Asset_ID())
        .append(",C_Project_ID=")
        .append(getC_Project_ID())
        .append("]");
    return sb.toString();
  } //	toString
} //	MIssueProject
Esempio n. 3
0
public class C_Order implements ModelValidator {
  private static final Logger logger = LogManager.getLogger(C_OrderLine.class);

  private static final String MSG_ORDER_DATE_ORDERED_CHANGE_FORBIDDEN_1P =
      "Order_DateOrdered_Change_Forbidden";

  private int m_AD_Client_ID = -1;

  @Override
  public int getAD_Client_ID() {
    return m_AD_Client_ID;
  }

  @Override
  public void initialize(final ModelValidationEngine engine, final MClient client) {
    if (client != null) m_AD_Client_ID = client.getAD_Client_ID();

    engine.addModelChange(I_C_Order.Table_Name, this);
    engine.addDocValidate(I_C_Order.Table_Name, this);
  }

  @Override
  public String login(final int AD_Org_ID, final int AD_Role_ID, final int AD_User_ID) {
    return null;
  }

  @Override
  public String modelChange(final PO po, final int type) throws Exception {
    if (type == TYPE_BEFORE_CHANGE) {
      final I_C_Order order = InterfaceWrapperHelper.create(po, I_C_Order.class);

      if (po.is_ValueChanged(I_C_Invoice_Candidate.COLUMNNAME_DateOrdered)) {
        final IOrderPA orderPA = Services.get(IOrderPA.class);
        final IInvoiceCandDAO invoiceCandDB = Services.get(IInvoiceCandDAO.class);

        for (final I_C_OrderLine ol : orderPA.retrieveOrderLines(order, I_C_OrderLine.class)) {
          for (final I_C_Invoice_Candidate icOfOl : invoiceCandDB.retrieveReferencing(ol)) {
            if (icOfOl.isToClear()) {
              // If the column was updatable, we would have to
              // *check if new and old term are the same
              // *check if ICAs need update, creation or deletion and do it;
              // *check which dataEntries' ActualQty needs update and make sure that they are not
              // yet
              // completed
              // *check is isToClear needs update;
              throw new AdempiereException(
                  Env.getAD_Language(po.getCtx()),
                  MSG_ORDER_DATE_ORDERED_CHANGE_FORBIDDEN_1P,
                  new Object[] {ol.getLine()});
            }
          }
        }
      }
    }
    return null;
  }

  // Note: this code used to be located in
  // /sw01_swat_it/src/java/org/adempiere/order/subscription/modelvalidator/OrderValidator.java
  @Override
  public String docValidate(final PO po, final int timing) {
    if (timing != TIMING_AFTER_COMPLETE && timing != TIMING_AFTER_REACTIVATE) {
      return null;
    }

    final String trxName = po.get_TrxName();
    final I_C_Order order = InterfaceWrapperHelper.create(po, I_C_Order.class);

    final IOrderPA orderPA = Services.get(IOrderPA.class);

    for (final I_C_OrderLine ol : orderPA.retrieveOrderLines(order, I_C_OrderLine.class)) {
      if (ol.getC_Flatrate_Conditions_ID() <= 0) {
        logger.debug("Order line " + ol + " has no subscription");
        continue;
      }
      if (timing == TIMING_AFTER_COMPLETE) {
        handleOrderLineComplete(order, ol, trxName);

      } else if (timing == TIMING_AFTER_REACTIVATE) {
        handleOrderLineReactivate(ol, trxName);
      }
    }
    return null;
  }

  private void handleOrderLineComplete(
      final I_C_Order order, final I_C_OrderLine ol, final String trxName) {
    final ISubscriptionDAO subscriptionDAO = Services.get(ISubscriptionDAO.class);

    final I_C_Flatrate_Term existingSc = subscriptionDAO.retrieveTermForOl(ol);
    if (existingSc != null) {
      logger.debug("{} has already {}", ol, existingSc);
      return;
    }

    logger.info("Creating new {} entry", I_C_Flatrate_Term.Table_Name);

    // Note that order.getDocStatus() might still return 'IP' at this point
    final I_C_Flatrate_Term newSc =
        Services.get(ISubscriptionBL.class).createSubscriptionTerm(ol, true, order);

    Check.assume(
        X_C_Flatrate_Term.DOCSTATUS_Completed.equals(newSc.getDocStatus()),
        "{} has DocStatus={}",
        newSc,
        newSc.getDocStatus());
    logger.info("Created and completed {}", newSc);
  }

  /**
   * Make sure the orderLine still has processed='Y', even if the order is reactivated. <br>
   * This was apparently added in task 03152.<br>
   * I can guess that if an order line already has a C_Flatrate_Term, then we don't want that order
   * line to be editable, because it could create inconsistencies with the term.
   *
   * @param ol
   * @param trxName
   */
  private void handleOrderLineReactivate(final I_C_OrderLine ol, final String trxName) {
    logger.info(
        "Setting processed status of subscription order line " + ol + " back to Processed='Y'");

    final String sql = "UPDATE C_OrderLine SET Processed='Y' WHERE C_OrderLine_ID=?";
    final int no = DB.executeUpdateEx(sql, new Object[] {ol.getC_OrderLine_ID()}, trxName);
    logger.trace("Update result: " + no);
  }
}
Esempio n. 4
0
/**
 * Order Line Model. <code>
 * 			MOrderLine ol = new MOrderLine(m_order);
 * ol.setM_Product_ID(wbl.getM_Product_ID());
 * ol.setQtyOrdered(wbl.getQuantity());
 * ol.setPrice();
 * ol.setPriceActual(wbl.getPrice());
 * ol.setTax();
 * ol.save();
 *
 * </code>
 *
 * @author Jorg Janke
 * @version $Id: MOrderLine.java,v 1.6 2006/10/02 05:18:39 jjanke Exp $
 * @author Teo Sarca, SC ARHIPAC SERVICE SRL
 *     <ul>
 *       <li>BF [ 2588043 ] Insufficient message ProductNotOnPriceList
 * @author Michael Judd, www.akunagroup.com
 *     <ul>
 *       <li>BF [ 1733602 ] Price List including Tax Error - when a user changes the orderline or
 *           invoice line for a product on a price list that includes tax, the net amount is
 *           incorrectly calculated.
 */
public class MOrderLine extends X_C_OrderLine {
  /** */
  private static final long serialVersionUID = 7305265800857547603L;

  public static final String MSG_PriceListVersionInvalid = "PriceListVersionInvalid";

  /**
   * Get Order Unreserved Qty
   *
   * @param ctx context
   * @param M_Warehouse_ID wh
   * @param M_Product_ID product
   * @param M_AttributeSetInstance_ID asi
   * @param excludeC_OrderLine_ID exclude C_OrderLine_ID
   * @return Unreserved Qty
   */
  public static BigDecimal getNotReserved(
      Properties ctx,
      int M_Warehouse_ID,
      int M_Product_ID,
      int M_AttributeSetInstance_ID,
      int excludeC_OrderLine_ID) {
    BigDecimal retValue = Env.ZERO;
    String sql =
        "SELECT SUM(ol.QtyOrdered-ol.QtyDelivered-ol.QtyReserved) "
            + "FROM C_OrderLine ol"
            + " INNER JOIN C_Order o ON (ol.C_Order_ID=o.C_Order_ID) "
            + "WHERE ol.M_Warehouse_ID=?" // #1
            // metas: adding table alias "ol" to M_Product_ID to distinguish it from C_Order's
            // M_Product_ID
            + " AND ol.M_Product_ID=?" // #2
            + " AND o.IsSOTrx='Y' AND o.DocStatus='DR'"
            + " AND ol.QtyOrdered-ol.QtyDelivered-ol.QtyReserved<>0"
            + " AND ol.C_OrderLine_ID<>?";
    if (M_AttributeSetInstance_ID != 0) sql += " AND ol.M_AttributeSetInstance_ID=?";

    PreparedStatement pstmt = null;
    try {
      pstmt = DB.prepareStatement(sql, ITrx.TRXNAME_None);
      pstmt.setInt(1, M_Warehouse_ID);
      pstmt.setInt(2, M_Product_ID);
      pstmt.setInt(3, excludeC_OrderLine_ID);
      if (M_AttributeSetInstance_ID != 0) pstmt.setInt(4, M_AttributeSetInstance_ID);
      ResultSet rs = pstmt.executeQuery();
      if (rs.next()) retValue = rs.getBigDecimal(1);
      rs.close();
      pstmt.close();
      pstmt = null;
    } catch (Exception e) {
      s_log.error(sql, e);
    }
    try {
      if (pstmt != null) pstmt.close();
      pstmt = null;
    } catch (Exception e) {
      pstmt = null;
    }
    if (retValue == null) s_log.debug("-");
    else s_log.debug(retValue.toString());
    return retValue;
  } // getNotReserved

  /** Logger */
  private static Logger s_log = LogManager.getLogger(MOrderLine.class);

  /**
   * ************************************************************************ Default Constructor
   *
   * @param ctx context
   * @param C_OrderLine_ID order line to load
   * @param trxName trx name
   */
  public MOrderLine(Properties ctx, int C_OrderLine_ID, String trxName) {
    super(ctx, C_OrderLine_ID, trxName);
    if (C_OrderLine_ID == 0) {
      // setC_Order_ID (0);
      // setLine (0);
      // setM_Warehouse_ID (0); // @M_Warehouse_ID@
      // setC_BPartner_ID(0);
      // setC_BPartner_Location_ID (0); // @C_BPartner_Location_ID@
      // setC_Currency_ID (0); // @C_Currency_ID@
      // setDateOrdered (new Timestamp(System.currentTimeMillis())); // @DateOrdered@
      //
      // setC_Tax_ID (0);
      // setC_UOM_ID (0);
      //
      setFreightAmt(Env.ZERO);
      setLineNetAmt(Env.ZERO);
      //
      setPriceEntered(Env.ZERO);
      setPriceActual(Env.ZERO);
      setPriceLimit(Env.ZERO);
      setPriceList(Env.ZERO);
      //
      setM_AttributeSetInstance_ID(0);
      //
      setQtyEntered(Env.ZERO);
      setQtyOrdered(Env.ZERO); // 1
      setQtyDelivered(Env.ZERO);
      setQtyInvoiced(Env.ZERO);
      // task 09358: get rid of this; instead, update qtyReserved at one central place
      // setQtyReserved(Env.ZERO);
      //
      setIsDescription(false); // N
      setProcessed(false);
      setLine(0);
    }
  } // MOrderLine

  /**
   * Parent Constructor.
   *
   * <ul>
   *   <li>ol.setM_Product_ID(wbl.getM_Product_ID());
   *   <li>ol.setQtyOrdered(wbl.getQuantity());
   *   <li>ol.setPrice();
   *   <li>ol.setPriceActual(wbl.getPrice());
   *   <li>ol.setTax();
   *   <li>ol.save();
   *
   * @param order parent order
   */
  public MOrderLine(MOrder order) {
    this(order.getCtx(), 0, order.get_TrxName());
    if (order.get_ID() == 0) throw new IllegalArgumentException("Header not saved");
    setC_Order_ID(order.getC_Order_ID()); // parent
    setOrder(order);
  } // MOrderLine

  /**
   * Load Constructor
   *
   * @param ctx context
   * @param rs result set record
   * @param trxName transaction
   */
  public MOrderLine(Properties ctx, ResultSet rs, String trxName) {
    super(ctx, rs, trxName);
  } // MOrderLine

  private int m_M_PriceList_ID = 0;
  //
  private boolean m_IsSOTrx = true;
  // Product Pricing
  private MProductPricing m_productPrice = null;

  /** Tax */
  private MTax m_tax = null;

  /** Cached Currency Precision */
  private Integer m_precision = null;
  /** Product */
  private MProduct m_product = null;
  /** Charge */
  private MCharge m_charge = null;

  /**
   * Set Defaults from Order. Does not set Parent !!
   *
   * @param order order
   */
  public void setOrder(MOrder order) {
    setClientOrg(order);
    final boolean isDropShip = order.isDropShip();
    final int C_BPartner_ID =
        isDropShip && order.getDropShip_BPartner_ID() > 0
            ? order.getDropShip_BPartner_ID()
            : order.getC_BPartner_ID();
    setC_BPartner_ID(C_BPartner_ID);

    final int C_BPartner_Location_ID =
        isDropShip && order.getDropShip_Location_ID() > 0
            ? order.getDropShip_Location_ID()
            : order.getC_BPartner_Location_ID();
    setC_BPartner_Location_ID(C_BPartner_Location_ID);

    // metas: begin: copy AD_User_ID
    final de.metas.interfaces.I_C_OrderLine oline =
        InterfaceWrapperHelper.create(this, de.metas.interfaces.I_C_OrderLine.class);
    final int AD_User_ID =
        isDropShip && order.getDropShip_User_ID() > 0
            ? order.getDropShip_User_ID()
            : order.getAD_User_ID();
    oline.setAD_User_ID(AD_User_ID);
    // metas: end

    oline.setM_PriceList_Version_ID(
        0); // the current PLV might be add or'd with the new order's PL.

    setM_Warehouse_ID(order.getM_Warehouse_ID());
    setDateOrdered(order.getDateOrdered());
    setDatePromised(order.getDatePromised());
    setC_Currency_ID(order.getC_Currency_ID());
    //
    setHeaderInfo(order); // sets m_order
    // Don't set Activity, etc as they are overwrites
  } // setOrder

  /**
   * Set Header Info
   *
   * @param order order
   */
  public void setHeaderInfo(final MOrder order) {
    final IOrderBL orderBL = Services.get(IOrderBL.class);

    m_precision = orderBL.getPrecision(order);
    m_M_PriceList_ID = orderBL.retrievePriceListId(order);
    m_IsSOTrx = order.isSOTrx();
  } // setHeaderInfo

  /**
   * Get Parent
   *
   * @return parent
   */
  public MOrder getParent() {
    return LegacyAdapters.convertToPO(getC_Order());
  } // getParent

  /**
   * Set Price Entered/Actual. Use this Method if the Line UOM is the Product UOM
   *
   * @param PriceActual price
   */
  public void setPrice(BigDecimal PriceActual) {
    setPriceEntered(PriceActual);
    setPriceActual(PriceActual);
  } // setPrice

  /**
   * Set Price Actual. (actual price is not updateable)
   *
   * @param PriceActual actual price
   */
  @Override
  public void setPriceActual(BigDecimal PriceActual) {
    if (PriceActual == null) throw new IllegalArgumentException("PriceActual is mandatory");
    set_ValueNoCheck("PriceActual", PriceActual);
  } // setPriceActual

  /**
   * Set Price for Product and PriceList. Use only if newly created. Uses standard price list of not
   * set by order constructor
   */
  public void setPrice() {
    if (getM_Product_ID() <= 0) return;
    if (m_M_PriceList_ID <= 0) {
      throw new AdempiereException(
          "@NotFound@ @M_Pricelist_ID@ @C_BPartner_ID@ " + getC_BPartner().getName());
    }
    setPrice(m_M_PriceList_ID);
  } // setPrice

  /**
   * Set Price for Product and PriceList
   *
   * @param M_PriceList_ID price list
   */
  public void setPrice(int M_PriceList_ID) {
    if (getM_Product_ID() <= 0) return;
    //
    final de.metas.interfaces.I_C_OrderLine ol =
        InterfaceWrapperHelper.create(this, de.metas.interfaces.I_C_OrderLine.class);
    Services.get(IOrderLineBL.class).updatePrices(ol);
  } // setPrice

  /**
   * Get and calculate Product Pricing
   *
   * @param M_PriceList_ID id
   * @param M_PriceList_Version_ID
   * @return product pricing
   */
  private MProductPricing getProductPricing(int M_PriceList_ID, int M_PriceList_Version_ID) {
    final I_M_PriceList_Version plv =
        InterfaceWrapperHelper.create(
            getCtx(), M_PriceList_Version_ID, I_M_PriceList_Version.class, get_TrxName());
    if (M_PriceList_Version_ID > 0) {
      // If we have a pricelist version, make sure it belongs to the pricelist
      Check.assume(
          M_PriceList_ID == plv.getM_PriceList_ID(),
          Msg.getMsg(getCtx(), MSG_PriceListVersionInvalid));
    }

    m_productPrice =
        new MProductPricing(getM_Product_ID(), getC_BPartner_ID(), getQtyOrdered(), m_IsSOTrx);

    m_productPrice.setReferencedObject(
        this); // 03152: setting the 'ol' to allow the subscription system to compute the right
               // price
    m_productPrice.setPriceDate(
        getDatePromised()); // important: need to use the data when the service will be provided, so
                            // we make sure that we get the right PLV
    m_productPrice.setM_PriceList_ID(M_PriceList_ID);
    m_productPrice.setPriceDate(getDateOrdered());
    m_productPrice.setM_PriceList_Version_ID(M_PriceList_Version_ID);
    //
    m_productPrice.calculatePrice();
    return m_productPrice;
  } // getProductPrice

  /**
   * Set Tax
   *
   * @return true if tax is set
   */
  public boolean setTax() {
    // metas: we need to fetch the Tax based on pricing tax category and not directly

    // int ii = Tax.get(getCtx(), getM_Product_ID(), getC_Charge_ID(), getDateOrdered(),
    // getDateOrdered(),
    // getAD_Org_ID(),
    // Services.get(IWarehouseAdvisor.class).evaluateWarehouse(this).getM_Warehouse_ID(),
    // getC_BPartner_Location_ID(), // should be bill to
    // getC_BPartner_Location_ID(), m_IsSOTrx);

    final int taxCategoryId = Services.get(IOrderLineBL.class).getC_TaxCategory_ID(this);
    if (taxCategoryId <= 0) {
      log.error("No Tax Category found");
      return false;
    }

    final I_M_Warehouse warehouse = Services.get(IWarehouseAdvisor.class).evaluateWarehouse(this);
    final I_C_Location locationFrom = Services.get(IWarehouseBL.class).getC_Location(warehouse);
    final int countryFromId = locationFrom.getC_Country_ID();
    final int taxId =
        Services.get(ITaxBL.class)
            .retrieveTaxIdForCategory(
                getCtx(),
                countryFromId,
                getAD_Org_ID(),
                getC_BPartner_Location(), // should be bill to
                getDateOrdered(),
                taxCategoryId,
                m_IsSOTrx,
                get_TrxName(),
                true); // throwEx

    if (taxId <= 0) {
      log.error("No Tax found");
      return false;
    }
    setC_Tax_ID(taxId);

    final I_C_Tax tax =
        InterfaceWrapperHelper.create(getCtx(), taxId, I_C_Tax.class, ITrx.TRXNAME_None);

    final I_C_TaxCategory taxCategory = tax.getC_TaxCategory();
    setC_TaxCategory(taxCategory);

    return true;
  } // setTax

  /** Calculate Extended Amt. May or may not include tax */
  public void setLineNetAmt() {
    BigDecimal bd = getPriceActual().multiply(getQtyOrdered());
    // metas: tsa: begin: 01459
    // Line Net Amt shall be zero if the line is not active
    if (!isActive()) {
      bd = Env.ZERO;
    }
    // metas: tsa: end: 01459

    final boolean documentLevel = getTax().isDocumentLevel();
    final boolean isTaxIncluded = Services.get(IOrderLineBL.class).isTaxIncluded(this);
    final int taxPrecision = Services.get(IOrderLineBL.class).getPrecision(this);

    // juddm: Tax Exempt & Tax Included in Price List & not Document Level - Adjust Line Amount
    // http://sourceforge.net/tracker/index.php?func=detail&aid=1733602&group_id=176962&atid=879332
    if (isTaxIncluded && !documentLevel) {
      BigDecimal taxStdAmt = Env.ZERO, taxThisAmt = Env.ZERO;

      MTax orderTax = getTax();
      MTax stdTax = null;

      // get the standard tax
      if (getProduct() == null) {
        if (getCharge() != null) // Charge
        {
          stdTax =
              new MTax(
                  getCtx(),
                  ((MTaxCategory) getCharge().getC_TaxCategory()).getDefaultTax().getC_Tax_ID(),
                  get_TrxName());
        }
      } else
      // Product
      {
        // FIXME metas 05129 need proper concept (link between M_Product and C_TaxCategory_ID was
        // removed!!!!!)
        throw new AdempiereException(
            "Unsupported tax calculation when tax is included, but it's not on document level");
        // stdTax = new MTax (getCtx(),
        // ((MTaxCategory) getProduct().getC_TaxCategory()).getDefaultTax().getC_Tax_ID(),
        // get_TrxName());
      }

      if (stdTax != null) {
        log.debug("stdTax rate is " + stdTax.getRate());
        log.debug("orderTax rate is " + orderTax.getRate());

        final ITaxBL taxBL = Services.get(ITaxBL.class);
        taxThisAmt = taxThisAmt.add(taxBL.calculateTax(orderTax, bd, isTaxIncluded, taxPrecision));
        taxStdAmt = taxStdAmt.add(taxBL.calculateTax(stdTax, bd, isTaxIncluded, taxPrecision));

        bd = bd.subtract(taxStdAmt).add(taxThisAmt);

        log.debug(
            "Price List includes Tax and Tax Changed on Order Line: New Tax Amt: "
                + taxThisAmt
                + " Standard Tax Amt: "
                + taxStdAmt
                + " Line Net Amt: "
                + bd);
      }
    }

    if (bd.scale() > taxPrecision) bd = bd.setScale(taxPrecision, BigDecimal.ROUND_HALF_UP);
    super.setLineNetAmt(bd);
  } // setLineNetAmt

  /**
   * Get Charge
   *
   * @return product or null
   */
  public MCharge getCharge() {
    if (m_charge == null && getC_Charge_ID() != 0)
      m_charge = MCharge.get(getCtx(), getC_Charge_ID());
    return m_charge;
  }

  /**
   * Get Tax
   *
   * @return tax
   */
  protected MTax getTax() {
    if (m_tax == null) m_tax = MTax.get(getCtx(), getC_Tax_ID());
    return m_tax;
  } // getTax

  /**
   * Get Currency Precision from Currency
   *
   * @return precision
   */
  public int getPrecision() {
    if (m_precision != null) {
      return m_precision;
    }

    //
    if (getC_Currency_ID() == 0) {
      setOrder(getParent());
      if (m_precision != null) return m_precision;
    }
    if (getC_Currency_ID() > 0) {
      final I_C_Currency cur =
          Services.get(ICurrencyDAO.class).retrieveCurrency(getCtx(), getC_Currency_ID());
      if (cur.getC_Currency_ID() != 0) {
        m_precision = cur.getStdPrecision();
        return m_precision;
      }
    }

    //
    // Fallback
    // FIXME: drop this, i guess is not used AT ALL
    final String sql =
        "SELECT c.StdPrecision "
            + "FROM C_Currency c INNER JOIN C_Order x ON (x.C_Currency_ID=c.C_Currency_ID) "
            + "WHERE x.C_Order_ID=?";
    m_precision = DB.getSQLValue(get_TrxName(), sql, getC_Order_ID());
    return m_precision;
  } // getPrecision

  /**
   * Set Product
   *
   * @param product product
   */
  public void setProduct(MProduct product) {
    Services.get(IOrderLineBL.class)
        .setM_Product_ID(
            InterfaceWrapperHelper.create(this, de.metas.interfaces.I_C_OrderLine.class),
            product.getM_Product_ID(),
            true);
  } // setProduct

  /**
   * Set M_Product_ID
   *
   * @param M_Product_ID product
   * @param setUOM set also UOM
   */
  public void setM_Product_ID(int M_Product_ID, boolean setUOM) {
    if (setUOM) setProduct(MProduct.get(getCtx(), M_Product_ID));
    else super.setM_Product_ID(M_Product_ID);
    setM_AttributeSetInstance_ID(0);
  } // setM_Product_ID

  /**
   * Set Product and UOM
   *
   * @param M_Product_ID product
   * @param C_UOM_ID uom
   */
  public void setM_Product_ID(int M_Product_ID, int C_UOM_ID) {
    super.setM_Product_ID(M_Product_ID);
    if (C_UOM_ID != 0) super.setC_UOM_ID(C_UOM_ID);
    setM_AttributeSetInstance_ID(0);
  } // setM_Product_ID

  /**
   * Get Product
   *
   * @return product or null
   */
  public MProduct getProduct() {
    if (m_product == null && getM_Product_ID() != 0)
      m_product = MProduct.get(getCtx(), getM_Product_ID());
    return m_product;
  } // getProduct

  /**
   * Set M_AttributeSetInstance_ID
   *
   * @param M_AttributeSetInstance_ID id
   */
  @Override
  public void setM_AttributeSetInstance_ID(int M_AttributeSetInstance_ID) {
    if (M_AttributeSetInstance_ID == 0) // 0 is valid ID
    set_Value("M_AttributeSetInstance_ID", new Integer(0));
    else super.setM_AttributeSetInstance_ID(M_AttributeSetInstance_ID);
  } // setM_AttributeSetInstance_ID

  /**
   * Set Warehouse
   *
   * @param M_Warehouse_ID warehouse
   */
  @Override
  public void setM_Warehouse_ID(int M_Warehouse_ID) {
    if (getM_Warehouse_ID() > 0
        && getM_Warehouse_ID() != M_Warehouse_ID
        && !canChangeWarehouse(
            false) // trowEx=false for legacy purposes. We need to evaluate and consider to throw
                   // exception
    ) {
      log.error("Ignored - Already Delivered/Invoiced/Reserved");
    } else {
      super.setM_Warehouse_ID(M_Warehouse_ID);
    }
  } // setM_Warehouse_ID

  /**
   * Can Change Warehouse
   *
   * @param throwEx if <code>true</code> an exception will be thrown in case something is not valid
   * @return true if warehouse can be changed
   */
  public final boolean canChangeWarehouse(final boolean throwEx) {
    if (getQtyDelivered().signum() != 0) {
      return new AdempiereException("@CannotChangeWarehouse@ @QtyDelivered@=" + getQtyDelivered())
          .throwOrLogSevere(throwEx, log);
    }
    if (getQtyInvoiced().signum() != 0) {
      return new AdempiereException("@CannotChangeWarehouse@ @QtyInvoiced@=" + getQtyInvoiced())
          .throwOrLogSevere(throwEx, log);
    }
    if (getQtyReserved().signum() != 0) {
      return new AdempiereException("@CannotChangeWarehouse@ @QtyReserved@=" + getQtyReserved())
          .throwOrLogSevere(throwEx, log);
    }
    // We can change
    return true;
  } // canChangeWarehouse

  /**
   * Get C_Project_ID
   *
   * @return project
   */
  @Override
  public int getC_Project_ID() {
    int ii = super.getC_Project_ID();
    if (ii == 0) ii = getParent().getC_Project_ID();
    return ii;
  } // getC_Project_ID

  /**
   * Get C_Activity_ID
   *
   * @return Activity
   */
  @Override
  public int getC_Activity_ID() {
    int ii = super.getC_Activity_ID();
    if (ii == 0) ii = getParent().getC_Activity_ID();
    return ii;
  } // getC_Activity_ID

  /**
   * Get C_Campaign_ID
   *
   * @return Campaign
   */
  @Override
  public int getC_Campaign_ID() {
    int ii = super.getC_Campaign_ID();
    if (ii == 0) ii = getParent().getC_Campaign_ID();
    return ii;
  } // getC_Campaign_ID

  /**
   * Get User2_ID
   *
   * @return User2
   */
  @Override
  public int getUser1_ID() {
    int ii = super.getUser1_ID();
    if (ii == 0) ii = getParent().getUser1_ID();
    return ii;
  } // getUser1_ID

  /**
   * Get User2_ID
   *
   * @return User2
   */
  @Override
  public int getUser2_ID() {
    int ii = super.getUser2_ID();
    if (ii == 0) ii = getParent().getUser2_ID();
    return ii;
  } // getUser2_ID

  /**
   * Get AD_OrgTrx_ID
   *
   * @return trx org
   */
  @Override
  public int getAD_OrgTrx_ID() {
    int ii = super.getAD_OrgTrx_ID();
    if (ii == 0) ii = getParent().getAD_OrgTrx_ID();
    return ii;
  } // getAD_OrgTrx_ID

  /**
   * ************************************************************************ String Representation
   *
   * @return info
   */
  @Override
  public String toString() {
    StringBuffer sb =
        new StringBuffer("MOrderLine[")
            .append(get_ID())
            .append(", Line=")
            .append(getLine())
            .append(", Ordered=")
            .append(getQtyOrdered())
            .append(", Delivered=")
            .append(getQtyDelivered())
            .append(", Invoiced=")
            .append(getQtyInvoiced())
            .append(", Reserved=")
            .append(getQtyReserved())
            .append(", LineNet=")
            .append(getLineNetAmt())
            .append("]");
    return sb.toString();
  } // toString

  /**
   * Add to Description
   *
   * @param description text
   */
  public void addDescription(String description) {
    String desc = getDescription();
    if (desc == null) setDescription(description);
    else setDescription(desc + " | " + description);
  } // addDescription

  /**
   * Get Description Text. For jsp access (vs. isDescription)
   *
   * @return description
   */
  public String getDescriptionText() {
    return super.getDescription();
  } // getDescriptionText

  /**
   * Get Name
   *
   * @return get the name of the line (from Product)
   */
  public String getName() {
    getProduct();
    if (m_product != null) return m_product.getName();
    if (getC_Charge_ID() != 0) {
      MCharge charge = MCharge.get(getCtx(), getC_Charge_ID());
      return charge.getName();
    }
    return "";
  } // getName

  /**
   * Set C_Charge_ID
   *
   * @param C_Charge_ID charge
   */
  @Override
  public void setC_Charge_ID(int C_Charge_ID) {
    super.setC_Charge_ID(C_Charge_ID);
    if (C_Charge_ID > 0) set_ValueNoCheck("C_UOM_ID", null);
  } // setC_Charge_ID

  /** Set Discount */
  public void setDiscount() {
    BigDecimal list = getPriceList();
    // No List Price
    if (Env.ZERO.compareTo(list) == 0) return;
    // final int precision = getPrecision();
    final int precision = 1; // metas
    // TODO: metas: why we are using precision=1 instead of getPrecision()?
    BigDecimal discount =
        list.subtract(getPriceActual())
            .multiply(new BigDecimal(100))
            .divide(list, precision, BigDecimal.ROUND_HALF_UP);
    setDiscount(discount);
  } // setDiscount

  /**
   * Set Qty Entered/Ordered. Use this Method if the Line UOM is the Product UOM
   *
   * @param Qty QtyOrdered/Entered
   */
  public void setQty(BigDecimal Qty) {
    super.setQtyEntered(Qty);
    super.setQtyOrdered(getQtyEntered());
  } // setQty

  /**
   * Set Qty Entered - enforce entered UOM
   *
   * @param QtyEntered
   */
  @Override
  public void setQtyEntered(BigDecimal QtyEntered) {
    if (QtyEntered != null && getC_UOM_ID() != 0) {
      int precision = MUOM.getPrecision(getCtx(), getC_UOM_ID());
      QtyEntered = QtyEntered.setScale(precision, BigDecimal.ROUND_HALF_UP);
    }
    super.setQtyEntered(QtyEntered);
  } // setQtyEntered

  /**
   * Set Qty Ordered - enforce Product UOM
   *
   * @param QtyOrdered
   */
  @Override
  public void setQtyOrdered(BigDecimal QtyOrdered) {
    MProduct product = getProduct();
    if (QtyOrdered != null && product != null) {
      int precision = product.getUOMPrecision();
      QtyOrdered = QtyOrdered.setScale(precision, BigDecimal.ROUND_HALF_UP);
    }
    super.setQtyOrdered(QtyOrdered);
  } // setQtyOrdered

  /**
   * ************************************************************************ Before Save
   *
   * @param newRecord
   * @return true if it can be saved
   */
  @Override
  protected boolean beforeSave(boolean newRecord) {

    final boolean complete = getParent().isComplete();
    if (newRecord && complete) {
      throw new AdempiereException("@ParentComplete@ @C_OrderLine_ID@");
    }

    // In case our order is complete do nothing, don't update any field
    if (complete) {
      // TODO: make sure that only QtyDelivered, QtyInvoiced fields are updated.
      // The rest shall be forbidden.
      // NOTE: also please check if those are the only fields which are updated after an order is
      // completed
      return true;
    }

    // Get Defaults from Parent
    final I_M_Warehouse warehouse = Services.get(IWarehouseAdvisor.class).evaluateWarehouse(this);
    if (getC_BPartner_ID() <= 0
        || getC_BPartner_Location_ID() <= 0
        || warehouse == null
        || warehouse.getM_Warehouse_ID() <= 0
        || getC_Currency_ID() <= 0) {
      setOrder(getParent());
    }

    // metas: try to get the pl-id from our plv
    if (m_M_PriceList_ID <= 0) {
      final int plvId =
          get_ValueAsInt(de.metas.interfaces.I_C_OrderLine.COLUMNNAME_M_PriceList_Version_ID);
      if (plvId > 0) {
        m_M_PriceList_ID =
            DB.getSQLValueEx(
                get_TrxName(),
                "SELECT M_PriceList_ID FROM M_PriceList_Version WHERE M_PriceList_Version_ID="
                    + plvId);
      }
    }
    // metas: end
    if (m_M_PriceList_ID <= 0) setHeaderInfo(getParent());

    // R/O Check - Product/Warehouse Change
    if (!newRecord && (is_ValueChanged("M_Product_ID") || is_ValueChanged("M_Warehouse_ID"))) {
      if (!canChangeWarehouse(true)) return false;
    } // Product Changed

    // Charge
    if (getC_Charge_ID() != 0 && getM_Product_ID() != 0) setM_Product_ID(0);
    // No Product
    if (getM_Product_ID() == 0) setM_AttributeSetInstance_ID(0);
    // Product
    else
    // Set/check Product Price
    {
      // Set Price if Actual = 0
      if (m_productPrice == null
          && getPriceActual().signum() == 0
          && getPriceList().signum() == 0) {
        setPrice();
      }

      // Check if on Price list
      if (m_productPrice == null)
        getProductPricing(
            m_M_PriceList_ID,
            get_ValueAsInt(de.metas.interfaces.I_C_OrderLine.COLUMNNAME_M_PriceList_Version_ID));
      if (!m_productPrice.isCalculated()) {
        throw new ProductNotOnPriceListException(m_productPrice, getLine());
      }
    }

    // metas: Not allowed to save without (Product or Charge) and qty > 0
    if (getM_Product_ID() == 0 && getC_Charge_ID() == 0 && getQtyEntered().intValue() > 0)
      throw new AdempiereException("@NotFound@ @M_Product_ID@/@C_Charge_ID@ (@QtyEntered@>0)");

    // UOM
    if (getC_UOM_ID() == 0
        && (getM_Product_ID() != 0
            || getPriceEntered().compareTo(Env.ZERO) != 0
            || getC_Charge_ID() != 0)) {
      int C_UOM_ID = MUOM.getDefault_UOM_ID(getCtx());
      if (C_UOM_ID > 0) setC_UOM_ID(C_UOM_ID);
    }

    // Price_UOM task 06942
    // note: we do not set the price-UOM, because that would only make sense if we also set the
    // prices themselves.

    // Qty Precision
    if (newRecord || is_ValueChanged("QtyEntered")) setQtyEntered(getQtyEntered());
    if (newRecord || is_ValueChanged("QtyOrdered")) setQtyOrdered(getQtyOrdered());

    // task 05295: commenting this out because also for ASI-Order-Lines it shall be allowed to order
    // qty that is not yet fully avalable on stock
    // // Qty on instance ASI for SO
    // if (m_IsSOTrx
    // && getM_AttributeSetInstance_ID() != 0
    // && (newRecord || is_ValueChanged("M_Product_ID")
    // || is_ValueChanged("M_AttributeSetInstance_ID")
    // || is_ValueChanged("M_Warehouse_ID")))
    // {
    // MProduct product = getProduct();
    // if (product.isStocked())
    // {
    // int M_AttributeSet_ID = product.getM_AttributeSet_ID();
    // boolean isInstance = M_AttributeSet_ID != 0;
    // if (isInstance)
    // {
    // MAttributeSet mas = MAttributeSet.get(getCtx(), M_AttributeSet_ID);
    // isInstance = mas.isInstanceAttribute();
    // }
    // // Max
    // if (isInstance)
    // {
    // MStorage[] storages = MStorage.getWarehouse(getCtx(),
    // Services.get(IWarehouseAdvisor.class).evaluateWarehouse(this).getM_Warehouse_ID(),
    // getM_Product_ID(), getM_AttributeSetInstance_ID(),
    // M_AttributeSet_ID, false, null, true, get_TrxName());
    // BigDecimal qty = Env.ZERO;
    // for (int i = 0; i < storages.length; i++)
    // {
    // if (storages[i].getM_AttributeSetInstance_ID() == getM_AttributeSetInstance_ID())
    // qty = qty.add(storages[i].getQtyOnHand());
    // }
    //
    // if (getQtyOrdered().compareTo(qty) > 0)
    // {
    // log.warn("Qty - Stock=" + qty + ", Ordered=" + getQtyOrdered());
    // log.error("QtyInsufficient", "=" + qty);
    // return false;
    // }
    // }
    // } // stocked
    // } // SO instance

    // FreightAmt Not used
    if (Env.ZERO.compareTo(getFreightAmt()) != 0) setFreightAmt(Env.ZERO);

    // Set Tax
    // metas: Steuer muss immer ermittelt werden, da durch eine Anschriftenaenderung im Kopf auch
    // Steueraenderungen in Positionen auftreten.
    // if (getC_Tax_ID() == 0)
    if (!setTax()) {
      return false;
    }
    // metas ende

    // Get Line No
    if (getLine() == 0) {
      String sql = "SELECT COALESCE(MAX(Line),0)+10 FROM C_OrderLine WHERE C_Order_ID=?";
      int ii = DB.getSQLValue(get_TrxName(), sql, getC_Order_ID());
      setLine(ii);
    }

    // Calculations & Rounding

    // FIXME: commented out because actually was doing nothing (see, it was updating another
    // instance of this order line, which is not saved), and more, setLineNetAmt is no longer called
    // from here
    // final I_C_OrderLine orderLine = InterfaceWrapperHelper.create(getCtx(), getC_OrderLine_ID(),
    // I_C_OrderLine.class, get_TrxName());
    // Services.get(IOrderLineBL.class).setPrices(orderLine);

    // 07264
    // commented out because we are not using this anymore
    // setLineNetAmt(); // extended Amount with or without tax

    // metas
    // setDiscount();
    // metas ende

    return true;
  } // beforeSave

  /**
   * Before Delete
   *
   * @return true if it can be deleted
   */
  @Override
  protected boolean beforeDelete() {
    // R/O Check - Something delivered. etc.
    if (Env.ZERO.compareTo(getQtyDelivered()) != 0) {
      throw new AdempiereException("@DeleteError@ @QtyDelivered@=" + getQtyDelivered());
    }
    if (Env.ZERO.compareTo(getQtyInvoiced()) != 0) {
      throw new AdempiereException("@DeleteError@ @QtyInvoiced@=" + getQtyInvoiced());
    }
    if (Env.ZERO.compareTo(getQtyReserved()) != 0) {
      // metas: attempt to unreserve stock
      BigDecimal oldVal = getQtyOrdered();
      if (oldVal.signum() != 0) {
        setQty(Env.ZERO);
        setLineNetAmt(Env.ZERO);
        saveEx(get_TrxName());
      }

      if (!getParent().reserveStock(null, new MOrderLine[] {this})) {
        throw new AdempiereException("@DeleteError@ @QtyReserved@=" + getQtyReserved());
      }
      // metas end
    }

    // UnLink All Requisitions
    MRequisitionLine.unlinkC_OrderLine_ID(getCtx(), get_ID(), get_TrxName());

    return true;
  } // beforeDelete

  /**
   * After Save
   *
   * @param newRecord new
   * @param success success
   * @return saved
   */
  @Override
  protected boolean afterSave(boolean newRecord, boolean success) {
    if (!success) return success;
    if (!newRecord && is_ValueChanged("C_Tax_ID")) {
      // Recalculate Tax for old Tax
      if (!getParent().isProcessed()) if (!updateOrderTax(true)) return false;
    }
    return updateHeaderTax();
  } // afterSave

  /**
   * After Delete
   *
   * @param success success
   * @return deleted
   */
  @Override
  protected boolean afterDelete(boolean success) {
    if (!success) return success;
    if (getS_ResourceAssignment_ID() != 0) {
      MResourceAssignment ra =
          new MResourceAssignment(getCtx(), getS_ResourceAssignment_ID(), get_TrxName());
      ra.delete(true);
    }

    return updateHeaderTax();
  } // afterDelete

  /**
   * Recalculate order tax
   *
   * @param oldTax true if the old C_Tax_ID should be used
   * @return true if success, false otherwise
   * @author teo_sarca [ 1583825 ]
   */
  private boolean updateOrderTax(final boolean oldTax) {
    // NOTE: keep in sync with org.compiere.model.MInvoiceLine.updateInvoiceTax(boolean)

    final String trxName = get_TrxName();
    final int taxPrecision = getPrecision();
    final MOrderTax tax = MOrderTax.get(this, taxPrecision, oldTax, trxName);
    if (tax == null) {
      return true;
    }

    if (!tax.calculateTaxFromLines()) {
      return false;
    }

    //
    // If tax has invoice lines behind => fine, save it
    if (tax.isActive()) {
      InterfaceWrapperHelper.save(tax, trxName);
    }
    // Tax has no longer any invoice lines behind => deleted it if it's not new
    else {
      if (!InterfaceWrapperHelper.isNew(tax)) {
        InterfaceWrapperHelper.delete(tax);
      }
    }

    return true;
  }

  /**
   * Update Tax & Header
   *
   * @return true if header updated
   */
  private boolean updateHeaderTax() {
    // Recalculate Tax for this Tax
    if (!getParent().isProcessed()) {
      if (!updateOrderTax(false)) {
        return false;
      }
    }

    // task 08999:
    // Avoid a possible deadlock by updating the C_Order *after* the current transaction, because at
    // this point we might already hold a lot of locks to different objects.
    // The updates in updateHeader0 will try aggregate and obtain any number of additional shared
    // locks.
    // Concrete, we observed a deadlock between this code and
    // M_ReceiptSchedule.propagateQtysToOrderLine()
    final ITrxManager trxManager = Services.get(ITrxManager.class);
    trxManager
        .getTrxListenerManager(get_TrxName())
        .registerListener(
            new TrxListenerAdapter() {
              @Override
              public void afterCommit(final ITrx trx) {
                trxManager.run(
                    new TrxRunnableAdapter() {
                      @Override
                      public void run(final String localTrxName) throws Exception {
                        updateHeader0(getC_Order_ID());
                      }
                    });
              }
            });
    return true;
  } // updateHeaderTax

  /**
   * See the comment in {@link #updateHeaderTax()}.
   *
   * @param orderId
   */
  private static void updateHeader0(final int orderId) {

    // Update Order Header: TotalLines
    {
      final String sql =
          "UPDATE C_Order i"
              + " SET TotalLines="
              + "(SELECT COALESCE(SUM(LineNetAmt),0) FROM C_OrderLine il WHERE i.C_Order_ID=il.C_Order_ID) "
              + "WHERE C_Order_ID="
              + orderId;
      final int no = DB.executeUpdateEx(sql, ITrx.TRXNAME_ThreadInherited);
      if (no != 1) {
        new AdempiereException("Updating TotalLines failed for C_Order_ID=" + orderId);
      }
    }
    // Update Order Header: GrandTotal
    {
      final String sql =
          "UPDATE C_Order i "
              + " SET GrandTotal=TotalLines+"
              // SUM up C_OrderTax.TaxAmt only for those lines which does not have Tax Included
              + "(SELECT COALESCE(SUM(TaxAmt),0) FROM C_OrderTax it WHERE i.C_Order_ID=it.C_Order_ID AND it.IsActive='Y' AND it.IsTaxIncluded='N') "
              + "WHERE C_Order_ID="
              + orderId;
      final int no = DB.executeUpdateEx(sql, ITrx.TRXNAME_ThreadInherited);
      if (no != 1) {
        new AdempiereException("Updating GrandTotal failed for C_Order_ID=" + orderId);
      }
    }
  }
} // MOrderLine
/**
 * Payment Validion Routines
 *
 * @author Jorg Janke
 * @version $Id: MPaymentValidate.java,v 1.2 2006/07/30 00:51:05 jjanke Exp $
 */
public class MPaymentValidate {
  /** Static Logger */
  private static Logger s_log = LogManager.getLogger(MPaymentValidate.class);

  /**
   * Is this a valid Credit Card Exp Date?
   *
   * @param mmyy Exp in form of mmyy
   * @return "" or Error AD_Message
   */
  public static String validateCreditCardExp(String mmyy) {
    String exp = checkNumeric(mmyy);
    if (exp.length() != 4) return "CreditCardExpFormat";
    //
    String mmStr = exp.substring(0, 2);
    String yyStr = exp.substring(2, 4);
    //
    int mm = 0;
    int yy = 0;
    try {
      mm = Integer.parseInt(mmStr);
      yy = Integer.parseInt(yyStr);
    } catch (Exception e) {
      return "CreditCardExpFormat";
    }
    return validateCreditCardExp(mm, yy);
  } //  validateCreditCardExp

  /**
   * Return Month of Exp
   *
   * @param mmyy Exp in form of mmyy
   * @return month
   */
  public static int getCreditCardExpMM(String mmyy) {
    String mmStr = mmyy.substring(0, 2);
    int mm = 0;
    try {
      mm = Integer.parseInt(mmStr);
    } catch (Exception e) {
    }
    return mm;
  } //  getCreditCardExpMM

  /**
   * Return Year of Exp
   *
   * @param mmyy Exp in form of mmyy
   * @return year
   */
  public static int getCreditCardExpYY(String mmyy) {
    String yyStr = mmyy.substring(2);
    int yy = 0;
    try {
      yy = Integer.parseInt(yyStr);
    } catch (Exception e) {
    }
    return yy;
  } //  getCreditCardExpYY

  /**
   * Is this a valid Credit Card Exp Date?
   *
   * @param mm month
   * @param yy year
   * @return "" or Error AD_Message
   */
  public static String validateCreditCardExp(int mm, int yy) {
    if (mm < 1 || mm > 12) return "CreditCardExpMonth";
    //	if (yy < 0 || yy > EXP_YEAR)
    //		return "CreditCardExpYear";

    //  Today's date
    Calendar cal = Calendar.getInstance();
    int year = cal.get(Calendar.YEAR) - 2000; //  two digits
    int month = cal.get(Calendar.MONTH) + 1; //  zero based
    //
    if (yy < year) return "CreditCardExpired";
    else if (yy == year && mm < month) return "CreditCardExpired";
    return "";
  } //  validateCreditCardExp

  /**
   * Validate Credit Card Number. - Based on LUHN formula
   *
   * @param creditCardNumber credit card number
   * @return "" or Error AD_Message
   */
  public static String validateCreditCardNumber(String creditCardNumber) {
    if (creditCardNumber == null || creditCardNumber.length() == 0) return "CreditCardNumberError";

    /**
     * 1: Double the value of alternate digits beginning with the first right-hand digit (low
     * order). 2: Add the individual digits comprising the products obtained in step 1 to each of
     * the unaffected digits in the original number. 3: Subtract the total obtained in step 2 from
     * the next higher number ending in 0 [this in the equivalent of calculating the "tens
     * complement" of the low order digit (unit digit) of the total]. If the total obtained in step
     * 2 is a number ending in zero (30, 40 etc.), the check digit is 0. Example: Account number:
     * 4992 73 9871 6
     *
     * <p>4 9 9 2 7 3 9 8 7 1 6 x2 x2 x2 x2 x2 ------------------------------- 4 18 9 4 7 6 9 16 7 2
     * 6
     *
     * <p>4 + 1 + 8 + 9 + 4 + 7 + 6 + 9 + 1 + 6 + 7 + 2 + 6 = 70 70 % 10 = 0
     */

    //  Clean up number
    String ccNumber1 = checkNumeric(creditCardNumber);
    int ccLength = ccNumber1.length();
    //  Reverse string
    StringBuffer buf = new StringBuffer();
    for (int i = ccLength; i != 0; i--) buf.append(ccNumber1.charAt(i - 1));
    String ccNumber = buf.toString();

    int sum = 0;
    for (int i = 0; i < ccLength; i++) {
      int digit = Character.getNumericValue(ccNumber.charAt(i));
      if (i % 2 == 1) {
        digit *= 2;
        if (digit > 9) digit -= 9;
      }
      sum += digit;
    }
    if (sum % 10 == 0) return "";

    s_log.debug(
        "validateCreditCardNumber - " + creditCardNumber + " -> " + ccNumber + ", Luhn=" + sum);
    return "CreditCardNumberError";
  } //  validateCreditCardNumber

  /**
   * Validate Credit Card Number. - Check Card Type and Length
   *
   * @param creditCardNumber CC Number
   * @param creditCardType CC Type
   * @return "" or Error AD_Message
   */
  public static String validateCreditCardNumber(String creditCardNumber, String creditCardType) {
    if (creditCardNumber == null || creditCardType == null) return "CreditCardNumberError";

    //  http://www.beachnet.com/~hstiles/cardtype.html
    //	http://staff.semel.fi/~kribe/document/luhn.htm

    String ccStartList = ""; //  comma separated list of starting numbers
    String ccLengthList = ""; //  comma separated list of lengths
    //
    if (creditCardType.equals(X_C_Payment.CREDITCARDTYPE_MasterCard)) {
      ccStartList = "51,52,53,54,55";
      ccLengthList = "16";
    } else if (creditCardType.equals(X_C_Payment.CREDITCARDTYPE_Visa)) {
      ccStartList = "4";
      ccLengthList = "13,16";
    } else if (creditCardType.equals(X_C_Payment.CREDITCARDTYPE_Amex)) {
      ccStartList = "34,37";
      ccLengthList = "15";
    } else if (creditCardType.equals(X_C_Payment.CREDITCARDTYPE_Discover)) {
      ccStartList = "6011";
      ccLengthList = "16";
    } else if (creditCardType.equals(X_C_Payment.CREDITCARDTYPE_Diners)) {
      ccStartList = "300,301,302,303,304,305,36,38";
      ccLengthList = "14";
    } else {
      //  enRouteCard
      ccStartList = "2014,2149";
      ccLengthList = "15";
      //  JCBCard
      ccStartList += ",3088,3096,3112,3158,3337,3528";
      ccLengthList += ",16";
      //  JCBCard
      ccStartList += ",2131,1800";
      ccLengthList += ",15";
    }

    //  Clean up number
    String ccNumber = checkNumeric(creditCardNumber);

    /** Check Length */
    int ccLength = ccNumber.length();
    boolean ccLengthOK = false;
    StringTokenizer st = new StringTokenizer(ccLengthList, ",", false);
    while (st.hasMoreTokens() && !ccLengthOK) {
      int l = Integer.parseInt(st.nextToken());
      if (ccLength == l) ccLengthOK = true;
    }
    if (!ccLengthOK) {
      s_log.debug("validateCreditCardNumber Length=" + ccLength + " <> " + ccLengthList);
      return "CreditCardNumberError";
    }

    /** Check Start Digits */
    boolean ccIdentified = false;
    st = new StringTokenizer(ccStartList, ",", false);
    while (st.hasMoreTokens() && !ccIdentified) {
      if (ccNumber.startsWith(st.nextToken())) ccIdentified = true;
    }
    if (!ccIdentified)
      s_log.debug("validateCreditCardNumber Type=" + creditCardType + " <> " + ccStartList);

    //
    String check = validateCreditCardNumber(ccNumber);
    if (check.length() != 0) return check;
    if (!ccIdentified) return "CreditCardNumberProblem?";
    return "";
  } //  validateCreditCardNumber

  /**
   * Validate Validation Code
   *
   * @param creditCardVV CC Verification Code
   * @return "" or Error AD_Message
   */
  public static String validateCreditCardVV(String creditCardVV) {
    if (creditCardVV == null) return "";
    int length = checkNumeric(creditCardVV).length();
    if (length == 3 || length == 4) return "";
    try {
      Integer.parseInt(creditCardVV);
      return "";
    } catch (NumberFormatException ex) {
      s_log.debug("validateCreditCardVV - " + ex);
    }
    s_log.debug("validateCreditCardVV - length=" + length);
    return "CreditCardVVError";
  } //  validateCreditCardVV

  /**
   * Validate Validation Code
   *
   * @param creditCardVV CC Verification Code
   * @param creditCardType CC Type see CC_
   * @return "" or Error AD_Message
   */
  public static String validateCreditCardVV(String creditCardVV, String creditCardType) {
    //	no data
    if (creditCardVV == null
        || creditCardVV.length() == 0
        || creditCardType == null
        || creditCardType.length() == 0) return "";

    int length = checkNumeric(creditCardVV).length();

    //	Amex = 4 digits
    if (creditCardType.equals(X_C_Payment.CREDITCARDTYPE_Amex)) {
      if (length == 4) {
        try {
          Integer.parseInt(creditCardVV);
          return "";
        } catch (NumberFormatException ex) {
          s_log.debug("validateCreditCardVV - " + ex);
        }
      }
      s_log.debug("validateCreditCardVV(4) CC=" + creditCardType + ", length=" + length);
      return "CreditCardVVError";
    }
    //	Visa & MasterCard - 3 digits
    if (creditCardType.equals(X_C_Payment.CREDITCARDTYPE_Visa)
        || creditCardType.equals(X_C_Payment.CREDITCARDTYPE_MasterCard)) {
      if (length == 3) {
        try {
          Integer.parseInt(creditCardVV);
          return "";
        } catch (NumberFormatException ex) {
          s_log.debug("validateCreditCardVV - " + ex);
        }
      }
      s_log.debug("validateCreditCardVV(3) CC=" + creditCardType + ", length=" + length);
      return "CreditCardVVError";
    }

    //	Other
    return "";
  } //  validateCreditCardVV

  /**
   * ************************************************************************ Validate Routing
   * Number
   *
   * @param routingNo Routing No
   * @return "" or Error AD_Message
   */
  public static String validateRoutingNo(String routingNo) {
    int length = checkNumeric(routingNo).length();
    //  US - length 9
    //  Germany - length 8
    //	Japan - 7
    //	CH - 5
    //	Issue: Bank account country
    if (length > 0) return "";
    return "PaymentBankRoutingNotValid";
  } //  validateBankRoutingNo

  /**
   * Validate Account No
   *
   * @param AccountNo AccountNo
   * @return "" or Error AD_Message
   */
  public static String validateAccountNo(String AccountNo) {
    int length = checkNumeric(AccountNo).length();
    if (length > 0) return "";
    return "PaymentBankAccountNotValid";
  } //  validateBankAccountNo

  /**
   * Validate Check No
   *
   * @param CheckNo CheckNo
   * @return "" or Error AD_Message
   */
  public static String validateCheckNo(String CheckNo) {
    int length = checkNumeric(CheckNo).length();
    if (length > 0) return "";
    return "PaymentBankCheckNotValid";
  } //  validateBankCheckNo

  /**
   * Check Numeric
   *
   * @param data input
   * @return the digits of the data - ignore the rest
   */
  public static String checkNumeric(String data) {
    if (data == null || data.length() == 0) return "";
    //  Remove all non Digits
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < data.length(); i++) {
      if (Character.isDigit(data.charAt(i))) sb.append(data.charAt(i));
    }
    return sb.toString();
  } //  checkNumeric
} //	MPaymentValidate
Esempio n. 6
0
/** @author tsa */
public abstract class AbstractTerminalNumericField extends AbstractTerminalField<BigDecimal>
    implements ITerminalNumericField {
  private final transient Logger log = LogManager.getLogger(getClass());

  private final String name;
  private final int displayType;
  private final float fontSize;
  private final boolean withButtons;
  private final boolean withLabel;
  private boolean editable = true;

  protected ITerminalButton bPlus;
  protected ITerminalButton bMinus;
  protected IContainer panel;
  protected ITerminalTextField fNumber;

  protected String constraints = SwingTerminalFactory.BUTTON_Constraints;

  private BigDecimal increment = Env.ONE;

  @Override
  public void setMinusRO(final boolean ro) {
    if (withButtons) {
      bMinus.setEnabled(!ro);
    }
  }

  @Override
  public void setPlusRO(final boolean ro) {
    if (withButtons) {
      bPlus.setEnabled(!ro);
    }
  }

  @Override
  public void setNumberRO(final boolean ro) {
    fNumber.setEditable(!ro);
  }

  private class MinusButtonAction implements PropertyChangeListener {
    @Override
    public void propertyChange(final PropertyChangeEvent evt) {
      decValue();
    }
  }

  private class PlusButtonAction implements PropertyChangeListener {
    @Override
    public void propertyChange(final PropertyChangeEvent evt) {
      incValue();
    }
  }

  private final PropertyChangeListener numberChangeListener =
      new PropertyChangeListener() {
        @Override
        public void propertyChange(final PropertyChangeEvent evt) {
          if (ITerminalTextField.PROPERTY_ActionPerformed.equals(evt.getPropertyName())) {
            final BigDecimal valueOldBD = convertValueToType(evt.getOldValue());
            final BigDecimal valueNewBD = convertValueToType(evt.getNewValue());
            firePropertyChange(ITerminalField.ACTION_ValueChanged, valueOldBD, valueNewBD);
          } else if (ITerminalTextField.PROPERTY_FocusLost.equals(evt.getPropertyName())) {
            try {
              setValue(getValue(), true);
            } catch (final Exception ex) {
              showWarningAndRequestFocus(ex);
            }
          }
        }
      };

  protected AbstractTerminalNumericField(
      final ITerminalContext tc, final String name, final int displayType) {
    this(tc, name, displayType, true, true);
  }

  protected AbstractTerminalNumericField(
      final ITerminalContext tc,
      final String name,
      final int displayType,
      final float fontSize,
      final boolean withButtons,
      final boolean withLabel,
      final String constr) {
    super(tc);

    this.name = name;
    this.displayType = displayType;
    if (fontSize > 0) {
      this.fontSize = fontSize;
    } else {
      this.fontSize = tc.getDefaultFontSize();
    }

    this.withButtons = withButtons;
    this.withLabel = withLabel;
    if (!Check.isEmpty(constr, true)) {
      constraints = constr;
    }

    initComponents();
    initUI();
    setValue(Env.ZERO, false);
  }

  protected AbstractTerminalNumericField(
      final ITerminalContext tc,
      final String name,
      final int displayType,
      final boolean withButtons,
      final boolean withLabel,
      final String constr) {
    this(tc, name, displayType, 0f, withButtons, withLabel, constr);
  }

  protected AbstractTerminalNumericField(
      final ITerminalContext tc,
      final String name,
      final int displayType,
      final boolean withButtons,
      final boolean withLabel) {
    this(tc, name, displayType, withButtons, withLabel, null);
  }

  private void initComponents() {
    final ITerminalFactory factory = getTerminalFactory();

    fNumber = factory.createTerminalTextField(getName(), getDisplayType(), getFontSize());
    fNumber.addListener(numberChangeListener);

    final String panelColumnConstraints;
    if (withButtons) {
      bMinus = factory.createButtonAction(ITerminalNumericField.ACTION_Minus);
      bMinus.addListener(new MinusButtonAction());
      bPlus = factory.createButtonAction(ITerminalNumericField.ACTION_Plus);
      bPlus.addListener(new PlusButtonAction());

      panelColumnConstraints = "[][][]"; // 3 Columns: Minus button, Qty numeric field, Plus button

    } else {
      panelColumnConstraints = ""; // nothing, we have only the Qty numberic field
    }

    final String panelRowConstraints;
    if (withLabel) {
      panelRowConstraints =
          "[]" // Label row
              + "0" // gap between rows
              + "[shrink 0]"; // Qty field row
    } else {
      panelRowConstraints = "[shrink 0]"; // Qty field row only
    }

    final String panelLayoutConstraints = "insets 0";
    panel =
        factory.createContainer(
            panelLayoutConstraints, // Layout Constraints
            panelColumnConstraints, // Column constraints
            panelRowConstraints // Row constrants
            );
  }

  @Override
  public void setReadOnly(final boolean ro) {
    final boolean editable = !ro;
    setEditable(editable);
  }

  @Override
  public void setEditable(final boolean editable) {
    this.editable = editable;

    fNumber.setEditable(editable);
    if (withButtons) {
      bMinus.setEnabled(editable);
      bPlus.setEnabled(editable);
    }
  }

  @Override
  public boolean isEditable() {
    return editable;
  }

  protected abstract void initUI();

  @Override
  protected BigDecimal getFieldValue() {
    String text = fNumber.getText();
    if (text == null) {
      return BigDecimal.ZERO;
    }
    text = text.trim();
    if (text.isEmpty()) {
      return BigDecimal.ZERO;
    }

    final Format format = fNumber.getFormat();
    try {
      if (format == null) {
        return new BigDecimal(text);
      } else if (format instanceof DecimalFormat) {
        final DecimalFormat df = (DecimalFormat) format;
        df.setParseBigDecimal(true);
        final BigDecimal bd = (BigDecimal) df.parse(text);
        return bd;
      } else {
        log.info(
            "Invalid Format '{}' to be used to convert text '{}' to BigDecimal. Assuming ZERO.",
            new Object[] {format, text});
        return Env.ZERO;
      }
    } catch (final Exception e) {
      throw new WrongValueException(
          this, "@" + ITerminalField.MSG_ErrorParsingText + "@ " + text, e);
    }
  }

  @Override
  protected BigDecimal convertValueToType(final Object value) {
    if (value == null) {
      return null;
    } else if (value instanceof BigDecimal) {
      return (BigDecimal) value;
    } else if (value instanceof Integer) {
      return BigDecimal.valueOf((Integer) value);
    } else if (value instanceof String) {
      final String valueStr = value.toString().trim();
      if (valueStr.isEmpty()) {
        return null;
      } else {
        return new BigDecimal(valueStr);
      }
    } else {
      return new BigDecimal(value.toString());
    }
  }

  @Override
  protected final void setFieldValue(final BigDecimal value, final boolean fireEvent) {
    final BigDecimal valueOld = this._valueOld; // backup for event

    //
    // Fix value to set
    final BigDecimal valueNew;
    if (value == null) {
      valueNew = Env.ZERO;
    } else {
      // Qty Editor it shall be a small component and we don't want to clutter it with pointless
      // trailing zeros (06112)
      valueNew = NumberUtils.stripTrailingDecimalZeros(value);
    }

    //
    // Get the number editor
    final ITerminalTextField fNumber = this.fNumber;
    if (isDisposed() || fNumber == null) {
      // shall not happen but we are throwing an exception because we got a weird case (FRESH-331)
      new TerminalException(
              "Atempt to set value but field is disposed."
                  + "\n field: "
                  + this
                  + "\n value: "
                  + valueOld
                  + "->"
                  + valueNew
                  + "\n fireEvent: "
                  + fireEvent
                  + "\n fNumber: "
                  + fNumber)
          .throwOrLogWarningIfDeveloperMode(log);
      return;
    }

    //
    // Actually setting the new value
    fNumber.setText(valueNew.toString());
    this._valueOld = valueNew;

    //
    // Fire event
    if (fireEvent) {
      final boolean changed = valueOld == null || valueOld.compareTo(valueNew) != 0;
      if (changed) {
        firePropertyChange(ITerminalField.ACTION_ValueChanged, valueOld, valueNew);
      }
    }
  }

  private BigDecimal _valueOld = null;

  public void incValue() {
    setValue(getValue().add(increment));
  }

  public void decValue() {
    setValue(getValue().subtract(increment));
  }

  public BigDecimal getIncrement() {
    return increment;
  }

  public void setIncrement(final BigDecimal increment) {
    this.increment = increment;
  }

  @Override
  public String getName() {
    return name;
  }

  public int getDisplayType() {
    return displayType;
  }

  public float getFontSize() {
    return fontSize;
  }

  @Override
  public void requestFocus() {
    fNumber.requestFocus();
  }

  public boolean isWithButtons() {
    return withButtons;
  }

  public boolean isWithLabel() {
    return withLabel;
  }

  @Override
  public void dispose() {
    super.dispose();

    if (bMinus != null) {
      bMinus.dispose();
      bMinus = null;
    }
    if (bPlus != null) {
      bPlus.dispose();
      bPlus = null;
    }
    if (fNumber != null) {
      fNumber.removeListener(numberChangeListener);
      fNumber.dispose();
      fNumber = null;
    }
    if (panel != null) {
      panel.dispose();
      panel = null;
    }
  }
}
Esempio n. 7
0
/**
 * Performance Goal
 *
 * @author Jorg Janke
 * @version $Id: MGoal.java,v 1.2 2006/07/30 00:51:03 jjanke Exp $
 * @author Teo Sarca, SC ARHIPAC SERVICE SRL
 *     <li>BF [ 1887674 ] Deadlock when try to modify PA Goal's Measure Target
 *     <li>BF [ 1760482 ] New Dashboard broke old functionality
 *     <li>BF [ 1887691 ] I get NPE if the PA Goal's target is 0
 */
public class MGoal extends X_PA_Goal {
  /** */
  private static final long serialVersionUID = -4612113288233473730L;

  /**
   * Get User Goals
   *
   * @param ctx context
   * @param AD_User_ID user
   * @return array of goals
   */
  public static MGoal[] getUserGoals(Properties ctx, int AD_User_ID) {
    if (AD_User_ID < 0) return getTestGoals(ctx);
    ArrayList<MGoal> list = new ArrayList<MGoal>();
    String sql =
        "SELECT * FROM PA_Goal g "
            + "WHERE IsActive='Y'"
            + " AND AD_Client_ID=?" //	#1
            + " AND ((AD_User_ID IS NULL AND AD_Role_ID IS NULL)"
            + " OR AD_User_ID=?" //	#2
            + " OR EXISTS (SELECT * FROM AD_User_Roles ur "
            + "WHERE ur.AD_User_ID=? AND g.AD_Role_ID=ur.AD_Role_ID AND ur.IsActive='Y')) "
            + "ORDER BY SeqNo";
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
      pstmt = DB.prepareStatement(sql, null);
      pstmt.setInt(1, Env.getAD_Client_ID(ctx));
      pstmt.setInt(2, AD_User_ID);
      pstmt.setInt(3, AD_User_ID);
      rs = pstmt.executeQuery();
      while (rs.next()) {
        MGoal goal = new MGoal(ctx, rs, null);
        goal.updateGoal(false);
        list.add(goal);
      }
    } catch (Exception e) {
      s_log.error(sql, e);
    } finally {
      DB.close(rs, pstmt);
      rs = null;
      pstmt = null;
    }
    MGoal[] retValue = new MGoal[list.size()];
    list.toArray(retValue);
    return retValue;
  } //	getUserGoals

  /**
   * Get Accessible Goals
   *
   * @param ctx context
   * @return array of goals
   */
  public static MGoal[] getGoals(Properties ctx) {
    ArrayList<MGoal> list = new ArrayList<MGoal>();
    String sql = "SELECT * FROM PA_Goal WHERE IsActive='Y' " + "ORDER BY SeqNo";
    sql =
        Env.getUserRolePermissions(ctx)
            .addAccessSQL(sql, "PA_Goal", false, true); // RW to restrict Access
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
      pstmt = DB.prepareStatement(sql, null);
      rs = pstmt.executeQuery();
      while (rs.next()) {
        MGoal goal = new MGoal(ctx, rs, null);
        goal.updateGoal(false);
        list.add(goal);
      }
    } catch (Exception e) {
      s_log.error(sql, e);
    } finally {
      DB.close(rs, pstmt);
      rs = null;
      pstmt = null;
    }
    MGoal[] retValue = new MGoal[list.size()];
    list.toArray(retValue);
    return retValue;
  } //	getGoals

  /**
   * Create Test Goals
   *
   * @param ctx context
   * @return array of goals
   */
  public static MGoal[] getTestGoals(Properties ctx) {
    MGoal[] retValue = new MGoal[4];
    retValue[0] = new MGoal(ctx, "Test 1", "Description 1", new BigDecimal(1000), null);
    retValue[0].setMeasureActual(new BigDecimal(200));
    retValue[1] = new MGoal(ctx, "Test 2", "Description 2", new BigDecimal(1000), null);
    retValue[1].setMeasureActual(new BigDecimal(900));
    retValue[2] = new MGoal(ctx, "Test 3", "Description 3", new BigDecimal(1000), null);
    retValue[2].setMeasureActual(new BigDecimal(1200));
    retValue[3] = new MGoal(ctx, "Test 4", "Description 4", new BigDecimal(1000), null);
    retValue[3].setMeasureActual(new BigDecimal(3200));
    return retValue;
  } //	getTestGoals

  /**
   * Get Goals with Measure
   *
   * @param ctx context
   * @param PA_Measure_ID measure
   * @return goals
   */
  public static MGoal[] getMeasureGoals(Properties ctx, int PA_Measure_ID) {
    ArrayList<MGoal> list = new ArrayList<MGoal>();
    String sql = "SELECT * FROM PA_Goal WHERE IsActive='Y' AND PA_Measure_ID=? " + "ORDER BY SeqNo";
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
      pstmt = DB.prepareStatement(sql, null);
      pstmt.setInt(1, PA_Measure_ID);
      rs = pstmt.executeQuery();
      while (rs.next()) list.add(new MGoal(ctx, rs, null));
    } catch (Exception e) {
      s_log.error(sql, e);
    } finally {
      DB.close(rs, pstmt);
      rs = null;
      pstmt = null;
    }
    MGoal[] retValue = new MGoal[list.size()];
    list.toArray(retValue);
    return retValue;
  } //	getMeasureGoals

  /**
   * Get Multiplier from Scope to Display
   *
   * @param goal goal
   * @return null if error or multiplier
   */
  public static BigDecimal getMultiplier(MGoal goal) {
    String MeasureScope = goal.getMeasureScope();
    String MeasureDisplay = goal.getMeasureDisplay();
    if (MeasureDisplay == null || MeasureScope.equals(MeasureDisplay)) return Env.ONE; // 	1:1

    if (MeasureScope.equals(MEASURESCOPE_Total) || MeasureDisplay.equals(MEASUREDISPLAY_Total))
      return null; //	Error

    BigDecimal Multiplier = null;
    if (MeasureScope.equals(MEASURESCOPE_Year)) {
      if (MeasureDisplay.equals(MEASUREDISPLAY_Quarter)) Multiplier = new BigDecimal(1.0 / 4.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Month)) Multiplier = new BigDecimal(1.0 / 12.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Week)) Multiplier = new BigDecimal(1.0 / 52.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Day)) Multiplier = new BigDecimal(1.0 / 364.0);
    } else if (MeasureScope.equals(MEASURESCOPE_Quarter)) {
      if (MeasureDisplay.equals(MEASUREDISPLAY_Year)) Multiplier = new BigDecimal(4.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Month)) Multiplier = new BigDecimal(1.0 / 3.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Week)) Multiplier = new BigDecimal(1.0 / 13.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Day)) Multiplier = new BigDecimal(1.0 / 91.0);
    } else if (MeasureScope.equals(MEASURESCOPE_Month)) {
      if (MeasureDisplay.equals(MEASUREDISPLAY_Year)) Multiplier = new BigDecimal(12.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Quarter)) Multiplier = new BigDecimal(3.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Week)) Multiplier = new BigDecimal(1.0 / 4.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Day)) Multiplier = new BigDecimal(1.0 / 30.0);
    } else if (MeasureScope.equals(MEASURESCOPE_Week)) {
      if (MeasureDisplay.equals(MEASUREDISPLAY_Year)) Multiplier = new BigDecimal(52.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Quarter)) Multiplier = new BigDecimal(13.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Month)) Multiplier = new BigDecimal(4.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Day)) Multiplier = new BigDecimal(1.0 / 7.0);
    } else if (MeasureScope.equals(MEASURESCOPE_Day)) {
      if (MeasureDisplay.equals(MEASUREDISPLAY_Year)) Multiplier = new BigDecimal(364.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Quarter)) Multiplier = new BigDecimal(91.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Month)) Multiplier = new BigDecimal(30.0);
      else if (MeasureDisplay.equals(MEASUREDISPLAY_Week)) Multiplier = new BigDecimal(7.0);
    }
    return Multiplier;
  } //	getMultiplier

  /** Logger */
  private static Logger s_log = LogManager.getLogger(MGoal.class);

  /**
   * ************************************************************************ Standard Constructor
   *
   * @param ctx context
   * @param PA_Goal_ID id
   * @param trxName trx
   */
  public MGoal(Properties ctx, int PA_Goal_ID, String trxName) {
    super(ctx, PA_Goal_ID, trxName);
    if (PA_Goal_ID == 0) {
      //	setName (null);
      //	setAD_User_ID (0);
      //	setPA_ColorSchema_ID (0);
      setSeqNo(0);
      setIsSummary(false);
      setMeasureScope(MEASUREDISPLAY_Year);
      setGoalPerformance(Env.ZERO);
      setRelativeWeight(Env.ONE);
      setMeasureTarget(Env.ZERO);
      setMeasureActual(Env.ZERO);
    }
  } //	MGoal

  /**
   * Load Constructor
   *
   * @param ctx context
   * @param rs result set
   * @param trxName trx
   */
  public MGoal(Properties ctx, ResultSet rs, String trxName) {
    super(ctx, rs, trxName);
  } //	MGoal

  /**
   * Base Constructor
   *
   * @param ctx context
   * @param Name Name
   * @param Description Decsription
   * @param MeasureTarget target
   * @param trxName trx
   */
  public MGoal(
      Properties ctx, String Name, String Description, BigDecimal MeasureTarget, String trxName) {
    super(ctx, 0, trxName);
    setName(Name);
    setDescription(Description);
    setMeasureTarget(MeasureTarget);
  } //	MGoal

  /** Restrictions */
  private MGoalRestriction[] m_restrictions = null;
  /** Performance Color */
  private Color m_color = null;

  /**
   * Get Restriction Lines
   *
   * @param reload reload data
   * @return array of lines
   */
  public MGoalRestriction[] getRestrictions(boolean reload) {
    if (m_restrictions != null && !reload) return m_restrictions;
    ArrayList<MGoalRestriction> list = new ArrayList<MGoalRestriction>();
    //
    String sql =
        "SELECT * FROM PA_GoalRestriction "
            + "WHERE PA_Goal_ID=? AND IsActive='Y' "
            + "ORDER BY Org_ID, C_BPartner_ID, M_Product_ID";
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
      pstmt = DB.prepareStatement(sql, get_TrxName());
      pstmt.setInt(1, getPA_Goal_ID());
      rs = pstmt.executeQuery();
      while (rs.next()) list.add(new MGoalRestriction(getCtx(), rs, get_TrxName()));
    } catch (Exception e) {
      log.error(sql, e);
    } finally {
      DB.close(rs, pstmt);
      rs = null;
      pstmt = null;
    }
    //
    m_restrictions = new MGoalRestriction[list.size()];
    list.toArray(m_restrictions);
    return m_restrictions;
  } //	getRestrictions

  /**
   * Get Measure
   *
   * @return measure or null
   */
  public MMeasure getMeasure() {
    if (getPA_Measure_ID() != 0) return MMeasure.get(getCtx(), getPA_Measure_ID());
    return null;
  } //	getMeasure

  /**
   * ************************************************************************ Update/save Goals for
   * the same measure
   *
   * @param force force to update goal (default once per day)
   * @return true if updated
   */
  public boolean updateGoal(boolean force) {
    log.info("Force=" + force);
    MMeasure measure = MMeasure.get(getCtx(), getPA_Measure_ID());
    if (force || getDateLastRun() == null || !TimeUtil.isSameHour(getDateLastRun(), null)) {
      measure.set_TrxName(get_TrxName());
      if (measure.updateGoals()) // 	saves
      {
        load(get_ID(), get_TrxName());
        return true;
      }
    }
    return false;
  } //	updateGoal

  /**
   * Set Measure Actual
   *
   * @param MeasureActual actual
   */
  @Override
  public void setMeasureActual(BigDecimal MeasureActual) {
    if (MeasureActual == null) return;
    super.setMeasureActual(MeasureActual);
    setDateLastRun(new Timestamp(System.currentTimeMillis()));
    setGoalPerformance();
  } //	setMeasureActual

  /** Calculate Performance Goal as multiplier */
  public void setGoalPerformance() {
    BigDecimal MeasureTarget = getMeasureTarget();
    BigDecimal MeasureActual = getMeasureActual();
    BigDecimal GoalPerformance = Env.ZERO;
    if (MeasureTarget.signum() != 0)
      GoalPerformance = MeasureActual.divide(MeasureTarget, 6, BigDecimal.ROUND_HALF_UP);
    super.setGoalPerformance(GoalPerformance);
    m_color = null;
  } //	setGoalPerformance

  /**
   * Get Goal Performance as Double
   *
   * @return performance as multipier
   */
  public double getGoalPerformanceDouble() {
    BigDecimal bd = getGoalPerformance();
    return bd.doubleValue();
  } //	getGoalPerformanceDouble

  /**
   * Get Goal Performance in Percent
   *
   * @return performance in percent
   */
  public int getPercent() {
    BigDecimal bd = getGoalPerformance().multiply(Env.ONEHUNDRED);
    return bd.intValue();
  } //	getPercent

  /**
   * Get Color
   *
   * @return color - white if no target
   */
  public Color getColor() {
    if (m_color == null) {
      if (getMeasureTarget().signum() == 0) m_color = Color.white;
      else m_color = MColorSchema.getColor(getCtx(), getPA_ColorSchema_ID(), getPercent());
    }
    return m_color;
  } //	getColor

  /**
   * Get the color schema for this goal.
   *
   * @return the color schema
   */
  public MColorSchema getColorSchema() {
    return MColorSchema.get(getCtx(), getPA_ColorSchema_ID());
  }

  /**
   * Get Measure Display
   *
   * @return Measure Display
   */
  @Override
  public String getMeasureDisplay() {
    String s = super.getMeasureDisplay();
    if (s == null) {
      if (MEASURESCOPE_Week.equals(getMeasureScope())) s = MEASUREDISPLAY_Week;
      else if (MEASURESCOPE_Day.equals(getMeasureScope())) s = MEASUREDISPLAY_Day;
      else s = MEASUREDISPLAY_Month;
    }
    return s;
  } //	getMeasureDisplay

  /**
   * Get Measure Display Text
   *
   * @return Measure Display Text
   */
  public String getXAxisText() {
    MMeasure measure = getMeasure();
    if (measure != null
        && MMeasure.MEASUREDATATYPE_StatusQtyAmount.equals(measure.getMeasureDataType())) {
      if (MMeasure.MEASURETYPE_Request.equals(measure.getMeasureType()))
        return Msg.getElement(getCtx(), "R_Status_ID");
      if (MMeasure.MEASURETYPE_Project.equals(measure.getMeasureType()))
        return Msg.getElement(getCtx(), "C_Phase_ID");
    }
    String value = getMeasureDisplay();
    String display = MRefList.getListName(getCtx(), MEASUREDISPLAY_AD_Reference_ID, value);
    return display == null ? value : display;
  } //	getMeasureDisplayText

  /**
   * Goal has Target
   *
   * @return true if target
   */
  public boolean isTarget() {
    return getMeasureTarget().signum() != 0;
  } //	isTarget

  /**
   * String Representation
   *
   * @return info
   */
  @Override
  public String toString() {
    StringBuffer sb = new StringBuffer("MGoal[");
    sb.append(get_ID())
        .append("-")
        .append(getName())
        .append(",")
        .append(getGoalPerformance())
        .append("]");
    return sb.toString();
  } //	toString

  /**
   * Before Save
   *
   * @param newRecord new
   * @return true
   */
  @Override
  protected boolean beforeSave(boolean newRecord) {
    //	if (getMultiplier(this) == null)	//	error
    //		setMeasureDisplay(getMeasureScope());

    //	Measure required if nor Summary
    if (!isSummary() && getPA_Measure_ID() == 0) {
      throw new FillMandatoryException("PA_Measure_ID");
    }
    if (isSummary() && getPA_Measure_ID() != 0) setPA_Measure_ID(0);

    //	User/Role Check
    if ((newRecord || is_ValueChanged("AD_User_ID") || is_ValueChanged("AD_Role_ID"))
        && getAD_User_ID() != 0) {
      final List<IUserRolePermissions> roles =
          Services.get(IUserRolePermissionsDAO.class)
              .retrieveUserRolesPermissionsForUserWithOrgAccess(
                  getCtx(), getAD_User_ID(), getAD_Org_ID());
      if (roles.isEmpty()) // 	No Role
      {
        setAD_Role_ID(0);
      } else if (roles.size() == 1) // 	One
      {
        setAD_Role_ID(roles.get(0).getAD_Role_ID());
      } else {
        int AD_Role_ID = getAD_Role_ID();
        if (AD_Role_ID != 0) // 	validate
        {
          boolean found = false;
          for (IUserRolePermissions role : roles) {
            if (AD_Role_ID == role.getAD_Role_ID()) {
              found = true;
              break;
            }
          }
          if (!found) AD_Role_ID = 0;
        }
        if (AD_Role_ID == 0) // 	set to first one
        setAD_Role_ID(roles.get(0).getAD_Role_ID());
      } //	multiple roles
    } //	user check

    return true;
  } //	beforeSave

  /**
   * After Save
   *
   * @param newRecord new
   * @param success success
   * @return true
   */
  @Override
  protected boolean afterSave(boolean newRecord, boolean success) {
    if (!success) return success;

    //	Update Goal if Target / Scope Changed
    if (newRecord || is_ValueChanged("MeasureTarget") || is_ValueChanged("MeasureScope"))
      updateGoal(true);

    return success;
  }
} //	MGoal
/**
 * Factory used to create all sync objects that we are sending from metasfresh server to webui
 * server.
 *
 * @author metas-dev <*****@*****.**>
 */
public class SyncObjectsFactory {
  public static final SyncObjectsFactory newFactory(final Date date) {
    return new SyncObjectsFactory(date);
  }

  public static final SyncObjectsFactory newFactory() {
    return new SyncObjectsFactory(SystemTime.asDayTimestamp());
  }

  //
  // services
  private static final Logger logger = LogManager.getLogger(SyncObjectsFactory.class);

  private final transient IBPartnerDAO bpartnerDAO = Services.get(IBPartnerDAO.class);
  private final transient IPMMContractsDAO pmmContractsDAO = Services.get(IPMMContractsDAO.class);
  private final transient IPMMProductDAO pmmProductDAO = Services.get(IPMMProductDAO.class);
  private final transient IPMMBPartnerDAO pmmbPartnerDAO = Services.get(IPMMBPartnerDAO.class);
  private final transient IPMM_RfQ_DAO pmmRfQDAO = Services.get(IPMM_RfQ_DAO.class);
  private final transient IPMM_RfQ_BL pmmRfQBL = Services.get(IPMM_RfQ_BL.class);

  //
  // parameters
  private final Date date;

  //
  // state/cache

  private final Map<Integer, I_C_BPartner> bpartners = new HashMap<>();

  /** C_BPartner_ID to {@link I_C_Flatrate_Term}s */
  private final Multimap<Integer, I_C_Flatrate_Term> _bpartnerId2contract =
      MultimapBuilder.hashKeys().arrayListValues().build();

  private boolean _bpartnerId2contract_fullyLoaded = false;
  private boolean _bpartnerId2contract_fullyLoadedRequired = false;

  private final Multimap<Integer, I_C_RfQResponseLine> _bpartnerId2activeRfqResponseLines =
      MultimapBuilder.hashKeys().arrayListValues().build();
  private boolean _bpartnerId2activeRfqResponseLines_fullyLoaded = false;
  private boolean _bpartnerId2activeRfqResponseLines_fullyLoadedRequired = false;

  private Cache<String, SyncProduct> syncProductsCache = CacheBuilder.newBuilder().build();

  private SyncObjectsFactory(final Date date) {
    super();

    Check.assumeNotNull(date, "date not null");
    this.date = (Date) date.clone();
  }

  private Properties getCtx() {
    return Env.getCtx();
  }

  public List<SyncBPartner> createAllSyncBPartners() {
    // Optimization:
    setFlatrateTermsFullyLoadedRequired();
    setActiveRfqResponsesFullyLoadedRequired();

    final List<SyncBPartner> syncBPartners = new ArrayList<>();
    for (final int bpartnerId : getAllBPartnerIds()) {
      final SyncBPartner syncBPartner = createSyncBPartner(bpartnerId);
      syncBPartners.add(syncBPartner);
    }

    return syncBPartners;
  }

  private SyncContract createSyncContract(final I_C_Flatrate_Term term) {
    final SyncContract syncContract = new SyncContract();
    syncContract.setUuid(SyncUUIDs.toUUIDString(term));
    syncContract.setDateFrom(term.getStartDate());
    syncContract.setDateTo(term.getEndDate());

    final int rfqResponseLineId = term.getC_RfQResponseLine_ID();
    if (rfqResponseLineId > 0) {
      syncContract.setRfq_uuid(SyncUUIDs.toC_RfQReponseLine_UUID(rfqResponseLineId));
    }

    //
    // Contract Line: 1 line for our PMM_Product
    {
      final I_PMM_Product pmmProduct = term.getPMM_Product();
      if (pmmProduct == null) {
        logger.warn("Contract {} has no PMM_Product. Skip exporting.", term);
        return null;
      }

      final SyncProduct syncProduct = createSyncProduct(pmmProduct);
      if (syncProduct == null) {
        return null;
      }

      final SyncContractLine syncContractLine = new SyncContractLine();
      syncContractLine.setUuid(syncContract.getUuid());
      syncContractLine.setProduct(syncProduct);

      syncContract.getContractLines().add(syncContractLine);
    }

    return syncContract;
  }

  public SyncBPartner createSyncBPartner(final int bpartnerId) {
    //
    // Create SyncBPartner with Users populated
    final SyncBPartner syncBPartner = createSyncBPartnerWithoutContracts(bpartnerId);

    //
    // Populate contracts
    syncBPartner.setSyncContracts(true);
    for (final I_C_Flatrate_Term term : getC_Flatrate_Terms_ForBPartnerId(bpartnerId)) {
      final SyncContract syncContract = createSyncContract(term);
      if (syncContract == null) {
        continue;
      }
      syncBPartner.getContracts().add(syncContract);
    }

    //
    // Populate RfQs
    for (final I_C_RfQResponseLine rfqResponseLine :
        getActiveRfqResponseLines_ForBPartnerId(bpartnerId)) {
      final SyncRfQ syncRfQ = createSyncRfQ(rfqResponseLine);
      if (syncRfQ == null) {
        continue;
      }

      syncBPartner.getRfqs().add(syncRfQ);
    }

    return syncBPartner;
  }

  public SyncBPartner createSyncBPartnerWithoutContracts(final I_C_BPartner bpartner) {
    Check.assumeNotNull(bpartner, "bpartner not null");
    return createSyncBPartnerWithoutContracts(bpartner.getC_BPartner_ID());
  }

  private SyncBPartner createSyncBPartnerWithoutContracts(final int bpartnerId) {
    final I_C_BPartner bpartner = getC_BPartnerById(bpartnerId);

    final SyncBPartner syncBPartner = new SyncBPartner();
    syncBPartner.setName(bpartner.getName());
    syncBPartner.setUuid(SyncUUIDs.toUUIDString(bpartner));

    // Contracts: we are not populating them here, so, for now we flag them as "do not sync"
    syncBPartner.setSyncContracts(false);

    // not a vendor: no need to look at the contacts. delete the bpartner.
    if (!bpartner.isVendor()) {
      syncBPartner.setDeleted(true);
      return syncBPartner;
    }

    final String adLanguage = bpartner.getAD_Language();

    //
    // Fill Users
    final List<I_AD_User> contacts =
        InterfaceWrapperHelper.createList(bpartnerDAO.retrieveContacts(bpartner), I_AD_User.class);

    for (final I_AD_User contact : contacts) {
      final SyncUser syncUser = createSyncUser(contact, adLanguage);
      if (syncUser == null) {
        continue;
      }
      syncBPartner.getUsers().add(syncUser);
    }

    // no users: also delete the BPartner
    if (syncBPartner.getUsers().isEmpty()) {
      syncBPartner.setDeleted(true);
    }

    return syncBPartner;
  }

  private SyncUser createSyncUser(final I_AD_User contact, final String adLanguage) {
    if (!contact.isActive() || !contact.isIsMFProcurementUser()) {
      return null;
    }

    final String email = contact.getEMail();
    final String password = contact.getPassword();

    if (Check.isEmpty(email, true)) {
      return null;
    }

    final SyncUser syncUser = new SyncUser();
    syncUser.setLanguage(adLanguage);
    syncUser.setUuid(SyncUUIDs.toUUIDString(contact));
    syncUser.setEmail(email);
    syncUser.setPassword(password);

    return syncUser;
  }

  private final void setFlatrateTermsFullyLoadedRequired() {
    this._bpartnerId2contract_fullyLoadedRequired = true;
  }

  private Multimap<Integer, I_C_Flatrate_Term> getC_Flatrate_Terms_IndexedByBPartnerId() {
    if (_bpartnerId2contract_fullyLoadedRequired && !_bpartnerId2contract_fullyLoaded) {
      _bpartnerId2contract.clear(); // clear all first

      final List<I_C_Flatrate_Term> terms = pmmContractsDAO.retrieveAllRunningContractsOnDate(date);
      for (final I_C_Flatrate_Term term : terms) {
        final int bpartnerId = term.getDropShip_BPartner_ID();
        _bpartnerId2contract.put(bpartnerId, term);
      }

      _bpartnerId2contract_fullyLoaded = true;
    }
    return _bpartnerId2contract;
  }

  private Set<Integer> getAllBPartnerIds() {
    final List<I_C_BPartner> bpartnersList =
        pmmbPartnerDAO.retrieveAllPartnersWithProcurementUsers();
    for (final I_C_BPartner bpartner : bpartnersList) {
      bpartners.put(bpartner.getC_BPartner_ID(), bpartner);
    }
    return ImmutableSet.copyOf(bpartners.keySet());
  }

  private I_C_BPartner getC_BPartnerById(final int bpartnerId) {
    I_C_BPartner bpartner = bpartners.get(bpartnerId);
    if (bpartner == null) {
      bpartner =
          InterfaceWrapperHelper.create(
              getCtx(), bpartnerId, I_C_BPartner.class, ITrx.TRXNAME_ThreadInherited);
      bpartners.put(bpartnerId, bpartner);
    }
    return bpartner;
  }

  private List<I_C_Flatrate_Term> getC_Flatrate_Terms_ForBPartnerId(final int bpartnerId) {
    final Multimap<Integer, I_C_Flatrate_Term> bpartnerId2contract =
        getC_Flatrate_Terms_IndexedByBPartnerId();
    if (!bpartnerId2contract.containsKey(bpartnerId)) {
      final List<I_C_Flatrate_Term> contracts =
          pmmContractsDAO.retrieveRunningContractsOnDateForBPartner(date, bpartnerId);
      bpartnerId2contract.putAll(bpartnerId, contracts);
    }

    return ImmutableList.copyOf(bpartnerId2contract.get(bpartnerId));
  }

  public SyncProduct createSyncProduct(final I_PMM_Product pmmProduct) {
    final String product_uuid = SyncUUIDs.toUUIDString(pmmProduct);
    try {
      final SyncProduct syncProduct =
          syncProductsCache.get(
              product_uuid,
              new Callable<SyncProduct>() {

                @Override
                public SyncProduct call() throws Exception {
                  return createSyncProductNoCache(pmmProduct);
                }
              });
      return syncProduct.copy();
    } catch (final ExecutionException ex) {
      throw new RuntimeException(
          "Failed creating " + SyncProduct.class + " for " + pmmProduct, ex.getCause());
    }
  }

  private final SyncProduct createSyncProductNoCache(final I_PMM_Product pmmProduct) {
    final String product_uuid = SyncUUIDs.toUUIDString(pmmProduct);

    final I_M_Product product = pmmProduct.getM_Product();

    String productName = pmmProduct.getProductName();
    // Fallback to M_Product.Name (shall not happen)
    if (Check.isEmpty(productName, true)) {
      productName = product == null ? null : product.getName();
    }

    final SyncProduct syncProduct = new SyncProduct();

    final boolean valid =
        pmmProduct.isActive()
            && pmmProduct.getM_Warehouse_ID() > 0
            && pmmProduct.getM_Product_ID() > 0
            && pmmProduct.getM_HU_PI_Item_Product_ID() > 0;

    syncProduct.setUuid(product_uuid);
    syncProduct.setName(productName);
    syncProduct.setPackingInfo(pmmProduct.getPackDescription());

    syncProduct.setShared(
        pmmProduct.getC_BPartner_ID()
            <= 0); // share, unless it is assigned to a particular BPartner
    syncProduct.setDeleted(!valid);

    //
    // Translations
    {
      final Map<String, String> syncProductNamesTrl = syncProduct.getNamesTrl();
      final IModelTranslationMap productTrls =
          InterfaceWrapperHelper.getModelTranslationMap(product);
      final PMMProductNameBuilder productNameTrlBuilder =
          PMMProductNameBuilder.newBuilder().setPMM_Product(pmmProduct);
      for (final IModelTranslation productLanguageTrl : productTrls.getAllTranslations().values()) {
        final String adLanguage = productLanguageTrl.getAD_Language();

        final String productNamePartTrl =
            productLanguageTrl.getTranslation(I_M_Product.COLUMNNAME_Name);
        if (Check.isEmpty(productNamePartTrl, true)) {
          continue;
        }

        final String productNameTrl =
            productNameTrlBuilder.setProductNamePartIfUsingMProduct(productNamePartTrl).build();
        if (Check.isEmpty(productNameTrl, true)) {
          continue;
        }

        syncProductNamesTrl.put(adLanguage, productNameTrl.trim());
      }
    }

    return syncProduct;
  }

  public List<SyncProduct> createAllSyncProducts() {
    final List<I_PMM_Product> allPmmProducts =
        pmmProductDAO
            .retrieveAllPMMProductsValidOnDateQuery(date)
            .addEqualsFilter(
                I_PMM_Product.COLUMNNAME_C_BPartner_ID,
                null) // Not bound to a particular partner (i.e. C_BPartner_ID is null)
            //
            .orderBy()
            .addColumn(I_PMM_Product.COLUMN_PMM_Product_ID) // have a predictable order
            .endOrderBy()
            //
            .create()
            .list();

    final List<SyncProduct> syncProducts = new ArrayList<SyncProduct>(allPmmProducts.size());
    for (final I_PMM_Product pmmProduct : allPmmProducts) {
      final SyncProduct syncProduct = createSyncProduct(pmmProduct);
      if (syncProduct == null) {
        continue;
      }

      syncProducts.add(syncProduct);
    }
    return syncProducts;
  }

  public String createSyncInfoMessage() {
    return Services.get(IPMMMessageDAO.class).retrieveMessagesAsString(getCtx());
  }

  private final void setActiveRfqResponsesFullyLoadedRequired() {
    this._bpartnerId2activeRfqResponseLines_fullyLoadedRequired = true;
  }

  private List<I_C_RfQResponseLine> getActiveRfqResponseLines_ForBPartnerId(final int bpartnerId) {
    final Multimap<Integer, I_C_RfQResponseLine> bpartnerId2activeRfqResponseLines =
        getActiveRfqResponseLines_IndexedByBPartnerId();
    if (!bpartnerId2activeRfqResponseLines.containsKey(bpartnerId)) {
      final List<I_C_RfQResponseLine> rfqResponseLines =
          pmmRfQDAO.retrieveActiveResponseLines(getCtx(), bpartnerId);
      bpartnerId2activeRfqResponseLines.putAll(bpartnerId, rfqResponseLines);
    }
    return ImmutableList.copyOf(bpartnerId2activeRfqResponseLines.get(bpartnerId));
  }

  private List<I_C_RfQResponseLine> getActiveRfqResponseLines_ForRfQResponse(
      final I_C_RfQResponse rfqResponse) {
    // NOTE: we are not using the cache because usually this method is called only for one RfQ
    // response,
    // and loading the lines for all RfQResponses (even for same partner) would not be very
    // performant.
    return pmmRfQDAO.retrieveResponseLines(rfqResponse);
  }

  private Multimap<Integer, I_C_RfQResponseLine> getActiveRfqResponseLines_IndexedByBPartnerId() {
    if (_bpartnerId2activeRfqResponseLines_fullyLoadedRequired
        && !_bpartnerId2activeRfqResponseLines_fullyLoaded) {
      _bpartnerId2activeRfqResponseLines.clear(); // clear all first

      final List<I_C_RfQResponseLine> rfqResponseLines =
          pmmRfQDAO.retrieveAllActiveResponseLines(getCtx());
      for (final I_C_RfQResponseLine rfqResponseLine : rfqResponseLines) {
        final int bpartnerId = rfqResponseLine.getC_BPartner_ID();
        _bpartnerId2activeRfqResponseLines.put(bpartnerId, rfqResponseLine);
      }

      _bpartnerId2activeRfqResponseLines_fullyLoaded = true;
    }
    return _bpartnerId2activeRfqResponseLines;
  }

  public List<SyncRfQ> createSyncRfQs(final I_C_RfQResponse rfqResponse) {
    final List<SyncRfQ> syncRfQs = new ArrayList<>();

    for (final I_C_RfQResponseLine rfqResponseLine :
        getActiveRfqResponseLines_ForRfQResponse(rfqResponse)) {
      final SyncRfQ syncRfQ = createSyncRfQ(rfqResponseLine);
      if (syncRfQ == null) {
        continue;
      }

      syncRfQs.add(syncRfQ);
    }

    return syncRfQs;
  }

  private final SyncRfQ createSyncRfQ(final I_C_RfQResponseLine rfqResponseLine) {
    if (!pmmRfQBL.isDraft(rfqResponseLine)) {
      // shall not happen
      return null;
    }

    final SyncRfQ syncRfQ = new SyncRfQ();
    syncRfQ.setUuid(SyncUUIDs.toUUIDString(rfqResponseLine));

    syncRfQ.setDateStart(rfqResponseLine.getDateWorkStart());
    syncRfQ.setDateEnd(rfqResponseLine.getDateWorkComplete());
    syncRfQ.setDateClose(rfqResponseLine.getDateResponse());

    syncRfQ.setBpartner_uuid(SyncUUIDs.toUUIDString(rfqResponseLine.getC_BPartner()));

    final I_PMM_Product pmmProduct = rfqResponseLine.getPMM_Product();
    final SyncProduct syncProduct = createSyncProduct(pmmProduct);
    syncRfQ.setProduct(syncProduct);

    syncRfQ.setQtyRequested(rfqResponseLine.getQtyRequiered());
    syncRfQ.setQtyCUInfo(rfqResponseLine.getC_UOM().getUOMSymbol());

    syncRfQ.setCurrencyCode(rfqResponseLine.getC_Currency().getISO_Code());

    return syncRfQ;
  }

  public SyncRfQCloseEvent createSyncRfQCloseEvent(
      final I_C_RfQResponseLine rfqResponseLine, final boolean winnerKnown) {
    if (!pmmRfQBL.isCompletedOrClosed(rfqResponseLine)) {
      logger.warn(
          "Skip creating close event for {} because it's not completed or closed", rfqResponseLine);
      return null;
    }

    final SyncRfQCloseEvent event = new SyncRfQCloseEvent();
    event.setRfq_uuid(SyncUUIDs.toUUIDString(rfqResponseLine));
    event.setWinnerKnown(winnerKnown);
    if (winnerKnown) {
      event.setWinner(rfqResponseLine.isSelectedWinner());
    }

    if (winnerKnown && event.isWinner()) {
      final List<SyncProductSupply> plannedSyncProductSupplies =
          createPlannedSyncProductSupplies(rfqResponseLine);
      event.getPlannedSupplies().addAll(plannedSyncProductSupplies);
    }

    return event;
  }

  private List<SyncProductSupply> createPlannedSyncProductSupplies(
      final I_C_RfQResponseLine rfqResponseLine) {
    final I_C_Flatrate_Term contract = rfqResponseLine.getC_Flatrate_Term();
    Check.assumeNotNull(contract, "contract not null");

    final List<I_C_RfQResponseLineQty> rfqResponseLineQtys =
        pmmRfQDAO.retrieveResponseLineQtys(rfqResponseLine);
    if (rfqResponseLineQtys.isEmpty()) {
      return ImmutableList.of();
    }

    final String bpartner_uuid = SyncUUIDs.toUUIDString(contract.getDropShip_BPartner());
    final String contractLine_uuid = SyncUUIDs.toUUIDString(contract);
    final String product_uuid = SyncUUIDs.toUUIDString(contract.getPMM_Product());

    final List<SyncProductSupply> plannedSyncProductSupplies =
        new ArrayList<>(rfqResponseLineQtys.size());
    for (final I_C_RfQResponseLineQty rfqResponseLineQty : rfqResponseLineQtys) {
      final SyncProductSupply syncProductSupply = new SyncProductSupply();
      syncProductSupply.setBpartner_uuid(bpartner_uuid);
      syncProductSupply.setContractLine_uuid(contractLine_uuid);
      syncProductSupply.setProduct_uuid(product_uuid);

      syncProductSupply.setDay(rfqResponseLineQty.getDatePromised());
      syncProductSupply.setQty(rfqResponseLineQty.getQtyPromised());
      plannedSyncProductSupplies.add(syncProductSupply);
    }

    return plannedSyncProductSupplies;
  }
}
Esempio n. 9
0
 public final Logger getMRPLogger() {
   return LogManager.getLogger(IMRPContext.LOGGERNAME);
 }
Esempio n. 10
0
public class InvoiceLineBL implements IInvoiceLineBL {

  private static final Logger logger = LogManager.getLogger(InvoiceLineBL.class);

  @Override
  public void setTaxAmtInfo(
      final Properties ctx, final I_C_InvoiceLine il, final String getTrxName) {
    final IInvoiceBL invoiceBL = Services.get(IInvoiceBL.class);
    final ITaxBL taxBL = Services.get(ITaxBL.class);

    final int taxId = il.getC_Tax_ID();

    final boolean taxIncluded = invoiceBL.isTaxIncluded(il);
    final BigDecimal lineNetAmt = il.getLineNetAmt();
    final int taxPrecision = invoiceBL.getPrecision(il);

    final I_C_Tax tax = MTax.get(ctx, taxId);
    final BigDecimal taxAmtInfo = taxBL.calculateTax(tax, lineNetAmt, taxIncluded, taxPrecision);

    il.setTaxAmtInfo(taxAmtInfo);
  }

  @Override
  public boolean setTax(
      final Properties ctx, final org.compiere.model.I_C_InvoiceLine il, final String trxName) {
    int taxCategoryId = il.getC_TaxCategory_ID();
    if (taxCategoryId <= 0 && il.getM_Product_ID() > 0) {
      // NOTE: we can retrieve the tax category only if we have a product
      taxCategoryId = getC_TaxCategory_ID(il);
      il.setC_TaxCategory_ID(taxCategoryId);
    }

    if (il.getM_InOutLine_ID() <= 0) {
      logger.debug(il + "has M_InOutLine_ID=" + il.getM_InOutLine_ID() + ": returning");
      return false;
    }

    if (il.getM_Product_ID() <= 0) {
      // this might be the case if a descriptional il refers to an iol.
      logger.debug(il + "has M_Product_ID=" + il.getM_Product_ID() + ": returning");
      return false;
    }

    final I_M_InOut io = il.getM_InOutLine().getM_InOut();

    final I_C_Location locationFrom =
        Services.get(IWarehouseBL.class).getC_Location(io.getM_Warehouse());
    final int countryFromId = locationFrom.getC_Country_ID();

    final I_C_BPartner_Location locationTo =
        InterfaceWrapperHelper.create(io.getC_BPartner_Location(), I_C_BPartner_Location.class);

    final Timestamp shipDate = io.getMovementDate();
    final int taxId =
        Services.get(ITaxBL.class)
            .retrieveTaxIdForCategory(
                ctx,
                countryFromId,
                io.getAD_Org_ID(),
                locationTo,
                shipDate,
                taxCategoryId,
                il.getC_Invoice().isSOTrx(),
                trxName,
                false);

    if (taxId <= 0) {
      final I_C_Invoice invoice = il.getC_Invoice();
      throw new TaxNotFoundException(
          taxCategoryId,
          io.isSOTrx(),
          shipDate,
          locationFrom.getC_Location_ID(),
          locationTo.getC_Location_ID(),
          invoice.getDateInvoiced(),
          locationFrom.getC_Location_ID(),
          invoice.getC_BPartner_Location().getC_Location_ID());
    }

    final boolean taxChange = il.getC_Tax_ID() != taxId;
    if (taxChange) {
      logger.info("Changing C_Tax_ID to " + taxId + " for " + il);
      il.setC_Tax_ID(taxId);

      final I_C_Tax tax = il.getC_Tax();
      il.setC_TaxCategory_ID(tax.getC_TaxCategory_ID());
    }
    return taxChange;
  }

  @Override
  public boolean isPriceLocked(I_C_InvoiceLine invoiceLine) {
    // // Introduced by US1184, because having the same price on Order and Invoice
    // no - invoice does not generally have to have the same prive not generally
    // // is enforced by German Law
    // if (invoiceLine.getC_OrderLine_ID() > 0)
    // return true;
    //
    // return false;
    return false;
  }

  @Override
  public int getC_TaxCategory_ID(final org.compiere.model.I_C_InvoiceLine invoiceLine) {
    // FIXME: we need to retrieve the C_TaxCategory_ID by using Pricing Engine

    if (invoiceLine.getC_Charge_ID() > 0) {
      return invoiceLine.getC_Charge().getC_TaxCategory_ID();
    }

    final I_C_Invoice invoice = invoiceLine.getC_Invoice();

    final IPriceListDAO priceListDAO = Services.get(IPriceListDAO.class);
    final Boolean processedPLVFiltering =
        null; // task 09533: the user doesn't know about PLV's processed flag, so we can't filter by
              // it

    if (invoice.getM_PriceList_ID() != 100) // FIXME use PriceList_None constant
    {
      final I_M_PriceList priceList = invoice.getM_PriceList();

      final I_M_PriceList_Version priceListVersion =
          priceListDAO.retrievePriceListVersionOrNull(
              priceList, invoice.getDateInvoiced(), processedPLVFiltering);
      Check.errorIf(
          priceListVersion == null, "Missing PLV for M_PriceList and DateInvoiced of {}", invoice);

      final int m_Product_ID = invoiceLine.getM_Product_ID();
      Check.assume(m_Product_ID > 0, "M_Product_ID > 0 for {}", invoiceLine);

      final I_M_ProductPrice productPrice =
          priceListDAO.retrieveProductPrice(priceListVersion, m_Product_ID);

      return productPrice.getC_TaxCategory_ID();
    }

    // Fallback: try getting from Order Line
    if (invoiceLine.getC_OrderLine_ID() > 0) {
      return invoiceLine.getC_OrderLine().getC_TaxCategory_ID();
    }

    // Fallback: try getting from Invoice -> Order
    if (invoiceLine.getC_Invoice().getC_Order_ID() > 0) {
      final Properties ctx = InterfaceWrapperHelper.getCtx(invoiceLine);
      final String trxName = InterfaceWrapperHelper.getTrxName(invoiceLine);

      final I_C_Order order =
          InterfaceWrapperHelper.create(
              ctx, invoiceLine.getC_Invoice().getC_Order_ID(), I_C_Order.class, trxName);

      final I_M_PriceList priceList = order.getM_PriceList();

      final I_M_PriceList_Version priceListVersion =
          priceListDAO.retrievePriceListVersionOrNull(
              priceList, invoice.getDateInvoiced(), processedPLVFiltering);
      Check.errorIf(
          priceListVersion == null, "Missing PLV for M_PriceList and DateInvoiced of {}", invoice);

      final int m_Product_ID = invoiceLine.getM_Product_ID();
      Check.assume(m_Product_ID > 0, "M_Product_ID > 0 for {}", invoiceLine);

      final I_M_ProductPrice productPrice =
          priceListDAO.retrieveProductPrice(priceListVersion, m_Product_ID);

      return productPrice.getC_TaxCategory_ID();
    }

    throw new AdempiereException(
        "@NotFound@ @C_TaxCategory_ID@ (" + "@C_InvoiceLine_ID@:" + invoiceLine + ")");
  }

  @Override
  public void setQtyInvoicedInPriceUOM(final I_C_InvoiceLine invoiceLine) {
    final BigDecimal qtyInvoicedInPriceUOM = calculateQtyInvoicedInPriceUOM(invoiceLine);
    invoiceLine.setQtyInvoicedInPriceUOM(qtyInvoicedInPriceUOM);
  }

  private BigDecimal calculateQtyInvoicedInPriceUOM(final I_C_InvoiceLine invoiceLine) {
    Check.assumeNotNull(invoiceLine, "invoiceLine not null");

    final BigDecimal qty = invoiceLine.getQtyInvoiced();

    Check.assumeNotNull(qty, "qty not null");

    final I_C_UOM priceUOM = invoiceLine.getPrice_UOM();
    if (invoiceLine.getPrice_UOM_ID() <= 0) {
      return qty;
    }
    if (invoiceLine.getM_Product_ID() <= 0) {
      return qty;
    }

    final I_M_Product product = invoiceLine.getM_Product();
    if (product.getC_UOM_ID() <= 0) {
      return qty;
    }

    final Properties ctx = InterfaceWrapperHelper.getCtx(invoiceLine);
    final BigDecimal qtyInPriceUOM =
        Services.get(IUOMConversionBL.class).convertFromProductUOM(ctx, product, priceUOM, qty);

    return qtyInPriceUOM;
  }

  @Override
  public IEditablePricingContext createPricingContext(final I_C_InvoiceLine invoiceLine) {
    final I_C_Invoice invoice = invoiceLine.getC_Invoice();
    final int priceListId = invoice.getM_PriceList_ID();

    final BigDecimal qtyInvoicedInPriceUOM = calculateQtyInvoicedInPriceUOM(invoiceLine);

    return createPricingContext(invoiceLine, priceListId, qtyInvoicedInPriceUOM);
  }

  public IEditablePricingContext createPricingContext(
      I_C_InvoiceLine invoiceLine, final int priceListId, final BigDecimal priceQty) {
    final org.compiere.model.I_C_Invoice invoice = invoiceLine.getC_Invoice();

    final boolean isSOTrx = invoice.isSOTrx();

    final int productId = invoiceLine.getM_Product_ID();

    int bPartnerId = invoice.getC_BPartner_ID();

    final Timestamp date = invoice.getDateInvoiced();

    final IEditablePricingContext pricingCtx =
        Services.get(IPricingBL.class)
            .createInitialContext(
                productId, bPartnerId, invoiceLine.getPrice_UOM_ID(), priceQty, isSOTrx);
    pricingCtx.setPriceDate(date);

    // 03152: setting the 'ol' to allow the subscription system to compute the right price
    pricingCtx.setReferencedObject(invoiceLine);

    pricingCtx.setM_PriceList_ID(priceListId);
    // PLV is only accurate if PL selected in header
    // metas: relay on M_PriceList_ID only, don't use M_PriceList_Version_ID
    // pricingCtx.setM_PriceList_Version_ID(orderLine.getM_PriceList_Version_ID());

    return pricingCtx;
  }

  @Override
  public void updateLineNetAmt(final I_C_InvoiceLine line, final BigDecimal qtyEntered) {
    if (qtyEntered != null) {
      final Properties ctx = InterfaceWrapperHelper.getCtx(line);
      final I_C_Invoice invoice = line.getC_Invoice();
      final int priceListId = invoice.getM_PriceList_ID();

      //
      // We need to get the quantity in the pricing's UOM (if different)
      final BigDecimal convertedQty = calculateQtyInvoicedInPriceUOM(line);

      // this code has been borrowed from
      // org.compiere.model.CalloutOrder.amt
      final int stdPrecision = MPriceList.getStandardPrecision(ctx, priceListId);

      BigDecimal lineNetAmt = convertedQty.multiply(line.getPriceActual());

      if (lineNetAmt.scale() > stdPrecision) {
        lineNetAmt = lineNetAmt.setScale(stdPrecision, BigDecimal.ROUND_HALF_UP);
      }
      logger.info("LineNetAmt=" + lineNetAmt);
      line.setLineNetAmt(lineNetAmt);
    }
  }

  @Override
  public void updatePrices(final I_C_InvoiceLine invoiceLine) {
    // Product was not set yet. There is no point to calculate the prices
    if (invoiceLine.getM_Product_ID() <= 0) {
      return;
    }

    //
    // Calculate Pricing Result
    final IEditablePricingContext pricingCtx = createPricingContext(invoiceLine);
    final boolean usePriceUOM = InterfaceWrapperHelper.isNew(invoiceLine);
    pricingCtx.setConvertPriceToContextUOM(!usePriceUOM);

    pricingCtx.setManualPrice(invoiceLine.isManualPrice());

    if (pricingCtx.isManualPrice()) {
      // Task 08908: do not calculate the prices in case the price is manually set
      return;
    }

    final IPricingResult pricingResult = Services.get(IPricingBL.class).calculatePrice(pricingCtx);
    if (!pricingResult.isCalculated()) {
      throw new ProductNotOnPriceListException(pricingCtx, invoiceLine.getLine());
    }

    //
    // PriceList
    final BigDecimal priceList = pricingResult.getPriceList();
    invoiceLine.setPriceList(priceList);

    invoiceLine.setPriceLimit(pricingResult.getPriceLimit());
    invoiceLine.setPrice_UOM_ID(pricingResult.getPrice_UOM_ID());

    invoiceLine.setPriceEntered(pricingResult.getPriceStd());
    invoiceLine.setPriceActual(pricingResult.getPriceStd());

    //
    // Discount

    invoiceLine.setDiscount(pricingResult.getDiscount());

    //
    // Calculate PriceActual from PriceEntered and Discount
    calculatePriceActual(invoiceLine, pricingResult.getPrecision());

    invoiceLine.setPrice_UOM_ID(pricingResult.getPrice_UOM_ID()); //
  }

  private void calculatePriceActual(final I_C_InvoiceLine invoiceLine, final int precision) {
    final BigDecimal discount = invoiceLine.getDiscount();
    final BigDecimal priceEntered = invoiceLine.getPriceEntered();

    BigDecimal priceActual;
    if (priceEntered.signum() == 0) {
      priceActual = priceEntered;
    } else {
      final int precisionToUse;
      if (precision >= 0) {
        precisionToUse = precision;
      } else {
        final I_C_Invoice invoice = invoiceLine.getC_Invoice();

        precisionToUse = invoice.getM_PriceList().getPricePrecision();
      }

      priceActual = subtractDiscount(priceEntered, discount, precisionToUse);
    }

    invoiceLine.setPriceActual(priceActual);
  }

  private BigDecimal subtractDiscount(
      final BigDecimal baseAmount, final BigDecimal discount, final int precision) {
    BigDecimal multiplier = Env.ONEHUNDRED.subtract(discount);
    multiplier = multiplier.divide(Env.ONEHUNDRED, precision * 3, RoundingMode.HALF_UP);

    final BigDecimal result =
        baseAmount.multiply(multiplier).setScale(precision, RoundingMode.HALF_UP);
    return result;
  }
}
Esempio n. 11
0
@Validator(I_C_BPartner.class)
public class C_BPartner {
  private static final transient Logger logger = LogManager.getLogger(C_BPartner.class);

  /**
   * If the bpartner's <code>DocumentCopies</code> changes, then this method updates all unprocessed
   * C_Printing_Queues which reference the bpartner. The update is performed in a dedicated
   * transaction, after the MV's current trx is committed.
   *
   * @task
   *     http://dewiki908/mediawiki/index.php/08958_Druck_Warteschlange_Sortierung_Massendruck_%28103271838939%29
   */
  @ModelChange(
      timings = {ModelValidator.TYPE_AFTER_CHANGE},
      ifColumnsChanged = I_C_BPartner.COLUMNNAME_DocumentCopies)
  public void setCopiesFromBPartner(final I_C_BPartner bPartner) {
    final int documentCopies =
        bPartner.getDocumentCopies() > 0 ? bPartner.getDocumentCopies() : 1; // default

    final ITrxManager trxManager = Services.get(ITrxManager.class);
    trxManager
        .getTrxListenerManager(InterfaceWrapperHelper.getTrxName(bPartner))
        .registerListener(
            new TrxListenerAdapter() {
              @Override
              public void afterCommit(final ITrx trx) {
                trxManager.run(
                    new TrxRunnable() {
                      @Override
                      public void run(final String localTrxName) throws Exception {
                        final Properties ctx = InterfaceWrapperHelper.getCtx(bPartner);

                        final IQueryBL queryBL = Services.get(IQueryBL.class);

                        // 08958: for the starts, we only update queue items that reference invoices
                        final IQuery<I_C_DocType> invoicedocTypeQuery =
                            queryBL
                                .createQueryBuilder(
                                    I_C_DocType.class, ctx, ITrx.TRXNAME_ThreadInherited)
                                .addOnlyActiveRecordsFilter()
                                .addInArrayFilter(
                                    I_C_DocType.COLUMNNAME_DocBaseType,
                                    X_C_DocType.DOCBASETYPE_APCreditMemo,
                                    X_C_DocType.DOCBASETYPE_APInvoice,
                                    X_C_DocType.DOCBASETYPE_ARCreditMemo,
                                    X_C_DocType.DOCBASETYPE_ARInvoice,
                                    X_C_DocType.DOCBASETYPE_ARProFormaInvoice)
                                .create();

                        final int updatedCount =
                            queryBL
                                .createQueryBuilder(
                                    I_C_Printing_Queue.class, ctx, ITrx.TRXNAME_ThreadInherited)
                                .addOnlyActiveRecordsFilter()
                                .addEqualsFilter(
                                    I_C_Printing_Queue.COLUMN_C_BPartner_ID,
                                    bPartner.getC_BPartner_ID())
                                .addEqualsFilter(I_C_Printing_Queue.COLUMN_Processed, false)
                                .addInSubQueryFilter(
                                    I_C_Printing_Queue.COLUMN_C_DocType_ID,
                                    I_C_DocType.COLUMN_C_DocType_ID,
                                    invoicedocTypeQuery)
                                .addNotEqualsFilter(
                                    I_C_Printing_Queue.COLUMN_Copies, documentCopies)
                                .create()
                                .updateDirectly()
                                .addSetColumnValue(
                                    I_C_Printing_Queue.COLUMNNAME_Copies, documentCopies)
                                .setExecuteDirectly(true) // just to be sure
                                .execute();

                        logger.debug(
                            "C_BPartner={}: set C_Printing_Queue.Copies={} for {} C_Printing_Queue records",
                            new Object[] {bPartner, documentCopies, updatedCount});
                      }
                    });
              }
            });
  }
}
/**
 * Watches current test and dumps the database to console in case of failure.
 *
 * <p>To include in your tests, you need to declare a public field like this:
 *
 * <pre>
 * &#64;Rule
 * public final {@link TestWatcher} testWatcher = new {@link AdempiereTestWatcher}()
 * </pre>
 */
public class AdempiereTestWatcher extends TestWatcher {
  private static final Logger logger = LogManager.getLogger(AdempiereTestWatcher.class);

  /** Context variables to be printed to screen in case the test fails */
  private final Map<String, Object> context = new LinkedHashMap<>();

  /**
   * Called after a test succeed.
   *
   * <p>Does nothing at this level.
   */
  @Override
  protected void succeeded(final Description description) {
    // nothing
  }

  /**
   * Called after a test failed. It:
   *
   * <ul>
   *   <li>dump database content
   * </ul>
   */
  @Override
  protected void failed(final Throwable e, final Description description) {
    POJOLookupMap.get().dumpStatus("After test failed: " + description.getDisplayName());

    //
    // Dump retained context values
    for (final Entry<String, Object> entry : context.entrySet()) {
      final String name = entry.getKey();
      final Object value = entry.getValue();
      System.out.println("\n" + name + ": " + value);
    }
  }

  /**
   * Called after a test finished (successful or not). It:
   *
   * <ul>
   *   <li>clears database content
   * </ul>
   */
  @Override
  protected void finished(final Description description) {
    POJOLookupMap.get().clear();
    context.clear();
  }

  /**
   * Put given variable to context.
   *
   * <p>In case the test will fail, all context variables will be printed to console.
   *
   * @param name
   * @param value
   */
  public void putContext(final String name, final Object value) {
    Check.assumeNotEmpty(name, "name is not empty");
    context.put(name, value);
  }

  /**
   * Convenient variant for {@link #putContext(String, Object)} which considers value's class name
   * as the context variable <code>name</code>.
   *
   * @param value value, not null.
   */
  public void putContext(final Object value) {
    if (value == null) {
      logger.error(
          "Cannot put a null value to context. This is a development error. Skipped for now",
          new Exception("TRACE"));
      return;
    }

    final String name = value.getClass().getName();
    putContext(name, value);
  }
}
Esempio n. 13
0
/** @author tsa */
public class PrinterRoutingBL implements IPrinterRoutingBL {
  private final Logger log = LogManager.getLogger(getClass());

  private static final String DEFAULT_PrinterType = PRINTERTYPE_General;

  public IPrintingService findPrintingService(final ProcessInfo pi) {
    final Properties ctx = Env.getCtx();
    final int AD_Client_ID = Env.getAD_Client_ID(ctx);
    final int AD_Org_ID = Env.getAD_Org_ID(ctx);
    final int AD_Role_ID = Env.getAD_Role_ID(ctx);
    final int AD_User_ID = Env.getAD_User_ID(ctx);
    final int AD_Process_ID = pi.getAD_Process_ID();
    final int C_DocType_ID =
        Services.get(IDocActionBL.class).getC_DocType_ID(ctx, pi.getTable_ID(), pi.getRecord_ID());
    final String printerType = null;

    return findPrintingService0(
        ctx,
        AD_Client_ID,
        AD_Org_ID,
        AD_Role_ID,
        AD_User_ID,
        C_DocType_ID,
        AD_Process_ID,
        printerType);
  }

  @Override
  public IPrintingService findPrintingService(
      final Properties ctx,
      final int C_DocType_ID,
      final int AD_Process_ID,
      final String printerType) {
    final int AD_Client_ID = Env.getAD_Client_ID(ctx);
    final int AD_Org_ID = Env.getAD_Org_ID(ctx);
    final int AD_Role_ID = Env.getAD_Role_ID(ctx);
    final int AD_User_ID = Env.getAD_User_ID(ctx);

    return findPrintingService0(
        ctx,
        AD_Client_ID,
        AD_Org_ID,
        AD_Role_ID,
        AD_User_ID,
        C_DocType_ID,
        AD_Process_ID,
        printerType);
  }

  // @Cached
  private IPrintingService findPrintingService0(
      @CacheCtx final Properties ctx,
      final int AD_Client_ID,
      final int AD_Org_ID,
      final int AD_Role_ID,
      final int AD_User_ID,
      final int C_DocType_ID,
      final int AD_Process_ID,
      final String printerType) {
    final IPrinterRoutingDAO dao = Services.get(IPrinterRoutingDAO.class);

    final List<I_AD_PrinterRouting> rs =
        dao.fetchPrinterRoutings(
            ctx,
            AD_Client_ID,
            AD_Org_ID,
            AD_Role_ID,
            AD_User_ID,
            C_DocType_ID,
            AD_Process_ID,
            printerType,
            I_AD_PrinterRouting.class);
    for (final I_AD_PrinterRouting route : rs) {
      final IPrintingService printingService = getPrintingService(route);
      if (printingService != null) {
        if (LogManager.isLevelFine()) {
          log.debug("Found: " + printingService);
        }
        return printingService;
      }
    }
    return getDefaultPrintingService(ctx, printerType);
  }

  // set it protected to make it testable
  private IPrintingService getPrintingService(final I_AD_PrinterRouting route) {
    if (LogManager.isLevelFine()) {
      log.debug("Checking route: " + route);
    }

    final I_AD_Printer printer = route.getAD_Printer();
    if (LogManager.isLevelFine()) {
      log.debug("Printer: " + printer.getPrinterName());
    }

    final PrintService systemPrintService = getSystemPrintService(printer.getPrinterName());
    if (systemPrintService == null) {
      log.info("Printer not found in system: " + printer.getPrinterName());
      return null;
    }
    if (LogManager.isLevelFine()) {
      log.debug("System Print Service: " + systemPrintService);
    }

    final String printerName = systemPrintService.getName();
    Boolean isDirectPrint = null;
    if (isDirectPrint == null && route.getIsDirectPrint() != null) {
      isDirectPrint = X_AD_PrinterRouting.ISDIRECTPRINT_Yes.equals(route.getIsDirectPrint());
      if (LogManager.isLevelFine()) {
        log.debug("IsDirectPrint: " + isDirectPrint + " (From: " + route + ")");
      }
    }
    if (isDirectPrint == null) {
      isDirectPrint = isDirectPrint(printer);
    }

    final PrintingServiceImpl printingService =
        new PrintingServiceImpl(printerName, printer.getPrinterType(), isDirectPrint);

    if (LogManager.isLevelFine()) {
      log.debug("Printing Service: " + printingService);
    }
    return printingService;
  }

  @Cached
  /* package */ PrintService getSystemPrintService(final String printerName) {
    final PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
    if (services == null || services.length == 0) {
      return null;
    }
    for (final PrintService service : services) {
      if (service.getName().equals(printerName)) {
        return service;
      }
    }
    return null;
  }

  private boolean isDirectPrint(final I_AD_Printer printer) {
    Boolean isDirectPrint = null;
    if (printer != null && printer.getIsDirectPrint() != null) {
      isDirectPrint = X_AD_Printer.ISDIRECTPRINT_Yes.equals(printer.getIsDirectPrint());
      if (LogManager.isLevelFine()) {
        log.debug("IsDirectPrint for: " + isDirectPrint + " (From: " + printer + ")");
      }
    }
    if (isDirectPrint == null) {
      isDirectPrint = !Ini.isPropertyBool(Ini.P_PRINTPREVIEW);
      if (LogManager.isLevelFine()) {
        log.debug("IsDirectPrint: " + isDirectPrint + " (From: ini)");
      }
    }
    return isDirectPrint;
  }

  private IPrintingService getDefaultPrintingService(final Properties ctx, String printerType) {
    if (printerType == null) {
      printerType = DEFAULT_PrinterType;
    }

    final String printerName = getDefaultPrinterName(printerType);
    if (printerName == null) {
      throw new AdempiereException("No default printer found for type " + printerType); // TODO: trl
    }

    final I_AD_Printer printer =
        Services.get(IPrinterRoutingDAO.class).findPrinterByName(ctx, printerName);
    final boolean isDirectPrint = isDirectPrint(printer);
    return new PrintingServiceImpl(printerName, printerType, isDirectPrint);
  }

  @Override
  public String getDefaultPrinterName() {
    return getDefaultPrinterName(DEFAULT_PrinterType);
  }

  @Override
  public String getDefaultPrinterName(String printerType) {
    log.debug("Looking for printerType=" + printerType);

    if (printerType == null) {
      printerType = DEFAULT_PrinterType;
    }

    final String printerName;
    if (PRINTERTYPE_General.equals(printerType)) {
      printerName = getDefaultPrinterNameFromIni(Ini.P_PRINTER);
    } else if (PRINTERTYPE_Label.equals(printerType)) {
      printerName = getDefaultPrinterNameFromIni(Ini.P_LABELPRINTER);
    } else {
      // TODO: trl
      throw new AdempiereException(
          "No default printer logic defined for PrinterType " + printerType);
    }

    log.debug("PrinterName: " + printerName);
    return printerName;
  }

  private String getDefaultPrinterNameFromIni(final String propName) {
    log.debug("Looking for " + propName + " in ini file");

    String printerName = Ini.getProperty(propName);
    if (!Check.isEmpty(printerName)) {
      log.debug("Found printerName: " + printerName);
      return printerName;
    }

    log.debug("Looking for machine's printers");
    final PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
    if (services == null || services.length == 0) {
      // [email protected]: so what? we don't need a printer to generate PDFs
      // log.warn("No default printer found on this machine");
      return "";
    }

    printerName = services[0].getName();
    Ini.setProperty(propName, printerName);
    log.debug("Found printerName: " + printerName);
    return printerName;
  }

  @Override
  public String findPrinterName(
      final Properties ctx,
      final int C_DocType_ID,
      final int AD_Process_ID,
      final String printerType) {
    final IPrintingService printingService =
        findPrintingService(ctx, C_DocType_ID, AD_Process_ID, printerType);
    return printingService.getPrinterName();
  }

  @Override
  public String findPrinterName(final ProcessInfo pi) {
    final IPrintingService printingService = findPrintingService(pi);
    return printingService.getPrinterName();
  }
}
Esempio n. 14
0
/**
 * Default implementation of {@link IEntityTypesCache}, which is retrieving the entity types from
 * database.
 *
 * @author tsa
 */
public class EntityTypesCache implements IEntityTypesCache {
  /** Shared instance */
  public static final transient EntityTypesCache instance = new EntityTypesCache();

  // services
  private final transient Logger logger = LogManager.getLogger(getClass());

  /** Cache: EntityType to {@link EntityTypeEntry} */
  private final CCache<String, EntityTypeEntry> cache =
      new CCache<>(
          I_AD_EntityType.Table_Name + "#EntityTypeEntry" // cacheName
          ,
          50 // initialCapacity
          ,
          0 // NoExpire (NOTE: this is important because we don't want some of our records to get
            // stale, because we would never load them again partially)
          );

  /** First Not System Entity ID 10=D, 20=C, 100=U, 110=CUST, 200=A, 210=EXT, 220=XX etc */
  private static final int MAX_SystemMaintained_AD_EntityType_ID = 1000000;

  @VisibleForTesting
  EntityTypesCache() {
    super();

    // NOTE: we are lazy loading the cache because it might be that initially we don't have a
    // database connection anyways
    // loadIfNeeded();
  }

  @Override
  public String toString() {
    return "EntityTypesCache [cache=" + cache + "]";
  }

  private void loadIfNeeded() {
    //
    // Check if it's loaded
    if (!cache.isEmpty()) {
      return;
    }

    //
    // Preload the cache will all entity types
    synchronized (cache) {
      if (cache.isEmpty()) {
        final Map<String, EntityTypeEntry> entityTypeEntries = retrieveEntityTypeEntries();
        cache.clear();
        cache.putAll(entityTypeEntries);
      }
    }
  }

  /**
   * Retrieves all entity types from underlying data source.
   *
   * @return all entity types as a map of EntityType to {@link EntityTypeEntry}.
   */
  @VisibleForTesting
  Map<String, EntityTypeEntry> retrieveEntityTypeEntries() {
    final Map<String, EntityTypeEntry> entityTypeEntries = new HashMap<>(50);
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    final String sql = "SELECT * FROM AD_EntityType WHERE IsActive=? ORDER BY AD_EntityType_ID";
    final Object[] params = new Object[] {true};
    try {
      pstmt = DB.prepareStatement(sql, ITrx.TRXNAME_None);
      DB.setParameters(pstmt, params);
      rs = pstmt.executeQuery();
      while (rs.next()) {
        final EntityTypeEntry entry = loadEntityTypeEntry(rs);
        entityTypeEntries.put(entry.getEntityType(), entry);
      }
    } catch (SQLException e) {
      throw new DBException(e, sql, params);
    } finally {
      DB.close(rs, pstmt);
      rs = null;
      pstmt = null;
    }

    return entityTypeEntries;
  }

  private final EntityTypeEntry loadEntityTypeEntry(final ResultSet rs) throws SQLException {
    final String entityType = rs.getString(I_AD_EntityType.COLUMNNAME_EntityType);
    final String modelPackage = rs.getString(I_AD_EntityType.COLUMNNAME_ModelPackage);
    final String webUIServletListenerClass =
        rs.getString(I_AD_EntityType.COLUMNNAME_WebUIServletListenerClass);
    final boolean displayedInUI =
        DisplayType.toBoolean(rs.getString(I_AD_EntityType.COLUMNNAME_IsDisplayed));

    final int adEntityTypeId = rs.getInt(I_AD_EntityType.COLUMNNAME_AD_EntityType_ID);
    final boolean systemMaintained = adEntityTypeId < MAX_SystemMaintained_AD_EntityType_ID;

    final EntityTypeEntry entry =
        EntityTypeEntry.builder()
            .setEntityType(entityType)
            .setModelPackage(modelPackage)
            .setWebUIServletListenerClass(webUIServletListenerClass)
            .setDisplayedInUI(displayedInUI)
            .setSystemMaintained(systemMaintained)
            .build();
    return entry;
  }

  @Override
  public List<String> getEntityTypeNames() {
    loadIfNeeded();
    return ImmutableList.copyOf(cache.keySet());
  }

  private EntityTypeEntry getEntityTypeEntryOrNull(final String entityType) {
    Check.assumeNotEmpty(
        entityType,
        "entityType not empty"); // fail because in most of the cases is a development error

    loadIfNeeded();

    final EntityTypeEntry entry = cache.get(entityType);
    return entry;
  }

  private EntityTypeEntry getEntityTypeEntry(final String entityType) {
    final EntityTypeEntry entry = getEntityTypeEntryOrNull(entityType);
    if (entry == null) {
      final AdempiereException ex =
          new AdempiereException(
              "No EntityType entry found for entity type: "
                  + entityType
                  + "\n Available EntityTypes are: "
                  + cache.keySet());
      logger.warn(ex.getLocalizedMessage(), ex);
    }
    return entry;
  }

  @Override
  public String getModelPackage(final String entityType) {
    final EntityTypeEntry entry = getEntityTypeEntry(entityType);
    if (entry == null) {
      return null;
    }

    return entry.getModelPackage();
  }

  @Override
  public boolean isActive(final String entityType) {
    final EntityTypeEntry entry = getEntityTypeEntryOrNull(entityType);
    if (entry == null) {
      return false;
    }
    return true;
  }

  @Override
  public boolean isDisplayedInUI(final String entityType) {
    final EntityTypeEntry entry = getEntityTypeEntryOrNull(entityType);
    if (entry == null) {
      return false;
    }
    return entry.isDisplayedInUI();
  }

  public final String getDisplayedInUIEntityTypeSQLWhereClause(final String joinEntityTypeColumn) {
    return "exists (select 1 from AD_EntityType et where et.EntityType="
        + joinEntityTypeColumn
        + " and et.IsActive='Y'" // EntityType shall be active
        + " and et."
        + I_AD_EntityType.COLUMNNAME_IsDisplayed
        + "='Y'" // shall be displayed in UI
        + ")";
  }

  public boolean isSystemMaintained(final String entityType) {
    return getEntityTypeEntry(entityType).isSystemMaintained();
  }

  public String getWebUIServletListenerClass(final String entityType) {
    return getEntityTypeEntry(entityType).getWebUIServletListenerClass();
  }

  public static final class EntityTypeEntry {
    @VisibleForTesting
    static final Builder builder() {
      return new Builder();
    }

    private final String entityType;
    private final String modelPackage;
    private final boolean displayedInUI;
    private final boolean systemMaintained;
    private final String webUIServletListenerClass;

    private EntityTypeEntry(final Builder builder) {
      super();

      this.entityType = builder.entityType;
      Check.assumeNotEmpty(entityType, "entityType not empty");

      this.modelPackage = builder.modelPackage;
      this.displayedInUI = builder.displayedInUI;
      this.systemMaintained = builder.systemMaintained;
      this.webUIServletListenerClass = builder.webUIServletListenerClass;
    }

    @Override
    public String toString() {
      return "EntityTypeEntry [entityType=" + entityType + ", modelPackage=" + modelPackage + "]";
    }

    public String getEntityType() {
      return entityType;
    }

    public String getModelPackage() {
      return modelPackage;
    }

    public boolean isDisplayedInUI() {
      return displayedInUI;
    }

    public String getWebUIServletListenerClass() {
      return webUIServletListenerClass;
    }

    /**
     * Is System Maintained. Any Entity Type with ID < 1000000.
     *
     * @return true if D/C/U/CUST/A/EXT/XX (ID < 1000000)
     */
    public boolean isSystemMaintained() {
      return systemMaintained;
    }

    public static final class Builder {
      private String entityType;
      private String modelPackage;
      private boolean displayedInUI = true; // default true
      private boolean systemMaintained = false;
      public String webUIServletListenerClass;

      private Builder() {
        super();
      }

      public EntityTypeEntry build() {
        return new EntityTypeEntry(this);
      }

      public Builder setEntityType(String entityType) {
        this.entityType = entityType;
        return this;
      }

      public Builder setModelPackage(String modelPackage) {
        this.modelPackage = modelPackage;
        return this;
      }

      public Builder setDisplayedInUI(boolean displayedInUI) {
        this.displayedInUI = displayedInUI;
        return this;
      }

      public Builder setSystemMaintained(boolean systemMaintained) {
        this.systemMaintained = systemMaintained;
        return this;
      }

      public Builder setWebUIServletListenerClass(String webUIServletListenerClass) {
        this.webUIServletListenerClass = webUIServletListenerClass;
        return this;
      }
    }
  }
}
Esempio n. 15
0
/**
 * Warehouse Locator Control
 *
 * @author Jorg Janke
 * @version $Id: VLocator.java,v 1.5 2006/07/30 00:51:27 jjanke Exp $
 */
public class VLocator extends JComponent
    implements VEditor,
        ActionListener,
        IRefreshableEditor,
        IZoomableEditor,
        ICopyPasteSupportEditorAware {
  /** */
  private static final long serialVersionUID = 1953277256988665242L;

  /** IDE Constructor */
  public VLocator() {
    this("M_Locator_ID", false, false, true, null, 0);
  } //  VLocator

  /**
   * Constructor
   *
   * @param columnName ColumnName
   * @param mandatory mandatory
   * @param isReadOnly read only
   * @param isUpdateable updateable
   * @param mLocator locator (lookup) model
   * @param WindowNo window no
   */
  public VLocator(
      String columnName,
      boolean mandatory,
      boolean isReadOnly,
      boolean isUpdateable,
      MLocatorLookup mLocator,
      int WindowNo) {
    super();
    super.setName(columnName);
    m_columnName = columnName;
    m_mLocator = mLocator;
    m_WindowNo = WindowNo;
    //
    LookAndFeel.installBorder(this, "TextField.border");
    this.setLayout(new BorderLayout());

    //
    // Text
    VEditorUtils.setupInnerTextComponentUI(m_text);
    m_text.setEditable(true);
    m_text.setFocusable(true);
    m_text.addActionListener(this);
    this.add(m_text, BorderLayout.CENTER);

    //
    // Button
    {
      m_button = VEditorUtils.createActionButton("Locator", m_text);
      m_button.addActionListener(this);
      VEditorDialogButtonAlign.addVEditorButtonUsingBorderLayout(getClass(), this, m_button);
    }

    //
    // Size
    VEditorUtils.setupVEditorDimensionFromInnerTextDimension(this, m_text);

    //	ReadWrite
    if (isReadOnly || !isUpdateable) setReadWrite(false);
    else setReadWrite(true);
    setMandatory(mandatory);

    setDefault_Locator_ID(); // set default locator, teo_sarca [ 1661546 ]

    //
    // Create and bind the context menu
    new EditorContextPopupMenu(this);
  } //	VLocator

  /** Dispose */
  @Override
  public void dispose() {
    m_text = null;
    m_button = null;
    m_mLocator = null;
  } //  dispose

  private CTextField m_text = new CTextField(VLookup.DISPLAY_LENGTH);
  private VEditorActionButton m_button = null;
  private MLocatorLookup m_mLocator;
  private Object m_value;
  //
  private final String m_columnName;
  private int m_WindowNo;
  private boolean mandatory = false;
  private boolean readWrite = true;
  /** Logger */
  private static final Logger log = LogManager.getLogger(VLocator.class);

  private GridField m_mField;

  /**
   * Enable/disable
   *
   * @param value r/w
   */
  @Override
  public void setReadWrite(boolean value) {
    this.readWrite = value;
    m_button.setReadWrite(value);
    setBackground(false);
  } //	setReadWrite

  /**
   * IsReadWrite
   *
   * @return true if ReadWrite
   */
  @Override
  public boolean isReadWrite() {
    return readWrite;
  } //	isReadWrite

  /**
   * Set Mandatory (and back bolor)
   *
   * @param mandatory true if mandatory
   */
  @Override
  public void setMandatory(boolean mandatory) {
    this.mandatory = mandatory;
    setBackground(false);
  } //	setMandatory

  /**
   * Is it mandatory
   *
   * @return true if mandatory
   */
  @Override
  public boolean isMandatory() {
    return mandatory;
  } //	isMandatory

  /**
   * Set Background
   *
   * @param color color
   */
  @Override
  public void setBackground(Color color) {
    if (!color.equals(m_text.getBackground())) m_text.setBackground(color);
  } //	setBackground

  /**
   * Set Background based on editable / mandatory / error
   *
   * @param error if true, set background to error color, otherwise mandatory/editable
   */
  @Override
  public void setBackground(boolean error) {
    if (error) setBackground(AdempierePLAF.getFieldBackground_Error());
    else if (!isReadWrite()) setBackground(AdempierePLAF.getFieldBackground_Inactive());
    else if (isMandatory()) setBackground(AdempierePLAF.getFieldBackground_Mandatory());
    else setBackground(AdempierePLAF.getFieldBackground_Normal());
  } //  setBackground

  /**
   * Set Foreground
   *
   * @param fg color
   */
  @Override
  public void setForeground(Color fg) {
    m_text.setForeground(fg);
  } //  setForeground

  /** Request Focus */
  @Override
  public void requestFocus() {
    m_text.requestFocus();
  } //	requestFocus

  /**
   * Set Editor to value
   *
   * @param value Integer
   */
  @Override
  public void setValue(Object value) {
    setValue(value, false);
  } //	setValue

  /**
   * Set Value
   *
   * @param value value
   * @param fire data binding
   */
  private void setValue(Object value, boolean fire) {
    if (m_mLocator == null) {
      return;
    }

    if (value != null) {
      m_mLocator.setOnly_Warehouse_ID(getOnly_Warehouse_ID());
      m_mLocator.setOnly_Product_ID(getOnly_Product_ID());
      if (!m_mLocator.isValid(value)) value = null;
    }
    //
    m_value = value;
    m_text.setText(m_mLocator.getDisplay(value)); // 	loads value

    //	Data Binding
    try {
      fireVetoableChange(m_columnName, null, value);
    } catch (PropertyVetoException pve) {
    }
  } //	setValue

  /**
   * Property Change Listener
   *
   * @param evt PropertyChangeEvent
   */
  @Override
  public void propertyChange(PropertyChangeEvent evt) {
    if (evt.getPropertyName().equals(org.compiere.model.GridField.PROPERTY))
      setValue(evt.getNewValue());

    // metas: request focus (2009_0027_G131)
    if (evt.getPropertyName().equals(org.compiere.model.GridField.REQUEST_FOCUS)) requestFocus();
    // metas end

  } //  propertyChange

  /**
   * Return Editor value
   *
   * @return value
   */
  @Override
  public Object getValue() {
    if (getM_Locator_ID() == 0) return null;
    return m_value;
  } //	getValue

  /**
   * Get M_Locator_ID
   *
   * @return id
   */
  public int getM_Locator_ID() {
    if (m_value != null && m_value instanceof Integer) return ((Integer) m_value).intValue();
    return 0;
  } //	getM_Locator_ID

  /**
   * Return Display Value
   *
   * @return display value
   */
  @Override
  public String getDisplay() {
    return m_text.getText();
  } //  getDisplay

  @Override
  public void refreshValue() {
    if (m_mLocator != null) {
      m_mLocator.refresh();
    }
  }

  /**
   * ActionListener
   *
   * @param e ActionEvent
   */
  @Override
  public void actionPerformed(ActionEvent e) {
    //	Warehouse/Product
    int only_Warehouse_ID = getOnly_Warehouse_ID();
    int only_Product_ID = getOnly_Product_ID();
    log.info("Only Warehouse_ID=" + only_Warehouse_ID + ", Product_ID=" + only_Product_ID);

    //	Text Entry ok
    if (e.getSource() == m_text && actionText(only_Warehouse_ID, only_Product_ID)) return;

    //	 Button - Start Dialog
    int M_Locator_ID = 0;
    if (m_value instanceof Integer) M_Locator_ID = ((Integer) m_value).intValue();
    //
    m_mLocator.setOnly_Warehouse_ID(only_Warehouse_ID);
    m_mLocator.setOnly_Product_ID(getOnly_Product_ID());
    VLocatorDialog ld =
        new VLocatorDialog(
            Env.getFrame(this),
            Services.get(IMsgBL.class).translate(Env.getCtx(), m_columnName),
            m_mLocator,
            M_Locator_ID,
            isMandatory(),
            only_Warehouse_ID);
    //	display
    ld.setVisible(true);
    m_mLocator.setOnly_Warehouse_ID(0);

    //	redisplay
    if (!ld.isChanged()) return;
    setValue(ld.getValue(), true);
  } //	actionPerformed

  /**
   * Hit Enter in Text Field
   *
   * @param only_Warehouse_ID if not 0 restrict warehouse
   * @param only_Product_ID of not 0 restricted product
   * @return true if found
   */
  private boolean actionText(int only_Warehouse_ID, int only_Product_ID) {
    String text = m_text.getText();
    log.debug(text);
    //	Null
    if (text == null || text.length() == 0) {
      if (isMandatory()) return false;
      else {
        setValue(null, true);
        return true;
      }
    }
    if (text.endsWith("%")) text = text.toUpperCase();
    else text = text.toUpperCase() + "%";

    //	Look up - see MLocatorLookup.run
    StringBuffer sql =
        new StringBuffer("SELECT M_Locator_ID FROM M_Locator ")
            .append(" WHERE IsActive='Y' AND UPPER(Value) LIKE ")
            .append(DB.TO_STRING(text));
    if (getOnly_Warehouse_ID() != 0) sql.append(" AND M_Warehouse_ID=?");
    if (getOnly_Product_ID() != 0)
      sql.append(" AND (IsDefault='Y' ") // 	Default Locator
          .append("OR EXISTS (SELECT * FROM M_Product p ") // 	Product Locator
          .append("WHERE p.M_Locator_ID=M_Locator.M_Locator_ID AND p.M_Product_ID=?)")
          .append("OR EXISTS (SELECT * FROM M_Storage s ") // 	Storage Locator
          .append("WHERE s.M_Locator_ID=M_Locator.M_Locator_ID AND s.M_Product_ID=?))");
    String finalSql =
        Env.getUserRolePermissions()
            .addAccessSQL(
                sql.toString(),
                "M_Locator",
                IUserRolePermissions.SQL_NOTQUALIFIED,
                IUserRolePermissions.SQL_RO);
    //
    int M_Locator_ID = 0;
    PreparedStatement pstmt = null;
    try {
      pstmt = DB.prepareStatement(finalSql, null);
      int index = 1;
      if (only_Warehouse_ID != 0) pstmt.setInt(index++, only_Warehouse_ID);
      if (only_Product_ID != 0) {
        pstmt.setInt(index++, only_Product_ID);
        pstmt.setInt(index++, only_Product_ID);
      }
      ResultSet rs = pstmt.executeQuery();
      if (rs.next()) {
        M_Locator_ID = rs.getInt(1);
        if (rs.next()) M_Locator_ID = 0; // 	more than one
      }
      rs.close();
      pstmt.close();
      pstmt = null;
    } catch (SQLException ex) {
      log.error(finalSql, ex);
    }
    try {
      if (pstmt != null) pstmt.close();
    } catch (SQLException ex1) {
    }
    pstmt = null;
    if (M_Locator_ID == 0) return false;

    setValue(new Integer(M_Locator_ID), true);
    return true;
  } //	actionText

  /**
   * Action Listener Interface
   *
   * @param listener listener
   */
  @Override
  public void addActionListener(ActionListener listener) {
    m_text.addActionListener(listener);
  } //  addActionListener

  @Override
  public void addMouseListener(final MouseListener l) {
    m_text.addMouseListener(l);
  }

  /** Action - Zoom */
  @Override
  public void actionZoom() {
    int AD_Window_ID = MTable.get(Env.getCtx(), MLocator.Table_ID).getAD_Window_ID();
    if (AD_Window_ID <= 0) AD_Window_ID = 139; // 	hardcoded window Warehouse & Locators
    log.info("");
    //
    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    AWindow frame = new AWindow();

    MQuery zoomQuery = new MQuery();
    zoomQuery.addRestriction(MLocator.COLUMNNAME_M_Locator_ID, Operator.EQUAL, getValue());
    zoomQuery.setRecordCount(1); // 	guess

    if (!frame.initWindow(AD_Window_ID, zoomQuery)) return;
    AEnv.addToWindowManager(frame);
    AEnv.showCenterScreen(frame);
    frame = null;
    setCursor(Cursor.getDefaultCursor());
  } //	actionZoom

  /**
   * Set Field/WindowNo for ValuePreference (NOP)
   *
   * @param mField Model Field
   */
  @Override
  public void setField(org.compiere.model.GridField mField) {
    this.m_mField = mField;

    EditorContextPopupMenu.onGridFieldSet(this);
  } //  setField

  @Override
  public GridField getField() {
    return m_mField;
  }

  /**
   * Get Warehouse restriction if any.
   *
   * @return M_Warehouse_ID or 0
   */
  private int getOnly_Warehouse_ID() {
    String only_Warehouse = Env.getContext(Env.getCtx(), m_WindowNo, "M_Warehouse_ID", true);
    int only_Warehouse_ID = 0;
    try {
      if (only_Warehouse != null && only_Warehouse.length() > 0)
        only_Warehouse_ID = Integer.parseInt(only_Warehouse);
    } catch (Exception ex) {
    }
    return only_Warehouse_ID;
  } //	getOnly_Warehouse_ID

  /**
   * Get Product restriction if any.
   *
   * @return M_Product_ID or 0
   */
  private int getOnly_Product_ID() {
    if (!Env.isSOTrx(Env.getCtx(), m_WindowNo)) return 0; // 	No product restrictions for PO
    //
    String only_Product = Env.getContext(Env.getCtx(), m_WindowNo, "M_Product_ID", true);
    int only_Product_ID = 0;
    try {
      if (only_Product != null && only_Product.length() > 0)
        only_Product_ID = Integer.parseInt(only_Product);
    } catch (Exception ex) {
    }
    return only_Product_ID;
  } //	getOnly_Product_ID

  /**
   * Set the default locator if this field is mandatory and we have a warehouse restriction.
   *
   * @since 3.1.4
   */
  private void setDefault_Locator_ID() {
    // teo_sarca, FR [ 1661546 ] Mandatory locator fields should use defaults
    if (!isMandatory() || m_mLocator == null) {
      return;
    }
    int M_Warehouse_ID = getOnly_Warehouse_ID();
    if (M_Warehouse_ID <= 0) {
      return;
    }
    MWarehouse wh = MWarehouse.get(Env.getCtx(), M_Warehouse_ID);
    if (wh == null || wh.get_ID() <= 0) {
      return;
    }
    MLocator loc = wh.getDefaultLocator();
    if (loc == null || loc.get_ID() <= 0) {
      return;
    }
    setValue(Integer.valueOf(loc.get_ID()));
  }

  // metas
  @Override
  public boolean isAutoCommit() {
    return true;
  }

  @Override
  public ICopyPasteSupportEditor getCopyPasteSupport() {
    return m_text == null ? NullCopyPasteSupportEditor.instance : m_text.getCopyPasteSupport();
  }
} //	VLocator
Esempio n. 16
0
/**
 * @author ts
 * @see "<a
 *     href='http://dewiki908/mediawiki/index.php/Transportverpackung_%282009_0022_G61%29'>(2009_0022_G61)</a>"
 */
public class BinPacker implements IBinPacker {

  private static final Logger logger = LogManager.getLogger(BinPacker.class);

  public void pack(final Properties ctx, final PackingTreeModel model, final String trxName) {
    final DefaultMutableTreeNode unallocRoot = model.getUnPackedItems();
    final DefaultMutableTreeNode availBinRoot = model.getAvailableBins();

    final List<DefaultMutableTreeNode> unallocNodes = mkSortedListBigToSmall(unallocRoot);
    final List<DefaultMutableTreeNode> availBins = mkSortedListBigToSmall(availBinRoot);
    final List<DefaultMutableTreeNode> usedBins = new ArrayList<DefaultMutableTreeNode>();

    while (!unallocNodes.isEmpty()) {
      // make another bin available for using
      addBinForUsage(ctx, unallocNodes, availBins, usedBins, model, trxName);

      // try to put all our unallocated items into the currently available bins
      alloc(unallocNodes, usedBins, model, trxName);
    }
  }

  /**
   * Sums up the volume and weight of all items still unpacked, selects a bin from <code>availBins
   * </code> and updates <code>model</code> accordingly.
   *
   * <p>If there is no bin large enough for the summed up weight and volume, the largest bin
   * available is added.
   *
   * @param unallocNodes
   * @param availBins
   * @param usedBins
   * @param model
   * @param trxName
   */
  private void addBinForUsage(
      final Properties ctx,
      final List<DefaultMutableTreeNode> unallocNodes,
      final List<DefaultMutableTreeNode> availBins,
      final List<DefaultMutableTreeNode> usedBins,
      final PackingTreeModel model,
      final String trxName) {

    logger.debug("Computing the overall volume and weight we still need to cover");

    BigDecimal unallocVolumeSum = BigDecimal.ZERO;
    BigDecimal unallocWeightSum = BigDecimal.ZERO;
    BigDecimal unallocVolumeMax = BigDecimal.ZERO;
    BigDecimal unallocWeightMax = BigDecimal.ZERO;

    for (final DefaultMutableTreeNode currentNode : unallocNodes) {

      final LegacyPackingItem pi = getPI(currentNode);
      final BigDecimal volSingle = pi.retrieveVolumeSingle(trxName);
      final BigDecimal weightSingle = pi.retrieveWeightSingle(trxName);
      unallocVolumeSum = unallocVolumeSum.add(pi.getQtySum().multiply(volSingle));
      unallocWeightSum = unallocWeightSum.add(pi.getQtySum().multiply(weightSingle));

      if (unallocVolumeMax.compareTo(volSingle) < 0) {
        unallocVolumeMax = volSingle;
      }
      if (unallocWeightMax.compareTo(weightSingle) < 0) {
        unallocWeightMax = weightSingle;
      }
    }
    logger.debug(
        "Still required: volume-sum="
            + unallocVolumeSum
            + "; volume-max="
            + unallocVolumeMax
            + "; weight-sum:"
            + unallocWeightSum
            + "; weight-max="
            + unallocWeightMax);

    removeUnavailableBins(availBins, unallocVolumeMax, unallocWeightMax);

    final DefaultMutableTreeNode nexBinToUseNode =
        findBinNode(availBins, unallocVolumeSum, unallocWeightSum);
    final AvailableBins foundBin = getBin(nexBinToUseNode);

    foundBin.setQtyAvail(foundBin.getQtyAvail() - 1);

    final I_M_PackagingContainer foundPC = foundBin.getPc();

    final UsedBin newPack = new UsedBin(ctx, foundPC, trxName);
    logger.info("Adding " + newPack);

    final DefaultMutableTreeNode newPackNode = new DefaultMutableTreeNode(newPack);
    usedBins.add(newPackNode);
    model.insertNodeInto(newPackNode, model.getUsedBins(), model.getUsedBins().getChildCount());
  }

  private void alloc(
      final List<DefaultMutableTreeNode> unallocNodes,
      final List<DefaultMutableTreeNode> usedBins,
      final PackingTreeModel model,
      final String trxName) {
    final Set<DefaultMutableTreeNode> packedNodes = new HashSet<DefaultMutableTreeNode>();

    sortUsedBins(usedBins, trxName);
    Iterator<DefaultMutableTreeNode> itUsedBinNodes = usedBins.iterator();

    boolean progressMade = false;

    for (final DefaultMutableTreeNode packedNode : unallocNodes) {
      if (!itUsedBinNodes.hasNext()) {
        break;
      }

      final DefaultMutableTreeNode usedBinNode = itUsedBinNodes.next();
      final UsedBin usedPack = getPack(usedBinNode);

      // find out how much space we have left
      final BigDecimal maxVolume = usedPack.getPackagingContainer().getMaxVolume();
      final BigDecimal usedVolume = PackingTreeModel.getUsedVolume(usedBinNode, trxName);
      final BigDecimal freeVolume = maxVolume.subtract(usedVolume);

      final LegacyPackingItem unpackedPi = getPI(packedNode);
      final BigDecimal singleVol = unpackedPi.retrieveVolumeSingle(trxName);

      final BigDecimal binFreeQty;
      if (singleVol.signum() > 0) {
        binFreeQty =
            freeVolume
                .divide(singleVol, BigDecimal.ROUND_FLOOR)
                .setScale(0, BigDecimal.ROUND_FLOOR);
      } else {
        binFreeQty = new BigDecimal(Long.MAX_VALUE);
      }
      if (binFreeQty.signum() <= 0) {
        // there is no space left in the bins that we are currently using
        break;
      }

      final BigDecimal requiredQty = unpackedPi.getQtySum();
      if (binFreeQty.compareTo(requiredQty) >= 0) {
        // the pl fits completely into our bin
        model.insertNodeInto(packedNode, usedBinNode, usedBinNode.getChildCount());
        packedNodes.add(packedNode);

        // sort the used bins again because the current bin might not be
        // the one with the biggest free volume anymore
        sortUsedBins(usedBins, trxName);
        itUsedBinNodes = usedBins.iterator();
      } else {
        // split the current item and move a part into a new item to be
        // located in our bin
        final Map<I_M_ShipmentSchedule, BigDecimal> qtysToTransfer =
            unpackedPi.subtract(binFreeQty);
        final LegacyPackingItem newPi = new LegacyPackingItem(qtysToTransfer, trxName);

        final DefaultMutableTreeNode newItemNode = new DefaultMutableTreeNode(newPi);
        model.insertNodeInto(newItemNode, usedBinNode, usedBinNode.getChildCount());
      }
      progressMade = true;
    }
    unallocNodes.removeAll(packedNodes);

    if (!progressMade) {
      throw new NoContainerException(false, false);
    }
  }

  /**
   * @param unallocRoot
   * @return list with the biggest first
   */
  private List<DefaultMutableTreeNode> mkSortedListBigToSmall(
      final DefaultMutableTreeNode unallocRoot) {
    final List<DefaultMutableTreeNode> unallocNodes = mkList(unallocRoot);
    sortList(unallocNodes, -1);
    return unallocNodes;
  }

  /**
   * sort the used bins according to their free volume. The one with the biggest free volume comes
   * first.
   *
   * @param nodes
   * @param trxName
   */
  private void sortUsedBins(final List<DefaultMutableTreeNode> nodes, final String trxName) {
    Collections.sort(
        nodes,
        new Comparator<DefaultMutableTreeNode>() {
          public int compare(final DefaultMutableTreeNode o1, final DefaultMutableTreeNode o2) {
            final BigDecimal usedVol1 = PackingTreeModel.getUsedVolume(o1, trxName);
            final BigDecimal usedVol2 = PackingTreeModel.getUsedVolume(o2, trxName);
            final BigDecimal freeVol1 =
                getPack(o1).getPackagingContainer().getMaxVolume().subtract(usedVol1);
            final BigDecimal freeVol2 =
                getPack(o2).getPackagingContainer().getMaxVolume().subtract(usedVol2);

            return freeVol2.compareTo(freeVol1);
          }
        });
  }

  /**
   * @param unallocNodes
   * @param factor if factor is <0, the nodes are sorted in reverse order
   */
  private void sortList(final List<DefaultMutableTreeNode> unallocNodes, final int factor) {
    Collections.sort(
        unallocNodes,
        new Comparator<DefaultMutableTreeNode>() {
          @SuppressWarnings("unchecked")
          public int compare(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
            final Comparable p1 = (Comparable) o1.getUserObject();
            final Comparable p2 = (Comparable) o2.getUserObject();
            return p1.compareTo(p2) * factor;
          }
        });
  }

  @SuppressWarnings("unchecked")
  private List<DefaultMutableTreeNode> mkList(final DefaultMutableTreeNode unallocRoot) {
    final List<DefaultMutableTreeNode> unallocNodes =
        new ArrayList<DefaultMutableTreeNode>(unallocRoot.getChildCount());

    final Enumeration<DefaultMutableTreeNode> unallocEnum = unallocRoot.children();
    while (unallocEnum.hasMoreElements()) {
      unallocNodes.add(unallocEnum.nextElement());
    }
    return unallocNodes;
  }

  private LegacyPackingItem getPI(DefaultMutableTreeNode o2) {
    return (LegacyPackingItem) o2.getUserObject();
  }

  private UsedBin getPack(DefaultMutableTreeNode o2) {
    return (UsedBin) o2.getUserObject();
  }

  private AvailableBins getBin(DefaultMutableTreeNode n) {
    return (AvailableBins) n.getUserObject();
  }

  private void removeUnavailableBins(
      final List<DefaultMutableTreeNode> availBins,
      final BigDecimal requiredVolume,
      final BigDecimal requiredWeigth) {

    final Set<DefaultMutableTreeNode> nodesToRemove = new HashSet<DefaultMutableTreeNode>();

    boolean insufficientVolume = false;
    boolean insufficientWeight = false;

    for (final DefaultMutableTreeNode binNode : availBins) {

      final AvailableBins bins = getBin(binNode);

      if (bins.getQtyAvail() <= 0) {

        logger.info("Bins '" + bins + "' are no longer available");
        nodesToRemove.add(binNode);
      }
      if (bins.getPc().getMaxVolume().compareTo(requiredVolume) < 0) {

        logger.info("Payload volume of available bins '" + bins + "' is too small");
        nodesToRemove.add(binNode);
      }
      if (bins.getPc().getMaxWeight().compareTo(requiredWeigth) < 0) {

        logger.info("Payload weight of available bins '" + bins + "' is too small");
        nodesToRemove.add(binNode);
      }
    }

    availBins.removeAll(nodesToRemove);

    if (availBins.isEmpty()) {
      throw new NoContainerException(insufficientVolume, insufficientWeight);
    }
  }

  /**
   * @param availBins the bins to choose from. This method asserts that they are sorted in
   *     descending order.
   * @param vol
   * @param weight
   * @return
   */
  private DefaultMutableTreeNode findBinNode(
      final List<DefaultMutableTreeNode> availBins, final BigDecimal vol, final BigDecimal weight) {

    if (availBins.isEmpty()) {
      throw new NoContainerException(false, false);
    }

    // check if everything fits into one bin, starting with the smallest one
    for (int i = availBins.size() - 1; i >= 0; i--) {

      final DefaultMutableTreeNode packNode = availBins.get(i);

      final AvailableBins bin = (AvailableBins) packNode.getUserObject();
      final I_M_PackagingContainer pc = bin.getPc();

      if (pc.getMaxVolume().compareTo(vol) >= 0
          && pc.getMaxWeight().compareTo(weight) >= 0
          && bin.getQtyAvail() > 0) {

        return packNode;
      }
    }

    // no bin is big enough, return the biggest one we got
    return availBins.get(0);
  }
}
Esempio n. 17
0
/**
 * Persistent Column Model
 *
 * @author Jorg Janke
 * @version $Id: MColumn.java,v 1.6 2006/08/09 05:23:49 jjanke Exp $
 */
public class MColumn extends X_AD_Column {
  /** */
  private static final long serialVersionUID = 6543789555737635129L;

  /**
   * Get MColumn from Cache
   *
   * @param ctx context
   * @param AD_Column_ID id
   * @return MColumn
   */
  public static MColumn get(Properties ctx, int AD_Column_ID) {
    Integer key = new Integer(AD_Column_ID);
    MColumn retValue = s_cache.get(key);
    if (retValue != null) return retValue;
    retValue = new MColumn(ctx, AD_Column_ID, null);
    if (retValue.get_ID() != 0) s_cache.put(key, retValue);
    return retValue;
  } // get

  /**
   * Get Column Name
   *
   * @param ctx context
   * @param AD_Column_ID id
   * @return Column Name or null
   */
  public static String getColumnName(Properties ctx, int AD_Column_ID) {
    MColumn col = MColumn.get(ctx, AD_Column_ID);
    if (col == null || col.getAD_Column_ID() <= 0) return null;
    return col.getColumnName();
  } // getColumnName

  /** Cache */
  private static CCache<Integer, MColumn> s_cache = new CCache<>("AD_Column", 20);

  /** Static Logger */
  private static Logger s_log = LogManager.getLogger(MColumn.class);;

  /**
   * ************************************************************************ Standard Constructor
   *
   * @param ctx context
   * @param AD_Column_ID
   * @param trxName transaction
   */
  public MColumn(Properties ctx, int AD_Column_ID, String trxName) {
    super(ctx, AD_Column_ID, trxName);
    if (AD_Column_ID == 0) {
      // setAD_Element_ID (0);
      // setAD_Reference_ID (0);
      // setColumnName (null);
      // setName (null);
      // setEntityType (null); // U
      setIsAlwaysUpdateable(false); // N
      setIsEncrypted(false);
      setIsIdentifier(false);
      setIsKey(false);
      setIsMandatory(false);
      setIsParent(false);
      setIsSelectionColumn(false);
      setIsTranslated(false);
      setIsUpdateable(true); // Y
      setVersion(Env.ZERO);
    }
  } // MColumn

  /**
   * Load Constructor
   *
   * @param ctx context
   * @param rs result set
   * @param trxName transaction
   */
  public MColumn(Properties ctx, ResultSet rs, String trxName) {
    super(ctx, rs, trxName);
  } // MColumn

  /**
   * Parent Constructor
   *
   * @param parent table
   */
  public MColumn(MTable parent) {
    this(parent.getCtx(), 0, parent.get_TrxName());
    setClientOrg(parent);
    setAD_Table_ID(parent.getAD_Table_ID());
    setEntityType(parent.getEntityType());
  } // MColumn

  /**
   * Is Standard Column
   *
   * @return true for AD_Client_ID, etc.
   */
  public boolean isStandardColumn() {
    String columnName = getColumnName();
    if (columnName.equals("AD_Client_ID")
        || columnName.equals("AD_Org_ID")
        || columnName.equals("IsActive")
        || columnName.startsWith("Created")
        || columnName.startsWith("Updated")) return true;

    return false;
  } // isStandardColumn

  /**
   * Is Virtual Column
   *
   * @return true if virtual column
   * @deprecated please use {@link IADTableDAO#isVirtualColumn(I_AD_Column)}
   */
  @Deprecated
  public boolean isVirtualColumn() {
    final IADTableDAO tableDAO = Services.get(IADTableDAO.class);
    return tableDAO.isVirtualColumn(this);
  } // isVirtualColumn

  /**
   * Is the Column Encrypted?
   *
   * @return true if encrypted
   */
  public boolean isEncrypted() {
    String s = getIsEncrypted();
    return "Y".equals(s);
  } // isEncrypted

  /**
   * Set Encrypted
   *
   * @param IsEncrypted encrypted
   */
  public void setIsEncrypted(boolean IsEncrypted) {
    setIsEncrypted(IsEncrypted ? "Y" : "N");
  } // setIsEncrypted

  /**
   * Before Save
   *
   * @param newRecord new
   * @return true
   */
  @Override
  protected boolean beforeSave(boolean newRecord) {
    int displayType = getAD_Reference_ID();
    if (DisplayType.isLOB(displayType)) // LOBs are 0
    {
      if (getFieldLength() != 0) setFieldLength(0);
    } else if (getFieldLength() == 0) {
      if (DisplayType.isID(displayType)) setFieldLength(10);
      else if (DisplayType.isNumeric(displayType)) setFieldLength(14);
      else if (DisplayType.isDate(displayType)) setFieldLength(7);
      else {
        throw new FillMandatoryException(COLUMNNAME_FieldLength);
      }
    }

    /**
     * Views are not updateable UPDATE AD_Column c SET IsUpdateable='N', IsAlwaysUpdateable='N'
     * WHERE AD_Table_ID IN (SELECT AD_Table_ID FROM AD_Table WHERE IsView='Y')
     */

    /* Diego Ruiz - globalqss - BF [1651899] - AD_Column: Avoid dup. SeqNo for IsIdentifier='Y' */
    if (isIdentifier()) {
      int cnt =
          DB.getSQLValue(
              get_TrxName(),
              "SELECT COUNT(*) FROM AD_Column "
                  + "WHERE AD_Table_ID=?"
                  + " AND AD_Column_ID!=?"
                  + " AND IsIdentifier='Y'"
                  + " AND SeqNo=?",
              new Object[] {getAD_Table_ID(), getAD_Column_ID(), getSeqNo()});
      if (cnt > 0) {
        throw new AdempiereException(
            "@SaveErrorNotUnique@ @" + COLUMNNAME_SeqNo + "@: " + getSeqNo());
      }
    }

    // Virtual Column
    if (isVirtualColumn()) {
      // Make sure there are no context variables in ColumnSQL
      final String columnSql = getColumnSQL();
      if (columnSql != null && columnSql.indexOf(CtxName.NAME_Marker) >= 0) {
        throw new AdempiereException(
            "Context variables are not allowed in ColumnSQL: " + columnSql);
      }

      if (isMandatory()) setIsMandatory(false);
      if (isUpdateable()) setIsUpdateable(false);
    }
    // Updateable
    if (isParent() || isKey()) setIsUpdateable(false);
    if (isAlwaysUpdateable() && !isUpdateable()) setIsAlwaysUpdateable(false);
    // Encrypted
    if (isEncrypted()) {
      int dt = getAD_Reference_ID();
      if (isKey()
          || isParent()
          || isStandardColumn()
          || isVirtualColumn()
          || isIdentifier()
          || isTranslated()
          || DisplayType.isLookup(dt)
          || DisplayType.isLOB(dt)
          || "DocumentNo".equalsIgnoreCase(getColumnName())
          || "Value".equalsIgnoreCase(getColumnName())
          || "Name".equalsIgnoreCase(getColumnName())) {
        log.warn("Encryption not sensible - " + getColumnName());
        setIsEncrypted(false);
      }
    }

    // Sync Terminology
    if ((newRecord || is_ValueChanged("AD_Element_ID")) && getAD_Element_ID() != 0) {
      M_Element element = new M_Element(getCtx(), getAD_Element_ID(), get_TrxName());
      setColumnName(element.getColumnName());
      setName(element.getName());
      setDescription(element.getDescription());
      setHelp(element.getHelp());
    }
    return true;
  } // beforeSave

  /**
   * After Save
   *
   * @param newRecord new
   * @param success success
   * @return success
   */
  @Override
  protected boolean afterSave(boolean newRecord, boolean success) {
    // Update Fields
    if (!newRecord) {
      if (is_ValueChanged(MColumn.COLUMNNAME_Name)
          || is_ValueChanged(MColumn.COLUMNNAME_Description)
          || is_ValueChanged(MColumn.COLUMNNAME_Help)) {
        StringBuffer sql =
            new StringBuffer("UPDATE AD_Field SET Name=")
                .append(DB.TO_STRING(getName()))
                .append(", Description=")
                .append(DB.TO_STRING(getDescription()))
                .append(", Help=")
                .append(DB.TO_STRING(getHelp()))
                .append(" WHERE AD_Column_ID=")
                .append(get_ID())
                .append(" AND IsCentrallyMaintained='Y'");
        int no = DB.executeUpdate(sql.toString(), get_TrxName());
        log.debug("afterSave - Fields updated #" + no);
      }
    }
    return success;
  } // afterSave

  /**
   * Get SQL Add command
   *
   * @param table table
   * @return sql
   */
  public String getSQLAdd(I_AD_Table table) {
    final String tableName = table.getTableName();

    final StringBuilder sql =
        new StringBuilder("ALTER TABLE ")
            .append(
                "public.") // if the table is already DLM'ed then there is a view with the sale name
            // in the dlm schema.
            .append(tableName)
            .append(" ADD ")
            .append(getSQLDDL());

    final String constraint = getConstraint(tableName);
    if (constraint != null && constraint.length() > 0) {
      sql.append(DB.SQLSTATEMENT_SEPARATOR)
          .append("ALTER TABLE ")
          .append(tableName)
          .append(" ADD ")
          .append(constraint);
    }
    return sql.toString();
  } // getSQLAdd

  /**
   * Get SQL DDL
   *
   * @return columnName datataype ..
   */
  public String getSQLDDL() {
    if (isVirtualColumn()) return null;

    StringBuffer sql = new StringBuffer(getColumnName()).append(" ").append(getSQLDataType());

    // Default
    String defaultValue = getDefaultValue();
    if (defaultValue != null
        && defaultValue.length() > 0
        && defaultValue.indexOf('@') == -1 // no variables
        && (!(DisplayType.isID(getAD_Reference_ID())
            && defaultValue.equals("-1")))) // not for ID's with default -1
    {
      if (DisplayType.isText(getAD_Reference_ID())
          || getAD_Reference_ID() == DisplayType.List
          || getAD_Reference_ID() == DisplayType.YesNo
          // Two special columns: Defined as Table but DB Type is String
          || getColumnName().equals("EntityType")
          || getColumnName().equals("AD_Language")
          || (getAD_Reference_ID() == DisplayType.Button && !(getColumnName().endsWith("_ID")))) {
        if (!defaultValue.startsWith("'") && !defaultValue.endsWith("'"))
          defaultValue = DB.TO_STRING(defaultValue);
      }
      sql.append(" DEFAULT ").append(defaultValue);
    } else {
      if (!isMandatory()) sql.append(" DEFAULT NULL ");
      defaultValue = null;
    }

    // Inline Constraint
    if (getAD_Reference_ID() == DisplayType.YesNo)
      sql.append(" CHECK (").append(getColumnName()).append(" IN ('Y','N'))");

    // Null
    if (isMandatory()) sql.append(" NOT NULL");
    return sql.toString();
  } // getSQLDDL

  /**
   * Get SQL Modify command
   *
   * @param table table
   * @param setNullOption generate null / not null statement
   * @return sql separated by ;
   */
  public String getSQLModify(final I_AD_Table table, final boolean setNullOption) {
    final String tableName = table.getTableName();
    final String columnName = getColumnName();
    final int displayType = getAD_Reference_ID();
    String defaultValue = getDefaultValue();
    final boolean mandatory = isMandatory();
    final String sqlDataType = getSQLDataType();

    final StringBuilder sql = new StringBuilder();
    final StringBuilder sqlBase =
        new StringBuilder("ALTER TABLE ").append(tableName).append(" MODIFY ").append(columnName);

    // Default
    final StringBuilder sqlDefault = new StringBuilder(sqlBase).append(" ").append(sqlDataType);
    if (defaultValue != null
        && defaultValue.length() > 0
        && defaultValue.indexOf('@') == -1 // no variables
        && (!(DisplayType.isID(displayType)
            && defaultValue.equals("-1")))) // not for ID's with default -1
    {
      if (DisplayType.isText(displayType)
          || displayType == DisplayType.List
          || displayType == DisplayType.YesNo
          // Two special columns: Defined as Table but DB Type is String
          || columnName.equals("EntityType")
          || columnName.equals("AD_Language")
          || (displayType == DisplayType.Button && !(columnName.endsWith("_ID")))) {
        if (!defaultValue.startsWith("'") && !defaultValue.endsWith("'"))
          defaultValue = DB.TO_STRING(defaultValue);
      }
      sqlDefault.append(" DEFAULT ").append(defaultValue);
    } else {
      if (!mandatory) sqlDefault.append(" DEFAULT NULL ");
      defaultValue = null;
    }
    sql.append(DB.convertSqlToNative(sqlDefault.toString()));

    // Constraint

    // Null Values
    if (mandatory && defaultValue != null && defaultValue.length() > 0) {
      StringBuffer sqlSet =
          new StringBuffer("UPDATE ")
              .append(tableName)
              .append(" SET ")
              .append(columnName)
              .append("=")
              .append(defaultValue)
              .append(" WHERE ")
              .append(columnName)
              .append(" IS NULL");
      sql.append(DB.SQLSTATEMENT_SEPARATOR).append(sqlSet);
    }

    // Null
    if (setNullOption) {
      StringBuffer sqlNull = new StringBuffer(sqlBase);
      if (mandatory) sqlNull.append(" NOT NULL");
      else sqlNull.append(" NULL");
      sql.append(DB.SQLSTATEMENT_SEPARATOR).append(DB.convertSqlToNative(sqlNull.toString()));
    }
    //
    return sql.toString();
  } // getSQLModify

  /**
   * Get SQL Data Type
   *
   * @return e.g. NVARCHAR2(60)
   */
  private String getSQLDataType() {
    final String columnName = getColumnName();
    final int displayType = getAD_Reference_ID();
    final int fieldLength = getFieldLength();
    return DB.getSQLDataType(displayType, columnName, fieldLength);
  } // getSQLDataType

  /**
   * Get Table Constraint
   *
   * @param tableName table name
   * @return table constraint
   */
  public String getConstraint(String tableName) {
    if (isKey()) {
      final String constraintName = tableName + "_Key";
      return "CONSTRAINT " + constraintName + " PRIMARY KEY (" + getColumnName() + ")";
    } else if (DisplayType.isID(getAD_Reference_ID()) && !isDDL_NoForeignKey()) {
      // gh #539 Add missing FK constraints
      // create a FK-constraint, using the same view we also used to "manually" create
      // FK-constraints in the past.

      // get an FK-constraint for this table, if any
      // this returns something like
      // "ALTER TABLE A_Asset_Change ADD CONSTRAINT ADepreciationCalcT_AAssetChang FOREIGN KEY
      // (A_Depreciation_Calc_Type) REFERENCES A_Depreciation_Method DEFERRABLE INITIALLY DEFERRED;"
      final String fkConstraintDDL =
          DB.getSQLValueStringEx(
              ITrx.TRXNAME_None,
              "SELECT SqlText FROM db_columns_fk WHERE TableName=? AND ColumnName=?",
              tableName,
              getColumnName());
      if (!Check.isEmpty(fkConstraintDDL, true)) {
        // remove the "ALTER TABLE ... ADD" and the trailing ";"
        // the (?iu) means the the patters is created with Pattern.CASE_INSENSITIVE |
        // Pattern.UNICODE_CASE
        // thanks to https://blogs.oracle.com/xuemingshen/entry/case_insensitive_matching_in_java
        final String constraint =
            fkConstraintDDL
                .replaceFirst("(?iu)ALTER *TABLE *" + tableName + " *ADD *", "")
                .replaceFirst(";$", "");
        return constraint;
      }
    }
    return "";
  } // getConstraint

  /**
   * String Representation
   *
   * @return info
   */
  @Override
  public String toString() {
    StringBuffer sb = new StringBuffer("MColumn[");
    sb.append(get_ID()).append("-").append(getColumnName()).append("]");
    return sb.toString();
  } // toString

  // begin vpj-cd e-evolution
  /**
   * get Column ID
   *
   * @param String windowName
   * @param String columnName
   * @return int retValue
   */
  public static int getColumn_ID(String TableName, String columnName) {
    int m_table_id = MTable.getTable_ID(TableName);
    if (m_table_id == 0) return 0;

    int retValue = 0;
    String SQL = "SELECT AD_Column_ID FROM AD_Column WHERE AD_Table_ID = ?  AND columnname = ?";
    try {
      PreparedStatement pstmt = DB.prepareStatement(SQL, null);
      pstmt.setInt(1, m_table_id);
      pstmt.setString(2, columnName);
      ResultSet rs = pstmt.executeQuery();
      if (rs.next()) retValue = rs.getInt(1);
      rs.close();
      pstmt.close();
    } catch (SQLException e) {
      s_log.error(SQL, e);
      retValue = -1;
    }
    return retValue;
  }
  // end vpj-cd e-evolution

  /**
   * Get Table Id for a column
   *
   * @param ctx context
   * @param AD_Column_ID id
   * @param trxName transaction
   * @return MColumn
   */
  public static int getTable_ID(Properties ctx, int AD_Column_ID, String trxName) {
    String sqlStmt = "SELECT AD_Table_ID FROM AD_Column WHERE AD_Column_ID=?";
    return DB.getSQLValue(trxName, sqlStmt, AD_Column_ID);
  }

  /**
   * Sync this column with the database
   *
   * @return
   */
  public String syncDatabase() {

    MTable table = new MTable(getCtx(), getAD_Table_ID(), get_TrxName());
    table.set_TrxName(get_TrxName()); // otherwise table.getSQLCreate may miss current column
    if (table.get_ID() == 0)
      throw new AdempiereException("@NotFound@ @AD_Table_ID@ " + getAD_Table_ID());

    // Find Column in Database
    Connection conn = null;
    try {
      conn = DB.getConnectionRO();
      DatabaseMetaData md = conn.getMetaData();
      String catalog = DB.getDatabase().getCatalog();
      String schema = DB.getDatabase().getSchema();
      String tableName = table.getTableName();
      if (md.storesUpperCaseIdentifiers()) {
        tableName = tableName.toUpperCase();
      } else if (md.storesLowerCaseIdentifiers()) {
        tableName = tableName.toLowerCase();
      }
      int noColumns = 0;
      String sql = null;
      //
      ResultSet rs = md.getColumns(catalog, schema, tableName, null);
      while (rs.next()) {
        noColumns++;
        String columnName = rs.getString("COLUMN_NAME");
        if (!columnName.equalsIgnoreCase(getColumnName())) continue;

        // update existing column
        boolean notNull = DatabaseMetaData.columnNoNulls == rs.getInt("NULLABLE");
        sql = getSQLModify(table, isMandatory() != notNull);
        break;
      }
      rs.close();
      rs = null;

      // No Table
      if (noColumns == 0) sql = table.getSQLCreate();
      // No existing column
      else if (sql == null) sql = getSQLAdd(table);

      if (sql == null) return "No sql";

      int no = 0;
      if (sql.indexOf(DB.SQLSTATEMENT_SEPARATOR) == -1) {
        DB.executeUpdateEx(sql, get_TrxName());
      } else {
        String statements[] = sql.split(DB.SQLSTATEMENT_SEPARATOR);
        for (int i = 0; i < statements.length; i++) {
          DB.executeUpdateEx(statements[i], get_TrxName());
        }
      }

      return sql;

    } catch (SQLException e) {
      throw new AdempiereException(e);
    } finally {
      if (conn != null) {
        try {
          conn.close();
        } catch (Exception e) {
        }
      }
    }
  }

  public static boolean isSuggestSelectionColumn(String columnName, boolean caseSensitive) {
    if (Check.isEmpty(columnName, true)) return false;
    //
    if (columnName.equals("Value") || (!caseSensitive && columnName.equalsIgnoreCase("Value")))
      return true;
    else if (columnName.equals("Name") || (!caseSensitive && columnName.equalsIgnoreCase("Name")))
      return true;
    else if (columnName.equals("DocumentNo")
        || (!caseSensitive && columnName.equalsIgnoreCase("DocumentNo"))) return true;
    else if (columnName.equals("Description")
        || (!caseSensitive && columnName.equalsIgnoreCase("Description"))) return true;
    else if (columnName.indexOf("Name") != -1
        || (!caseSensitive && columnName.toUpperCase().indexOf("Name".toUpperCase()) != -1))
      return true;
    else return false;
  }
} // MColumn
Esempio n. 18
0
/**
 * LDAP Server Model
 *
 * @author Jorg Janke
 * @version $Id$
 */
public class MLdapProcessor extends X_AD_LdapProcessor implements AdempiereProcessor {
  /** */
  private static final long serialVersionUID = 7577593682255409240L;

  /**
   * Get Active LDAP Server
   *
   * @return array of Servers
   */
  public static MLdapProcessor[] getActive(Properties ctx) {
    ArrayList<MLdapProcessor> list = new ArrayList<MLdapProcessor>();
    String sql = "SELECT * FROM AD_LdapProcessor WHERE IsActive='Y'";
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
      pstmt = DB.prepareStatement(sql, null);
      rs = pstmt.executeQuery();
      while (rs.next()) list.add(new MLdapProcessor(ctx, rs, null));
    } catch (Exception e) {
      log.error(sql, e);
    } finally {
      DB.close(rs, pstmt);
      rs = null;
      pstmt = null;
    }
    MLdapProcessor[] retValue = new MLdapProcessor[list.size()];
    list.toArray(retValue);
    return retValue;
  } //	getActive

  /** Logger */
  private static Logger log = LogManager.getLogger(MLdapProcessor.class);

  /**
   * ************************************************************************ Ldap Processor
   *
   * @param ctx context
   * @param AD_LdapProcessor_ID id
   * @param trxName transaction
   */
  public MLdapProcessor(Properties ctx, int AD_LdapProcessor_ID, String trxName) {
    super(ctx, AD_LdapProcessor_ID, trxName);
  } //	MLdapProcessor

  /**
   * Ldap Processor
   *
   * @param ctx context
   * @param rs result set
   * @param trxName transaction
   */
  public MLdapProcessor(Properties ctx, ResultSet rs, String trxName) {
    super(ctx, rs, trxName);
  } //	MLdapProcessor

  /** Array of Clients */
  private MClient[] m_clients = null;
  /** Array of Interest Areas */
  private MInterestArea[] m_interests = null;

  private int m_auth = 0;
  private int m_ok = 0;
  private int m_error = 0;

  /**
   * Get Server ID
   *
   * @return id
   */
  public String getServerID() {
    return "Ldap" + get_ID();
  } //	getServerID

  /**
   * Get Info
   *
   * @return info
   */
  public String getInfo() {
    return "Auth=" + m_auth + ", OK=" + m_ok + ", Error=" + m_error;
  } //	getInfo

  /**
   * Get Date Next Run
   *
   * @param requery requery
   * @return date next run
   */
  public Timestamp getDateNextRun(boolean requery) {
    if (requery) load(get_TrxName());
    return getDateNextRun();
  } //	getDateNextRun

  /**
   * Get Logs
   *
   * @return logs
   */
  public AdempiereProcessorLog[] getLogs() {
    ArrayList<MLdapProcessorLog> list = new ArrayList<MLdapProcessorLog>();
    String sql =
        "SELECT * "
            + "FROM AD_LdapProcessorLog "
            + "WHERE AD_LdapProcessor_ID=? "
            + "ORDER BY Created DESC";
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
      pstmt = DB.prepareStatement(sql, get_TrxName());
      pstmt.setInt(1, getAD_LdapProcessor_ID());
      rs = pstmt.executeQuery();
      while (rs.next()) list.add(new MLdapProcessorLog(getCtx(), rs, get_TrxName()));
    } catch (Exception e) {
      log.error(sql, e);
    } finally {
      DB.close(rs, pstmt);
      rs = null;
      pstmt = null;
    }
    MLdapProcessorLog[] retValue = new MLdapProcessorLog[list.size()];
    list.toArray(retValue);
    return retValue;
  } //	getLogs

  /**
   * Delete old Request Log
   *
   * @return number of records
   */
  public int deleteLog() {
    if (getKeepLogDays() < 1) return 0;
    String sql =
        "DELETE FROM AD_LdapProcessorLog "
            + "WHERE AD_LdapProcessor_ID="
            + getAD_LdapProcessor_ID()
            + " AND (Created+"
            + getKeepLogDays()
            + ") < now()";
    int no = DB.executeUpdate(sql, get_TrxName());
    return no;
  } //	deleteLog

  /**
   * Get Frequency (n/a)
   *
   * @return 1
   */
  public int getFrequency() {
    return 1;
  } //	getFrequency

  /**
   * Get Frequency Type (n/a)
   *
   * @return minute
   */
  public String getFrequencyType() {
    return X_R_RequestProcessor.FREQUENCYTYPE_Minute;
  } //	getFrequencyType

  /**
   * String Representation
   *
   * @return info
   */
  public String toString() {
    StringBuffer sb = new StringBuffer("MLdapProcessor[");
    sb.append(get_ID())
        .append("-")
        .append(getName())
        .append(",Port=")
        .append(getLdapPort())
        .append("]");
    return sb.toString();
  } //	toString

  /**
   * ************************************************************************ Authenticate and
   * Authorize
   *
   * @param ldapUser MLdapUser object
   * @param usr user name
   * @param o organization = Client Name
   * @param ou optional organization unit = Interest Group
   * @return ldapUser MLdapUser with updated information
   */
  public MLdapUser authenticate(MLdapUser ldapUser, String usr, String o, String ou) {
    // Ensure something to return
    if (ldapUser == null) ldapUser = new MLdapUser();

    String error = null;
    String info = null;

    //	User
    if (usr == null || usr.trim().length() == 0) {
      error = "@NotFound@ User";
      ldapUser.setErrorString(error);
      m_error++;
      log.warn(error);
      return ldapUser;
    }
    usr = usr.trim();
    //	Client
    if (o == null || o.length() == 0) {
      error = "@NotFound@ O";
      ldapUser.setErrorString(error);
      m_error++;
      log.warn(error);
      return ldapUser;
    }
    int AD_Client_ID = findClient(o);
    if (AD_Client_ID == 0) {
      error = "@NotFound@ O=" + o;
      ldapUser.setErrorString(error);
      m_error++;
      log.info(error);
      return ldapUser;
    }
    //	Optional Interest Area
    int R_InterestArea_ID = 0;
    if (ou != null && ou.length() > 0) {
      R_InterestArea_ID = findInterestArea(AD_Client_ID, ou);
      if (R_InterestArea_ID == 0) {
        error = "@NotFound@ OU=" + ou;
        ldapUser.setErrorString(error);
        m_error++;
        log.info(error);
        return ldapUser;
      }
    }

    m_auth++;
    //	Query 1 - Validate User
    int AD_User_ID = 0;
    String Value = null;
    String LdapUser = null;
    String EMail = null;
    String Name = null;
    String Password = null;
    boolean IsActive = false;
    String EMailVerify = null; // 	 is timestamp
    boolean isUnique = false;
    //
    String sql =
        "SELECT AD_User_ID, Value, LdapUser, EMail," //	1..4
            + " Name, Password, IsActive, EMailVerify "
            + "FROM AD_User "
            + "WHERE AD_Client_ID=? AND (EMail=? OR Value=? OR LdapUser=?)";
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
      pstmt = DB.prepareStatement(sql, null);
      pstmt.setInt(1, AD_Client_ID);
      pstmt.setString(2, usr);
      pstmt.setString(3, usr);
      pstmt.setString(4, usr);
      rs = pstmt.executeQuery();
      if (rs.next()) {
        AD_User_ID = rs.getInt(1);
        Value = rs.getString(2);
        LdapUser = rs.getString(3);
        EMail = rs.getString(4);
        //
        Name = rs.getString(5);
        Password = rs.getString(6);
        IsActive = "Y".equals(rs.getString(7));
        EMailVerify = rs.getString(8);
        isUnique = rs.next();
      }
    } catch (Exception e) {
      log.error(sql, e);
      error = "System Error";
    } finally {
      DB.close(rs, pstmt);
      rs = null;
      pstmt = null;
    }
    if (error != null) {
      m_error++;
      ldapUser.setErrorString(error);
      return ldapUser;
    }
    //
    if (AD_User_ID == 0) {
      error = "@NotFound@ User="******"User not found - " + usr;
    } else if (!IsActive) {
      error = "@NotFound@ User="******"User not active - " + usr;
    } else if (EMailVerify == null) {
      error = "@UserNotVerified@ User="******"User EMail not verified - " + usr;
    } else if (usr.equalsIgnoreCase(LdapUser))
      info = "User verified - Ldap=" + usr + (isUnique ? "" : " - Not Unique");
    else if (usr.equalsIgnoreCase(Value))
      info = "User verified - Value=" + usr + (isUnique ? "" : " - Not Unique");
    else if (usr.equalsIgnoreCase(EMail))
      info = "User verified - EMail=" + usr + (isUnique ? "" : " - Not Unique");
    else
      info =
          "User verified ?? "
              + usr
              + " - Name="
              + Name
              + ", Ldap="
              + LdapUser
              + ", Value="
              + Value
              + (isUnique ? "" : " - Not Unique");

    //	Error
    if (error != null) // 	should use Language of the User
    {
      logAccess(AD_Client_ID, AD_User_ID, R_InterestArea_ID, info, error);
      ldapUser.setErrorString(Msg.translate(getCtx(), error));
      return ldapUser;
    }
    //	Done
    if (R_InterestArea_ID == 0) {
      logAccess(AD_Client_ID, AD_User_ID, R_InterestArea_ID, info, null);
      ldapUser.setOrg(o);
      ldapUser.setOrgUnit(ou);
      ldapUser.setUserId(usr);
      ldapUser.setPassword(Password);
      return ldapUser;
    }

    //	Query 2 - Validate Subscription
    String OptOutDate = null;
    boolean found = false;
    sql =
        "SELECT IsActive, OptOutDate "
            + "FROM R_ContactInterest "
            + "WHERE R_InterestArea_ID=? AND AD_User_ID=?";
    try {
      pstmt = DB.prepareStatement(sql, null);
      pstmt.setInt(1, R_InterestArea_ID);
      pstmt.setInt(2, AD_User_ID);
      rs = pstmt.executeQuery();
      if (rs.next()) {
        found = true;
        IsActive = "Y".equals(rs.getString(1));
        OptOutDate = rs.getString(2);
        isUnique = rs.next();
      }
    } catch (Exception e) {
      log.error(sql, e);
      error = "System Error (2)";
    } finally {
      DB.close(rs, pstmt);
      rs = null;
      pstmt = null;
    }
    //	System Error
    if (error != null) {
      m_error++;
      ldapUser.setErrorString(error);
      return ldapUser;
    }

    if (!found) {
      error = "@UserNotSubscribed@ User="******"No User Interest - " + usr + " - R_InterestArea_ID=" + R_InterestArea_ID;
    } else if (OptOutDate != null) {
      error = "@UserNotSubscribed@ User="******" @OptOutDate@=" + OptOutDate;
      info = "Opted out - " + usr + " - OptOutDate=" + OptOutDate;
    } else if (!IsActive) {
      error = "@UserNotSubscribed@ User="******"User Interest Not Active - " + usr;
    } else info = "User subscribed - " + usr;

    if (error != null) // 	should use Language of the User
    {
      logAccess(AD_Client_ID, AD_User_ID, R_InterestArea_ID, info, error);
      ldapUser.setErrorString(Msg.translate(getCtx(), error));
      return ldapUser;
    }
    //	Done
    logAccess(AD_Client_ID, AD_User_ID, R_InterestArea_ID, info, null);
    ldapUser.setOrg(o);
    ldapUser.setOrgUnit(ou);
    ldapUser.setUserId(usr);
    ldapUser.setPassword(Password);
    return ldapUser;
  } //	authenticate

  /**
   * Find Client
   *
   * @param client client name
   * @return AD_Client_ID
   */
  private int findClient(String client) {
    if (m_clients == null) m_clients = MClient.getAll(getCtx());
    for (int i = 0; i < m_clients.length; i++) {
      if ((client.equalsIgnoreCase(m_clients[i].getValue()))) return m_clients[i].getAD_Client_ID();
    }
    return 0;
  } //	findClient

  /**
   * Find Interest Area
   *
   * @param interset Name client name
   * @return AD_Client_ID
   */
  private int findInterestArea(int AD_Client_ID, String interestArea) {
    if (m_interests == null) m_interests = MInterestArea.getAll(getCtx());
    for (int i = 0; i < m_interests.length; i++) {
      if (AD_Client_ID == m_interests[i].getAD_Client_ID()
          && interestArea.equalsIgnoreCase(m_interests[i].getValue()))
        return m_interests[i].getR_InterestArea_ID();
    }
    return 0;
  } //	findInterestArea

  /**
   * Log Access
   *
   * @param AD_Client_ID client
   * @param AD_User_ID user
   * @param R_InterestArea_ID interest area
   * @param info info
   * @param error error
   */
  private void logAccess(
      int AD_Client_ID, int AD_User_ID, int R_InterestArea_ID, String info, String error) {
    if (error != null) {
      log.info(info);
      m_error++;
    } else {
      log.info(info);
      m_ok++;
    }
    //
    MLdapAccess access = new MLdapAccess(getCtx(), 0, null);
    access.setAD_Client_ID(AD_Client_ID);
    access.setAD_User_ID(AD_User_ID);
    access.setR_InterestArea_ID(R_InterestArea_ID);
    access.setIsError(error != null);
    access.setSummary(info);
    access.save();
  } //	logAccess
} //	MLdapProcessor
public abstract class AbstractClientUIInvoker implements IClientUIInvoker {
  // Services
  protected static final transient Logger logger =
      LogManager.getLogger(AbstractClientUIInvoker.class);
  protected final transient IDeveloperModeBL developerModeBL = Services.get(IDeveloperModeBL.class);
  private final transient IClientUIInstance clientUI;

  private boolean invokeLater;
  private boolean longOperation;
  private boolean showGlassPane = false;
  private OnFail onFail = OnFail.ShowErrorPopup;
  private IExceptionHandler exceptionHandler;
  private Object parentComponent;
  private int parentWindowNo;
  //
  private Runnable runnable = null;

  public AbstractClientUIInvoker(final IClientUIInstance clientUI) {
    super();
    Check.assumeNotNull(clientUI, "clientUI not null");
    this.clientUI = clientUI;
  }

  @Override
  public String toString() {
    return ObjectUtils.toString(this);
  }

  @Override
  public final void invoke(final Runnable runnable) {
    setRunnable(runnable);
    invoke();
  }

  @Override
  public final void invoke() {
    Runnable runnableWrapped = getRunnable();
    Check.assumeNotNull(runnableWrapped, "runnable shall be configured");

    // Wrap to Long Operation
    // NOTE: this needs to be wrapped BEFORE "invoke later" or "glass pane" because those will
    // execute the runnable asynchronously
    if (isLongOperation()) {
      runnableWrapped = asLongOperationRunnable(runnableWrapped);
    }

    // Wrap to Exception handled (if needed)
    runnableWrapped = asExceptionHandledRunnable(runnableWrapped);

    // Wrap to invoke later
    // NOTE: this needs to be wrapped after "exception handled" because else the exception won't be
    // catched.
    if (isInvokeLater()) {
      runnableWrapped = asInvokeLaterRunnable(runnableWrapped);
    }

    // Wrap to showing glass pane runnable
    // NOTE: this needs to be wrapped after "long operation", "exception handled" and "invoke later"
    if (isShowGlassPane()) {
      runnableWrapped = asShowGlassPaneRunnable(runnableWrapped);
    }

    //
    // Execute it
    runnableWrapped.run();
  }

  protected abstract Runnable asInvokeLaterRunnable(final Runnable runnable);

  protected abstract Runnable asLongOperationRunnable(final Runnable runnable);

  protected abstract Runnable asShowGlassPaneRunnable(Runnable runnable);

  private final Runnable asExceptionHandledRunnable(final Runnable runnable) {
    final OnFail onFail = getOnFail();
    if (OnFail.ThrowException == onFail) {
      return runnable;
    }

    return new Runnable() {
      @Override
      public String toString() {
        return "ExceptionHandled[" + runnable + "]";
      }

      @Override
      public void run() {
        try {
          runnable.run();
        } catch (Exception e) {
          handleException(e);
        }
      }
    };
  }

  protected final void handleException(final Exception e) {
    final OnFail onFail = getOnFail();
    if (OnFail.ThrowException == onFail) {
      throw AdempiereException.wrapIfNeeded(e);
    } else if (OnFail.ShowErrorPopup == onFail) {
      clientUI.error(getParentWindowNo(), e);
    } else if (OnFail.SilentlyIgnore == onFail) {
      // Ignore it silently. Don't do logging.
      // logger.warn("Got error while running: " + runnable + ". Ignored.", e);
      return;
    } else if (OnFail.UseHandler == onFail) {
      final IExceptionHandler exceptionHandler = getExceptionHandler();
      if (exceptionHandler == null) {
        logger.warn(
            "No exception handler was configurated and OnFail=UseHandler. Throwing the exception");
        // fallback
        throw AdempiereException.wrapIfNeeded(e);
      } else {
        exceptionHandler.handleException(e);
      }
    }
    // Fallback: throw the exception
    else {
      throw AdempiereException.wrapIfNeeded(e);
    }
  }

  @Override
  public final IClientUIInvoker setInvokeLater(final boolean invokeLater) {
    this.invokeLater = invokeLater;
    return this;
  }

  protected final boolean isInvokeLater() {
    return invokeLater;
  }

  @Override
  public final IClientUIInvoker setLongOperation(final boolean longOperation) {
    this.longOperation = longOperation;
    return this;
  }

  protected final boolean isLongOperation() {
    return longOperation;
  }

  @Override
  public IClientUIInvoker setShowGlassPane(final boolean showGlassPane) {
    this.showGlassPane = showGlassPane;
    return this;
  }

  protected final boolean isShowGlassPane() {
    return showGlassPane;
  }

  @Override
  public final IClientUIInvoker setOnFail(OnFail onFail) {
    Check.assumeNotNull(onFail, "onFail not null");
    this.onFail = onFail;
    return this;
  }

  private final OnFail getOnFail() {
    return onFail;
  }

  @Override
  public final IClientUIInvoker setExceptionHandler(IExceptionHandler exceptionHandler) {
    Check.assumeNotNull(exceptionHandler, "exceptionHandler not null");
    this.exceptionHandler = exceptionHandler;
    return this;
  }

  private final IExceptionHandler getExceptionHandler() {
    return exceptionHandler;
  }

  @Override
  public abstract IClientUIInvoker setParentComponent(final Object parentComponent);

  @Override
  public abstract IClientUIInvoker setParentComponentByWindowNo(final int windowNo);

  protected final void setParentComponent(final int windowNo, final Object component) {
    this.parentWindowNo = windowNo;
    this.parentComponent = component;
  }

  protected final Object getParentComponent() {
    return parentComponent;
  }

  protected final int getParentWindowNo() {
    return parentWindowNo;
  }

  @Override
  public final IClientUIInvoker setRunnable(final Runnable runnable) {
    this.runnable = runnable;
    return this;
  }

  private final Runnable getRunnable() {
    return runnable;
  }
}
Esempio n. 20
0
/**
 * Misc helper used when testing ASync module
 *
 * @author tsa
 */
public class Helper {
  private final Logger logger = LogManager.getLogger(getClass());

  private Properties ctx;

  public Helper() {
    super();
    this.ctx = Env.getCtx();
  }

  public Properties getCtx() {
    return ctx;
  }

  public void assertNothingLocked() {
    final PlainLockManager lockManager = (PlainLockManager) Services.get(ILockManager.class);
    final PlainLockDatabase lockDatabase = lockManager.getLockDatabase();
    Assert.assertTrue(
        "No locks expecteded\n" + lockDatabase.getLockedObjectsInfo(),
        lockDatabase.getLocks().isEmpty());
  }

  public I_C_Queue_Processor createQueueProcessor(
      final String name, final int poolSize, final int maxPoolSize, final int keepAliveTimeMillis) {
    final I_C_Queue_Processor queueProcessorDef =
        InterfaceWrapperHelper.create(ctx, I_C_Queue_Processor.class, ITrx.TRXNAME_None);
    queueProcessorDef.setName(name);
    queueProcessorDef.setPoolSize(poolSize);
    queueProcessorDef.setKeepAliveTimeMillis(keepAliveTimeMillis);
    InterfaceWrapperHelper.save(queueProcessorDef);
    return queueProcessorDef;
  }

  public I_C_Queue_PackageProcessor createPackageProcessor(
      Properties ctx,
      Class<? extends IWorkpackageProcessor> packageProcessorClass,
      String trxName) {
    final I_C_Queue_PackageProcessor packageProcessorDef =
        InterfaceWrapperHelper.create(ctx, I_C_Queue_PackageProcessor.class, trxName);
    packageProcessorDef.setClassname(packageProcessorClass.getCanonicalName());
    InterfaceWrapperHelper.save(packageProcessorDef);

    return packageProcessorDef;
  }

  public I_C_Queue_PackageProcessor createPackageProcessor(
      Properties ctx, Class<? extends IWorkpackageProcessor> packageProcessorClass) {
    return createPackageProcessor(ctx, packageProcessorClass, ITrx.TRXNAME_None);
  }

  public I_C_Queue_PackageProcessor assignPackageProcessor(
      I_C_Queue_Processor queueProcessorDef,
      Class<? extends IWorkpackageProcessor> packageProcessorClass) {
    final Properties ctx = InterfaceWrapperHelper.getCtx(queueProcessorDef);
    final String trxName = InterfaceWrapperHelper.getTrxName(queueProcessorDef);

    final I_C_Queue_PackageProcessor packageProcessorDef =
        createPackageProcessor(ctx, packageProcessorClass, trxName);
    assignPackageProcessor(queueProcessorDef, packageProcessorDef);

    return packageProcessorDef;
  }

  public void assignPackageProcessor(
      final I_C_Queue_Processor queueProcessorDef,
      final I_C_Queue_PackageProcessor packageProcessorDef) {
    final Properties ctx = InterfaceWrapperHelper.getCtx(queueProcessorDef);
    final String trxName = InterfaceWrapperHelper.getTrxName(queueProcessorDef);

    final I_C_Queue_Processor_Assign assignment =
        InterfaceWrapperHelper.create(ctx, I_C_Queue_Processor_Assign.class, trxName);
    assignment.setC_Queue_Processor(queueProcessorDef);
    assignment.setC_Queue_PackageProcessor(packageProcessorDef);
    InterfaceWrapperHelper.save(assignment);
  }

  public List<I_C_Queue_WorkPackage> createAndEnqueueWorkpackages(
      final IWorkPackageQueue workpackageQueue,
      final int count,
      final boolean markReadyForProcessing) {
    final I_C_Queue_Block block = workpackageQueue.enqueueBlock(ctx);

    final List<I_C_Queue_WorkPackage> workpackages = new ArrayList<I_C_Queue_WorkPackage>(count);
    for (int i = 1; i <= count; i++) {
      final I_C_Queue_WorkPackage wp =
          workpackageQueue.enqueueWorkPackage(block, IWorkPackageQueue.PRIORITY_AUTO);
      POJOWrapper.setInstanceName(wp, "workpackage-" + i);
      if (markReadyForProcessing) {
        workpackageQueue.markReadyForProcessing(wp);
      }

      workpackages.add(wp);
    }

    return workpackages;
  }

  public void markReadyForProcessing(List<I_C_Queue_WorkPackage> workpackages) {
    for (I_C_Queue_WorkPackage wp : workpackages) {
      wp.setIsReadyForProcessing(true);
      InterfaceWrapperHelper.save(wp);
    }
  }

  public void markReadyForProcessing(I_C_Queue_WorkPackage... workpackages) {
    Check.assumeNotNull(workpackages, "workpackages not null");
    if (workpackages.length == 0) {
      return;
    }

    markReadyForProcessing(Arrays.asList(workpackages));
  }

  /**
   * Mark the workpackage as ready for processing and wait until the workpackage gets processed
   *
   * @param workpackageQueue
   * @param workpackage
   * @throws TimeoutException
   * @throws ExecutionException
   * @throws InterruptedException
   */
  public void markReadyForProcessingAndWait(
      final IWorkPackageQueue workpackageQueue, final I_C_Queue_WorkPackage workpackage)
      throws InterruptedException, ExecutionException, TimeoutException {
    Future<IWorkpackageProcessorExecutionResult> futureResult =
        workpackageQueue.markReadyForProcessingAndReturn(workpackage);

    futureResult.get(1, TimeUnit.MINUTES);
  }

  public void markUnprocessed(final I_C_Queue_WorkPackage workpackage) {
    workpackage.setIsReadyForProcessing(false);
    InterfaceWrapperHelper.save(workpackage);

    workpackage.setProcessed(false);
    workpackage.setIsError(false);
    workpackage.setSkippedAt(null);
    workpackage.setAD_Issue(null);

    InterfaceWrapperHelper.save(workpackage);
  }

  /**
   * Method waits until the given {@code list} has reached the given {@code targetSize} or if the
   * given {@code timeoutMillis} has passed. If the timeout has pased without the size beeing
   * reached, the method throws an Exception.
   *
   * @param list
   * @param targetSize
   * @param timeoutMillis how many millis to wait for result. If ZERO, we will wait forever
   * @throws InterruptedException
   * @throws TimeoutException if the timeout exceeded
   */
  public void waitUntilSize(final List<?> list, final int targetSize, final long timeoutMillis)
      throws InterruptedException, TimeoutException {
    Thread.sleep(
        100); // wait a bit, because the processor need not only to increase list.size(), but also
              // flag the package as "processed"

    int retryCount = 0;

    final long beginTS = SystemTime.millis();
    int lastSize = -1;
    int size = list.size();
    retryCount++;

    while (size < targetSize) {
      long currentTS = SystemTime.millis();
      if (lastSize != -1 && timeoutMillis > 0) {
        final long elapsedMillis = currentTS - beginTS;
        if (elapsedMillis >= timeoutMillis) {
          throw new TimeoutException(
              "Timeout while waiting for " + list + "to have at least " + targetSize + " items");
        }
      }

      Thread.sleep(200);

      size = list.size();
      lastSize = size;
      retryCount++;

      logger.trace("Retry " + retryCount + " => size=" + size);
    }
    logger.trace("Finished after " + retryCount + " retries => size=" + size);
    Thread.sleep(200); // without further delay, the future we wait for might still not be "done"
  }
}
Esempio n. 21
0
/**
 * Class responsible for loading {@link PO}.
 *
 * @author tsa
 */
public final class TableModelLoader {
  public static final transient TableModelLoader instance = new TableModelLoader();

  private static final Logger log = LogManager.getLogger(TableModelLoader.class);
  private final TableModelClassLoader tableModelClassLoader = TableModelClassLoader.instance;

  private TableModelLoader() {
    super();
  }

  public PO newPO(final Properties ctx, final String tableName, final String trxName) {
    final int recordId = 0; // marker for new records
    final PO po = retrievePO(ctx, tableName, recordId, trxName);
    return po;
  }

  public PO getPO(
      final Properties ctx, final String tableName, final int Record_ID, final String trxName) {
    boolean checkCache = true;

    // Respect cache interceptor's temporary disabled flag
    if (CacheInterceptor.isCacheDisabled()) {
      checkCache = false;
    }

    return getPO(ctx, tableName, Record_ID, checkCache, trxName);
  }

  /**
   * @param Record_ID
   * @param checkCache true if object shall be checked in cache first
   * @param trxName
   * @return loaded PO
   */
  public PO getPO(
      final Properties ctx,
      final String tableName,
      final int Record_ID,
      final boolean checkCache,
      final String trxName) {
    final IModelCacheService modelCacheService = Services.get(IModelCacheService.class);
    if (checkCache) {
      final PO poCached = modelCacheService.retrieveObject(ctx, tableName, Record_ID, trxName);
      if (poCached != null) {
        return poCached;
      }
    }

    final PO po = retrievePO(ctx, tableName, Record_ID, trxName);
    modelCacheService.addToCache(po);

    return po;
  }

  /**
   * Loads the PO from database. In case some errors were encountered, they will be logged and
   * <code>null</code> will be returned.
   *
   * @param ctx
   * @param tableName
   * @param Record_ID
   * @param trxName
   * @return PO or null
   */
  private final PO retrievePO(
      final Properties ctx, final String tableName, int Record_ID, String trxName) {
    final POInfo poInfo = POInfo.getPOInfo(tableName);
    if (Record_ID > 0 && poInfo.getKeyColumnName() == null) {
      log.warn("(id) - Multi-Key " + tableName);
      return null;
    }

    final Class<?> clazz = tableModelClassLoader.getClass(tableName);
    if (clazz == null) {
      log.info("Using GenericPO for {}", tableName);
      final GenericPO po = new GenericPO(tableName, ctx, Record_ID, trxName);
      return po;
    }

    boolean errorLogged = false;
    try {
      final Constructor<?> constructor = tableModelClassLoader.getIDConstructor(clazz);
      final PO po = (PO) constructor.newInstance(ctx, Record_ID, trxName);
      if (po != null && po.get_ID() != Record_ID && Record_ID > 0) {
        return null;
      }
      return po;
    } catch (Exception e) {
      final Throwable cause = e.getCause() == null ? e : e.getCause();
      log.error("(id) - Table=" + tableName + ",Class=" + clazz, cause);
      MetasfreshLastError.saveError(log, "Error", cause);
      errorLogged = true;
    }

    if (!errorLogged) {
      log.error("(id) - Not found - Table=" + tableName + ", Record_ID=" + Record_ID);
    }

    return null;
  } // getPO

  /**
   * Get PO Class Instance
   *
   * @param ctx
   * @param tableName
   * @param rs result set
   * @param trxName transaction
   * @return PO for Record; never return null
   */
  public PO getPO(final Properties ctx, final String tableName, ResultSet rs, String trxName) {
    final PO po = retrievePO(ctx, tableName, rs, trxName);

    final IModelCacheService modelCacheService = Services.get(IModelCacheService.class);
    modelCacheService.addToCache(po);

    return po;
  }

  /**
   * Retrieve model from given result set.
   *
   * @param ctx
   * @param tableName
   * @param rs
   * @param trxName
   * @return loaded model; never return null
   */
  private final PO retrievePO(
      final Properties ctx, final String tableName, final ResultSet rs, final String trxName) {
    final Class<?> clazz = tableModelClassLoader.getClass(tableName);
    if (clazz == null) {
      log.info("Using GenericPO for {}", tableName);
      final GenericPO po = new GenericPO(tableName, ctx, rs, trxName);
      return po;
    }

    try {
      final Constructor<?> constructor = tableModelClassLoader.getResultSetConstructor(clazz);

      final PO po = (PO) constructor.newInstance(ctx, rs, trxName);
      return po;
    } catch (final Exception e) {
      throw new AdempiereException(
          "Error while loading model from ResultSet"
              + "\n@TableName@: "
              + tableName
              + "\nClass: "
              + clazz,
          e);
    }
  } // getPO

  /**
   * Get PO Class Instance
   *
   * @param whereClause where clause
   * @param trxName transaction
   * @return PO for Record or null
   */
  public PO getPO(
      final Properties ctx, final String tableName, String whereClause, String trxName) {
    final Object[] params = null;
    return getPO(ctx, tableName, whereClause, params, trxName);
  } // getPO

  /**
   * Get PO class instance
   *
   * @param whereClause
   * @param params
   * @param trxName
   * @return
   */
  public PO getPO(
      final Properties ctx,
      final String tableName,
      String whereClause,
      Object[] params,
      String trxName) {
    final PO po = retrievePO(ctx, tableName, whereClause, params, trxName);
    if (po != null) {
      final IModelCacheService modelCacheService = Services.get(IModelCacheService.class);
      modelCacheService.addToCache(po);
    }

    return po;
  }

  private final PO retrievePO(
      final Properties ctx,
      final String tableName,
      final String whereClause,
      final Object[] params,
      final String trxName) {
    if (whereClause == null || whereClause.length() == 0) {
      return null;
    }

    //
    PO po = null;
    final POInfo info = POInfo.getPOInfo(tableName);
    if (info == null) {
      return null;
    }

    final StringBuilder sqlBuffer = info.buildSelect();
    sqlBuffer.append(" WHERE ").append(whereClause);
    final String sql = sqlBuffer.toString();

    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
      pstmt = DB.prepareStatement(sql, trxName);
      DB.setParameters(pstmt, params);

      rs = pstmt.executeQuery();
      if (rs.next()) {
        po = getPO(ctx, tableName, rs, trxName);
      }
    } catch (Exception e) {
      log.error(sql, e);
      MetasfreshLastError.saveError(log, "Error", e);
    } finally {
      DB.close(rs, pstmt);
    }

    return po;
  }

  public final <ModelType> ModelType retrieveModel(
      final Properties ctx,
      final String tableName,
      final Class<?> modelClass,
      final ResultSet rs,
      final String trxName) {
    final PO po = getPO(ctx, tableName, rs, trxName);

    //
    // Case: we have a modelClass specified
    if (modelClass != null) {
      final Class<? extends PO> poClass = po.getClass();
      if (poClass.isAssignableFrom(modelClass)) {
        @SuppressWarnings("unchecked")
        final ModelType model = (ModelType) po;
        return model;
      } else {
        @SuppressWarnings("unchecked")
        final ModelType model = (ModelType) InterfaceWrapperHelper.create(po, modelClass);
        return model;
      }
    }
    //
    // Case: no "clazz" and no "modelClass"
    else {
      if (log.isDebugEnabled()) {
        final AdempiereException ex =
            new AdempiereException(
                "Query does not have a modelClass defined and no 'clazz' was specified as parameter."
                    + "We need to avoid this case, but for now we are trying to do a force casting"
                    + "\nQuery: "
                    + this
                    + "\nPO: "
                    + po);
        log.debug(ex.getLocalizedMessage(), ex);
      }

      @SuppressWarnings("unchecked")
      final ModelType model = (ModelType) po;
      return model;
    }
  }
}
Esempio n. 22
0
/**
 * Customer Sub Panel
 *
 * @author Comunidad de Desarrollo OpenXpertya *Basado en Codigo Original Modificado, Revisado y
 *     Optimizado de: *Copyright � Jorg Janke
 * @version $Id: SubBPartner.java,v 1.1 2004/07/12 04:10:04 jjanke Exp $
 */
public class SubOrder extends PosSubPanel implements ActionListener, FocusListener {
  /** */
  private static final long serialVersionUID = 5895558315889871887L;

  /**
   * Constructor
   *
   * @param posPanel POS Panel
   */
  public SubOrder(PosBasePanel posPanel) {
    super(posPanel);
  } //	PosSubCustomer

  private CButton f_history;
  private CTextField f_name;
  private CButton f_bNew;
  private CButton f_bSearch;
  private CComboBox f_location;
  private CComboBox f_user;
  private CButton f_cashPayment;
  private CButton f_process;
  private CButton f_print;
  private CTextField f_DocumentNo;
  private CButton f_logout;
  private JFormattedTextField f_net;
  private JFormattedTextField f_tax;
  private JFormattedTextField f_total;
  private CTextField f_RepName;

  /** The Business Partner */
  private MBPartner m_bpartner;
  /** Price List Version to use */
  private int m_M_PriceList_Version_ID = 0;

  private CTextField f_currency = new CTextField();
  private CButton f_bEdit;
  private CButton f_bSettings;
  /** Logger */
  private static Logger log = LogManager.getLogger(SubOrder.class);

  /** Initialize */
  @Override
  public void init() {
    //	Content
    MigLayout layout = new MigLayout("ins 0 0", "[fill|fill|fill|fill]", "[nogrid]unrel[||]");
    setLayout(layout);

    Font bigFont = AdempierePLAF.getFont_Field().deriveFont(16f);

    String buttonSize = "w 50!, h 50!,";
    // NEW
    f_bNew = createButtonAction("New", KeyStroke.getKeyStroke(KeyEvent.VK_F2, Event.F2));
    add(f_bNew, buttonSize);

    // EDIT
    f_bEdit = createButtonAction("Edit", null);
    add(f_bEdit, buttonSize);
    f_bEdit.setEnabled(false);

    // HISTORY
    f_history = createButtonAction("History", null);
    add(f_history, buttonSize);

    // CANCEL
    f_process = createButtonAction("Cancel", null);
    add(f_process, buttonSize);
    f_process.setEnabled(false);

    // PAYMENT
    f_cashPayment = createButtonAction("Payment", null);
    f_cashPayment.setActionCommand("Cash");
    add(f_cashPayment, buttonSize);
    f_cashPayment.setEnabled(false);

    // PRINT
    f_print = createButtonAction("Print", null);
    add(f_print, buttonSize);
    f_print.setEnabled(false);

    // Settings
    f_bSettings = createButtonAction("Preference", null);
    add(f_bSettings, buttonSize);

    //
    f_logout = createButtonAction("Logout", null);
    add(f_logout, buttonSize + ", gapx 25, wrap");

    // DOC NO
    add(new CLabel(Msg.getMsg(Env.getCtx(), "DocumentNo")), "");

    f_DocumentNo = new CTextField("");
    f_DocumentNo.setName("DocumentNo");
    f_DocumentNo.setEditable(false);
    add(f_DocumentNo, "growx, pushx");

    CLabel lNet = new CLabel(Msg.translate(Env.getCtx(), "SubTotal"));
    add(lNet, "");
    f_net = new JFormattedTextField(DisplayType.getNumberFormat(DisplayType.Amount));
    f_net.setHorizontalAlignment(JTextField.TRAILING);
    f_net.setEditable(false);
    f_net.setFocusable(false);
    lNet.setLabelFor(f_net);
    add(f_net, "wrap, growx, pushx");
    f_net.setValue(Env.ZERO);
    //

    /*
    	// BPARTNER
    	f_bSearch = createButtonAction ("BPartner", KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.SHIFT_MASK+Event.CTRL_MASK));
    	add (f_bSearch,buttonSize + ", spany 2");
    */

    /*
     * f_name.setName("Name");
    f_name.addActionListener(this);
    f_name.addFocusListener(this);
    add (f_name, "wrap");
    */

    // SALES REP
    add(new CLabel(Msg.translate(Env.getCtx(), "SalesRep_ID")), "");
    f_RepName = new CTextField("");
    f_RepName.setName("SalesRep");
    f_RepName.setEditable(false);
    add(f_RepName, "growx, pushx");

    CLabel lTax = new CLabel(Msg.translate(Env.getCtx(), "TaxAmt"));
    add(lTax);
    f_tax = new JFormattedTextField(DisplayType.getNumberFormat(DisplayType.Amount));
    f_tax.setHorizontalAlignment(JTextField.TRAILING);
    f_tax.setEditable(false);
    f_tax.setFocusable(false);
    lTax.setLabelFor(f_tax);
    add(f_tax, "wrap, growx, pushx");
    f_tax.setValue(Env.ZERO);
    //

    /*
    	f_location = new CComboBox();
    	add (f_location, " wrap");
    */

    // BP
    add(new CLabel(Msg.translate(Env.getCtx(), "C_BPartner_ID")), "");
    f_name = new CTextField();
    f_name.setEditable(false);
    f_name.setName("Name");
    add(f_name, "growx, pushx");

    //
    CLabel lTotal = new CLabel(Msg.translate(Env.getCtx(), "GrandTotal"));
    lTotal.setFont(bigFont);
    add(lTotal, "");
    f_total = new JFormattedTextField(DisplayType.getNumberFormat(DisplayType.Amount));
    f_total.setHorizontalAlignment(JTextField.TRAILING);
    f_total.setFont(bigFont);
    f_total.setEditable(false);
    f_total.setFocusable(false);
    lTotal.setLabelFor(f_total);
    add(f_total, "growx, pushx");
    f_total.setValue(Env.ZERO);
    /*
    //
    f_user = new CComboBox();
    add (f_user, "skip 1");
    */
  } //	init

  /** Dispose - Free Resources */
  @Override
  public void dispose() {
    if (f_name != null) f_name.removeFocusListener(this);
    f_name = null;
    removeAll();
    super.dispose();
  } //	dispose

  /**
   * ************************************************************************ Action Listener
   *
   * @param e event
   */
  @Override
  public void actionPerformed(ActionEvent e) {
    String action = e.getActionCommand();
    if (action == null || action.length() == 0) return;
    log.info("PosSubCustomer - actionPerformed: " + action);
    //	New
    if (action.equals("New")) {
      p_posPanel.newOrder(); // red1 New POS Order instead - B_Partner already has direct field
      return;
    }
    //	Register
    if (action.equals("History")) {
      PosQuery qt = new QueryTicket(p_posPanel);
      qt.setVisible(true);
      return;
    } else if (action.equals("Cancel")) deleteOrder();
    else if (action.equals("Cash")) payOrder();
    else if (action.equals("Print")) printOrder();
    else if (action.equals("BPartner")) {
      PosQuery qt = new QueryBPartner(p_posPanel);
      qt.setVisible(true);
    }
    // Logout
    else if (action.equals("Logout")) {
      p_posPanel.dispose();
      return;
    }
    //	Name
    else if (e.getSource() == f_name) findBPartner();

    p_posPanel.updateInfo();
  } //	actionPerformed

  /** */
  private void printOrder() {
    {
      if (isOrderFullyPaid()) {
        updateOrder();
        printTicket();
        openCashDrawer();
      }
    }
  }

  /** */
  private void payOrder() {

    // Check if order is completed, if so, print and open drawer, create an empty order and set
    // cashGiven to zero

    if (p_posPanel.m_order != null) {
      if (!p_posPanel.m_order.isProcessed() && !p_posPanel.m_order.processOrder()) {
        ADialog.warn(0, p_posPanel, "PosOrderProcessFailed");
        return;
      }

      if (PosPayment.pay(p_posPanel)) {
        printTicket();
        p_posPanel.setOrder(0);
      }
    }
  }

  /** */
  private void deleteOrder() {
    if (p_posPanel != null && ADialog.ask(0, this, "Delete order?"))
      p_posPanel.m_order.deleteOrder();
    // p_posPanel.newOrder();

  }

  /**
   * Focus Gained
   *
   * @param e
   */
  @Override
  public void focusGained(FocusEvent e) {} // 	focusGained

  /**
   * Focus Lost
   *
   * @param e
   */
  @Override
  public void focusLost(FocusEvent e) {
    if (e.isTemporary()) return;
    log.info(e.toString());
    findBPartner();
  } //	focusLost

  /** Find/Set BPartner */
  private void findBPartner() {

    String query = f_name.getText();

    if (query == null || query.length() == 0) return;

    // unchanged
    if (m_bpartner != null && m_bpartner.getName().equals(query)) return;

    query = query.toUpperCase();
    //	Test Number
    boolean allNumber = true;
    boolean noNumber = true;
    char[] qq = query.toCharArray();
    for (int i = 0; i < qq.length; i++) {
      if (Character.isDigit(qq[i])) {
        noNumber = false;
        break;
      }
    }
    try {
      Integer.parseInt(query);
    } catch (Exception e) {
      allNumber = false;
    }
    String Value = query;
    String Name = (allNumber ? null : query);
    String EMail = (query.indexOf('@') != -1 ? query : null);
    String Phone = (noNumber ? null : query);
    String City = null;
    //
    // TODO: contact have been remove from rv_bpartner
    MBPartnerInfo[] results =
        MBPartnerInfo.find(p_ctx, Value, Name, /*Contact, */ null, EMail, Phone, City);

    //	Set Result
    if (results.length == 0) {
      setC_BPartner_ID(0);
    } else if (results.length == 1) {
      setC_BPartner_ID(results[0].getC_BPartner_ID());
      f_name.setText(results[0].getName());
    } else //	more than one
    {
      QueryBPartner qt = new QueryBPartner(p_posPanel);
      qt.setResults(results);
      qt.setVisible(true);
    }
  } //	findBPartner

  /**
   * ************************************************************************ Set BPartner
   *
   * @param C_BPartner_ID id
   */
  public void setC_BPartner_ID(int C_BPartner_ID) {
    log.debug("PosSubCustomer.setC_BPartner_ID=" + C_BPartner_ID);
    if (C_BPartner_ID == 0) m_bpartner = null;
    else {
      m_bpartner = new MBPartner(p_ctx, C_BPartner_ID, null);
      if (m_bpartner.get_ID() == 0) m_bpartner = null;
    }

    //	Set Info
    if (m_bpartner != null) {
      f_name.setText(m_bpartner.getName());
    } else {
      f_name.setText(null);
    }
    //	Sets Currency
    m_M_PriceList_Version_ID = 0;
    getM_PriceList_Version_ID();
    // fillCombos();
    if (p_posPanel.m_order != null && m_bpartner != null)
      p_posPanel.m_order.setBPartner(
          m_bpartner); // added by ConSerTi to update the client in the request
  } //	setC_BPartner_ID

  /** Fill Combos (Location, User) */
  private void fillCombos() {
    Vector<KeyNamePair> locationVector = new Vector<KeyNamePair>();
    if (m_bpartner != null) {
      MBPartnerLocation[] locations = m_bpartner.getLocations(false);
      for (int i = 0; i < locations.length; i++)
        locationVector.add(
            new KeyNamePair(locations[i].getC_BPartner_Location_ID(), locations[i].getName()));
    }
    DefaultComboBoxModel locationModel = new DefaultComboBoxModel(locationVector);
    f_location.setModel(locationModel);
    //
    Vector<KeyNamePair> userVector = new Vector<KeyNamePair>();
    if (m_bpartner != null) {
      MUser[] users = m_bpartner.getContacts(false);
      for (int i = 0; i < users.length; i++)
        userVector.add(new KeyNamePair(users[i].getAD_User_ID(), users[i].getName()));
    }
    DefaultComboBoxModel userModel = new DefaultComboBoxModel(userVector);
    f_user.setModel(userModel);
  } //	fillCombos

  /**
   * Get BPartner
   *
   * @return C_BPartner_ID
   */
  public int getC_BPartner_ID() {
    if (m_bpartner != null) return m_bpartner.getC_BPartner_ID();
    return 0;
  } //	getC_BPartner_ID

  /**
   * Get BPartner
   *
   * @return BPartner
   */
  public MBPartner getBPartner() {
    return m_bpartner;
  } //	getBPartner

  /**
   * Get BPartner Location
   *
   * @return C_BPartner_Location_ID
   */
  public int getC_BPartner_Location_ID() {
    if (m_bpartner != null) {
      KeyNamePair pp = (KeyNamePair) f_location.getSelectedItem();
      if (pp != null) return pp.getKey();
    }
    return 0;
  } //	getC_BPartner_Location_ID

  /**
   * Get BPartner Contact
   *
   * @return AD_User_ID
   */
  public int getAD_User_ID() {
    if (m_bpartner != null) {
      KeyNamePair pp = (KeyNamePair) f_user.getSelectedItem();
      if (pp != null) return pp.getKey();
    }
    return 0;
  } //	getC_BPartner_Location_ID

  /**
   * Get M_PriceList_Version_ID. Set Currency
   *
   * @return plv
   */
  public int getM_PriceList_Version_ID() {
    if (m_M_PriceList_Version_ID == 0) {
      int M_PriceList_ID = p_pos.getM_PriceList_ID();
      if (m_bpartner != null && m_bpartner.getM_PriceList_ID() != 0)
        M_PriceList_ID = m_bpartner.getM_PriceList_ID();
      //
      MPriceList pl = MPriceList.get(p_ctx, M_PriceList_ID, null);
      setCurrency(Services.get(ICurrencyDAO.class).getISO_Code(p_ctx, pl.getC_Currency_ID()));
      f_name.setToolTipText(pl.getName());
      //
      MPriceListVersion plv = pl.getPriceListVersion(p_posPanel.getToday());
      if (plv != null && plv.getM_PriceList_Version_ID() != 0)
        m_M_PriceList_Version_ID = plv.getM_PriceList_Version_ID();
    }
    return m_M_PriceList_Version_ID;
  } //	getM_PriceList_Version_ID

  /**
   * ************************************************************************* Set Currency
   *
   * @param currency currency
   */
  public void setCurrency(String currency) {
    if (currency == null) f_currency.setText("---");
    else f_currency.setText(currency);
  } //	setCurrency

  /**
   * Print Ticket
   *
   * @author Comunidad de Desarrollo OpenXpertya *Basado en Codigo Original Modificado, Revisado y
   *     Optimizado de: *Copyright � ConSerTi
   */
  public void printTicket() {
    if (p_posPanel.m_order == null) return;

    MOrder order = p_posPanel.m_order;
    // int windowNo = p_posPanel.getWindowNo();
    // Properties m_ctx = p_posPanel.getPropiedades();

    if (order != null) {
      try {
        // TODO: drop it - https://github.com/metasfresh/metasfresh/issues/456
        throw new UnsupportedOperationException();

        //				/*
        //				if (p_pos.getAD_PrintLabel_ID() != 0)
        //					PrintLabel.printLabelTicket(order.getC_Order_ID(), p_pos.getAD_PrintLabel_ID());
        //				*/
        //				//print standard document
        //				ReportCtl.startDocumentPrint(ReportEngine.ORDER, order.getC_Order_ID(), null,
        // Env.getWindowNo(this), true);

      } catch (Exception e) {
        log.error("PrintTicket - Error Printing Ticket");
      }
    }
  }

  /**
   * Is order fully pay ? Calculates if the given money is sufficient to pay the order
   *
   * @author Comunidad de Desarrollo OpenXpertya *Basado en Codigo Original Modificado, Revisado y
   *     Optimizado de: *Copyright � ConSerTi
   */
  public boolean isOrderFullyPaid() {
    /*TODO
    BigDecimal given = new BigDecimal(f_cashGiven.getValue().toString());
    boolean paid = false;
    if (p_posPanel != null && p_posPanel.f_curLine != null)
    {
    	MOrder order = p_posPanel.f_curLine.getOrder();
    	BigDecimal total = new BigDecimal(0);
    	if (order != null)
    		total = order.getGrandTotal();
    	paid = given.doubleValue() >= total.doubleValue();
    }
    return paid;
    */
    return true;
  }

  /**
   * Display cash return Display the difference between tender amount and bill amount
   *
   * @author Comunidad de Desarrollo OpenXpertya *Basado en Codigo Original Modificado, Revisado y
   *     Optimizado de: *Copyright � ConSerTi
   */
  public void updateOrder() {
    if (p_posPanel != null) {
      MOrder order = p_posPanel.m_order;
      if (order != null) {
        f_DocumentNo.setText(order.getDocumentNo());
        setC_BPartner_ID(order.getC_BPartner_ID());
        f_bNew.setEnabled(order.getLines().length != 0);
        f_bEdit.setEnabled(true);
        f_history.setEnabled(order.getLines().length != 0);
        f_process.setEnabled(true);
        f_print.setEnabled(order.isProcessed());
        f_cashPayment.setEnabled(order.getLines().length != 0);
      } else {
        f_DocumentNo.setText(null);
        setC_BPartner_ID(0);
        f_bNew.setEnabled(true);
        f_bEdit.setEnabled(false);
        f_history.setEnabled(true);
        f_process.setEnabled(false);
        f_print.setEnabled(false);
        f_cashPayment.setEnabled(false);
      }
    }
  }

  /**
   * Abrir caja Abre la caja registradora
   *
   * @author Comunidad de Desarrollo OpenXpertya *Basado en Codigo Original Modificado, Revisado y
   *     Optimizado de: *Copyright � ConSerTi
   */
  public void openCashDrawer() {
    String port = "/dev/lp";

    byte data[] = new byte[] {0x1B, 0x40, 0x1C};
    try {
      FileOutputStream m_out = null;
      if (m_out == null) {
        m_out = new FileOutputStream(port); // No poner append = true.
      }
      m_out.write(data);
    } catch (IOException e) {
    }
  }

  /** Set Sums from Table */
  void setSums(PosOrderModel order) {
    int noLines = p_posPanel.f_curLine.m_table.getRowCount();
    if (order == null || noLines == 0) {
      f_net.setValue(Env.ZERO);
      f_total.setValue(Env.ZERO);
      f_tax.setValue(Env.ZERO);
    } else {
      // order.getMOrder().prepareIt();
      f_net.setValue(order.getSubtotal());
      f_total.setValue(order.getGrandTotal());
      f_tax.setValue(order.getTaxAmt());
    }
  } //	setSums
} //	PosSubCustomer
Esempio n. 23
0
/**
 * Cost Engine
 *
 * @author [email protected] http://www.e-evolution.com
 */
public class CostEngine {
  /** Logger */
  protected transient Logger log = LogManager.getLogger(getClass());

  public String getCostingMethod() {
    return MCostElement.COSTINGMETHOD_StandardCosting;
  }

  public BigDecimal getResourceStandardCostRate(
      I_PP_Cost_Collector cc, int S_Resource_ID, CostDimension d, String trxName) {
    final Properties ctx = InterfaceWrapperHelper.getCtx(cc);
    final I_M_Product resourceProduct =
        MProduct.forS_Resource_ID(ctx, S_Resource_ID, ITrx.TRXNAME_None);
    return getProductStandardCostPrice(
        cc,
        resourceProduct,
        MAcctSchema.get(ctx, d.getC_AcctSchema_ID()),
        MCostElement.get(ctx, d.getM_CostElement_ID()));
  }

  public BigDecimal getResourceActualCostRate(
      I_PP_Cost_Collector cc, int S_Resource_ID, CostDimension d, String trxName) {
    if (S_Resource_ID <= 0) return Env.ZERO;
    final MProduct resourceProduct = MProduct.forS_Resource_ID(Env.getCtx(), S_Resource_ID, null);
    return getProductActualCostPrice(
        cc,
        resourceProduct,
        MAcctSchema.get(Env.getCtx(), d.getC_AcctSchema_ID()),
        MCostElement.get(Env.getCtx(), d.getM_CostElement_ID()),
        trxName);
  }

  public BigDecimal getProductActualCostPrice(
      final I_PP_Cost_Collector cc,
      final I_M_Product product,
      final I_C_AcctSchema as,
      final I_M_CostElement element,
      final String trxName) {
    final boolean failIfNoCostFound = true;
    return getProductActualCostPrice(cc, product, as, element, failIfNoCostFound, trxName);
  }

  public BigDecimal getProductActualCostPriceOrZero(
      final I_PP_Cost_Collector cc,
      final I_M_Product product,
      final I_C_AcctSchema as,
      final I_M_CostElement element,
      final String trxName) {
    final boolean failIfNoCostFound = false;
    final BigDecimal price =
        getProductActualCostPrice(cc, product, as, element, failIfNoCostFound, trxName);
    if (price == null) {
      return BigDecimal.ZERO;
    }
    return price;
  }

  /**
   * @param cc
   * @param product
   * @param as
   * @param element
   * @param failIfNoCostFound
   * @param trxName
   * @return cost price or null if no cost was found and <code>failIfNoCostFound</code> is <code>
   *     true</code>.
   */
  public BigDecimal getProductActualCostPrice(
      final I_PP_Cost_Collector cc,
      final I_M_Product product,
      final I_C_AcctSchema as,
      final I_M_CostElement element,
      final boolean failIfNoCostFound,
      final String trxName) {
    final CostDimension d =
        new CostDimension(
            product,
            as,
            as.getM_CostType_ID(),
            cc.getAD_Org_ID(), // AD_Org_ID,
            cc.getM_AttributeSetInstance_ID(), // M_ASI_ID,
            element.getM_CostElement_ID());

    final I_M_Cost cost = d.toQuery(I_M_Cost.class, trxName).firstOnly(I_M_Cost.class);
    if (cost == null) {
      if (!failIfNoCostFound) {
        return null;
      }

      throw new LiberoException(
          "@NotFound@ @M_Cost@ - "
              + "@M_AcctSchema_ID@="
              + as
              + ", @M_CostElement_ID@="
              + element
              + ", @M_Product_ID@="
              + product
              + ", Dimension="
              + d);
    }

    final BigDecimal price = cost.getCurrentCostPrice().add(cost.getCurrentCostPriceLL());
    return roundCost(price, as.getC_AcctSchema_ID());
  }

  public BigDecimal getProductStandardCostPrice(
      I_PP_Cost_Collector cc, I_M_Product product, I_C_AcctSchema as, I_M_CostElement element) {
    final String trxName = InterfaceWrapperHelper.getTrxName(cc);

    final CostDimension d =
        new CostDimension(
            product,
            as,
            as.getM_CostType_ID(),
            0, // AD_Org_ID,
            0, // M_ASI_ID,
            element.getM_CostElement_ID());

    final I_PP_Order_Cost oc =
        d.toQueryBuilder(I_PP_Order_Cost.class, trxName)
            .addEqualsFilter(I_PP_Order_Cost.COLUMNNAME_PP_Order_ID, cc.getPP_Order_ID())
            .create()
            .firstOnly(I_PP_Order_Cost.class);
    if (oc == null) {
      return BigDecimal.ZERO;
    }

    final BigDecimal costs = oc.getCurrentCostPrice().add(oc.getCurrentCostPriceLL());
    return roundCost(costs, as.getC_AcctSchema_ID());
  }

  protected BigDecimal roundCost(BigDecimal price, int C_AcctSchema_ID) {
    // Fix Cost Precision
    int precision = MAcctSchema.get(Env.getCtx(), C_AcctSchema_ID).getCostingPrecision();
    BigDecimal priceRounded = price;
    if (priceRounded.scale() > precision) {
      priceRounded = priceRounded.setScale(precision, RoundingMode.HALF_UP);
    }
    return priceRounded;
  }

  public Collection<MCost> getByElement(
      MProduct product,
      MAcctSchema as,
      int M_CostType_ID,
      int AD_Org_ID,
      int M_AttributeSetInstance_ID,
      int M_CostElement_ID) {
    CostDimension cd =
        new CostDimension(
            product, as, M_CostType_ID, AD_Org_ID, M_AttributeSetInstance_ID, M_CostElement_ID);
    return cd.toQuery(MCost.class, product.get_TrxName()).setOnlyActiveRecords(true).list();
  }

  /**
   * Get Cost Detail
   *
   * @param model Model Inventory Line
   * @param as Account Schema
   * @param M_CostElement_ID Cost Element
   * @param M_AttributeSetInstance_ID
   * @return MCostDetail
   */
  private MCostDetail getCostDetail(
      IDocumentLine model, MTransaction mtrx, I_C_AcctSchema as, int M_CostElement_ID) {
    final String whereClause =
        "AD_Client_ID=? AND AD_Org_ID=?"
            + " AND "
            + model.get_TableName()
            + "_ID=?"
            + " AND "
            + MCostDetail.COLUMNNAME_M_Product_ID
            + "=?"
            + " AND "
            + MCostDetail.COLUMNNAME_M_AttributeSetInstance_ID
            + "=?"
            + " AND "
            + MCostDetail.COLUMNNAME_C_AcctSchema_ID
            + "=?"
            // +" AND "+MCostDetail.COLUMNNAME_M_CostType_ID+"=?"
            + " AND "
            + MCostDetail.COLUMNNAME_M_CostElement_ID
            + "=?";
    final Object[] params =
        new Object[] {
          mtrx.getAD_Client_ID(),
          mtrx.getAD_Org_ID(),
          model.get_ID(),
          mtrx.getM_Product_ID(),
          mtrx.getM_AttributeSetInstance_ID(),
          as.getC_AcctSchema_ID(),
          // as.getM_CostType_ID(),
          M_CostElement_ID,
        };
    return new Query(mtrx.getCtx(), MCostDetail.Table_Name, whereClause, mtrx.get_TrxName())
        .setParameters(params)
        .firstOnly();
  }

  /**
   * Create Cost Detail (Material Issue, Material Receipt)
   *
   * @param model
   * @param mtrx Material Transaction
   */
  public void createCostDetail(IDocumentLine model, MTransaction mtrx) {
    final I_PP_Cost_Collector cc =
        (model instanceof MPPCostCollector ? (MPPCostCollector) model : null);

    final Properties ctx = mtrx.getCtx();

    for (I_C_AcctSchema as : getAcctSchema(mtrx)) {
      // Cost Detail
      final I_M_Product product = MProduct.get(ctx, mtrx.getM_Product_ID());
      final String costingMethod = Services.get(IProductBL.class).getCostingMethod(product, as);
      // Check costing method
      if (!getCostingMethod().equals(costingMethod)) {
        throw new LiberoException("Costing method not supported - " + costingMethod);
      }
      //
      for (I_M_CostElement element : getCostElements(ctx)) {
        //
        // Delete Unprocessed zero Differences
        deleteCostDetail(
            model, as, element.getM_CostElement_ID(), mtrx.getM_AttributeSetInstance_ID());
        //
        // Get Costs
        final BigDecimal qty = mtrx.getMovementQty();
        final BigDecimal price =
            getProductActualCostPriceOrZero(cc, product, as, element, mtrx.get_TrxName());
        final BigDecimal amt = roundCost(price.multiply(qty), as.getC_AcctSchema_ID());
        //
        // Create / Update Cost Detail
        MCostDetail cd = getCostDetail(model, mtrx, as, element.getM_CostElement_ID());
        if (cd == null) // createNew
        {
          cd =
              new MCostDetail(
                  as,
                  mtrx.getAD_Org_ID(),
                  mtrx.getM_Product_ID(),
                  mtrx.getM_AttributeSetInstance_ID(),
                  element.getM_CostElement_ID(),
                  amt,
                  qty,
                  model.getDescription(),
                  mtrx.get_TrxName());
          // cd.setMovementDate(mtrx.getMovementDate());
          // if (cost != null)
          // {
          // cd.setCurrentCostPrice(cost.getCurrentCostPrice());
          // cd.setCurrentCostPriceLL(cost.getCurrentCostPriceLL());
          // }
          // else
          // {
          // cd.setCurrentCostPrice(Env.ZERO);
          // cd.setCurrentCostPriceLL(Env.ZERO);
          // }
          // cd.setM_CostType_ID(as.getM_CostType_ID());
          // //cd.setCostingMethod(element.getCostingMethod());
          // cd.setM_Transaction_ID(mtrx.get_ID());
          if (cc != null) {
            cd.setPP_Cost_Collector_ID(cc.getPP_Cost_Collector_ID());
          }
        } else {
          cd.setDeltaAmt(amt.subtract(cd.getAmt()));
          cd.setDeltaQty(mtrx.getMovementQty().subtract(cd.getQty()));
          if (cd.isDelta()) {
            cd.setProcessed(false);
            cd.setAmt(amt);
            cd.setQty(mtrx.getMovementQty());
          }
        }
        cd.saveEx();
        processCostDetail(cd);
        log.info("" + cd);
      } // for ELements
    } // Account Schema
  }

  private int deleteCostDetail(
      IDocumentLine model, I_C_AcctSchema as, int M_CostElement_ID, int M_AttributeSetInstance_ID) {
    // Delete Unprocessed zero Differences
    String sql =
        "DELETE FROM "
            + MCostDetail.Table_Name
            + " WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0"
            + " AND "
            + model.get_TableName()
            + "_ID=?"
            + " AND "
            + MCostDetail.COLUMNNAME_C_AcctSchema_ID
            + "=?"
            + " AND "
            + MCostDetail.COLUMNNAME_M_AttributeSetInstance_ID
            + "=?"
            // + " AND "+MCostDetail.COLUMNNAME_M_CostType_ID+"=?"
            + " AND "
            + MCostDetail.COLUMNNAME_M_CostElement_ID
            + "=?";
    Object[] parameters =
        new Object[] {
          model.get_ID(),
          as.getC_AcctSchema_ID(),
          M_AttributeSetInstance_ID,
          // as.getM_CostType_ID(),
          M_CostElement_ID
        };

    int no = DB.executeUpdateEx(sql, parameters, model.get_TrxName());
    if (no != 0) log.info("Deleted #" + no);
    return no;
  }

  private void processCostDetail(I_M_CostDetail cd) {
    if (!cd.isProcessed()) {
      final Properties ctx = InterfaceWrapperHelper.getCtx(cd);
      final I_C_AcctSchema as = MAcctSchema.get(ctx, cd.getC_AcctSchema_ID());
      final I_AD_Client client = MClient.get(ctx, as.getAD_Client_ID());
      if (client.isCostImmediate()) {
        final MCostDetail cdPO = LegacyAdapters.convertToPO(cd);
        cdPO.process();
      }
    }
  }

  public static boolean isActivityControlElement(I_M_CostElement element) {
    String costElementType = element.getCostElementType();
    return MCostElement.COSTELEMENTTYPE_Resource.equals(costElementType)
        || MCostElement.COSTELEMENTTYPE_Overhead.equals(costElementType)
        || MCostElement.COSTELEMENTTYPE_BurdenMOverhead.equals(costElementType);
  }

  private Collection<I_M_CostElement> getCostElements(Properties ctx) {
    return MCostElement.getByCostingMethod(ctx, getCostingMethod());
  }

  private Collection<MAcctSchema> getAcctSchema(PO po) {
    int AD_Org_ID = po.getAD_Org_ID();
    MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(po.getCtx(), po.getAD_Client_ID());
    ArrayList<MAcctSchema> list = new ArrayList<MAcctSchema>(ass.length);
    for (MAcctSchema as : ass) {
      if (!as.isSkipOrg(AD_Org_ID)) list.add(as);
    }
    return list;
  }

  private MCostDetail getCostDetail(I_PP_Cost_Collector cc, int M_CostElement_ID) {
    final String whereClause =
        MCostDetail.COLUMNNAME_PP_Cost_Collector_ID
            + "=?"
            + " AND "
            + MCostDetail.COLUMNNAME_M_CostElement_ID
            + "=?";

    final Properties ctx = InterfaceWrapperHelper.getCtx(cc);
    final String trxName = InterfaceWrapperHelper.getTrxName(cc);
    MCostDetail cd =
        new Query(ctx, MCostDetail.Table_Name, whereClause, trxName)
            .setParameters(new Object[] {cc.getPP_Cost_Collector_ID(), M_CostElement_ID})
            .setOrderBy(
                MCostDetail.COLUMNNAME_Qty
                    + " DESC") // TODO : fix this; we have 2 cost details; now we are taking the one
                               // with bigger qty; we must find a proper way
            .first();
    return cd;
  }

  private I_PP_Cost_Collector createVarianceCostCollector(
      I_PP_Cost_Collector cc, String CostCollectorType) {
    final I_PP_Cost_Collector ccv =
        InterfaceWrapperHelper.newInstance(I_PP_Cost_Collector.class, cc);
    InterfaceWrapperHelper.copyValues(cc, ccv);
    ccv.setProcessing(false);
    ccv.setProcessed(false);
    ccv.setDocStatus(X_PP_Cost_Collector.DOCSTATUS_Drafted);
    ccv.setDocAction(X_PP_Cost_Collector.DOCACTION_Complete);
    ccv.setCostCollectorType(CostCollectorType);
    ccv.setDocumentNo(null); // reset
    cc.setPP_Cost_Collector_Parent(cc); // link to parent
    InterfaceWrapperHelper.save(ccv);
    return ccv;
  }

  /**
   * Create & Proce Cost Detail for Variances
   *
   * @param ccv
   * @param amt
   * @param qty
   * @param cd (optional)
   * @param product
   * @param as
   * @param element
   * @return
   */
  private MCostDetail createVarianceCostDetail(
      I_PP_Cost_Collector ccv,
      BigDecimal amt,
      BigDecimal qty,
      MCostDetail cd,
      I_M_Product product,
      I_C_AcctSchema as,
      I_M_CostElement element) {
    final Properties ctx = InterfaceWrapperHelper.getCtx(ccv);
    final String trxName = InterfaceWrapperHelper.getTrxName(ccv);
    final MCostDetail cdv = new MCostDetail(ctx, 0, trxName);
    if (cd != null) {
      MCostDetail.copyValues(cd, cdv);
      cdv.setProcessed(false);
    }
    if (product != null) {
      cdv.setM_Product_ID(product.getM_Product_ID());
      cdv.setM_AttributeSetInstance_ID(0);
    }
    if (as != null) {
      cdv.setC_AcctSchema_ID(as.getC_AcctSchema_ID());
    }
    if (element != null) {
      cdv.setM_CostElement_ID(element.getM_CostElement_ID());
    }
    //
    cdv.setPP_Cost_Collector_ID(ccv.getPP_Cost_Collector_ID());
    cdv.setAmt(amt);
    cdv.setQty(qty);
    cdv.saveEx();
    processCostDetail(cdv);
    return cdv;
  }

  public void createActivityControl(MPPCostCollector cc) {
    if (!cc.isCostCollectorType(X_PP_Cost_Collector.COSTCOLLECTORTYPE_ActivityControl)) return;
    //
    final I_M_Product product = MProduct.forS_Resource_ID(cc.getCtx(), cc.getS_Resource_ID(), null);
    final RoutingService routingService =
        RoutingServiceFactory.get().getRoutingService(cc.getAD_Client_ID());
    final BigDecimal qty = routingService.getResourceBaseValue(cc.getS_Resource_ID(), cc);
    for (MAcctSchema as : getAcctSchema(cc)) {
      for (I_M_CostElement element : getCostElements(cc.getCtx())) {
        if (!isActivityControlElement(element)) {
          continue;
        }
        final CostDimension d =
            new CostDimension(
                product,
                as,
                as.getM_CostType_ID(),
                0, // AD_Org_ID,
                0, // M_ASI_ID
                element.getM_CostElement_ID());
        final BigDecimal price =
            getResourceActualCostRate(cc, cc.getS_Resource_ID(), d, cc.get_TrxName());
        BigDecimal costs = price.multiply(qty);
        if (costs.scale() > as.getCostingPrecision())
          costs = costs.setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
        //
        MCostDetail cd =
            new MCostDetail(
                as,
                0, // AD_Org_ID,
                d.getM_Product_ID(),
                0, // M_AttributeSetInstance_ID,
                element.getM_CostElement_ID(),
                costs.negate(),
                qty.negate(),
                "", // Description,
                cc.get_TrxName());
        cd.setPP_Cost_Collector_ID(cc.getPP_Cost_Collector_ID());
        cd.saveEx();
        processCostDetail(cd);
      }
    }
  }

  public void createUsageVariances(MPPCostCollector ccuv) {
    // Apply only for material Usage Variance
    if (!ccuv.isCostCollectorType(X_PP_Cost_Collector.COSTCOLLECTORTYPE_UsegeVariance)) {
      throw new IllegalArgumentException("Cost Collector is not Material Usage Variance");
    }
    //
    final I_M_Product product;
    final BigDecimal qty;
    if (ccuv.getPP_Order_BOMLine_ID() > 0) {
      product = MProduct.get(ccuv.getCtx(), ccuv.getM_Product_ID());
      qty = ccuv.getMovementQty();
    } else {
      product = MProduct.forS_Resource_ID(ccuv.getCtx(), ccuv.getS_Resource_ID(), null);
      final RoutingService routingService =
          RoutingServiceFactory.get().getRoutingService(ccuv.getAD_Client_ID());
      qty = routingService.getResourceBaseValue(ccuv.getS_Resource_ID(), ccuv);
    }
    //
    for (I_C_AcctSchema as : getAcctSchema(ccuv)) {
      for (I_M_CostElement element : getCostElements(ccuv.getCtx())) {
        final BigDecimal price =
            getProductActualCostPrice(ccuv, product, as, element, ccuv.get_TrxName());
        final BigDecimal amt = roundCost(price.multiply(qty), as.getC_AcctSchema_ID());
        //
        // Create / Update Cost Detail
        createVarianceCostDetail(
            ccuv, amt, qty, null, // no original cost detail
            product, as, element);
      } // for ELements
    } // Account Schema
  }

  public void createRateVariances(MPPCostCollector cc) {
    final I_M_Product product;
    if (cc.isCostCollectorType(X_PP_Cost_Collector.COSTCOLLECTORTYPE_ActivityControl)) {
      final I_AD_WF_Node node = cc.getPP_Order_Node().getAD_WF_Node();
      product = MProduct.forS_Resource_ID(cc.getCtx(), node.getS_Resource_ID(), null);
    } else if (cc.isCostCollectorType(X_PP_Cost_Collector.COSTCOLLECTORTYPE_ComponentIssue)) {
      final I_PP_Order_BOMLine bomLine = cc.getPP_Order_BOMLine();
      product = MProduct.get(cc.getCtx(), bomLine.getM_Product_ID());
    } else {
      return;
    }

    I_PP_Cost_Collector ccrv = null; // Cost Collector - Rate Variance
    for (MAcctSchema as : getAcctSchema(cc)) {
      for (I_M_CostElement element : getCostElements(cc.getCtx())) {
        final MCostDetail cd = getCostDetail(cc, element.getM_CostElement_ID());
        if (cd == null) {
          continue;
        }

        //
        final BigDecimal qty = cd.getQty();
        final BigDecimal priceStd = getProductStandardCostPrice(cc, product, as, element);
        final BigDecimal priceActual =
            getProductActualCostPriceOrZero(cc, product, as, element, cc.get_TrxName());
        final BigDecimal amtStd = roundCost(priceStd.multiply(qty), as.getC_AcctSchema_ID());
        final BigDecimal amtActual = roundCost(priceActual.multiply(qty), as.getC_AcctSchema_ID());
        if (amtStd.compareTo(amtActual) == 0) {
          continue;
        }

        //
        if (ccrv == null) {
          ccrv =
              createVarianceCostCollector(cc, X_PP_Cost_Collector.COSTCOLLECTORTYPE_RateVariance);
        }
        //
        createVarianceCostDetail(ccrv, amtActual.negate(), qty.negate(), cd, null, as, element);
        createVarianceCostDetail(ccrv, amtStd, qty, cd, null, as, element);
      }
    }
    //
    if (ccrv != null) {
      Services.get(IDocActionBL.class)
          .processEx(ccrv, DocAction.ACTION_Complete, DocAction.STATUS_Completed);
    }
  }

  public void createMethodVariances(MPPCostCollector cc) {
    if (!cc.isCostCollectorType(X_PP_Cost_Collector.COSTCOLLECTORTYPE_ActivityControl)) return;
    //
    final int std_resource_id = cc.getPP_Order_Node().getAD_WF_Node().getS_Resource_ID();
    final int actual_resource_id = cc.getS_Resource_ID();
    if (std_resource_id == actual_resource_id) {
      return;
    }
    //
    I_PP_Cost_Collector ccmv = null; // Cost Collector - Method Change Variance
    final RoutingService routingService =
        RoutingServiceFactory.get().getRoutingService(cc.getAD_Client_ID());
    for (I_C_AcctSchema as : getAcctSchema(cc)) {
      for (I_M_CostElement element : getCostElements(cc.getCtx())) {
        final I_M_Product resourcePStd =
            MProduct.forS_Resource_ID(cc.getCtx(), std_resource_id, null);
        final I_M_Product resourcePActual =
            MProduct.forS_Resource_ID(cc.getCtx(), actual_resource_id, null);
        final BigDecimal priceStd =
            getProductActualCostPrice(cc, resourcePStd, as, element, cc.get_TrxName());
        final BigDecimal priceActual =
            getProductActualCostPrice(cc, resourcePActual, as, element, cc.get_TrxName());
        if (priceStd.compareTo(priceActual) == 0) {
          continue;
        }
        //
        if (ccmv == null) {
          ccmv =
              createVarianceCostCollector(
                  cc, X_PP_Cost_Collector.COSTCOLLECTORTYPE_MethodChangeVariance);
        }
        //
        final BigDecimal qty = routingService.getResourceBaseValue(cc.getS_Resource_ID(), cc);
        final BigDecimal amtStd = priceStd.multiply(qty);
        final BigDecimal amtActual = priceActual.multiply(qty);
        //
        createVarianceCostDetail(ccmv, amtActual, qty, null, resourcePActual, as, element);
        createVarianceCostDetail(
            ccmv, amtStd.negate(), qty.negate(), null, resourcePStd, as, element);
      }
    }
    //
    if (ccmv != null) {
      Services.get(IDocActionBL.class)
          .processEx(ccmv, DocAction.ACTION_Complete, DocAction.STATUS_Completed);
    }
  }

  public void createReversals(final I_PP_Cost_Collector reversalCostCollector) {
    Check.assumeNotNull(reversalCostCollector, "reversalCostCollector not null");

    //
    // Get the original cost collector
    final I_PP_Cost_Collector costCollector = reversalCostCollector.getReversal();
    Check.assumeNotNull(costCollector, "costCollector not null");

    //
    // Get the original cost details
    final List<I_M_CostDetail> costDetails =
        Services.get(IPPCostCollectorDAO.class).retrieveCostDetails(costCollector);

    for (final I_M_CostDetail cd : costDetails) {
      createReversal(cd, reversalCostCollector);
    }
  }

  private final void createReversal(
      final I_M_CostDetail costDetail, final I_PP_Cost_Collector reversalCostCollector) {
    final I_M_CostDetail costDetailReversal =
        InterfaceWrapperHelper.newInstance(I_M_CostDetail.class, costDetail);
    InterfaceWrapperHelper.copyValues(
        costDetail, costDetailReversal, true); // honorIsCalculated=true
    costDetailReversal.setPP_Cost_Collector_ID(reversalCostCollector.getPP_Cost_Collector_ID());
    costDetailReversal.setQty(costDetail.getQty().negate());
    costDetailReversal.setAmt(costDetail.getAmt().negate());
    costDetailReversal.setDeltaQty(costDetail.getDeltaQty().negate());
    costDetailReversal.setDeltaAmt(costDetail.getDeltaAmt().negate());
    costDetailReversal.setProcessed(false);
    InterfaceWrapperHelper.save(costDetailReversal);

    processCostDetail(costDetailReversal);
  }
}
Esempio n. 24
0
/**
 * Payment Processor Model
 *
 * @author Jorg Janke
 * @version $Id: MPaymentProcessor.java,v 1.3 2006/07/30 00:51:03 jjanke Exp $
 */
public class MPaymentProcessor extends X_C_PaymentProcessor {
  /** */
  private static final long serialVersionUID = 1825454310856682804L;

  public static MPaymentProcessor[] find(
      Properties ctx,
      String tender,
      String CCType,
      int AD_Client_ID,
      int AD_Org_ID,
      int C_Currency_ID,
      BigDecimal Amt,
      String trxName) {
    return find(ctx, tender, CCType, AD_Client_ID, C_Currency_ID, Amt, trxName);
  }

  /**
   * Get BankAccount & PaymentProcessor
   *
   * @param ctx context
   * @param tender optional Tender see TENDER_
   * @param CCType optional CC Type see CC_
   * @param AD_Client_ID Client
   * @param C_Currency_ID Currency (ignored)
   * @param Amt Amount (ignored)
   * @param trxName transaction
   * @return Array of BankAccount[0] & PaymentProcessor[1] or null
   */
  protected static MPaymentProcessor[] find(
      Properties ctx,
      String tender,
      String CCType,
      int AD_Client_ID,
      int C_Currency_ID,
      BigDecimal Amt,
      String trxName) {
    ArrayList<MPaymentProcessor> list = new ArrayList<MPaymentProcessor>();
    StringBuffer sql =
        new StringBuffer(
            "SELECT * "
                + "FROM C_PaymentProcessor "
                + "WHERE AD_Client_ID=? AND IsActive='Y'" //	#1
                + " AND (C_Currency_ID IS NULL OR C_Currency_ID=?)" //	#2
                + " AND (MinimumAmt IS NULL OR MinimumAmt = 0 OR MinimumAmt <= ?)"); //	#3
    if (MPayment.TENDERTYPE_DirectDeposit.equals(tender))
      sql.append(" AND AcceptDirectDeposit='Y'");
    else if (MPayment.TENDERTYPE_DirectDebit.equals(tender))
      sql.append(" AND AcceptDirectDebit='Y'");
    else if (MPayment.TENDERTYPE_Check.equals(tender)) sql.append(" AND AcceptCheck='Y'");
    //  CreditCards
    else if (MPayment.CREDITCARDTYPE_ATM.equals(CCType)) sql.append(" AND AcceptATM='Y'");
    else if (MPayment.CREDITCARDTYPE_Amex.equals(CCType)) sql.append(" AND AcceptAMEX='Y'");
    else if (MPayment.CREDITCARDTYPE_Visa.equals(CCType)) sql.append(" AND AcceptVISA='Y'");
    else if (MPayment.CREDITCARDTYPE_MasterCard.equals(CCType)) sql.append(" AND AcceptMC='Y'");
    else if (MPayment.CREDITCARDTYPE_Diners.equals(CCType)) sql.append(" AND AcceptDiners='Y'");
    else if (MPayment.CREDITCARDTYPE_Discover.equals(CCType)) sql.append(" AND AcceptDiscover='Y'");
    else if (MPayment.CREDITCARDTYPE_PurchaseCard.equals(CCType))
      sql.append(" AND AcceptCORPORATE='Y'");
    //
    try {
      PreparedStatement pstmt = DB.prepareStatement(sql.toString(), trxName);
      pstmt.setInt(1, AD_Client_ID);
      pstmt.setInt(2, C_Currency_ID);
      pstmt.setBigDecimal(3, Amt);
      ResultSet rs = pstmt.executeQuery();
      while (rs.next()) list.add(new MPaymentProcessor(ctx, rs, trxName));
      rs.close();
      pstmt.close();
    } catch (SQLException e) {
      s_log.error("find - " + sql, e);
      return null;
    }
    //
    if (list.size() == 0)
      s_log.warn(
          "find - not found - AD_Client_ID="
              + AD_Client_ID
              + ", C_Currency_ID="
              + C_Currency_ID
              + ", Amt="
              + Amt);
    else
      s_log.debug(
          "find - #"
              + list.size()
              + " - AD_Client_ID="
              + AD_Client_ID
              + ", C_Currency_ID="
              + C_Currency_ID
              + ", Amt="
              + Amt);
    MPaymentProcessor[] retValue = new MPaymentProcessor[list.size()];
    list.toArray(retValue);
    return retValue;
  } //  find

  /** Static Logger */
  private static Logger s_log = LogManager.getLogger(MPaymentProcessor.class);

  /**
   * ************************************************************************ Payment Processor
   * Model
   *
   * @param ctx context
   * @param C_PaymentProcessor_ID payment processor
   * @param trxName transaction
   */
  public MPaymentProcessor(Properties ctx, int C_PaymentProcessor_ID, String trxName) {
    super(ctx, C_PaymentProcessor_ID, trxName);
    if (C_PaymentProcessor_ID == 0) {
      //	setC_BankAccount_ID (0);		//	Parent
      //	setUserID (null);
      //	setPassword (null);
      //	setHostAddress (null);
      //	setHostPort (0);
      setCommission(Env.ZERO);
      setAcceptVisa(false);
      setAcceptMC(false);
      setAcceptAMEX(false);
      setAcceptDiners(false);
      setCostPerTrx(Env.ZERO);
      setAcceptCheck(false);
      setRequireVV(false);
      setAcceptCorporate(false);
      setAcceptDiscover(false);
      setAcceptATM(false);
      setAcceptDirectDeposit(false);
      setAcceptDirectDebit(false);
      //	setName (null);
    }
  } //	MPaymentProcessor

  /**
   * Payment Processor Model
   *
   * @param ctx context
   * @param rs result set
   * @param trxName transaction
   */
  public MPaymentProcessor(Properties ctx, ResultSet rs, String trxName) {
    super(ctx, rs, trxName);
  } //	MPaymentProcessor

  /**
   * String representation
   *
   * @return info
   */
  public String toString() {
    StringBuffer sb =
        new StringBuffer("MPaymentProcessor[")
            .append(get_ID())
            .append("-")
            .append(getName())
            .append("]");
    return sb.toString();
  } //	toString

  /**
   * Does Payment Processor accepts tender / CC
   *
   * @param TenderType tender type
   * @param CreditCardType credit card type
   * @return true if acceptes
   */
  public boolean accepts(String TenderType, String CreditCardType) {
    if ((MPayment.TENDERTYPE_DirectDeposit.equals(TenderType) && isAcceptDirectDeposit())
        || (MPayment.TENDERTYPE_DirectDebit.equals(TenderType) && isAcceptDirectDebit())
        || (MPayment.TENDERTYPE_Check.equals(TenderType) && isAcceptCheck())
        //
        || (MPayment.CREDITCARDTYPE_ATM.equals(CreditCardType) && isAcceptATM())
        || (MPayment.CREDITCARDTYPE_Amex.equals(CreditCardType) && isAcceptAMEX())
        || (MPayment.CREDITCARDTYPE_PurchaseCard.equals(CreditCardType) && isAcceptCorporate())
        || (MPayment.CREDITCARDTYPE_Diners.equals(CreditCardType) && isAcceptDiners())
        || (MPayment.CREDITCARDTYPE_Discover.equals(CreditCardType) && isAcceptDiscover())
        || (MPayment.CREDITCARDTYPE_MasterCard.equals(CreditCardType) && isAcceptMC())
        || (MPayment.CREDITCARDTYPE_Visa.equals(CreditCardType) && isAcceptVisa())) return true;
    return false;
  } //	accepts
} //	MPaymentProcessor
@Immutable
public class C_Order_CounterDocHandler extends CounterDocumentHandlerAdapter {
  private static final transient Logger logger =
      LogManager.getLogger(C_Order_CounterDocHandler.class);

  public static final ICounterDocHandler instance = new C_Order_CounterDocHandler();

  private C_Order_CounterDocHandler() {}

  /**
   * Code taken from {@link org.compiere.model.MOrder}.
   *
   * @return <code>true</code> if <code>Ref_Order_ID() > 0</code>.
   */
  @Override
  public boolean isCounterDocument(final DocAction document) {
    final I_C_Order order = InterfaceWrapperHelper.create(document, I_C_Order.class);
    return order.getRef_Order_ID() > 0;
  }

  /**
   * Creates a counter document for an order. The counter document is also processed, if there is a
   * {@link I_C_DocTypeCounter} with a <code>DocAction</code> configured.
   *
   * <p>This implementation partially uses legacy code. I didn't yet get to refactor/remove/replace
   * it all.
   */
  @Override
  public DocAction createCounterDocument(final DocAction document) {
    final I_C_Order order = InterfaceWrapperHelper.create(document, I_C_Order.class);
    final MOrder orderPO = LegacyAdapters.convertToPO(order);

    final I_C_DocType counterDocType = retrieveCounterDocTypeOrNull(document);

    final I_AD_Org counterOrg = retrieveCounterOrgOrNull(document);

    final de.metas.adempiere.model.I_C_Order counterOrder =
        InterfaceWrapperHelper.newInstance(
            de.metas.adempiere.model.I_C_Order.class, document.getCtx());
    final MOrder counterOrderPO = (MOrder) LegacyAdapters.convertToPO(counterOrder);

    counterOrder.setAD_Org(counterOrg); // 09700

    //
    counterOrder.setC_DocTypeTarget(counterDocType);
    counterOrder.setIsSOTrx(counterDocType.isSOTrx());

    // the new order needs to figure out the pricing by itself
    counterOrder.setM_PricingSystem(null);
    counterOrder.setM_PriceList(null);

    counterOrder.setDateOrdered(order.getDateOrdered());
    counterOrder.setDateAcct(order.getDateAcct());
    counterOrder.setDatePromised(order.getDatePromised());

    counterOrder.setRef_Order_ID(order.getC_Order_ID());

    final I_C_BPartner counterBP = retrieveCounterPartnerOrNull(document);
    counterOrderPO.setBPartner(counterBP);

    final I_M_Warehouse counterWarehouse =
        Services.get(IWarehouseAdvisor.class).evaluateOrderWarehouse(counterOrder);
    counterOrder.setM_Warehouse(counterWarehouse);

    // References (should not be required)
    counterOrder.setSalesRep_ID(order.getSalesRep_ID());
    InterfaceWrapperHelper.save(counterOrder);

    // copy the order lines
    final boolean counter = true;
    final boolean copyASI = true;
    counterOrderPO.copyLinesFrom(orderPO, counter, copyASI);

    // Update copied lines
    final boolean requery = true;
    final MOrderLine[] counterLines = counterOrderPO.getLines(requery, null);
    for (int i = 0; i < counterLines.length; i++) {
      final MOrderLine counterLine = counterLines[i];
      counterLine.setOrder(counterOrderPO); // copies header values (BP, etc.)
      counterLine.setPrice();
      counterLine.setTax();
      InterfaceWrapperHelper.save(counterLine);
    }
    logger.debug(counterOrder.toString());

    // Document Action
    final MDocTypeCounter counterDT =
        MDocTypeCounter.getCounterDocType(document.getCtx(), order.getC_DocType_ID());
    if (counterDT != null) {
      if (counterDT.getDocAction() != null) {
        counterOrder.setDocAction(counterDT.getDocAction());
        Services.get(IDocActionBL.class)
            .processEx(
                counterOrder,
                counterDT.getDocAction(),
                null); // not expecting a particular docStatus (e.g. for prepay orders, it might be
                       // "waiting to payment")
      }
    }
    return counterOrderPO;
  }

  @Override
  public String toString() {
    return "C_Order_CounterDocHandler[]";
  }
}
Esempio n. 26
0
/**
 * Transaction Management. - Create new Transaction by Trx.get(name); - ..transactions.. - commit();
 * ---- start(); ---- commit(); - close();
 *
 * @author Jorg Janke
 * @author Low Heng Sin - added rollback(boolean) and commit(boolean) [20070105] - remove
 *     unnecessary use of savepoint - use UUID for safer transaction name generation
 * @author Teo Sarca, http://www.arhipac.ro
 *     <li>FR [ 2080217 ] Implement TrxRunnable
 *     <li>BF [ 2876927 ] Oracle JDBC driver problem
 *         https://sourceforge.net/tracker/?func=detail&atid=879332&aid=2876927&group_id=176962
 * @author Teo Sarca, [email protected]
 *     <li>BF [ 2849122 ] PO.AfterSave is not rollback on error - add releaseSavepoint method
 *         https://sourceforge.net/tracker/index.php?func=detail&aid=2849122&group_id=176962&atid=879332#
 */
public class Trx extends AbstractTrx implements VetoableChangeListener {
  /**
   * trxName=null marker
   *
   * @deprecated Please use {@link ITrx#TRXNAME_None} instead
   */
  // metas
  @Deprecated public static final String TRXNAME_None = ITrx.TRXNAME_None;

  /**
   * Get Transaction
   *
   * @param trxName trx name
   * @param createNew if false, null is returned if not found
   * @return Transaction or null
   */
  public static synchronized Trx get(String trxName, boolean createNew) {
    final ITrx trx = Services.get(ITrxManager.class).get(trxName, createNew);
    return (Trx) trx;
  } // get

  /**
   * Create unique Transaction Name <b>and instantly create the new trx</b>.
   *
   * @param prefix optional prefix
   * @return unique name
   */
  public static String createTrxName(final String prefix) {
    return Services.get(ITrxManager.class).createTrxName(prefix);
  }

  /**
   * Create unique Transaction Name
   *
   * @param prefix optional prefix
   * @return unique name
   */
  public static String createTrxName(String prefix, final boolean createNew) {
    return Services.get(ITrxManager.class).createTrxName(prefix, createNew);
  } // createTrxName

  /**
   * Create unique Transaction Name
   *
   * @return unique name
   */
  public static String createTrxName() {
    final String prefix = null;
    return Services.get(ITrxManager.class).createTrxName(prefix);
  } // createTrxName

  /**
   * ************************************************************************ Transaction
   * Constructor
   *
   * @param trxName unique name
   */
  public Trx(final ITrxManager trxManager, final String trxName, final boolean autocommit) {
    this(trxManager, trxName, null, autocommit);

    // String threadName = Thread.currentThread().getName(); // for debugging
  } // Trx

  /**
   * Transaction Constructor
   *
   * @param trxName unique name
   * @param con optional connection ( ignore for remote transaction )
   */
  private Trx(
      final ITrxManager trxManager,
      final String trxName,
      final Connection con,
      final boolean autocommit) {
    super(trxManager, trxName, autocommit);

    setConnection(con);
  } // Trx

  /** Static Log */
  // private static final Logger s_log = CLogMgt.getLogger(Trx.class);
  /** Logger */
  private Logger log = LogManager.getLogger(getClass());

  private Connection m_connection = null;

  /**
   * Get Connection
   *
   * @return connection
   */
  public Connection getConnection() {
    log.trace("Active={}, Connection={}", new Object[] {isActive(), m_connection});

    // metas: tsa: begin: Handle the case when the connection was already closed
    // Example: one case when we can get this is when we start a process with a transaction
    // and that process is commiting the transaction somewhere
    if (m_connection != null) {
      boolean isClosed = false;
      try {
        isClosed = m_connection.isClosed();
      } catch (SQLException e) {
        log.error(
            "Error checking if the connection is closed. Assume closed - "
                + e.getLocalizedMessage(),
            e);
        isClosed = true;
      }
      if (isClosed) {
        log.info("Connection is closed. Trying to create another connection.");
        m_connection = null;
      }
    }
    // metas: tsa: end:
    if (m_connection == null) // get new Connection
    {
      setConnection(DB.createConnection(isAutoCommit(), Connection.TRANSACTION_READ_COMMITTED));
    }
    if (!isActive()) {
      start();
    }

    try {
      m_connection.setAutoCommit(isAutoCommit());
    } catch (SQLException e) {
      throw DBException.wrapIfNeeded(e);
    }

    return m_connection;
  } // getConnection

  /**
   * Set Connection
   *
   * @param conn connection
   */
  private void setConnection(Connection conn) {
    if (conn == null) {
      return;
    }

    m_connection = conn;
    log.trace("Connection={}", conn);

    //
    // Configure the connection
    try {
      m_connection.setAutoCommit(false);

      //
      // Get Connection's backend PID (if configured)
      if (getTrxManager().isDebugConnectionBackendId()) {
        final boolean throwDBException = true; // we expect everything to be in order to get a PID
        final String debugConnectionBackendId =
            DB.getDatabase().getConnectionBackendId(m_connection, throwDBException);
        setDebugConnectionBackendId(debugConnectionBackendId);
      }
      // NOTE: works with c3p0 from version 0.9.5 (released on 02.01.2015)
      m_connection.setClientInfo("ApplicationName", "adempiere/" + getTrxName()); // task 08353
    } catch (Exception e) {
      log.error("connection", e);
    }
  } // setConnection

  @Override
  protected boolean isJustStarted() {
    if (!isActive()) {
      return false;
    }

    final boolean justStarted = m_connection == null;
    return justStarted;
  }

  @Override
  protected boolean rollbackNative(boolean throwException) throws SQLException {
    final String trxName = getTrxName();

    //
    // Get current connection
    // NOTE: we are not calling getConnection() because we don't want to acquire a new connection in
    // case it was not used already.
    final Connection connection = m_connection;

    if (connection == null || connection.getAutoCommit()) {
      log.debug(
          "rollbackNative: doing nothing because we have a null or autocommit connection; this={}, connection={}",
          this,
          m_connection);
      // => consider this a success because if there was no open transaction then there is nothing
      // to rollback
      return true;
    }

    // Case: we really have something to rollback (because connection was acquired and used)
    if (connection != null) {
      try {
        connection.rollback();
        log.debug("rollbackNative: OK - {}", trxName);
        return true;
      } catch (SQLException e) {
        log.error("rollbackNative: FAILED - {} (throwException={})", trxName, throwException, e);
        if (throwException) {
          throw e;
        }
        return false;
      }
    }
    //
    // Case: nothing was done on this transaction (because connection is null, so it was not
    // acquired)
    else {
      // => consider this a success because if nothing was done then there is nothing to rollback
      return true;
    }
  } // rollback

  @Override
  protected boolean rollbackNative(ITrxSavepoint savepoint) throws SQLException
        // metas: end: 02367
      {
    if (m_connection == null || m_connection.getAutoCommit()) {
      log.debug(
          "rollbackNative: doing nothing because we have a null or autocomit connection; this={}, connection={}",
          this,
          m_connection);
      return false;
    }

    final String trxName = getTrxName();
    final Savepoint jdbcSavepoint = (Savepoint) savepoint.getNativeSavepoint();

    // local
    try {
      if (m_connection != null) {
        m_connection.rollback(jdbcSavepoint);
        log.debug("**** {}", trxName);
        return true;
      }
    } catch (SQLException e) {
      // Do nothing. The Savepoint might have been discarded because of an intermediate commit or
      // rollback
      // FIXME: track in AbstractTrx which savepoints where implicitly discarded in this way and
      // don't call rollbackNative in such a case.
      // log.error(trxName, e);
      // throw e;
    }
    return false;
  } // rollback

  @Override
  protected boolean commitNative(boolean throwException) throws SQLException {
    if (m_connection == null || m_connection.getAutoCommit()) {
      log.debug(
          "commitNative: doing nothing because we have an autocomit connection; this={}, connection={}",
          this,
          m_connection);
      return true;
    }

    final String trxName = getTrxName();

    //
    // Get current connection
    // NOTE: we are not calling getConnection() because we don't want to acquire a new connection in
    // case it was not used already.
    final Connection connection = this.m_connection;

    //
    // Case: we really have something to commit (because connection was acquired and used)
    if (connection != null) {
      try {
        connection.commit();
        log.debug("commitNative: OK - {}", trxName);
        // m_active = false;
        return true;
      } catch (SQLException e) {
        log.error("commitNative: FAILED - {} (throwException={})", trxName, throwException, e);
        if (throwException) {
          // m_active = false;
          throw e;
        }
        return false;
      }
    }
    //
    // Case: nothing was done on this transaction (because connection is null, so it was not
    // acquired)
    else {
      // => consider this a success because even if nothing was done on this transaction, nothing
      // failed neither
      return true;
    }
  } // commit

  @Override
  protected synchronized boolean closeNative()
        // metas: end: 02367
      {
    if (m_connection == null) {
      return true; // nothing to do
    }

    // Close Connection
    try {
      // NOTE: let the org.compiere.db.DB_PostgreSQL_ConnectionCustomizer to update the
      // ApplicationName because
      // it will be performed in a separate thread so here we don't have to wait.
      // m_connection.setClientInfo("ApplicationName", "adempiere/CLOSED"); // task 08353

      m_connection.close();
    } catch (SQLException e) {
      log.error(getTrxName(), e);
    }
    m_connection = null;
    // m_active = false;
    return true;
  } // close

  /**
   * @param name
   * @return Savepoint
   * @throws SQLException @Deprecated Please use {@link #createTrxSavepoint(String)}
   */
  @Deprecated
  public Savepoint setSavepoint(String name) throws SQLException {
    final ITrxSavepoint savepoint = createTrxSavepoint(name);
    final Savepoint jdbcSavepoint = (Savepoint) savepoint.getNativeSavepoint();
    return jdbcSavepoint;
  }

  @Override
  protected ITrxSavepoint createTrxSavepointNative(final String name) throws Exception {
    if (m_connection == null) {
      getConnection();
    }

    if (m_connection.getAutoCommit()) {
      log.debug(
          "createTrxSavepointNative: returning null because we have an autocomit connection; this={}, connection={}",
          this,
          m_connection);
      return null;
    }

    if (m_connection != null) {
      final Savepoint jdbcSavepoint;
      if (name != null) jdbcSavepoint = m_connection.setSavepoint(name);
      else jdbcSavepoint = m_connection.setSavepoint();

      final JdbcTrxSavepoint savepoint = new JdbcTrxSavepoint(this, jdbcSavepoint);
      return savepoint;
    } else {
      return null;
    }
  }

  @Override
  protected boolean releaseSavepointNative(final ITrxSavepoint savepoint) throws Exception {
    final Savepoint jdbcSavepoint = (Savepoint) savepoint.getNativeSavepoint();

    if (m_connection == null) {
      log.warn(
          "Cannot release savepoint "
              + savepoint
              + " because there is no active connection. Ignoring it.");
      return false;
    }
    if (m_connection.isClosed()) {
      log.warn(
          "Cannot release savepoint "
              + savepoint
              + " because the connection is closed. Ignoring it.");
      return false;
    }

    m_connection.releaseSavepoint(jdbcSavepoint);
    return true;
  }

  /**
   * Vetoable Change. Called from CCache to close connections
   *
   * @param evt event
   * @throws PropertyVetoException
   */
  @Override
  public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
    log.debug(evt.toString());
  } // vetoableChange

  /**
   * @return Trx[]
   * @deprecated Please use {@link ITrxManager#getActiveTransactions()}
   */
  @Deprecated
  public static ITrx[] getActiveTransactions() {
    return Services.get(ITrxManager.class).getActiveTransactions();
  }

  /**
   * Delegates to {@link ITrxManager#run(TrxRunnable)}.
   *
   * @deprecated Please use {@link ITrxManager#run(TrxRunnable)}
   */
  // metas: backward compatibility
  @Deprecated
  public static void run(TrxRunnable r) {
    Services.get(ITrxManager.class).run(r);
  }

  /**
   * Delegates to {@link ITrxManager#run(String, TrxRunnable)}.
   *
   * @deprecated Please use {@link ITrxManager#run(String, TrxRunnable)}
   */
  // metas: backward compatibility
  @Deprecated
  public static void run(String trxName, TrxRunnable r) {
    Services.get(ITrxManager.class).run(trxName, r);
  }

  /**
   * Delegates to {@link ITrxManager#run(String, boolean, TrxRunnable)}.
   *
   * @deprecated Please use {@link ITrxManager#run(String, boolean, TrxRunnable)}
   */
  // metas: added manageTrx parameter
  // metas: backward compatibility
  @Deprecated
  public static void run(String trxName, boolean manageTrx, TrxRunnable r) {
    Services.get(ITrxManager.class).run(trxName, manageTrx, r);
  }
} // Trx
/**
 * Helper class to manage the shipments that might actually be created in the end.
 *
 * @author ts
 */
public class ShipmentCandidates implements IShipmentCandidates {

  static final Logger logger = LogManager.getLogger(ShipmentCandidates.class);

  /** List to store the shipments before it is decided if they are persisted to the database. */
  private final List<MInOut> orderedCandidates = new ArrayList<MInOut>();

  /**
   * Maps inOuts to their inOutLines. Usually this is done using the inOut's ID stored in the
   * inOutLine. But as the inOut hasn't been saved yet, it doesn't have an ID.
   */
  private final Map<MInOut, List<MInOutLine>> inOut2Line = new HashMap<MInOut, List<MInOutLine>>();

  private final Map<MInOutLine, MInOut> line2InOut = new HashMap<MInOutLine, MInOut>();

  private final Map<MInOutLine, StringBuilder> line2StatusInfo =
      new HashMap<MInOutLine, StringBuilder>();

  /**
   * Contains the quantities that would be delivered when postage free amount and order delivery
   * rules where ignored.
   */
  private final Map<Integer, BigDecimal> olId2QtyDeliverable = new HashMap<Integer, BigDecimal>();

  private final Map<MInOutLine, CompleteStatus> line2CompleteStatus =
      new HashMap<MInOutLine, CompleteStatus>();

  private final Map<MInOutLine, PostageFreeStatus> line2PostageFreeStatus =
      new HashMap<MInOutLine, PostageFreeStatus>();

  private final Map<MInOutLine, OverallStatus> line2OverallStatus =
      new HashMap<MInOutLine, OverallStatus>();

  /**
   * Used to keep track of which inOutLine's order has which delivery rule.
   *
   * @see {@link #updateCompleteStatus()}
   */
  private final Map<MOrder, Set<MInOutLine>> order2InOutLine =
      new HashMap<MOrder, Set<MInOutLine>>();

  private final Map<Integer, MOrderLine> orderLineCache;

  private final Map<Integer, MInOutLine> orderLineId2InOutLine = new HashMap<Integer, MInOutLine>();

  private final Map<MInOutLine, I_M_ShipmentSchedule> line2sched =
      new HashMap<MInOutLine, I_M_ShipmentSchedule>();

  /** Used when multiple orders need to be consolidated to one shipment */
  private final Map<ArrayKey, MInOut> shipperKey2Candidate = new HashMap<ArrayKey, MInOut>();

  /** Used when one shipment per order is required */
  private final Map<ArrayKey, MInOut> orderKey2Candidate = new HashMap<ArrayKey, MInOut>();

  /**
   * Use this constructor if you won't need the method {@link
   * ShipmentCandidates#updatePostageFreeStatus(boolean)}.
   */
  public ShipmentCandidates() {
    orderLineCache = null;
  }

  public ShipmentCandidates(final Map<Integer, I_C_OrderLine> myOrderLineCache) {
    orderLineCache = new HashMap<Integer, MOrderLine>();

    for (final Integer orderLineId : myOrderLineCache.keySet()) {
      final I_C_OrderLine orderLine = myOrderLineCache.get(orderLineId);
      final MOrderLine orderLinePO = InterfaceWrapperHelper.getPO(orderLine);
      orderLineCache.put(orderLineId, orderLinePO);
    }
  }

  @Override
  public void addInOut(final I_M_InOut inOut) {
    if (inOut == null) {
      throw new NullPointerException("inOut");
    }
    if (orderedCandidates.contains(inOut)) {
      throw new IllegalArgumentException("Each input may be added only once");
    }

    orderedCandidates.add(getPO(inOut));

    final ArrayKey shipperKey =
        Util.mkKey(inOut.getBPartnerAddress(), inOut.getM_Warehouse_ID(), inOut.getM_Shipper_ID());
    shipperKey2Candidate.put(shipperKey, getPO(inOut));

    final ArrayKey orderKey =
        Util.mkKey(inOut.getBPartnerAddress(), inOut.getM_Warehouse_ID(), inOut.getC_Order_ID());
    orderKey2Candidate.put(orderKey, getPO(inOut));

    final List<MInOutLine> inOutLines = new ArrayList<MInOutLine>();
    inOut2Line.put(getPO(inOut), inOutLines);
  }

  private MInOut getPO(final I_M_InOut inOut) {
    final MInOut inOutPO = InterfaceWrapperHelper.getPO(inOut);
    return inOutPO;
  }

  @Override
  public void addLine(
      final I_M_InOut inOut,
      final I_M_InOutLine inOutLine,
      final I_M_ShipmentSchedule sched,
      final CompleteStatus completeStatus,
      final I_C_Order order) {
    Check.assumeNotNull(inOut, "inOut not null");
    Check.assumeNotNull(inOutLine, "inOutLine not null");
    Check.assumeNotNull(sched, "sched not null");
    Check.assumeNotNull(completeStatus, "completeStatus not null");

    if (!orderedCandidates.contains(inOut)) {
      throw new IllegalStateException(
          "inOut needs to be added using 'addInOut' first"
              + "\n InOut: "
              + inOut
              + "\n orderedCandidates: "
              + orderedCandidates);
    }
    if (inOutLine.getC_OrderLine_ID() < 1) {
      throw new IllegalStateException(
          "inOutLine needs to have a C_OrderLine_ID > 0" + "\n inOutLine: " + inOutLine);
    }
    if (CompleteStatus.INCOMPLETE_ORDER.equals(completeStatus)) {
      throw new IllegalArgumentException(
          "completeStatus may not be "
              + CompleteStatus.INCOMPLETE_ORDER
              + " (this will be figured out later by this class)");
    }

    final MInOutLine inOutLinePO = getPO(inOutLine);
    final MInOut inOutPO = getPO(inOut);

    inOut2Line.get(inOutPO).add(inOutLinePO);
    line2InOut.put(inOutLinePO, inOutPO);

    // store the inoutLine's orderId and status as well to support our
    // later purge
    line2CompleteStatus.put(inOutLinePO, completeStatus);
    line2PostageFreeStatus.put(inOutLinePO, PostageFreeStatus.OK);

    final MOrder orderPO = InterfaceWrapperHelper.getPO(order);

    Set<MInOutLine> inOutLines = order2InOutLine.get(orderPO);
    if (inOutLines == null) {
      inOutLines = new HashSet<MInOutLine>();
      order2InOutLine.put(orderPO, inOutLines);
    }
    inOutLines.add(inOutLinePO);

    //
    // C_OrderLine_ID to M_InOutLine mapping
    {
      final MInOutLine inOutLinePO_Old =
          orderLineId2InOutLine.put(inOutLine.getC_OrderLine_ID(), inOutLinePO);
      if (inOutLinePO_Old != null && inOutLinePO_Old != inOutLinePO) {
        throw new IllegalArgumentException(
            "An InOutLine was already set for order line in orderLineId2InOutLine mapping"
                + "\n InOutLine: "
                + inOutLinePO
                + "\n InOutLine (old): "
                + inOutLinePO_Old
                + "\n orderLineId2InOutLine (after change): "
                + orderLineId2InOutLine);
      }
    }

    line2sched.put(inOutLinePO, sched);
  }

  private MInOutLine getPO(final I_M_InOutLine inOutLine) {
    return InterfaceWrapperHelper.getPO(inOutLine);
  }

  public void removeLine(final I_M_InOutLine inOutLine) {
    if (inOutLine == null) {
      throw new NullPointerException("inOutLine");
    }

    final MInOutLine inOutLinePO = getPO(inOutLine);
    final MInOut inOutPO = line2InOut.remove(inOutLinePO);
    if (inOutPO == null) {
      throw new IllegalStateException("inOutLine wasn't in line2InOut");
    }

    boolean success = inOut2Line.get(inOutPO).remove(inOutLine);
    if (!success) {
      throw new IllegalStateException("inOutLine wasn't in inOut2Line");
    }

    final int orderLineId = inOutLine.getC_OrderLine_ID();
    success = orderLineId2InOutLine.remove(orderLineId) != null;
    if (!success) {
      throw new IllegalStateException(
          "inOutLine wasn't in orderLineId2InOutLine."
              + "\n orderLineId2InOutLine: "
              + orderLineId2InOutLine);
    }

    success = line2sched.remove(inOutLinePO) != null;
    if (!success) {
      throw new IllegalStateException(
          "inOutLine wasn't in line2sched" + "\n line2sched: " + line2sched);
    }
  }

  /** @return a copy of the list of {@link I_M_InOut}s stored in this instance. */
  @Override
  public List<I_M_InOut> getCandidates() {
    final List<I_M_InOut> result = new ArrayList<I_M_InOut>(orderedCandidates.size());
    for (final MInOut inOutPO : orderedCandidates) {
      result.add(InterfaceWrapperHelper.create(inOutPO, I_M_InOut.class));
    }
    return result;
  }

  /** @return the number of {@link I_M_InOut}s this instance contains. */
  @Override
  public int size() {
    return orderedCandidates.size();
  }

  @Override
  public CompleteStatus getCompleteStatus(final I_M_InOutLine inOutLine) {
    if (inOutLine == null) {
      throw new NullPointerException("inOutLine");
    }
    if (!line2CompleteStatus.containsKey(inOutLine)) {
      throw new IllegalArgumentException(
          "inOutLine " + inOutLine + " is not contained in this instance");
    }
    return line2CompleteStatus.get(inOutLine);
  }

  @Override
  public PostageFreeStatus getPostageFreeStatus(final I_M_InOutLine inOutLine) {
    if (inOutLine == null) {
      throw new NullPointerException("inOutLine");
    }
    if (!line2PostageFreeStatus.containsKey(inOutLine)) {
      throw new IllegalArgumentException(
          "inOutLine " + inOutLine + " is not contained in this instance");
    }
    return line2PostageFreeStatus.get(inOutLine);
  }

  @Override
  public BigDecimal getQtyDeliverable(final int orderLineId) {
    if (olId2QtyDeliverable.containsKey(orderLineId)) {
      return olId2QtyDeliverable.get(orderLineId);
    }
    return BigDecimal.ZERO;
  }

  /**
   * @param inOut
   * @return a copy of the list of {@link I_M_InOutLine}s that belong to the given <code>inOut
   *     </code>.
   * @throws NullPointerException if <code>inOut</code> is <code>null</code>
   */
  @Override
  public List<I_M_InOutLine> getLines(final I_M_InOut inOut) {
    if (inOut == null) {
      throw new NullPointerException("inOut");
    }

    final List<I_M_InOutLine> result = new ArrayList<I_M_InOutLine>();
    for (final MInOutLine iol : inOut2Line.get(getPO(inOut))) {
      result.add(InterfaceWrapperHelper.create(iol, I_M_InOutLine.class));
    }
    return result;
  }

  @Override
  public I_M_InOut getInOut(final I_M_InOutLine inOutLine) {
    final MInOut inoutPO = line2InOut.get(getPO(inOutLine));

    if (inoutPO == null) {
      throw new IllegalArgumentException(
          "inOutLine " + inOutLine + " is not contained in this instance");
    }
    return InterfaceWrapperHelper.create(inoutPO, I_M_InOut.class);
  }

  /**
   * @param inOut
   * @return <code>true</code> if the given inOut has no inOutLines
   * @throws NullPointerException if <code>inOut</code> is <code>null</code> or hasn't been added to
   *     this instance using {@link #addInOut(I_M_InOut)} before.
   */
  @Override
  public boolean hasNoLines(final I_M_InOut inOut) {
    if (inOut == null) {
      throw new NullPointerException("inOut");
    }
    return inOut2Line.get(getPO(inOut)).isEmpty();
  }

  /**
   * @param shipperId
   * @param bPartNerLocationId
   * @return the inOut with the given parameters
   * @throws IllegalStateException if no inOut with the given bPartnerLocationId and shipperId has
   *     been added
   */
  @Override
  public I_M_InOut getInOutForShipper(
      final int shipperId, final int warehouseId, final String bPartnerAddress) {
    final ArrayKey key = Util.mkKey(bPartnerAddress, warehouseId, shipperId);

    final MInOut inOutPO = shipperKey2Candidate.get(key);
    return InterfaceWrapperHelper.create(inOutPO, I_M_InOut.class);
  }

  @Override
  public I_M_InOut getInOutForOrderId(
      final int orderId, final int warehouseId, final String bPartnerAddress) {
    final ArrayKey key = Util.mkKey(bPartnerAddress, warehouseId, orderId);

    final MInOut inOutPO = orderKey2Candidate.get(key);
    return InterfaceWrapperHelper.create(inOutPO, I_M_InOut.class);
  }

  @Override
  public I_C_OrderLine getOrderLine(final int olId) {
    return InterfaceWrapperHelper.create(orderLineCache.get(olId), I_C_OrderLine.class);
  }

  /**
   * Updates the {@link CompleteStatus} and the {@link PostageFreeStatus} of the candidates. Also
   * sets the candidates' quantities to zero if this is implied by the status.
   */
  public void afterFirstRun(final boolean ignorePostageFreeAmt) {
    resetQtyDeliverables();
    updateCompleteStatus();
    updatePostageFreeStatus(ignorePostageFreeAmt);
  }

  public I_M_InOutLine getInOutLineFor(final I_C_OrderLine orderLine) {
    final int orderLineId = orderLine.getC_OrderLine_ID();
    return getInOutLineForOrderLine(orderLineId);
  }

  /**
   * Removes first the inoutLines with {@link I_M_InOutLine#getQtyEntered()} = 0 and afterwards the
   * inouts that have no more lines.
   */
  public void purgeLinesEmpty() {
    int rmInOuts = 0, rmInOutLines = 0;
    // removing empty inOutLines
    for (final I_M_InOut inOut : getCandidates()) {
      for (final I_M_InOutLine inOutLine : getLines(inOut)) {
        if (inOutLine.getMovementQty().signum() <= 0) {
          removeLine(inOutLine);
          rmInOutLines++;
        }
      }
    }

    // removing empty inOuts
    for (final I_M_InOut inOut : getCandidates()) {
      if (hasNoLines(inOut)) {
        final ArrayKey key =
            Util.mkKey(
                inOut.getBPartnerAddress(), inOut.getM_Warehouse_ID(), inOut.getM_Shipper_ID());
        shipperKey2Candidate.remove(key);
        inOut2Line.remove(inOut);
        orderedCandidates.remove(inOut);
        rmInOuts++;
      }
    }
    logger.info(
        "Removed " + rmInOuts + " MInOut instances and " + rmInOutLines + " MInOutLine instances");
  }

  private void updateCompleteStatus() {

    for (final MOrder order : order2InOutLine.keySet()) {
      final String deliveryRule = order.getDeliveryRule();

      if (X_C_Order.DELIVERYRULE_CompleteLine.equals(deliveryRule)) {
        // We only deliver if the line qty is same as the qty
        // ordered by the customer
        for (final MInOutLine inOutLinePO : order2InOutLine.get(order)) {
          if (CompleteStatus.INCOMPLETE_LINE.equals(line2CompleteStatus.get(inOutLinePO))) {
            inOutLinePO.setQtyEntered(BigDecimal.ZERO);
            inOutLinePO.setMovementQty(BigDecimal.ZERO);
          }
        }
      } else if (X_C_Order.DELIVERYRULE_CompleteOrder.equals(deliveryRule)) {
        // We only deliver any line at all if all line qtys as the
        // same as the qty ordered by the customer
        boolean removeAll = false;
        for (final MInOutLine inOutLinePO : order2InOutLine.get(order)) {
          if (CompleteStatus.INCOMPLETE_LINE.equals(line2CompleteStatus.get(inOutLinePO))) {
            removeAll = true;
            break;
          }
        }
        if (removeAll) {
          for (final MInOutLine inOutLinePO : order2InOutLine.get(order)) {
            inOutLinePO.setQtyEntered(BigDecimal.ZERO);
            inOutLinePO.setMovementQty(BigDecimal.ZERO);

            // update the status to show why we set the quantity to zero
            line2CompleteStatus.put(inOutLinePO, CompleteStatus.INCOMPLETE_ORDER);
          }
        }
      } else {
        for (MInOutLine inOutLinePO : order2InOutLine.get(order)) {
          // update the status to show that the "completeness" of this inOuLine is irrelevant
          line2CompleteStatus.put(inOutLinePO, CompleteStatus.OK);
        }
      }
    }
  }

  /**
   * Updated all inoutLines' PostageFreeStatus according to the respective bPartner's postage free
   * amount.
   *
   * @param ignorePostageFreeAmt if true, the lines qty is not set to {@link BigDecimal#ZERO}, even
   *     if we are below the customer's postage free amount. However, the status is still set.
   */
  @Override
  public void updatePostageFreeStatus(final boolean ignorePostageFreeAmt) {
    for (final I_M_InOut shipmentCandidate : getCandidates()) {
      final I_C_BPartner bPartner =
          InterfaceWrapperHelper.create(shipmentCandidate.getC_BPartner(), I_C_BPartner.class);

      final BigDecimal postageFree = (BigDecimal) bPartner.getPostageFreeAmt();

      BigDecimal shipmentValue = BigDecimal.ZERO;

      // if ignorePostageFreeAmount is false and a postage free amount
      // is set, we need to check if the value of this shipment is
      // enough to make shipping profitable
      boolean sufficiantValue = postageFree == null;

      if (!sufficiantValue) {
        for (final I_M_InOutLine inOutLine : getLines(shipmentCandidate)) {

          // access orderLineCache instead of loading the line
          // from DB
          final MOrderLine orderLinePO = this.orderLineCache.get(inOutLine.getC_OrderLine_ID());
          final BigDecimal lineValue =
              orderLinePO.getPriceActual().multiply(inOutLine.getQtyEntered());

          shipmentValue = shipmentValue.add(lineValue);

          if (shipmentValue.compareTo(postageFree) >= 0) {
            sufficiantValue = true;
            break;
          }
        }
      }
      for (final I_M_InOutLine inOutLine : getLines(shipmentCandidate)) {
        final PostageFreeStatus status;

        if (sufficiantValue) {
          status = PostageFreeStatus.OK;
        } else {
          if (!ignorePostageFreeAmt) {
            inOutLine.setQtyEntered(BigDecimal.ZERO);
            inOutLine.setMovementQty(BigDecimal.ZERO);
          }

          status = PostageFreeStatus.BELOW_POSTAGEFREE_AMT;

          if (logger.isInfoEnabled()) {
            logger.info(
                "Shipment "
                    + shipmentCandidate.getDocumentNo()
                    + " has an insufficient value of "
                    + shipmentValue.toPlainString()
                    + " (minimum value is "
                    + postageFree.toPlainString()
                    + ")");
          }
        }

        final MInOutLine inOutLinePO = getPO(inOutLine);
        line2PostageFreeStatus.put(inOutLinePO, status);
      }
    }
  }

  private void resetQtyDeliverables() {
    olId2QtyDeliverable.clear();

    for (final MInOutLine inOutLine : line2InOut.keySet()) {
      if (inOutLine.getMovementQty().signum() != 0) {
        olId2QtyDeliverable.put(inOutLine.getC_OrderLine_ID(), inOutLine.getMovementQty());
      }
    }
  }

  @Override
  public void setPostageFreeStatusOK(final I_M_InOut inOut) {
    for (final I_M_InOutLine inOutLine : getLines(inOut)) {
      final MInOutLine inOutLinePO = getPO(inOutLine);
      line2PostageFreeStatus.put(inOutLinePO, PostageFreeStatus.OK);
    }
  }

  @Override
  public I_M_InOutLine getInOutLineForOrderLine(final int orderLineId) {
    final MInOutLine inoutLine = orderLineId2InOutLine.get(orderLineId);
    return InterfaceWrapperHelper.create(inoutLine, I_M_InOutLine.class);
  }

  @Override
  public void setOverallStatus(final I_M_InOutLine inOutLine, final OverallStatus status) {
    final MInOutLine inOutLinePO = getPO(inOutLine);
    line2OverallStatus.put(inOutLinePO, status);
  }

  @Override
  public boolean isLineDiscarded(final I_M_InOutLine inOutLine) {

    return line2OverallStatus.containsKey(inOutLine)
        && OverallStatus.DISCARD.equals(line2OverallStatus.get(inOutLine));
  }

  @Override
  public void addStatusInfo(final I_M_InOutLine inOutLine, final String string) {
    final MInOutLine inOutLinePO = getPO(inOutLine);

    StringBuilder currentInfos = line2StatusInfo.get(inOutLine);

    boolean firstInfo = false;
    if (currentInfos == null) {
      currentInfos = new StringBuilder();
      line2StatusInfo.put(inOutLinePO, currentInfos);
      firstInfo = true;
    }

    if (!firstInfo) {
      currentInfos.append("; ");
    }
    currentInfos.append(string);
  }

  @Override
  public String getStatusInfos(final I_M_InOutLine inOutLine) {

    final StringBuilder statusInfos = line2StatusInfo.get(inOutLine);
    if (statusInfos == null) {
      return "";
    }
    return statusInfos.toString();
  }

  @Override
  public I_M_ShipmentSchedule getShipmentSchedule(final I_M_InOutLine inOutLine) {

    return line2sched.get(inOutLine);
  }

  @Override
  public Set<I_M_ShipmentSchedule> getAllShipmentSchedules() {
    // NOTE: we assume there are not duplicate instances for same M_ShipmentSchedule record.
    // Even if they are, we are returning them "twice" because the caller code will just iterate the
    // result and do the proper updates and we want to have everything updated.

    final Set<I_M_ShipmentSchedule> shipmentSchedules =
        new IdentityHashSet<I_M_ShipmentSchedule>(line2sched.size());
    shipmentSchedules.addAll(line2sched.values());
    return shipmentSchedules;
  }
}