/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 *
 * Copyright 2005-2016 The Kuali Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.module.ar.document.service.impl;

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.ObjectCode;
import org.kuali.kfs.coa.service.ObjectCodeService;
import org.kuali.kfs.coreservice.framework.parameter.ParameterService;
import org.kuali.kfs.krad.service.BusinessObjectService;
import org.kuali.kfs.krad.service.DocumentService;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.module.ar.ArConstants;
import org.kuali.kfs.module.ar.ArPropertyConstants;
import org.kuali.kfs.module.ar.batch.service.CustomerInvoiceWriteoffBatchService;
import org.kuali.kfs.module.ar.batch.vo.CustomerInvoiceWriteoffBatchVO;
import org.kuali.kfs.module.ar.businessobject.AccountsReceivableDocumentHeader;
import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail;
import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceWriteoffLookupResult;
import org.kuali.kfs.module.ar.businessobject.InvoicePaidApplied;
import org.kuali.kfs.module.ar.businessobject.OrganizationAccountingDefault;
import org.kuali.kfs.module.ar.businessobject.lookup.CustomerInvoiceWriteoffLookupUtil;
import org.kuali.kfs.module.ar.document.CustomerCreditMemoDocument;
import org.kuali.kfs.module.ar.document.CustomerInvoiceDocument;
import org.kuali.kfs.module.ar.document.CustomerInvoiceWriteoffDocument;
import org.kuali.kfs.module.ar.document.service.AccountsReceivableDocumentHeaderService;
import org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService;
import org.kuali.kfs.module.ar.document.service.CustomerInvoiceWriteoffDocumentService;
import org.kuali.kfs.module.ar.document.service.CustomerService;
import org.kuali.kfs.module.ar.document.service.InvoicePaidAppliedService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.ChartOrgHolder;
import org.kuali.kfs.sys.service.FinancialSystemUserService;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kew.api.WorkflowDocumentFactory;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.Person;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Transactional
public class CustomerInvoiceWriteoffDocumentServiceImpl implements CustomerInvoiceWriteoffDocumentService {

    protected ParameterService parameterService;
    protected UniversityDateService universityDateService;
    protected BusinessObjectService businessObjectService;
    protected AccountsReceivableDocumentHeaderService accountsReceivableDocumentHeaderService;
    protected CustomerInvoiceDocumentService customerInvoiceDocumentService;
    protected CustomerService customerService;
    protected DocumentService documentService;
    protected CustomerInvoiceWriteoffBatchService invoiceWriteoffBatchService;
    protected DateTimeService dateTimeService;
    protected InvoicePaidAppliedService<CustomerInvoiceDetail> paidAppliedService;
    protected FinancialSystemUserService financialSystemUserService;
    protected ObjectCodeService objectCodeService;

