/**
  * Only build account details for corporate card accounting lines
  *
  * @see
  *     org.kuali.kfs.module.tem.document.service.TravelPaymentsHelperService#buildGenericPaymentAccountDetails(java.util.List)
  */
 protected List<PaymentAccountDetail> buildPaymentAccountDetails(
     List<? extends AccountingLine> accountingLines) {
   List<PaymentAccountDetail> details = new ArrayList<PaymentAccountDetail>();
   for (AccountingLine al : accountingLines) {
     final TemSourceAccountingLine accountingLine = (TemSourceAccountingLine) al;
     if (StringUtils.equals(accountingLine.getCardType(), TemConstants.TRAVEL_TYPE_CORP)) {
       PaymentAccountDetail pad = new PaymentAccountDetail();
       pad.setFinChartCode(accountingLine.getChartOfAccountsCode());
       pad.setAccountNbr(accountingLine.getAccountNumber());
       if (!StringUtils.isBlank(accountingLine.getSubAccountNumber())) {
         pad.setSubAccountNbr(accountingLine.getSubAccountNumber());
       } else {
         pad.setSubAccountNbr(KFSConstants.getDashSubAccountNumber());
       }
       pad.setFinObjectCode(accountingLine.getFinancialObjectCode());
       if (!StringUtils.isBlank(accountingLine.getFinancialSubObjectCode())) {
         pad.setFinSubObjectCode(accountingLine.getFinancialSubObjectCode());
       } else {
         pad.setFinSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
       }
       if (!StringUtils.isBlank(accountingLine.getOrganizationReferenceId())) {
         pad.setOrgReferenceId(accountingLine.getOrganizationReferenceId());
       }
       if (!StringUtils.isBlank(accountingLine.getProjectCode())) {
         pad.setProjectCode(accountingLine.getProjectCode());
       } else {
         pad.setProjectCode(KFSConstants.getDashProjectCode());
       }
       pad.setAccountNetAmount(accountingLine.getAmount());
       details.add(pad);
     }
   }
   return details;
 }
 /**
  * The sum of all CORP accounting lines on the document
  *
  * @see
  *     org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService#getPaymentAmount(org.kuali.kfs.sys.document.PaymentSource)
  */
 @Override
 public KualiDecimal getPaymentAmount(TEMReimbursementDocument document) {
   KualiDecimal amount = KualiDecimal.ZERO;
   for (TemSourceAccountingLine line :
       (List<TemSourceAccountingLine>) document.getSourceAccountingLines()) {
     if (StringUtils.equals(line.getCardType(), TemConstants.TRAVEL_TYPE_CORP)) {
       amount = amount.add(line.getAmount());
     }
   }
   return amount;
 }
  /**
   * @see
   *     org.kuali.kfs.sys.document.validation.Validation#validate(org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent)
   */
  @SuppressWarnings("rawtypes")
  @Override
  public boolean validate(AttributedDocumentEvent event) {
    final Person currentUser = GlobalVariables.getUserSession().getPerson();
    TemSourceAccountingLine line = null;
    if (event instanceof UpdateAccountingLineEvent) {
      line =
          (TemSourceAccountingLine) ((UpdateAccountingLineEvent) event).getUpdatedAccountingLine();
    } else {
      line = (TemSourceAccountingLine) ((AccountingLineEvent) event).getAccountingLine();
    }
    List<String> holdErrors = new ArrayList<String>();
    holdErrors.addAll(GlobalVariables.getMessageMap().getErrorPath());
    GlobalVariables.getMessageMap().clearErrorPath();
    TravelDocument travelDocument = (TravelDocument) event.getDocument();

    final boolean canUpdate =
        isAtTravelNode(event.getDocument().getDocumentHeader().getWorkflowDocument())
            || isAdvancePaymentMethodException(
                event.getDocument(),
                line); // Are we at the travel node?  If so, there's a chance that accounting lines
                       // changed; if they did, that
    // was a permission granted to the travel manager so we should allow it.  Also, if we're at
    // PaymentMethod and the line is an advance accounting line, that's allowed to

    boolean valid = true;
    String errorPath = TemPropertyConstants.NEW_SOURCE_ACCTG_LINE;
    for (TemSourceAccountingLine sourceLine :
        (List<TemSourceAccountingLine>) travelDocument.getSourceAccountingLines()) {
      if (line.equals(sourceLine)) {
        errorPath =
            "document."
                + TemPropertyConstants.SOURCE_ACCOUNTING_LINE
                + "["
                + travelDocument.getSourceAccountingLines().indexOf(line)
                + "]";
        break;
      }
    }

    // Test added accounting lines for null values and if there is an access change.
    valid = getTravelDocumentService().validateSourceAccountingLines(travelDocument, false);

    if ((!travelDocument
            .getAppDocStatus()
            .equalsIgnoreCase(TemConstants.TRAVEL_DOC_APP_DOC_STATUS_INIT))
        && (!travelDocument
            .getAppDocStatus()
            .equalsIgnoreCase(TemConstants.TravelAuthorizationStatusCodeKeys.IN_PROCESS))
        && (!travelDocument
            .getAppDocStatus()
            .equalsIgnoreCase(TemConstants.TravelAuthorizationStatusCodeKeys.CHANGE_IN_PROCESS))) {
      if (!line.getAccount()
              .getAccountFiscalOfficerUser()
              .getPrincipalId()
              .equals(currentUser.getPrincipalId())
          && !canUpdate) {
        GlobalVariables.getMessageMap()
            .putError(
                KFSPropertyConstants.ACCOUNT_NUMBER,
                TemKeyConstants.ERROR_TA_FISCAL_OFFICER_ACCOUNT,
                line.getAccountNumber());
        return false;
      }
    }
    GlobalVariables.getMessageMap().addToErrorPath(errorPath);

    // skip accounting line validation for TA
    if (!(event.getDocument() instanceof TravelAuthorizationDocument)) {
      if (ObjectUtils.isNotNull(line.getObjectTypeCode())) {
        // check to make sure they're the same
        List<AccountingDistribution> list =
            getAccountingDistributionService().buildDistributionFrom(travelDocument);
        List<AccountingLineDistributionKey> distributionList =
            new ArrayList<AccountingLineDistributionKey>();
        List<String> expectedObjectCodes = new ArrayList<String>();
        for (AccountingDistribution dist : list) {
          distributionList.add(
              new AccountingLineDistributionKey(dist.getObjectCode(), dist.getCardType()));
          expectedObjectCodes.add(dist.getObjectCode());
        }
        final String expectedObjectCodesString = StringUtils.join(expectedObjectCodes, ", ");

        if (!distributionList.contains(
            new AccountingLineDistributionKey(line.getFinancialObjectCode(), line.getCardType()))) {
          GlobalVariables.getMessageMap()
              .putError(
                  TravelAuthorizationFields.FIN_OBJ_CD,
                  TemKeyConstants.ERROR_TEM_ACCOUNTING_LINES_OBJECT_CODE_CARD_TYPE,
                  line.getFinancialObjectCode(),
                  line.getCardType(),
                  expectedObjectCodesString);
          valid &= false;
        }
      }
    }

    if (line.getAmount().isLessEqual(KualiDecimal.ZERO) && !travelDocument.getBlanketTravel()) {
      GlobalVariables.getMessageMap()
          .putError(
              KFSPropertyConstants.AMOUNT,
              KFSKeyConstants.ERROR_CUSTOM,
              "Amount must be greater than zero.");
      valid &= false;
    }

    if (valid) {
      // Fly America validation
      TravelDocument document = (TravelDocument) event.getDocument();
      List<TemExpense> allExpenses = new ArrayList<TemExpense>();
      allExpenses.addAll(document.getImportedExpenses());
      allExpenses.addAll(document.getActualExpenses());
      if (allExpenses.size() > 0) {
        boolean hasAttachment = false;
        boolean showFlyAmerica = false;
        for (Note note : document.getNotes()) {
          if (note.getAttachment() != null) {
            hasAttachment = true;
            break;
          }
        }
        boolean isCGEnabled =
            getParameterService()
                .getParameterValueAsBoolean(
                    KFSConstants.CoreModuleNamespaces.CHART,
                    KFSConstants.RouteLevelNames.ACCOUNT,
                    KFSConstants.ChartApcParms.ACCOUNT_FUND_GROUP_DENOTES_CG);
        if (isCGEnabled) {
          for (TemExpense expense : allExpenses) {
            if (expense.getExpenseTypeCode().equals(TemConstants.ExpenseTypes.AIRFARE)) {
              Map<String, Object> fieldValues = new HashMap<String, Object>();
              fieldValues.put(KRADPropertyConstants.CODE, TemConstants.ExpenseTypes.AIRFARE);
              fieldValues.put(KRADPropertyConstants.NAME, expense.getTravelCompanyCodeName());
              TravelCompanyCode travelCompany =
                  getBusinessObjectService().findByPrimaryKey(TravelCompanyCode.class, fieldValues);
              if (travelCompany != null && travelCompany.isForeignCompany()) {
                String financialObjectCode =
                    expense.getExpenseTypeObjectCode() != null
                        ? expense.getExpenseTypeObjectCode().getFinancialObjectCode()
                        : null;
                if (travelDocument instanceof TravelAuthorizationDocument
                    && expense instanceof ActualExpense) {
                  if (document.getTripType() != null) {
                    financialObjectCode = document.getTripType().getEncumbranceObjCode();
                  }
                }
                if (financialObjectCode != null
                    && financialObjectCode.equals(line.getFinancialObjectCode())) {
                  String cg =
                      getParameterService()
                          .getParameterValueAsString(
                              KFSConstants.CoreModuleNamespaces.CHART,
                              KFSConstants.RouteLevelNames.ACCOUNT,
                              KFSConstants.ChartApcParms.ACCOUNT_CG_DENOTING_VALUE);
                  if (line.getAccount() == null) {
                    line.refreshReferenceObject(KFSPropertyConstants.ACCOUNT);
                  }
                  if (line.getAccount().getSubFundGroup() == null) {
                    line.refreshReferenceObject(KFSPropertyConstants.SUB_FUND_GROUP);
                  }
                  if (line.getAccount().getSubFundGroup().getFundGroupCode().equals(cg)) {
                    showFlyAmerica = true;
                  }
                }
              }
            }
          }
        }

        // Fly America error has been triggered, determine what accounting line to show it on.
        if (showFlyAmerica && !hasAttachment) {
          boolean newLine = true;
          for (TemSourceAccountingLine sourceLine :
              (List<TemSourceAccountingLine>) travelDocument.getSourceAccountingLines()) {
            if (line.equals(sourceLine)) {
              newLine = false;
            }
          }
          // if line is a new accounting line or a current one being saved/submitted in the
          // document.
          // figure out where the new accounting line will be added and set the error to that line #
          if (newLine) {
            GlobalVariables.getMessageMap().clearErrorPath();

            int newIndex =
                document
                        .getSourceAccountingLine(document.getSourceAccountingLines().size() - 1)
                        .getSequenceNumber()
                    + 1;
            errorPath =
                "document." + TemPropertyConstants.SOURCE_ACCOUNTING_LINE + "[" + newIndex + "]";
            GlobalVariables.getMessageMap().addToErrorPath(errorPath);
          }

          GlobalVariables.getMessageMap()
              .putError(
                  KFSPropertyConstants.ACCOUNT_NUMBER, TemKeyConstants.ERROR_ACCOUNTING_LINE_CG);
        }
      }
    }

    GlobalVariables.getMessageMap().clearErrorPath();
    GlobalVariables.getMessageMap().getErrorPath().addAll(holdErrors);

    return valid;
  }