/**
  * ************************************************************************ Start Java Process
  * Class. instanciate the class implementing the interface ProcessCall. The class can be a
  * Server/Client class (when in Package org compiere.process or org.compiere.model) or a client
  * only class (e.g. in org.compiere.report)
  *
  * @return true if success
  */
 private boolean startProcess() {
   log.fine(m_pi.toString());
   boolean started = false;
   if (DB.isRemoteProcess()) {
     Server server = CConnection.get().getServer();
     try {
       if (server != null) { // 	See ServerBean
         m_pi = server.process(m_wscctx, m_pi);
         log.finest("server => " + m_pi);
         started = true;
       }
     } catch (UndeclaredThrowableException ex) {
       Throwable cause = ex.getCause();
       if (cause != null) {
         if (cause instanceof InvalidClassException)
           log.log(
               Level.SEVERE, "Version Server <> Client: " + cause.toString() + " - " + m_pi, ex);
         else
           log.log(Level.SEVERE, "AppsServer error(1b): " + cause.toString() + " - " + m_pi, ex);
       } else log.log(Level.SEVERE, " AppsServer error(1) - " + m_pi, ex);
       started = false;
     } catch (Exception ex) {
       Throwable cause = ex.getCause();
       if (cause == null) cause = ex;
       log.log(Level.SEVERE, "AppsServer error - " + m_pi, cause);
       started = false;
     }
   }
   //	Run locally
   if (!started && !m_IsServerProcess) {
     ProcessCall myObject = null;
     try {
       Class myClass = Class.forName(m_pi.getClassName());
       myObject = (ProcessCall) myClass.newInstance();
       if (myObject == null) m_pi.setSummary("No Instance for " + m_pi.getClassName(), true);
       else myObject.startProcess(m_wscctx, m_pi, m_trx);
       if (m_trx != null) {
         m_trx.commit();
         m_trx.close();
       }
     } catch (Exception e) {
       if (m_trx != null) {
         m_trx.rollback();
         m_trx.close();
       }
       m_pi.setSummary("Error starting Class " + m_pi.getClassName(), true);
       log.log(Level.SEVERE, m_pi.getClassName(), e);
     }
   }
   return !m_pi.isError();
 } //  startProcess
  public ActionForward createBlackListed(
      ActionMapping mapping,
      ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response)
      throws OperationException, ApplicationException {
    ActionForward fwd = init(mapping, form, request, response);
    if (fwd != null) return fwd;

    Properties ctx = TmkJSPEnv.getCtx(request);
    DefaultForm df = (DefaultForm) form;
    BlackListedBean bean = (BlackListedBean) df.getBean();

    Trx trx = Trx.get(TrxPrefix.getPrefix(), true);

    try {
      trx.start();
      BlackListedManager.createBlackListed(ctx, bean, trx.getTrxName());
      trx.commit();
    } catch (OperationException ex) {
      trx.rollback();
      throw ex;

    } finally {
      trx.close();
    }
    request.setAttribute(Constants.BLACKLISTED_DETAILS, bean);
    return mapping.findForward(CREATE_BLACKLISTED);
  }
  public ActionForward editCheque(
      ActionMapping mapping,
      ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response)
      throws ApplicationException, OperationException {

    ActionForward fwd = init(mapping, form, request, response);
    if (fwd != null) return fwd;
    Properties ctx = TmkJSPEnv.getCtx(request);
    BlackListForm f = (BlackListForm) form;
    BlackListedBean bean = (BlackListedBean) f.getBean();

    Integer blacklistedID = bean.getBlackListedId();
    if (blacklistedID == null) {
      throw new OperationException(
          "Cannot load Cheque details. Cause BlacklistedId cannot be null!");
    }

    Trx trx = Trx.get(TrxPrefix.getPrefix(), true);
    trx.start();

    try {
      BlackListedManager.editBlackListed(ctx, bean, trx.getTrxName());
      trx.commit();
    } catch (OperationException ex) {
      trx.rollback();
      throw ex;
    } finally {
      trx.close();
    }
    request.getSession().setAttribute(Constants.BLACKLISTED_DETAILS, bean);
    return mapping.findForward(EDIT_CHEQUE);
  }
  /**
   * Notify users
   *
   * @param users AD_User_ID list
   * @param subject email subject
   * @param message email message
   * @param attachments
   * @return how many email were sent
   */
  private int notifyUsers(
      Collection<Integer> users, String subject, String message, Collection<File> attachments) {
    int countMail = 0;
    for (int user_id : users) {
      MUser user = MUser.get(getCtx(), user_id);
      if (user.isNotificationEMail()) {
        if (m_client.sendEMailAttachments(user_id, subject, message, attachments)) {
          countMail++;
        }
      }

      if (user.isNotificationNote()) {
        Trx trx = null;
        try {
          trx = Trx.get(Trx.createTrxName("AP_NU"), true);
          // Notice
          int AD_Message_ID = 52244; /* TODO - Hardcoded message=notes */
          MNote note = new MNote(getCtx(), AD_Message_ID, user_id, trx.getTrxName());
          note.setClientOrg(m_model.getAD_Client_ID(), m_model.getAD_Org_ID());
          note.setTextMsg(message);
          note.saveEx();
          // Attachment
          MAttachment attachment =
              new MAttachment(getCtx(), MNote.Table_ID, note.getAD_Note_ID(), trx.getTrxName());
          for (File f : attachments) {
            attachment.addEntry(f);
          }
          attachment.setTextMsg(message);
          attachment.saveEx();
          countMail++;
          trx.commit();
        } catch (Throwable e) {
          if (trx != null) trx.rollback();
        } finally {
          if (trx != null) trx.close();
        }
      }
    }
    return countMail;
  }
  public ActionForward deactivateCheque(
      ActionMapping mapping,
      ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response)
      throws ApplicationException, OperationException {

    ActionForward fwd = init(mapping, form, request, response);
    if (fwd != null) return fwd;
    Properties ctx = TmkJSPEnv.getCtx(request);
    BlackListForm f = (BlackListForm) form;
    f.validate(mapping, request);

    BlackListedBean bean = (BlackListedBean) f.getBean();
    Integer blackListedId = bean.getBlackListedId();

    if (blackListedId == null) {
      throw new OperationException(
          "Cannot deactivate Blacklisted Cheques. Cause BlackListed id cannot be null!");
    }

    Trx trx = Trx.get(TrxPrefix.getPrefix(), true);

    try {
      trx.start();
      BlackListedManager.deactivateBListedCheque(ctx, blackListedId.intValue(), trx.getTrxName());
      trx.commit();
    } catch (OperationException e) {
      trx.rollback();
      postGlobalError("error.deactivate.customer", request);
      mapping.getInputForward();
    } finally {
      trx.close();
    }

    return mapping.findForward(DEACTIVATE_CHEQUE);
  }
  /**
   * Process Alert
   *
   * @param alert alert
   * @return true if processed
   */
  private boolean processAlert(MAlert alert) {
    if (!alert.isValid()) return false;
    log.info("" + alert);

    StringBuffer message = new StringBuffer(alert.getAlertMessage()).append(Env.NL);
    //
    boolean valid = true;
    boolean processed = false;
    ArrayList<File> attachments = new ArrayList<File>();
    MAlertRule[] rules = alert.getRules(false);
    for (int i = 0; i < rules.length; i++) {
      if (i > 0) message.append(Env.NL);
      String trxName = null; // 	assume r/o

      MAlertRule rule = rules[i];
      if (!rule.isValid()) continue;
      log.fine("" + rule);

      //	Pre
      String sql = rule.getPreProcessing();
      if (sql != null && sql.length() > 0) {
        int no = DB.executeUpdate(sql, false, trxName);
        if (no == -1) {
          ValueNamePair error = CLogger.retrieveError();
          rule.setErrorMsg("Pre=" + error.getName());
          m_errors.append("Pre=" + error.getName());
          rule.setIsValid(false);
          rule.save();
          valid = false;
          break;
        }
      } //	Pre

      //	The processing
      sql = rule.getSql(true);
      try {
        String text = null;
        if (MSysConfig.getBooleanValue(
            "ALERT_SEND_ATTACHMENT_AS_XLS", true, Env.getAD_Client_ID(getCtx())))
          text = getExcelReport(rule, sql, trxName, attachments);
        else text = getPlainTextReport(rule, sql, trxName, attachments);
        if (text != null && text.length() > 0) {
          message.append(text);
          processed = true;
        }
      } catch (Exception e) {
        rule.setErrorMsg("Select=" + e.getLocalizedMessage());
        m_errors.append("Select=" + e.getLocalizedMessage());
        rule.setIsValid(false);
        rule.save();
        valid = false;
        break;
      }

      //	Post
      sql = rule.getPostProcessing();
      if (sql != null && sql.length() > 0) {
        int no = DB.executeUpdate(sql, false, trxName);
        if (no == -1) {
          ValueNamePair error = CLogger.retrieveError();
          rule.setErrorMsg("Post=" + error.getName());
          m_errors.append("Post=" + error.getName());
          rule.setIsValid(false);
          rule.save();
          valid = false;
          break;
        }
      } //	Post

      /** Trx */
      if (trxName != null) {
        Trx trx = Trx.get(trxName, false);
        if (trx != null) {
          trx.commit();
          trx.close();
        }
      }
    } //	 for all rules

    //	Update header if error
    if (!valid) {
      alert.setIsValid(false);
      alert.save();
      return false;
    }

    //	Nothing to report
    if (!processed) {
      m_summary.append(alert.getName()).append("=No Result - ");
      return true;
    }

    //
    // Report footer - Date Generated
    DateFormat df = DisplayType.getDateFormat(DisplayType.DateTime);
    message.append("\n\n");
    message
        .append(Msg.translate(getCtx(), "Date"))
        .append(" : ")
        .append(df.format(new Timestamp(System.currentTimeMillis())));

    Collection<Integer> users = alert.getRecipientUsers();
    int countMail = notifyUsers(users, alert.getAlertSubject(), message.toString(), attachments);

    m_summary.append(alert.getName()).append(" (EMails+Notes=").append(countMail).append(") - ");
    return valid;
  } //	processAlert
  /** @param e */
  public void valueChange(ValueChangeEvent e) {
    if (gridTab.isProcessed()) //  only active records
    {
      Object source = e.getSource();
      if (source instanceof WEditor) {
        // Elaine 2009/05/06
        WEditor editor = (WEditor) source;
        GridField gridField = editor.getGridField();

        if (gridField != null) {
          if (!gridField.isEditable(true)) {
            logger.config("(" + gridTab.toString() + ") " + e.getPropertyName());
            return;
          }
        } else if (!editor.isReadWrite()) {
          logger.config("(" + gridTab.toString() + ") " + e.getPropertyName());
          return;
        }
      } else {
        logger.config("(" + gridTab.toString() + ") " + e.getPropertyName());
        return;
      }
    } //  processed
    logger.config(
        "("
            + gridTab.toString()
            + ") "
            + e.getPropertyName()
            + "="
            + e.getNewValue()
            + " ("
            + e.getOldValue()
            + ") "
            + (e.getOldValue() == null ? "" : e.getOldValue().getClass().getName()));

    //  Get Row/Col Info
    GridTable mTable = gridTab.getTableModel();
    int row = gridTab.getCurrentRow();
    int col = mTable.findColumn(e.getPropertyName());
    //
    if (e.getNewValue() == null
        && e.getOldValue() != null
        && e.getOldValue().toString().length() > 0) //  some editors return "" instead of null
      //        	  this is the original code from GridController, don't know what it does there but
      // it breaks ignore button for web ui
      //            mTable.setChanged (true);
      mTable.setValueAt(e.getNewValue(), row, col);
    else {

      Object newValue = e.getNewValue();
      Integer newValues[] = null;

      if (newValue instanceof Integer[]) {
        newValues = ((Integer[]) newValue);
        newValue = newValues[0];

        if (newValues.length > 1) {
          Integer valuesCopy[] = new Integer[newValues.length - 1];
          System.arraycopy(newValues, 1, valuesCopy, 0, valuesCopy.length);
          newValues = valuesCopy;
        } else {
          newValues = null;
        }
      } else if (newValue instanceof Object[]) {
        logger.severe("Multiple values can only be processed for IDs (Integer)");
        throw new IllegalArgumentException(
            "Multiple Selection values not available for this field. " + e.getPropertyName());
      }

      mTable.setValueAt(newValue, row, col);
      //  Force Callout
      if (e.getPropertyName().equals("S_ResourceAssignment_ID")) {
        GridField mField = gridTab.getField(col);
        if (mField != null && mField.getCallout().length() > 0) {
          gridTab.processFieldChange(mField); //  Dependencies & Callout
        }
      }

      if (newValues != null && newValues.length > 0) {
        // Save data, since record need to be used for generating clones.
        if (!gridTab.dataSave(false)) {
          throw new AdempiereException("SaveError");
        }

        // Retrieve the current record ID
        int recordId = gridTab.getKeyID(gridTab.getCurrentRow());

        Trx trx = Trx.get(Trx.createTrxName(), true);
        trx.start();
        try {
          saveMultipleRecords(
              Env.getCtx(),
              gridTab.getTableName(),
              e.getPropertyName(),
              recordId,
              newValues,
              trx.getTrxName());
          trx.commit();
          gridTab.dataRefreshAll();
        } catch (Exception ex) {
          trx.rollback();
          logger.severe(ex.getMessage());
          throw new AdempiereException("SaveError");
        } finally {
          trx.close();
        }
      }
    }
  } // ValueChange
  /** Save */
  public void onOK() {
    log.config("Activity=" + m_activity);
    if (m_activity == null) {
      Clients.showBusy(null, false);
      return;
    }
    int AD_User_ID = Env.getAD_User_ID(Env.getCtx());
    String textMsg = fTextMsg.getValue();
    //
    MWFNode node = m_activity.getNode();

    Object forward = null; // fForward.getValue();

    // ensure activity is ran within a transaction - [ 1953628 ]
    Trx trx = null;
    try {
      trx = Trx.get(Trx.createTrxName("FWFA"), true);
      m_activity.set_TrxName(trx.getTrxName());

      if (forward != null) {
        log.config("Forward to " + forward);
        int fw = ((Integer) forward).intValue();
        if (fw == AD_User_ID || fw == 0) {
          log.log(Level.SEVERE, "Forward User="******"CannotForward");
          trx.rollback();
          trx.close();
          return;
        }
      }
      //	User Choice - Answer
      else if (MWFNode.ACTION_UserChoice.equals(node.getAction())) {
        if (m_column == null) m_column = node.getColumn();
        //	Do we have an answer?
        int dt = m_column.getAD_Reference_ID();
        String value = fAnswerText.getText();
        if (dt == DisplayType.YesNo || dt == DisplayType.List) {
          ListItem li = fAnswerList.getSelectedItem();
          if (li != null) value = li.getValue().toString();
        }
        if (value == null || value.length() == 0) {
          FDialog.error(m_WindowNo, this, "FillMandatory", Msg.getMsg(Env.getCtx(), "Answer"));
          trx.rollback();
          trx.close();
          return;
        }
        //
        log.config("Answer=" + value + " - " + textMsg);
        try {
          m_activity.setUserChoice(AD_User_ID, value, dt, textMsg);
        } catch (Exception e) {
          log.log(Level.SEVERE, node.getName(), e);
          FDialog.error(m_WindowNo, this, "Error", e.toString());
          trx.rollback();
          trx.close();
          return;
        }
      }
      //	User Action
      else {
        log.config("Action=" + node.getAction() + " - " + textMsg);
        try {
          // ensure activity is ran within a transaction
          m_activity.setUserConfirmation(AD_User_ID, textMsg);
        } catch (Exception e) {
          log.log(Level.SEVERE, node.getName(), e);
          FDialog.error(m_WindowNo, this, "Error", e.toString());
          trx.rollback();
          trx.close();
          return;
        }
      }

      trx.commit();
    } finally {
      Clients.showBusy(null, false);
      if (trx != null) trx.close();
    }

    //	Next
    loadActivities();
    display(-1);
  } //	onOK