/**
   * @see
   *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#attachWorkflowHeadersToTheInvoices(java.util.Collection)
   */
  @Override
  public Collection<CustomerInvoiceDocument> attachWorkflowHeadersToTheInvoices(
      Collection<CustomerInvoiceDocument> invoices) {
    List<CustomerInvoiceDocument> docs = new ArrayList<CustomerInvoiceDocument>();
    if (invoices == null || invoices.isEmpty()) {
      return docs;
    }

    // make a list of necessary workflow docs to retrieve
    List<String> documentHeaderIds = new ArrayList<String>();
    for (CustomerInvoiceDocument invoice : invoices) {
      documentHeaderIds.add(invoice.getDocumentNumber());
    }

    // get all of our docs with full workflow headers
    try {
      for (Document doc :
          documentService.getDocumentsByListOfDocumentHeaderIds(
              CustomerInvoiceDocument.class, documentHeaderIds)) {
        docs.add((CustomerInvoiceDocument) doc);
      }
    } catch (WorkflowException e) {
      throw new RuntimeException("Unable to retrieve Customer Invoice Documents", e);
    }

    return docs;
  }
  /**
   * @see
   *     org.kuali.kfs.module.ar.document.service.AccountsReceivableTaxService#getPostalCodeForTaxation(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument)
   */
  @Override
  public String getPostalCodeForTaxation(CustomerInvoiceDocument document) {

    String postalCode = null;
    String customerNumber = document.getAccountsReceivableDocumentHeader().getCustomerNumber();
    Integer shipToAddressIdentifier = document.getCustomerShipToAddressIdentifier();

    // if customer number or ship to address id isn't provided, go to org options
    if (ObjectUtils.isNotNull(shipToAddressIdentifier) && StringUtils.isNotEmpty(customerNumber)) {

      CustomerAddressService customerAddressService =
          SpringContext.getBean(CustomerAddressService.class);
      CustomerAddress customerShipToAddress =
          customerAddressService.getByPrimaryKey(customerNumber, shipToAddressIdentifier);
      if (ObjectUtils.isNotNull(customerShipToAddress)) {
        postalCode = customerShipToAddress.getCustomerZipCode();
      }
    } else {
      Map<String, String> criteria = new HashMap<String, String>();
      criteria.put("chartOfAccountsCode", document.getBillByChartOfAccountCode());
      criteria.put("organizationCode", document.getBilledByOrganizationCode());
      OrganizationOptions organizationOptions =
          (OrganizationOptions)
              businessObjectService.findByPrimaryKey(OrganizationOptions.class, criteria);

      if (ObjectUtils.isNotNull(organizationOptions)) {
        postalCode = organizationOptions.getOrganizationPostalZipCode();
      }
    }
    return postalCode;
  }
  /**
   * @param includeWorkflowHeaders
   * @return
   */
  public Collection<CustomerInvoiceDocument> getAllOpenCustomerInvoiceDocuments(
      boolean includeWorkflowHeaders) {
    Collection<CustomerInvoiceDocument> invoices = new ArrayList<CustomerInvoiceDocument>();

    // retrieve the set of documents without workflow headers
    invoices = customerInvoiceDocumentDao.getAllOpen();

    // if we dont need workflow headers, then we're done
    if (!includeWorkflowHeaders) {
      return invoices;
    }

    // make a list of necessary workflow docs to retrieve
    List<String> documentHeaderIds = new ArrayList<String>();
    for (CustomerInvoiceDocument invoice : invoices) {
      documentHeaderIds.add(invoice.getDocumentNumber());
    }

    // get all of our docs with full workflow headers
    List<CustomerInvoiceDocument> docs = new ArrayList<CustomerInvoiceDocument>();
    try {
      for (Document doc :
          documentService.getDocumentsByListOfDocumentHeaderIds(
              CustomerInvoiceDocument.class, documentHeaderIds)) {
        docs.add((CustomerInvoiceDocument) doc);
      }
    } catch (WorkflowException e) {
      throw new RuntimeException("Unable to retrieve Customer Invoice Documents", e);
    }

    return docs;
  }
  /**
   * @see
   *     org.kuali.kfs.module.ar.document.service.InvoiceRecurrenceService#isInvoiceApproved(String)
   */
  @Override
  public boolean isInvoiceApproved(String invoiceNumber) {
    boolean success = true;

    if (ObjectUtils.isNull(invoiceNumber)) {
      return success;
    }

    CustomerInvoiceDocument customerInvoiceDocument = null;
    try {
      customerInvoiceDocument =
          (CustomerInvoiceDocument)
              SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(invoiceNumber);
    } catch (WorkflowException e) {

    }
    if (ObjectUtils.isNotNull(customerInvoiceDocument)) {
      WorkflowDocument workflowDocument =
          customerInvoiceDocument.getDocumentHeader().getWorkflowDocument();
      if (!(workflowDocument.isApproved())) {
        success = false;
      }
    } else {
      success = false;
    }
    return success;
  }
  /**
   * @see
   *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#loadCustomerAddressesForCustomerInvoiceDocument(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument)
   */
  @Override
  public void loadCustomerAddressesForCustomerInvoiceDocument(
      CustomerInvoiceDocument customerInvoiceDocument) {
    // if address identifier is provided, try to refresh customer address data
    if (ObjectUtils.isNotNull(customerInvoiceDocument.getAccountsReceivableDocumentHeader())) {
      CustomerAddress customerShipToAddress =
          customerAddressService.getByPrimaryKey(
              customerInvoiceDocument.getAccountsReceivableDocumentHeader().getCustomerNumber(),
              customerInvoiceDocument.getCustomerShipToAddressIdentifier());
      CustomerAddress customerBillToAddress =
          customerAddressService.getByPrimaryKey(
              customerInvoiceDocument.getAccountsReceivableDocumentHeader().getCustomerNumber(),
              customerInvoiceDocument.getCustomerBillToAddressIdentifier());

      if (ObjectUtils.isNotNull(customerShipToAddress)) {
        customerInvoiceDocument.setCustomerShipToAddress(customerShipToAddress);
        customerInvoiceDocument.setCustomerShipToAddressOnInvoice(customerShipToAddress);
      }

      if (ObjectUtils.isNotNull(customerBillToAddress)) {
        customerInvoiceDocument.setCustomerBillToAddress(customerBillToAddress);
        customerInvoiceDocument.setCustomerBillToAddressOnInvoice(customerBillToAddress);
      }
    }
  }
 /** @param document */
 protected void setupBasicDefaultValuesForCustomerInvoiceDocument(
     CustomerInvoiceDocument document) {
   ChartOrgHolder currentUser =
       financialSystemUserService.getPrimaryOrganization(
           GlobalVariables.getUserSession().getPerson(), ArConstants.AR_NAMESPACE_CODE);
   if (currentUser != null) {
     document.setBillByChartOfAccountCode(currentUser.getChartOfAccountsCode());
     document.setBilledByOrganizationCode(currentUser.getOrganizationCode());
   }
   document.setInvoiceDueDate(getDefaultInvoiceDueDate());
   document.setOpenInvoiceIndicator(true);
 }
 /**
  * @see
  *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#updateReportedDate(String)
  */
 @Override
 public void updateReportedDate(String docNumber) {
   HashMap<String, String> criteria = new HashMap<String, String>();
   criteria.put("documentNumber", docNumber);
   CustomerInvoiceDocument customerInvoiceDocument =
       businessObjectService.findByPrimaryKey(CustomerInvoiceDocument.class, criteria);
   Date reportedDate = dateTimeService.getCurrentSqlDate();
   if (ObjectUtils.isNotNull(customerInvoiceDocument)) {
     customerInvoiceDocument.setReportedDate(reportedDate);
     businessObjectService.save(customerInvoiceDocument);
   }
 }
  /**
   * @see
   *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#setupDefaultValuesForCopiedCustomerInvoiceDocument(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument)
   */
  @Override
  public void setupDefaultValuesForCopiedCustomerInvoiceDocument(CustomerInvoiceDocument document) {

    setupBasicDefaultValuesForCustomerInvoiceDocument(document);

    // Save customer number since it will get overwritten when we retrieve the accounts receivable
    // document header from service
    String customerNumber = document.getAccountsReceivableDocumentHeader().getCustomerNumber();

    // Set up the default values for the AR DOC Header
    AccountsReceivableDocumentHeader accountsReceivableDocumentHeader =
        accountsReceivableDocumentHeaderService
            .getNewAccountsReceivableDocumentHeaderForCurrentUser();
    accountsReceivableDocumentHeader.setDocumentNumber(document.getDocumentNumber());
    accountsReceivableDocumentHeader.setCustomerNumber(customerNumber);
    document.setAccountsReceivableDocumentHeader(accountsReceivableDocumentHeader);

    // set up the primary key for AR_INV_RCURRNC_DTL_T
    CustomerInvoiceRecurrenceDetails recurrenceDetails = new CustomerInvoiceRecurrenceDetails();
    recurrenceDetails.setInvoiceNumber(document.getDocumentNumber());
    // recurrenceDetails.setCustomerNumber(document.getAccountsReceivableDocumentHeader().getCustomerNumber());
    document.setCustomerInvoiceRecurrenceDetails(recurrenceDetails);

    // make open invoice indicator to true
    document.setOpenInvoiceIndicator(true);
    document.setPrintDate(null);
    document.setBillingDate(dateTimeService.getCurrentSqlDateMidnight());
  }
  /**
   * @see
   *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#convertDiscountsToPaidApplieds(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument)
   */
  @Override
  public void convertDiscountsToPaidApplieds(CustomerInvoiceDocument invoice) {

    // this needs a little explanation. we have to calculate manually
    // whether we've written off the whole thing, because the regular
    // code uses the invoice paid applieds to discount, but since those
    // are added but not committed in this transaction, they're also not
    // visible in this transaction, so we do it manually.
    KualiDecimal openAmount = invoice.getOpenAmount();

    String invoiceNumber = invoice.getDocumentNumber();
    List<CustomerInvoiceDetail> discounts = invoice.getDiscounts();

    // retrieve the number of current paid applieds, so we dont have item number overlap
    Integer paidAppliedItemNumber = 0;

    for (CustomerInvoiceDetail discount : discounts) {

      // if credit amount is zero, do nothing
      if (KualiDecimal.ZERO.equals(discount.getAmount())) {
        continue;
      }

      if (paidAppliedItemNumber == 0) {
        paidAppliedItemNumber =
            invoicePaidAppliedService.getNumberOfInvoicePaidAppliedsForInvoiceDetail(
                invoiceNumber, discount.getInvoiceItemNumber());
      }

      // create and save the paidApplied
      InvoicePaidApplied invoicePaidApplied = new InvoicePaidApplied();
      invoicePaidApplied.setDocumentNumber(invoiceNumber);
      invoicePaidApplied.setPaidAppliedItemNumber(paidAppliedItemNumber++);
      invoicePaidApplied.setFinancialDocumentReferenceInvoiceNumber(invoiceNumber);
      invoicePaidApplied.setInvoiceItemNumber(discount.getInvoiceItemNumber());
      invoicePaidApplied.setUniversityFiscalYear(universityDateService.getCurrentFiscalYear());
      invoicePaidApplied.setUniversityFiscalPeriodCode(
          universityDateService.getCurrentUniversityDate().getUniversityFiscalAccountingPeriod());
      invoicePaidApplied.setInvoiceItemAppliedAmount(discount.getAmount().abs());
      openAmount = openAmount.subtract(discount.getAmount().abs());
      businessObjectService.save(invoicePaidApplied);
    }

    // if its open, but now with a zero openamount, then close it
    if (KualiDecimal.ZERO.equals(openAmount)) {
      invoice.setOpenInvoiceIndicator(false);
      invoice.setClosedDate(dateTimeService.getCurrentSqlDate());
      documentService.updateDocument(invoice);
    }
  }
 /**
  * @see
  *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#getCustomerInvoiceDetailsForCustomerInvoiceDocumentWithCaching(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument)
  */
 @Override
 public Collection<CustomerInvoiceDetail>
     getCustomerInvoiceDetailsForCustomerInvoiceDocumentWithCaching(
         CustomerInvoiceDocument customerInvoiceDocument) {
   return customerInvoiceDetailService.getCustomerInvoiceDetailsForInvoiceWithCaching(
       customerInvoiceDocument.getDocumentNumber());
 }
 /**
  * @see
  *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#getOpenAmountForCustomerInvoiceDocument(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument)
  */
 @Override
 public KualiDecimal getOpenAmountForCustomerInvoiceDocument(
     CustomerInvoiceDocument customerInvoiceDocument) {
   KualiDecimal total = new KualiDecimal(0);
   if (customerInvoiceDocument.isOpenInvoiceIndicator()) {
     Collection<CustomerInvoiceDetail> customerInvoiceDetails =
         customerInvoiceDocument.getCustomerInvoiceDetailsWithoutDiscounts();
     for (CustomerInvoiceDetail detail : customerInvoiceDetails) {
       // note that we're now dealing with conditionally applying discounts
       // depending on whether the doc is saved or approved one level down,
       // in the CustomerInvoiceDetail.getAmountOpen()
       detail.setCustomerInvoiceDocument(customerInvoiceDocument);
       total = total.add(detail.getAmountOpen());
     }
   }
   return total;
 }
  /**
   * @see
   *     org.kuali.kfs.module.ar.document.service.AccountsReceivableTaxService#isCustomerInvoiceDetailTaxable(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument,
   *     org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail)
   */
  @Override
  public boolean isCustomerInvoiceDetailTaxable(
      CustomerInvoiceDocument customerInvoiceDocument,
      CustomerInvoiceDetail customerInvoiceDetail) {

    // check if sales tax is enabled
    if (!parameterService.getParameterValueAsBoolean(
        KfsParameterConstants.ACCOUNTS_RECEIVABLE_DOCUMENT.class,
        ArConstants.ENABLE_SALES_TAX_IND)) {
      return false;
    }

    // check if customer is tax exempt
    if (ObjectUtils.isNotNull(customerInvoiceDocument.getCustomer())) {
      if (customerInvoiceDocument.getCustomer().isCustomerTaxExemptIndicator()) {
        return false;
      }
    }

    // check item if the taxable indicator is checked
    if (!customerInvoiceDetail.isTaxableIndicator()) {
      return false;
    }

    // check item if item is taxable
    /*
    if( StringUtils.isNotEmpty(customerInvoiceDetail.getInvoiceItemCode()) ){
        Map<String, String> criteria = new HashMap<String, String>();
        criteria.put("invoiceItemCode", customerInvoiceDetail.getInvoiceItemCode());
        criteria.put("chartOfAccountsCode", customerInvoiceDocument.getAccountsReceivableDocumentHeader().getProcessingChartOfAccountCode());
        criteria.put("organizationCode", customerInvoiceDocument.getAccountsReceivableDocumentHeader().getProcessingOrganizationCode());
        CustomerInvoiceItemCode customerInvoiceItemCode = (CustomerInvoiceItemCode) businessObjectService.findByPrimaryKey(CustomerInvoiceItemCode.class, criteria);

        if (ObjectUtils.isNotNull(customerInvoiceItemCode) && !customerInvoiceItemCode.isTaxableIndicator()){
            return false;
        }
    }
    */

    // if address of billing org's postal code isn't the same as shipping address, return false???

    return true;
  }
  /**
   * @see
   *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#getOriginalTotalAmountForCustomerInvoiceDocument(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument)
   */
  @Override
  public KualiDecimal getOriginalTotalAmountForCustomerInvoiceDocument(
      CustomerInvoiceDocument customerInvoiceDocument) {
    LOG.info(
        "\n\n\n\t\t invoice: "
            + customerInvoiceDocument.getDocumentNumber()
            + "\n\t\t 111111111 HEADER TOTAL AMOUNT (should be null): "
            + customerInvoiceDocument
                .getFinancialSystemDocumentHeader()
                .getFinancialDocumentTotalAmount()
            + "\n\n");
    customerInvoiceDocument.getDocumentNumber();
    HashMap criteria = new HashMap();
    criteria.put(
        KFSPropertyConstants.DOCUMENT_NUMBER,
        customerInvoiceDocument.getDocumentHeader().getDocumentTemplateNumber());
    FinancialSystemDocumentHeader financialSystemDocumentHeader =
        businessObjectService.findByPrimaryKey(FinancialSystemDocumentHeader.class, criteria);
    KualiDecimal originalTotalAmount = KualiDecimal.ZERO;
    originalTotalAmount = financialSystemDocumentHeader.getFinancialDocumentTotalAmount();

    LOG.info(
        "\n\n\n\t\t invoice: "
            + customerInvoiceDocument.getDocumentNumber()
            + "\n\t\t 333333333333 HEADER TOTAL AMOUNT (should be set now): "
            + customerInvoiceDocument
                .getFinancialSystemDocumentHeader()
                .getFinancialDocumentTotalAmount()
            + "\n\n");
    return originalTotalAmount;
  }
  public boolean validate(AttributedDocumentEvent event) {
    if (StringUtils.isEmpty(customerInvoiceDocument.getPaymentAccountNumber())) {
      GlobalVariables.getMessageMap()
          .putError(
              DOCUMENT_ERROR_PREFIX
                  + ArPropertyConstants.CustomerInvoiceDocumentFields.PAYMENT_ACCOUNT_NUMBER,
              OrganizationAccountingDefaultErrors.ERROR_PAYMENT_ACCOUNT_NUMBER_REQUIRED);
      return false;
    } else {
      customerInvoiceDocument.refreshReferenceObject(
          ArPropertyConstants.CustomerInvoiceDocumentFields.PAYMENT_ACCOUNT);
      if (ObjectUtils.isNull(customerInvoiceDocument.getPaymentAccount())) {
        GlobalVariables.getMessageMap()
            .putError(
                DOCUMENT_ERROR_PREFIX
                    + ArPropertyConstants.CustomerInvoiceDocumentFields.PAYMENT_ACCOUNT_NUMBER,
                ArKeyConstants.ERROR_CUSTOMER_INVOICE_DOCUMENT_INVALID_PAYMENT_ACCOUNT_NUMBER);
        return false;
      }
    }

    return true;
  }
  /**
   * Refactor to have all the setters in here.
   *
   * @see
   *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#setupDefaultValuesForNewCustomerInvoiceDocument(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument)
   */
  @Override
  public void setupDefaultValuesForNewCustomerInvoiceDocument(CustomerInvoiceDocument document) {

    setupBasicDefaultValuesForCustomerInvoiceDocument(document);

    // set up the default values for the AR DOC Header

    AccountsReceivableDocumentHeader accountsReceivableDocumentHeader =
        accountsReceivableDocumentHeaderService
            .getNewAccountsReceivableDocumentHeaderForCurrentUser();
    accountsReceivableDocumentHeader.setDocumentNumber(document.getDocumentNumber());
    document.setAccountsReceivableDocumentHeader(accountsReceivableDocumentHeader);

    // set up the primary key for AR_INV_RCURRNC_DTL_T
    CustomerInvoiceRecurrenceDetails recurrenceDetails = new CustomerInvoiceRecurrenceDetails();
    recurrenceDetails.setInvoiceNumber(document.getDocumentNumber());
    // recurrenceDetails.setCustomerNumber(document.getCustomer().getCustomerNumber());
    document.setCustomerInvoiceRecurrenceDetails(recurrenceDetails);

    Map<String, String> criteria = new HashMap<String, String>();
    criteria.put(
        KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, document.getBillByChartOfAccountCode());
    criteria.put(KFSPropertyConstants.ORGANIZATION_CODE, document.getBilledByOrganizationCode());
    OrganizationOptions organizationOptions =
        businessObjectService.findByPrimaryKey(OrganizationOptions.class, criteria);

    if (ObjectUtils.isNotNull(organizationOptions)) {
      document.setPrintInvoiceIndicator(organizationOptions.getPrintInvoiceIndicator());
      document.setInvoiceTermsText(organizationOptions.getOrganizationPaymentTermsText());
    }

    // If document is using receivable option, set receivable accounting line for customer invoice
    // document
    String receivableOffsetOption =
        parameterService.getParameterValueAsString(
            CustomerInvoiceDocument.class, ArConstants.GLPE_RECEIVABLE_OFFSET_GENERATION_METHOD);
    boolean isUsingReceivableFAU =
        ArConstants.GLPE_RECEIVABLE_OFFSET_GENERATION_METHOD_FAU.equals(receivableOffsetOption);
    if (isUsingReceivableFAU) {
      receivableAccountingLineService.setReceivableAccountingLineForCustomerInvoiceDocument(
          document);
    }
  }
 @Override
 public void closeCustomerInvoiceDocument(CustomerInvoiceDocument customerInvoiceDocument) {
   customerInvoiceDocument.setOpenInvoiceIndicator(false);
   customerInvoiceDocument.setClosedDate(dateTimeService.getCurrentSqlDate());
   businessObjectService.save(customerInvoiceDocument);
 }
 /**
  * @see
  *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#getCustomerInvoiceDetailsForCustomerInvoiceDocument(org.kuali.kfs.module.ar.document.CustomerInvoiceDocument)
  */
 @Override
 public Collection<CustomerInvoiceDetail> getCustomerInvoiceDetailsForCustomerInvoiceDocument(
     CustomerInvoiceDocument customerInvoiceDocument) {
   return getCustomerInvoiceDetailsForCustomerInvoiceDocument(
       customerInvoiceDocument.getDocumentNumber());
 }
 /**
  * @param invoiceDocumentNumber
  * @return
  */
 @Override
 public Customer getCustomerByInvoiceDocumentNumber(String invoiceDocumentNumber) {
   CustomerInvoiceDocument invoice = getInvoiceByInvoiceDocumentNumber(invoiceDocumentNumber);
   return invoice.getAccountsReceivableDocumentHeader().getCustomer();
 }
  /**
   * @see
   *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#getPrintableCustomerInvoiceDocumentsByInitiatorPrincipalName(java.lang.String)
   */
  @Override
  public List<CustomerInvoiceDocument> getPrintableCustomerInvoiceDocumentsByInitiatorPrincipalName(
      String initiatorPrincipalName) {
    if (StringUtils.isBlank(initiatorPrincipalName)) {
      throw new IllegalArgumentException(
          "The parameter [initiatorPrincipalName] passed in was null or blank.");
    }

    // IMPORTANT NOTES ABOUT THIS METHOD
    //
    // This method behaves differently than the other invoice printing methods. This is
    // because there's no way from within KFS to do a direct DB call to get all the invoices
    // you want. This is because workflow holds the document initiator, and you cant guarantee
    // that in a given implementation that you have access to that other db. It could be on
    // another box in another network, and you only have web-services access to the Rice box.
    //
    // Given that, we try to minimize the resource hit of this call as much as possible. First
    // we retrieve all invoices that havent been printed (ie, dont have a print date) and that
    // are marked for the USER print queue. At any given time that should be a manageable number of
    // documents.
    //
    // Then we walk through them, retrieve the full workflow-populated version of it, and only
    // return the ones that match the initiator.
    //
    // This isnt as performant a solution as the other getPrintableCustomerInvoiceBy...
    // methods, but its the best we can do in this release, and it should be manageable.

    //
    // attempt to retrieve the initiator person specified, and puke if not found
    Principal initiator =
        KimApiServiceLocator.getIdentityService()
            .getPrincipalByPrincipalName(initiatorPrincipalName);
    if (initiator == null) {
      throw new IllegalArgumentException(
          "The parameter value for initiatorPrincipalName ["
              + initiatorPrincipalName
              + "] passed in doesnt map to a person.");
    }

    // retrieve all the ready-to-print docs in the user-queue for all users
    List<String> printableUserQueueDocNumbers =
        customerInvoiceDocumentDao.getPrintableCustomerInvoiceDocumentNumbersFromUserQueue();

    // get all the documents that might be right, but this set includes documents generated
    // by the wrong user
    List<CustomerInvoiceDocument> customerInvoiceDocumentsSuperSet =
        new ArrayList<CustomerInvoiceDocument>();
    if (printableUserQueueDocNumbers.size() > 0) {
      try {
        for (Document doc :
            documentService.getDocumentsByListOfDocumentHeaderIds(
                CustomerInvoiceDocument.class, printableUserQueueDocNumbers)) {
          customerInvoiceDocumentsSuperSet.add((CustomerInvoiceDocument) doc);
        }
      } catch (WorkflowException e) {
        throw new RuntimeException("Unable to retrieve Customer Invoice Documents", e);
      }
    } else {
      customerInvoiceDocumentsSuperSet = new ArrayList<CustomerInvoiceDocument>();
    }

    // filter only the ones initiated by the correct user
    List<CustomerInvoiceDocument> customerInvoiceDocuments =
        new ArrayList<CustomerInvoiceDocument>();
    for (CustomerInvoiceDocument superSetDocument : customerInvoiceDocumentsSuperSet) {
      if (StringUtils.equalsIgnoreCase(
          superSetDocument.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId(),
          initiator.getPrincipalId())) {
        customerInvoiceDocuments.add(superSetDocument);
      }
    }
    return customerInvoiceDocuments;
  }
  @Override
  public void completeCustomerCreditMemo(CustomerCreditMemoDocument creditMemo) {

    //  retrieve the document and make sure its not already closed, crash if so
    String invoiceNumber = creditMemo.getFinancialDocumentReferenceInvoiceNumber();
    CustomerInvoiceDocument invoice;
    try {
      invoice = (CustomerInvoiceDocument) documentService.getByDocumentHeaderId(invoiceNumber);
    } catch (WorkflowException e) {
      throw new RuntimeException(
          "A WorkflowException was generated when trying to load Customer Invoice #"
              + invoiceNumber
              + ".",
          e);
    }
    if (!invoice.isOpenInvoiceIndicator()) {
      throw new UnsupportedOperationException(
          "The CreditMemo Document #"
              + creditMemo.getDocumentNumber()
              + " attempted to credit "
              + "an Invoice [#"
              + invoiceNumber
              + "] that was already closed.  This is not supported.");
    }

    // this needs a little explanation.  we have to calculate manually
    // whether we've written off the whole thing, because the regular
    // code uses the invoice paid applieds to discount, but since those
    // are added but not committed in this transaction, they're also not
    // visible in this transaction, so we do it manually.
    KualiDecimal openAmount = invoice.getOpenAmount();

    Integer paidAppliedItemNumber = 0;

    //  retrieve the customer invoice details, and generate paid applieds for each
    List<CustomerCreditMemoDetail> details = creditMemo.getCreditMemoDetails();
    for (CustomerCreditMemoDetail detail : details) {
      CustomerInvoiceDetail invoiceDetail = detail.getCustomerInvoiceDetail();

      //   if credit amount is zero, do nothing
      if (detail.getCreditMemoLineTotalAmount().isZero()) {
        continue;
      }

      //  if credit amount is greater than the open amount, crash and complain
      if (detail
          .getCreditMemoLineTotalAmount()
          .abs()
          .isGreaterThan(invoiceDetail.getAmountOpen())) {
        throw new UnsupportedOperationException(
            "The credit detail for CreditMemo Document #"
                + creditMemo.getDocumentNumber()
                + " attempted "
                + "to credit more than the Open Amount on the Invoice Detail.  This is not supported.");
      }

      //  retrieve the number of current paid applieds, so we dont have item number overlap
      if (paidAppliedItemNumber == 0) {
        paidAppliedItemNumber =
            paidAppliedService.getNumberOfInvoicePaidAppliedsForInvoiceDetail(
                invoiceNumber, invoiceDetail.getInvoiceItemNumber());
      }

      //  create and save the paidApplied
      InvoicePaidApplied invoicePaidApplied = new InvoicePaidApplied();
      invoicePaidApplied.setDocumentNumber(creditMemo.getDocumentNumber());
      invoicePaidApplied.setPaidAppliedItemNumber(paidAppliedItemNumber++);
      invoicePaidApplied.setFinancialDocumentReferenceInvoiceNumber(invoiceNumber);
      invoicePaidApplied.setInvoiceItemNumber(invoiceDetail.getInvoiceItemNumber());
      invoicePaidApplied.setUniversityFiscalYear(universityDateService.getCurrentFiscalYear());
      invoicePaidApplied.setUniversityFiscalPeriodCode(
          universityDateService.getCurrentUniversityDate().getUniversityFiscalAccountingPeriod());
      invoicePaidApplied.setInvoiceItemAppliedAmount(detail.getCreditMemoLineTotalAmount().abs());
      openAmount = openAmount.subtract(detail.getCreditMemoLineTotalAmount().abs());
      businessObjectService.save(invoicePaidApplied);
    }

    //   if its open, but now with a zero openamount, then close it
    if (invoice.isOpenInvoiceIndicator() && KualiDecimal.ZERO.equals(openAmount)) {
      customerInvoiceDocumentService.addCloseNote(
          invoice, creditMemo.getDocumentHeader().getWorkflowDocument());
      invoice.setOpenInvoiceIndicator(false);
      invoice.setClosedDate(dateTimeService.getCurrentSqlDate());
      documentService.updateDocument(invoice);
    }
  }
 /**
  * @see
  *     org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService#getCustomerByOrganizationInvoiceNumber(java.lang.String)
  */
 @Override
 public Customer getCustomerByOrganizationInvoiceNumber(String organizationInvoiceNumber) {
   CustomerInvoiceDocument invoice =
       getInvoiceByOrganizationInvoiceNumber(organizationInvoiceNumber);
   return invoice.getAccountsReceivableDocumentHeader().getCustomer();
 }