/**
  * Perform process.
  *
  * @return Message (clear text)
  * @throws Exception if not successful
  */
 protected String doIt() throws Exception {
   if (m_C_ProjectLine_ID == 0) throw new IllegalArgumentException("No Project Line");
   MProjectLine projectLine = new MProjectLine(getCtx(), m_C_ProjectLine_ID, get_TrxName());
   log.info("doIt - " + projectLine);
   if (projectLine.getM_Product_ID() == 0) throw new IllegalArgumentException("No Product");
   //
   MProject project = new MProject(getCtx(), projectLine.getC_Project_ID(), get_TrxName());
   if (project.getM_PriceList_ID() == 0) throw new IllegalArgumentException("No PriceList");
   //
   boolean isSOTrx = true;
   MProductPricing pp =
       new MProductPricing(
           projectLine.getM_Product_ID(),
           project.getC_BPartner_ID(),
           projectLine.getPlannedQty(),
           isSOTrx);
   pp.setM_PriceList_ID(project.getM_PriceList_ID());
   pp.setPriceDate(project.getDateContract());
   //
   projectLine.setPlannedPrice(pp.getPriceStd());
   projectLine.setPlannedMarginAmt(pp.getPriceStd().subtract(pp.getPriceLimit()));
   projectLine.saveEx();
   //
   String retValue =
       Msg.getElement(getCtx(), "PriceList")
           + pp.getPriceList()
           + " - "
           + Msg.getElement(getCtx(), "PriceStd")
           + pp.getPriceStd()
           + " - "
           + Msg.getElement(getCtx(), "PriceLimit")
           + pp.getPriceLimit();
   return retValue;
 } //	doIt
  /**
   * Create Facts (the accounting logic) for PJI
   *
   * <pre>
   *  Issue
   *      ProjectWIP      DR
   *      Inventory               CR
   *  </pre>
   *
   * Project Account is either Asset or WIP depending on Project Type
   *
   * @param as accounting schema
   * @return Fact
   */
  public ArrayList<Fact> createFacts(MAcctSchema as) {
    //  create Fact Header
    Fact fact = new Fact(this, as, Fact.POST_Actual);
    setC_Currency_ID(as.getC_Currency_ID());

    MProject project = new MProject(getCtx(), m_issue.getC_Project_ID(), getTrxName());
    String ProjectCategory = project.getProjectCategory();
    MProduct product = MProduct.get(getCtx(), m_issue.getM_Product_ID());

    //  Line pointers
    FactLine dr = null;
    FactLine cr = null;

    //  Issue Cost
    BigDecimal costs = null;
    BigDecimal total = Env.ZERO;
    if (m_issue.getM_InOutLine_ID() != 0) costs = getPOCost(as);
    else if (m_issue.getS_TimeExpenseLine_ID() != 0) costs = getLaborCost(as);
    if (costs == null) // 	standard Product Costs
    {
      for (MCostDetail cost : m_line.getCostDetail(as)) {
        if (!MCostDetail.existsCost(cost)) continue;

        costs = MCostDetail.getTotalCost(cost, as);
        total = total.add(costs);
      }
    }

    if (total == null || total.signum() == 0) {
      p_Error = "Resubmit - No Costs for " + product.getName();
      log.log(Level.WARNING, p_Error);
      return null;
    }

    //  Project         DR
    int acctType = ACCTTYPE_ProjectWIP;
    if (MProject.PROJECTCATEGORY_AssetProject.equals(ProjectCategory))
      acctType = ACCTTYPE_ProjectAsset;
    dr = fact.createLine(m_line, getAccount(acctType, as), as.getC_Currency_ID(), costs, null);
    dr.setQty(m_line.getQty().negate());

    //  Inventory               CR
    acctType = ProductCost.ACCTTYPE_P_Asset;
    if (product.isService()) acctType = ProductCost.ACCTTYPE_P_Expense;
    cr =
        fact.createLine(
            m_line, m_line.getAccount(acctType, as), as.getC_Currency_ID(), null, costs);
    cr.setM_Locator_ID(m_line.getM_Locator_ID());
    cr.setLocationFromLocator(m_line.getM_Locator_ID(), true); // from Loc
    //
    ArrayList<Fact> facts = new ArrayList<Fact>();
    facts.add(fact);
    return facts;
  } //  createFact
  /**
   * Copy Lines From other Project
   *
   * @param project project
   * @return number of lines copied
   */
  public int copyLinesFrom(MProject project) {
    if (isProcessed() || project == null) return 0;
    int count = 0;
    MProjectLine[] fromLines = project.getLines();
    for (int i = 0; i < fromLines.length; i++) {
      MProjectLine line = new MProjectLine(getCtx(), 0, project.get_TrxName());
      PO.copyValues(fromLines[i], line, getAD_Client_ID(), getAD_Org_ID());
      line.setC_Project_ID(getC_Project_ID());
      line.setInvoicedAmt(Env.ZERO);
      line.setInvoicedQty(Env.ZERO);
      line.setC_OrderPO_ID(0);
      line.setC_Order_ID(0);
      line.setProcessed(false);
      if (line.save()) count++;
    }
    if (fromLines.length != count)
      log.log(
          Level.SEVERE, "Lines difference - Project=" + fromLines.length + " <> Saved=" + count);

    return count;
  } //	copyLinesFrom
  /**
   * Copy Phases/Tasks from other Project
   *
   * @param fromProject project
   * @return number of items copied
   */
  public int copyPhasesFrom(MProject fromProject) {
    if (isProcessed() || fromProject == null) return 0;
    int count = 0;
    int taskCount = 0;
    //	Get Phases
    MProjectPhase[] myPhases = getPhases();
    MProjectPhase[] fromPhases = fromProject.getPhases();
    //	Copy Phases
    for (int i = 0; i < fromPhases.length; i++) {
      //	Check if Phase already exists
      int C_Phase_ID = fromPhases[i].getC_Phase_ID();
      boolean exists = false;
      if (C_Phase_ID == 0) exists = false;
      else {
        for (int ii = 0; ii < myPhases.length; ii++) {
          if (myPhases[ii].getC_Phase_ID() == C_Phase_ID) {
            exists = true;
            break;
          }
        }
      }
      //	Phase exist
      if (exists) log.info("Phase already exists here, ignored - " + fromPhases[i]);
      else {
        MProjectPhase toPhase = new MProjectPhase(getCtx(), 0, get_TrxName());
        PO.copyValues(fromPhases[i], toPhase, getAD_Client_ID(), getAD_Org_ID());
        toPhase.setC_Project_ID(getC_Project_ID());
        toPhase.setC_Order_ID(0);
        toPhase.setIsComplete(false);
        if (toPhase.save()) {
          count++;
          taskCount += toPhase.copyTasksFrom(fromPhases[i]);
        }
      }
    }
    if (fromPhases.length != count)
      log.warning("Count difference - Project=" + fromPhases.length + " <> Saved=" + count);

    return count + taskCount;
  } //	copyPhasesFrom
  /**
   * Create new Project by copying
   *
   * @param ctx context
   * @param C_Project_ID project
   * @param dateDoc date of the document date
   * @param trxName transaction
   * @return Project
   */
  public static MProject copyFrom(
      Properties ctx, int C_Project_ID, Timestamp dateDoc, String trxName) {
    MProject from = new MProject(ctx, C_Project_ID, trxName);
    if (from.getC_Project_ID() == 0)
      throw new IllegalArgumentException("From Project not found C_Project_ID=" + C_Project_ID);
    //
    MProject to = new MProject(ctx, 0, trxName);
    PO.copyValues(from, to, from.getAD_Client_ID(), from.getAD_Org_ID());
    to.set_ValueNoCheck("C_Project_ID", I_ZERO);
    //	Set Value with Time
    String Value = to.getValue() + " ";
    String Time = dateDoc.toString();
    int length = Value.length() + Time.length();
    if (length <= 40) Value += Time;
    else Value += Time.substring(length - 40);
    to.setValue(Value);
    to.setInvoicedAmt(Env.ZERO);
    to.setProjectBalanceAmt(Env.ZERO);
    to.setProcessed(false);
    //
    if (!to.save()) throw new IllegalStateException("Could not create Project");

    if (to.copyDetailsFrom(from) == 0)
      throw new IllegalStateException("Could not create Project Details");

    return to;
  } //	copyFrom
 /**
  * Get DocumentNo
  *
  * @return document no
  */
 public String getDocumentNo() {
   MProject p = m_issue.getParent();
   if (p != null) return p.getValue() + " #" + m_issue.getLine();
   return "(" + m_issue.get_ID() + ")";
 } //	getDocumentNo