    /**
     * @see org.kuali.kfs.module.ar.document.service.CustomerInvoiceWriteoffDocumentService#completeWriteoffProcess(org.kuali.kfs.module.ar.document.CustomerInvoiceWriteoffDocument)
     */
    @Override
    public void completeWriteoffProcess(CustomerInvoiceWriteoffDocument writeoff) {

        //  retrieve the document and make sure its not already closed, crash if so
        String invoiceNumber = writeoff.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 Invoice Writeoff Document #" + writeoff.getDocumentNumber() + " attempted to writeoff " +
                "an Invoice [#" + invoiceNumber + "] that was already closed.  This is not supported.");
        }

        Integer paidAppliedItemNumber = 0;
        KualiDecimal totalApplied = KualiDecimal.ZERO;

        //  retrieve the customer invoice details, and generate paid applieds for each
        List<CustomerInvoiceDetail> invoiceDetails = invoice.getCustomerInvoiceDetailsWithoutDiscounts();
        for (CustomerInvoiceDetail invoiceDetail : invoiceDetails) {

            //   if no open amount on the detail, then do nothing
            if (invoiceDetail.getAmountOpen().isZero()) {
                continue;
            }

            //  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(writeoff.getDocumentNumber());
            invoicePaidApplied.setPaidAppliedItemNumber(paidAppliedItemNumber++);
            invoicePaidApplied.setFinancialDocumentReferenceInvoiceNumber(invoiceNumber);
            invoicePaidApplied.setInvoiceItemNumber(invoiceDetail.getInvoiceItemNumber());
            invoicePaidApplied.setUniversityFiscalYear(universityDateService.getCurrentFiscalYear());
            invoicePaidApplied.setUniversityFiscalPeriodCode(universityDateService.getCurrentUniversityDate().getUniversityFiscalAccountingPeriod());
            invoicePaidApplied.setInvoiceItemAppliedAmount(invoiceDetail.getAmountOpen());
            businessObjectService.save(invoicePaidApplied);

            //  record how much we're applying
            totalApplied = totalApplied.add(invoicePaidApplied.getInvoiceItemAppliedAmount());
        }

        //  close the document
        customerInvoiceDocumentService.addCloseNote(invoice, writeoff.getDocumentHeader().getWorkflowDocument());
        invoice.setOpenInvoiceIndicator(false);
        invoice.setClosedDate(dateTimeService.getCurrentSqlDate());
        documentService.updateDocument(invoice);

        //  set the final document total for the invoice
        writeoff.setInvoiceWriteoffAmount(totalApplied);
        writeoff.getFinancialSystemDocumentHeader().setFinancialDocumentTotalAmount(totalApplied);
        documentService.updateDocument(writeoff);
    }

    /**
     * @see org.kuali.kfs.module.ar.document.service.CustomerInvoiceWriteoffDocumentService#setupDefaultValuesForNewCustomerInvoiceWriteoffDocument(org.kuali.kfs.module.ar.document.CustomerInvoiceWriteoffDocument)
     */
    @Override
    public void setupDefaultValuesForNewCustomerInvoiceWriteoffDocument(CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument) {

        // update status
        customerInvoiceWriteoffDocument.setStatusCode(ArConstants.CustomerInvoiceWriteoffStatuses.IN_PROCESS);

        // set accounts receivable document header
        AccountsReceivableDocumentHeader accountsReceivableDocumentHeader = accountsReceivableDocumentHeaderService.getNewAccountsReceivableDocumentHeaderForCurrentUser();
        accountsReceivableDocumentHeader.setDocumentNumber(customerInvoiceWriteoffDocument.getDocumentNumber());
        accountsReceivableDocumentHeader.setCustomerNumber(customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getAccountsReceivableDocumentHeader().getCustomerNumber());
        customerInvoiceWriteoffDocument.setAccountsReceivableDocumentHeader(accountsReceivableDocumentHeader);

        // if writeoffs are generated based on organization accounting default, populate those fields now
        String writeoffGenerationOption = parameterService.getParameterValueAsString(CustomerInvoiceWriteoffDocument.class, ArConstants.GLPE_WRITEOFF_GENERATION_METHOD);
        boolean isUsingOrgAcctDefaultWriteoffFAU = ArConstants.GLPE_WRITEOFF_GENERATION_METHOD_ORG_ACCT_DEFAULT.equals(writeoffGenerationOption);

        String writeoffTaxGenerationOption = parameterService.getParameterValueAsString(CustomerInvoiceWriteoffDocument.class, ArConstants.ALLOW_SALES_TAX_LIABILITY_ADJUSTMENT_IND);
        boolean isUsingTaxGenerationMethodDisallow = ArConstants.ALLOW_SALES_TAX_LIABILITY_ADJUSTMENT_IND_NO.equals(writeoffTaxGenerationOption);
        if (isUsingOrgAcctDefaultWriteoffFAU || isUsingTaxGenerationMethodDisallow) {

            Integer currentUniversityFiscalYear = universityDateService.getCurrentFiscalYear();
            ChartOrgHolder currentUser = financialSystemUserService.getPrimaryOrganization(GlobalVariables.getUserSession().getPerson(), ArConstants.AR_NAMESPACE_CODE);

            Map<String, Object> criteria = new HashMap<String, Object>(3);
            criteria.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, currentUniversityFiscalYear);
            criteria.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getBillByChartOfAccountCode());
            criteria.put(KFSPropertyConstants.ORGANIZATION_CODE, customerInvoiceWriteoffDocument.getCustomerInvoiceDocument().getBilledByOrganizationCode());

            OrganizationAccountingDefault organizationAccountingDefault = businessObjectService.findByPrimaryKey(OrganizationAccountingDefault.class, criteria);
            if (ObjectUtils.isNotNull(organizationAccountingDefault)) {
                customerInvoiceWriteoffDocument.setChartOfAccountsCode(organizationAccountingDefault.getWriteoffChartOfAccountsCode());
                customerInvoiceWriteoffDocument.setAccountNumber(organizationAccountingDefault.getWriteoffAccountNumber());
                customerInvoiceWriteoffDocument.setSubAccountNumber(organizationAccountingDefault.getWriteoffSubAccountNumber());
                customerInvoiceWriteoffDocument.setFinancialObjectCode(organizationAccountingDefault.getWriteoffFinancialObjectCode());
                customerInvoiceWriteoffDocument.setFinancialSubObjectCode(organizationAccountingDefault.getWriteoffFinancialSubObjectCode());
                customerInvoiceWriteoffDocument.setProjectCode(organizationAccountingDefault.getWriteoffProjectCode());
                customerInvoiceWriteoffDocument.setOrganizationReferenceIdentifier(organizationAccountingDefault.getWriteoffOrganizationReferenceIdentifier());
            }
        }
    }


    @Override
    public boolean isCustomerInvoiceWriteoffDocumentApproved(String customerInvoiceWriteoffDocumentNumber) {
        Map<String, Object> criteria = new HashMap<String, Object>();
        criteria.put(KFSPropertyConstants.DOCUMENT_NUMBER, customerInvoiceWriteoffDocumentNumber);
        criteria.put(ArPropertyConstants.DOCUMENT_STATUS_CODE, KFSConstants.DocumentStatusCodes.APPROVED);
        return businessObjectService.countMatching(CustomerInvoiceWriteoffDocument.class, criteria) == 1;
    }

    @Override
    public Collection<CustomerInvoiceWriteoffLookupResult> getCustomerInvoiceDocumentsForInvoiceWriteoffLookup(Map<String, String> fieldValues) {

        //  only one of these four will be used, based on priority
        String customerNumber = fieldValues.get(ArPropertyConstants.CustomerFields.CUSTOMER_NUMBER);
        String customerName = fieldValues.get(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.CUSTOMER_NAME);
        String customerTypeCode = fieldValues.get(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.CUSTOMER_TYPE_CODE);
        String customerInvoiceNumber = fieldValues.get(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.CUSTOMER_INVOICE_NUMBER);

        //  this may be combined with any of the four above
        String age = fieldValues.get(ArPropertyConstants.CustomerInvoiceWriteoffLookupResultFields.AGE);

        //  this is the priority order for searching if multiples are entered
        Collection<CustomerInvoiceDocument> customerInvoiceDocuments;
        if (StringUtils.isNotEmpty(customerInvoiceNumber)) {
            customerInvoiceDocuments = new ArrayList<CustomerInvoiceDocument>();
            CustomerInvoiceDocument customerInvoiceDocument = customerInvoiceDocumentService.getInvoiceByInvoiceDocumentNumber(customerInvoiceNumber);
            if (ObjectUtils.isNotNull(customerInvoiceDocument) && customerInvoiceDocument.isOpenInvoiceIndicator()) {
                customerInvoiceDocuments.add(customerInvoiceDocument);
            }
        } else if (StringUtils.isNotEmpty(customerNumber)) {
            customerInvoiceDocuments = customerInvoiceDocumentService.getOpenInvoiceDocumentsByCustomerNumber(customerNumber);
        } else if (StringUtils.isNotEmpty(customerName) && StringUtils.isNotEmpty(customerTypeCode)) {
            customerInvoiceDocuments = customerInvoiceDocumentService.getOpenInvoiceDocumentsByCustomerNameByCustomerType(customerName, customerTypeCode);
        } else if (StringUtils.isNotEmpty(customerName)) {
            customerInvoiceDocuments = customerInvoiceDocumentService.getOpenInvoiceDocumentsByCustomerName(customerName);
        } else if (StringUtils.isNotEmpty(customerTypeCode)) {
            customerInvoiceDocuments = customerInvoiceDocumentService.getOpenInvoiceDocumentsByCustomerType(customerTypeCode);
        } else {
            customerInvoiceDocuments = customerInvoiceDocumentService.getAllOpenCustomerInvoiceDocumentsWithoutWorkflow();
        }

        // attach headers
        customerInvoiceDocuments = customerInvoiceDocumentService.attachWorkflowHeadersToTheInvoices(customerInvoiceDocuments);
        // filter invoices which have related CRMs and writeoffs in route.
        Collection<CustomerInvoiceDocument> filteredCustomerInvoiceDocuments = filterInvoices(customerInvoiceDocuments);

        //  if no age value was specified, then we're done!
        if (StringUtils.isEmpty(age)) {
            return CustomerInvoiceWriteoffLookupUtil.getPopulatedCustomerInvoiceWriteoffLookupResults(filteredCustomerInvoiceDocuments);
        }

        // walk through what we have, and do any extra filtering based on age, if necessary
        boolean eligibleInvoiceFlag;
        Collection<CustomerInvoiceDocument> eligibleInvoices = new ArrayList<CustomerInvoiceDocument>();
        for (CustomerInvoiceDocument invoice : filteredCustomerInvoiceDocuments) {
            eligibleInvoiceFlag = true;

            if (ObjectUtils.isNotNull(invoice.getAge())) {
                eligibleInvoiceFlag &= ((new Integer(age)).compareTo(invoice.getAge()) <= 0);
            } else {
                eligibleInvoiceFlag = false;
            }

            if (eligibleInvoiceFlag) {
                eligibleInvoices.add(invoice);
            }
        }

        return CustomerInvoiceWriteoffLookupUtil.getPopulatedCustomerInvoiceWriteoffLookupResults(eligibleInvoices);
    }

    /**
     * This method returns invoices which are in FINAL status and have no related CRMs and writeoffs in route
     *
     * @param customerInvoiceDocuments
     * @return filteredInvoices
     */
    @Override
    public Collection<CustomerInvoiceDocument> filterInvoices(Collection<CustomerInvoiceDocument> customerInvoiceDocuments) {
        Collection<CustomerInvoiceDocument> filteredInvoices = new ArrayList<CustomerInvoiceDocument>();
        boolean hasNoDocumentsInRouteFlag;

        for (CustomerInvoiceDocument invoice : customerInvoiceDocuments) {
            if (invoice.getDocumentHeader().getWorkflowDocument().isFinal()) {
                hasNoDocumentsInRouteFlag = checkIfThereIsNoAnotherCRMInRouteForTheInvoice(invoice.getDocumentNumber());
                if (hasNoDocumentsInRouteFlag) {
                    hasNoDocumentsInRouteFlag = checkIfThereIsNoAnotherWriteoffInRouteForTheInvoice(invoice.getDocumentNumber());
                }
                if (hasNoDocumentsInRouteFlag) {
                    filteredInvoices.add(invoice);
                }
            }
        }
        return filteredInvoices;
    }

    /**
     * This method checks if there is no another CRM in route for the invoice
     * Not in route if CRM status is one of the following: processed, cancelled, or disapproved
     *
     * @param invoice
     * @return
     */
    @Override
    public boolean checkIfThereIsNoAnotherCRMInRouteForTheInvoice(String invoiceDocumentNumber) {

        WorkflowDocument workflowDocument;
        boolean isSuccess = true;

        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(ArPropertyConstants.CustomerInvoiceDocumentFields.FINANCIAL_DOCUMENT_REF_INVOICE_NUMBER, invoiceDocumentNumber);

        Collection<CustomerCreditMemoDocument> customerCreditMemoDocuments = businessObjectService.findMatching(CustomerCreditMemoDocument.class, fieldValues);

        // no CRMs associated with the invoice are found
        if (customerCreditMemoDocuments.isEmpty()) {
            return isSuccess;
        }

        String userId = GlobalVariables.getUserSession().getPrincipalId();
        for (CustomerCreditMemoDocument customerCreditMemoDocument : customerCreditMemoDocuments) {
            workflowDocument = WorkflowDocumentFactory.loadDocument(userId, customerCreditMemoDocument.getDocumentNumber());
            if (!(workflowDocument.isApproved() || workflowDocument.isCanceled() || workflowDocument.isDisapproved())) {
                isSuccess = false;
                break;
            }
        }
        return isSuccess;
    }

    /**
     * This method checks if there is no another writeoff in route for the invoice
     * Not in route if writeoff status is one of the following: processed, cancelled, or disapproved
     *
     * @param invoice
     * @return
     */
    @Override
    public boolean checkIfThereIsNoAnotherWriteoffInRouteForTheInvoice(String invoiceDocumentNumber) {

        WorkflowDocument workflowDocument;
        boolean isSuccess = true;

        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(ArPropertyConstants.CustomerInvoiceDocumentFields.FINANCIAL_DOCUMENT_REF_INVOICE_NUMBER, invoiceDocumentNumber);

        Collection<CustomerInvoiceWriteoffDocument> customerInvoiceWriteoffDocuments = businessObjectService.findMatching(CustomerInvoiceWriteoffDocument.class, fieldValues);

        // no writeoffs associated with the invoice are found
        if (customerInvoiceWriteoffDocuments.isEmpty()) {
            return isSuccess;
        }

        String userId = GlobalVariables.getUserSession().getPrincipalId();
        for (CustomerInvoiceWriteoffDocument customerInvoiceWriteoffDocument : customerInvoiceWriteoffDocuments) {
            workflowDocument = WorkflowDocumentFactory.loadDocument(userId, customerInvoiceWriteoffDocument.getDocumentNumber());
            if (!(workflowDocument.isApproved() || workflowDocument.isCanceled() || workflowDocument.isDisapproved())) {
                isSuccess = false;
                break;
            }
        }
        return isSuccess;
    }


    /**
     * @see org.kuali.kfs.module.ar.document.service.CustomerInvoiceWriteoffDocumentService#sendCustomerInvoiceWriteoffDocumentsToBatch(org.kuali.rice.kim.api.identity.Person, java.util.Collection)
     */
    @Override
    public String sendCustomerInvoiceWriteoffDocumentsToBatch(Person person, Collection<CustomerInvoiceWriteoffLookupResult> customerInvoiceWriteoffLookupResults) {

        CustomerInvoiceWriteoffBatchVO batch = new CustomerInvoiceWriteoffBatchVO(person.getPrincipalName());

        //  add the date
        batch.setSubmittedOn(dateTimeService.getCurrentTimestamp().toString());

        //  add the customer note, if one was added
        String note = null;
        for (CustomerInvoiceWriteoffLookupResult customerInvoiceWriteoffLookupResult : customerInvoiceWriteoffLookupResults) {
            note = customerInvoiceWriteoffLookupResult.getCustomerNote();
        }
        if (StringUtils.isNotBlank(note)) {
            batch.setNote(note);
        }

        //  add the document numbers
        for (CustomerInvoiceWriteoffLookupResult customerInvoiceWriteoffLookupResult : customerInvoiceWriteoffLookupResults) {
            for (CustomerInvoiceDocument customerInvoiceDocument : customerInvoiceWriteoffLookupResult.getCustomerInvoiceDocuments()) {
                batch.addInvoiceNumber(customerInvoiceDocument.getDocumentNumber());
            }
        }

        // use the batch service to create the XML and drop it in the directory
        return invoiceWriteoffBatchService.createBatchDrop(person, batch);
    }

    @Override
    public String createCustomerInvoiceWriteoffDocument(String invoiceNumber, String note) throws WorkflowException {

        //  force the initiating user into the header
        // JHK: removed!  This can not work in production!
//        GlobalVariables.getUserSession().setBackdoorUser(initiator.getPrincipalName());

        //  create the new writeoff document
        CustomerInvoiceWriteoffDocument document = (CustomerInvoiceWriteoffDocument) documentService.getNewDocument(CustomerInvoiceWriteoffDocument.class);

        //  setup the defaults and tie it to the Invoice document
        document.setFinancialDocumentReferenceInvoiceNumber(invoiceNumber);
        setupDefaultValuesForNewCustomerInvoiceWriteoffDocument(document);
        document.getDocumentHeader().setDocumentDescription(ArConstants.CUSTOMER_INVOICE_WRITEOFF_DOCUMENT_DESCRIPTION + " " + invoiceNumber + ".");

        document.setCustomerNote(note);

        //  satisfy silly > 10 chars explanation rule
        if (StringUtils.isBlank(note)) {
            note = "Document created by batch process.";
        } else if (note.length() <= 10) {
            note = "Document created by batch process.  " + note;
        }
        document.setCustomerNote(note);

        //  route the document
        documentService.routeDocument(document, "Routed by Customer Invoice Writeoff Document Batch Service", null);

        //  clear the user overrid
//        GlobalVariables.getUserSession().clearBackdoorUser();

        return document.getDocumentNumber();
    }

    @Override
    public Collection<CustomerInvoiceWriteoffDocument> getCustomerCreditMemoDocumentByInvoiceDocument(String invoiceNumber) {
        Map<String, String> fieldValues = new HashMap<String, String>(1);
        fieldValues.put(ArPropertyConstants.CustomerInvoiceDocumentFields.FINANCIAL_DOCUMENT_REF_INVOICE_NUMBER, invoiceNumber);

        Collection<CustomerInvoiceWriteoffDocument> writeoffs = businessObjectService.findMatching(CustomerInvoiceWriteoffDocument.class, fieldValues);

        return writeoffs;
    }

    @Override
    public String getFinancialObjectCode(CustomerInvoiceDetail postable, CustomerInvoiceWriteoffDocument poster, boolean isUsingOrgAcctDefaultWriteoffFAU, boolean isUsingChartForWriteoff, String chartOfAccountsCode) {

        if (isUsingOrgAcctDefaultWriteoffFAU) {
            return poster.getFinancialObjectCode();
        } else if (isUsingChartForWriteoff) {
            return parameterService.getSubParameterValueAsString(CustomerInvoiceWriteoffDocument.class, ArConstants.GLPE_WRITEOFF_OBJECT_CODE_BY_CHART, chartOfAccountsCode);
        } else {
            return postable.getAccountsReceivableObjectCode();
        }
    }

    @Override
    public ObjectCode getObjectCode(CustomerInvoiceDetail postable, CustomerInvoiceWriteoffDocument poster, boolean isUsingOrgAcctDefaultWriteoffFAU, boolean isUsingChartForWriteoff, String chartOfAccountsCode) {
        return objectCodeService.getByPrimaryIdForCurrentYear(
            chartOfAccountsCode,
            getFinancialObjectCode(postable, poster, isUsingOrgAcctDefaultWriteoffFAU, isUsingChartForWriteoff, chartOfAccountsCode));
    }

    public void setParameterService(ParameterService parameterService) {
        this.parameterService = parameterService;
    }

    public void setUniversityDateService(UniversityDateService universityDateService) {
        this.universityDateService = universityDateService;
    }

    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
        this.businessObjectService = businessObjectService;
    }

    public void setCustomerInvoiceDocumentService(CustomerInvoiceDocumentService customerInvoiceDocumentService) {
        this.customerInvoiceDocumentService = customerInvoiceDocumentService;
    }

    public void setAccountsReceivableDocumentHeaderService(AccountsReceivableDocumentHeaderService accountsReceivableDocumentHeaderService) {
        this.accountsReceivableDocumentHeaderService = accountsReceivableDocumentHeaderService;
    }

    public void setCustomerService(CustomerService customerService) {
        this.customerService = customerService;
    }

    public void setDocumentService(DocumentService documentService) {
        this.documentService = documentService;
    }

    public void setInvoiceWriteoffBatchService(CustomerInvoiceWriteoffBatchService invoiceWriteoffBatchService) {
        this.invoiceWriteoffBatchService = invoiceWriteoffBatchService;
    }

    public void setDateTimeService(DateTimeService dateTimeService) {
        this.dateTimeService = dateTimeService;
    }

    public void setPaidAppliedService(InvoicePaidAppliedService<CustomerInvoiceDetail> paidAppliedService) {
        this.paidAppliedService = paidAppliedService;
    }

    public void setFinancialSystemUserService(FinancialSystemUserService financialSystemUserService) {
        this.financialSystemUserService = financialSystemUserService;
    }

    public void setObjectCodeService(ObjectCodeService objectCodeService) {
        this.objectCodeService = objectCodeService;
    }

}

