/*
 * 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.sys.document.service.impl;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.kuali.kfs.coa.businessobject.ObjectCode;
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.KRADConstants;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.krad.util.UrlFactory;
import org.kuali.kfs.pdp.PdpParameterConstants;
import org.kuali.kfs.pdp.businessobject.PaymentNoteText;
import org.kuali.kfs.pdp.businessobject.PurchasingPaymentDetail;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.batch.service.PaymentSourceToExtractService;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
import org.kuali.kfs.sys.businessobject.WireCharge;
import org.kuali.kfs.sys.document.PaymentSource;
import org.kuali.kfs.sys.document.service.AccountingDocumentRuleHelperService;
import org.kuali.kfs.sys.document.service.PaymentSourceHelperService;
import org.kuali.kfs.sys.document.validation.impl.AccountingDocumentRuleBaseConstants.GENERAL_LEDGER_PENDING_ENTRY_CODE;
import org.kuali.kfs.sys.service.BankService;
import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
import org.kuali.kfs.sys.service.HomeOriginationService;
import org.kuali.kfs.sys.service.OptionsService;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.core.api.util.type.KualiInteger;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import static org.kuali.kfs.sys.KFSConstants.GL_CREDIT_CODE;
import static org.kuali.kfs.sys.KFSConstants.GL_DEBIT_CODE;

public class PaymentSourceHelperServiceImpl implements PaymentSourceHelperService {
    org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PaymentSourceHelperServiceImpl.class);

    protected UniversityDateService universityDateService;
    protected BusinessObjectService businessObjectService;
    protected GeneralLedgerPendingEntryService generalLedgerPendingEntryService;
    protected BankService bankService;
    protected AccountingDocumentRuleHelperService accountingDocumentRuleHelperService;
    protected ParameterService parameterService;
    protected ConfigurationService configurationService;
    protected DocumentService documentService;
    protected OptionsService optionsService;
    protected HomeOriginationService homeOriginationService;

    /**
     * Retrieves the wire transfer information for the current fiscal year.
     *
     * @return <code>WireCharge</code>
     */
    @Override
    public WireCharge retrieveCurrentYearWireCharge() {
        WireCharge wireCharge = new WireCharge();
        wireCharge.setUniversityFiscalYear(getUniversityDateService().getCurrentFiscalYear());

        wireCharge = (WireCharge) getBusinessObjectService().retrieve(wireCharge);
        if (wireCharge == null) {
            LOG.error("Wire charge information not found for current fiscal year.");
            throw new RuntimeException("Wire charge information not found for current fiscal year.");
        }

        return wireCharge;
    }

    /**
     * Retrieves the wire charge for fiscal year based on the given date or null if one cannot be found
     *
     * @param date the date to find a wire charge for
     * @return the wire charge for the fiscal year of the given date, or null if the wire charge cannot be found
     */
    @Override
    public WireCharge retrieveWireChargeForDate(java.sql.Date date) {
        final Integer dateFiscalYear = getUniversityDateService().getFiscalYear(date);
        if (dateFiscalYear == null) {
            return null;
        }

        WireCharge wireCharge = new WireCharge();
        wireCharge.setUniversityFiscalYear(dateFiscalYear);
        wireCharge = (WireCharge) getBusinessObjectService().retrieve(wireCharge);
        return wireCharge;
    }

    /**
     * Builds an explicit and offset for the wire charge debit. The account associated with the first accounting is used for the
     * debit. The explicit and offset entries for the first accounting line and copied and customized for the wire charge.
     *
     * @param dvDocument     submitted disbursement voucher document
     * @param sequenceHelper helper class to keep track of GLPE sequence
     * @param wireCharge     wireCharge object from current fiscal year
     * @return GeneralLedgerPendingEntry generated wire charge debit
     */
    @Override
    public GeneralLedgerPendingEntry processWireChargeDebitEntries(PaymentSource paymentSource, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, WireCharge wireCharge) {
        LOG.debug("processWireChargeDebitEntries started");

        // grab the explicit entry for the first accounting line and adjust for wire charge entry
        GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry(paymentSource.getGeneralLedgerPendingEntry(0));
        explicitEntry.setTransactionLedgerEntrySequenceNumber(new Integer(sequenceHelper.getSequenceCounter()));
        explicitEntry.setFinancialObjectCode(wireCharge.getExpenseFinancialObjectCode());
        explicitEntry.setFinancialSubObjectCode(GENERAL_LEDGER_PENDING_ENTRY_CODE.getBlankFinancialSubObjectCode());
        explicitEntry.setTransactionDebitCreditCode(GL_DEBIT_CODE);

        String objectTypeCode = getOptionsService().getCurrentYearOptions().getFinObjTypeExpenditureexpCd();
        explicitEntry.setFinancialObjectTypeCode(objectTypeCode);

        String originationCode = getHomeOriginationService().getHomeOrigination().getFinSystemHomeOriginationCode();
        explicitEntry.setFinancialSystemOriginationCode(originationCode);

        if (KFSConstants.COUNTRY_CODE_UNITED_STATES.equals(paymentSource.getWireTransfer().getBankCountryCode())) {
            explicitEntry.setTransactionLedgerEntryAmount(wireCharge.getDomesticChargeAmt());
        } else {
            explicitEntry.setTransactionLedgerEntryAmount(wireCharge.getForeignChargeAmt());
        }

        explicitEntry.setTransactionLedgerEntryDescription("Automatic debit for wire transfer fee");
        explicitEntry.setFinancialBalanceTypeCode(wireCharge.getFiscalYear().getActualFinancialBalanceTypeCd());

        paymentSource.addPendingEntry(explicitEntry);
        sequenceHelper.increment();

        // handle the offset entry
        GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(explicitEntry);
        getGeneralLedgerPendingEntryService().populateOffsetGeneralLedgerPendingEntry(paymentSource.getPostingYear(), explicitEntry, sequenceHelper, offsetEntry);

        paymentSource.addPendingEntry(offsetEntry);
        sequenceHelper.increment();

        return explicitEntry;
    }

    /**
     * Builds an explicit and offset for the wire charge credit. The account and income object code found in the wire charge table
     * is used for the entry.
     *
     * @param dvDocument     submitted disbursement voucher document
     * @param sequenceHelper helper class to keep track of GLPE sequence
     * @param chargeEntry    GLPE charge
     * @param wireCharge     wireCharge object from current fiscal year
     */
    @Override
    public void processWireChargeCreditEntries(PaymentSource paymentSource, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, WireCharge wireCharge, GeneralLedgerPendingEntry chargeEntry) {
        LOG.debug("processWireChargeCreditEntries started");

        // copy the charge entry and adjust for credit
        GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry(chargeEntry);
        explicitEntry.setTransactionLedgerEntrySequenceNumber(new Integer(sequenceHelper.getSequenceCounter()));
        explicitEntry.setChartOfAccountsCode(wireCharge.getChartOfAccountsCode());
        explicitEntry.setAccountNumber(wireCharge.getAccountNumber());
        explicitEntry.setFinancialObjectCode(wireCharge.getIncomeFinancialObjectCode());

        // retrieve object type
        ObjectCode objectCode = wireCharge.getIncomeFinancialObject();
        explicitEntry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode());

        explicitEntry.setTransactionDebitCreditCode(GL_CREDIT_CODE);

        explicitEntry.setFinancialSubObjectCode(GENERAL_LEDGER_PENDING_ENTRY_CODE.getBlankFinancialSubObjectCode());
        explicitEntry.setSubAccountNumber(GENERAL_LEDGER_PENDING_ENTRY_CODE.getBlankSubAccountNumber());
        explicitEntry.setProjectCode(GENERAL_LEDGER_PENDING_ENTRY_CODE.getBlankProjectCode());

        explicitEntry.setTransactionLedgerEntryDescription("Automatic credit for wire transfer fee");
        explicitEntry.setFinancialBalanceTypeCode(wireCharge.getFiscalYear().getActualFinancialBalanceTypeCd());

        paymentSource.addPendingEntry(explicitEntry);
        sequenceHelper.increment();

        // handle the offset entry
        GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(explicitEntry);
        getGeneralLedgerPendingEntryService().populateOffsetGeneralLedgerPendingEntry(paymentSource.getPostingYear(), explicitEntry, sequenceHelper, offsetEntry);

        paymentSource.addPendingEntry(offsetEntry);
        sequenceHelper.increment();
    }

    /**
     * If bank specification is enabled generates bank offsetting entries for the document amount
     *
     * @param sequenceHelper    helper class to keep track of GLPE sequence
     * @param paymentMethodCode the payment method of this DV
     */
    @Override
    public boolean generateDocumentBankOffsetEntries(PaymentSource paymentSource, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, String wireTransferOrForeignDraftEntryDocumentType) {
        boolean success = true;

        if (!getBankService().isBankSpecificationEnabled()) {
            return success;
        }

        paymentSource.refreshReferenceObject(KFSPropertyConstants.BANK);

        final KualiDecimal bankOffsetAmount = getGeneralLedgerPendingEntryService().getOffsetToCashAmount(paymentSource).negated();
        GeneralLedgerPendingEntry bankOffsetEntry = new GeneralLedgerPendingEntry();
        success &= getGeneralLedgerPendingEntryService().populateBankOffsetGeneralLedgerPendingEntry(paymentSource.getBank(), bankOffsetAmount, paymentSource, paymentSource.getPostingYear(), sequenceHelper, bankOffsetEntry, KRADConstants.DOCUMENT_PROPERTY_NAME + "." + KFSPropertyConstants.DISB_VCHR_BANK_CODE);

        if (success) {
            bankOffsetEntry.setTransactionLedgerEntryDescription(getAccountingDocumentRuleHelperService().formatProperty(KFSKeyConstants.Bank.DESCRIPTION_GLPE_BANK_OFFSET));
            bankOffsetEntry.setFinancialDocumentTypeCode(wireTransferOrForeignDraftEntryDocumentType);
            paymentSource.addPendingEntry(bankOffsetEntry);
            sequenceHelper.increment();

            GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(bankOffsetEntry);
            success &= getGeneralLedgerPendingEntryService().populateOffsetGeneralLedgerPendingEntry(paymentSource.getPostingYear(), bankOffsetEntry, sequenceHelper, offsetEntry);
            bankOffsetEntry.setFinancialDocumentTypeCode(wireTransferOrForeignDraftEntryDocumentType);
            paymentSource.addPendingEntry(offsetEntry);
            sequenceHelper.increment();
        }

        return success;
    }

    /**
     * @see org.kuali.kfs.sys.document.service.PaymentSourceHelperService#generateDocumentBankOffsetEntries(org.kuali.kfs.sys.document.PaymentSource, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper, java.lang.String, org.kuali.rice.core.api.util.type.KualiDecimal)
     */
    @Override
    public boolean generateDocumentBankOffsetEntries(PaymentSource paymentSource, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, String wireTransferOrForeignDraftEntryDocumentType, KualiDecimal bankOffsetAmount) {
        boolean success = true;

        if (!getBankService().isBankSpecificationEnabled()) {
            return success;
        }

        paymentSource.refreshReferenceObject(KFSPropertyConstants.BANK);

        GeneralLedgerPendingEntry bankOffsetEntry = new GeneralLedgerPendingEntry();
        success &= getGeneralLedgerPendingEntryService().populateBankOffsetGeneralLedgerPendingEntry(paymentSource.getBank(), bankOffsetAmount, paymentSource, paymentSource.getPostingYear(), sequenceHelper, bankOffsetEntry, KRADConstants.DOCUMENT_PROPERTY_NAME + "." + KFSPropertyConstants.DISB_VCHR_BANK_CODE);

        if (success) {
            bankOffsetEntry.setTransactionLedgerEntryDescription(getAccountingDocumentRuleHelperService().formatProperty(KFSKeyConstants.Bank.DESCRIPTION_GLPE_BANK_OFFSET));
            bankOffsetEntry.setFinancialDocumentTypeCode(wireTransferOrForeignDraftEntryDocumentType);
            paymentSource.addPendingEntry(bankOffsetEntry);
            sequenceHelper.increment();

            GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(bankOffsetEntry);
            success &= getGeneralLedgerPendingEntryService().populateOffsetGeneralLedgerPendingEntry(paymentSource.getPostingYear(), bankOffsetEntry, sequenceHelper, offsetEntry);
            bankOffsetEntry.setFinancialDocumentTypeCode(wireTransferOrForeignDraftEntryDocumentType);
            paymentSource.addPendingEntry(offsetEntry);
            sequenceHelper.increment();
        }

        return success;
    }

    /**
     * Builds a link to a PurchasingPaymentDetail object
     *
     * @see org.kuali.kfs.sys.document.service.PaymentSourceHelperService#getDisbursementInfoUrl()
     */
    @Override
    public String getDisbursementInfoUrl() {
        final String basePath = getConfigurationService().getPropertyValueAsString(KFSConstants.APPLICATION_URL_KEY);

        Properties parameters = new Properties();
        parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.SEARCH_METHOD);
        parameters.put(KFSConstants.BACK_LOCATION, basePath + "/" + KFSConstants.MAPPING_PORTAL + ".do");
        parameters.put(KFSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, PurchasingPaymentDetail.class.getName());
        parameters.put(KFSConstants.HIDE_LOOKUP_RETURN_LINK, "true");
        parameters.put(KFSConstants.SUPPRESS_ACTIONS, "false");

        String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + KFSConstants.LOOKUP_ACTION, parameters);

        return lookupUrl;
    }

    /**
     * @see org.kuali.kfs.sys.batch.service.PaymentSourceExtractionService#buildNoteForCheckStubText(java.lang.String)
     */
    @Override
    public PaymentNoteText buildNoteForCheckStubText(String text, int previousLineCount) {
        PaymentNoteText pnt = null;
        final String maxNoteLinesParam = parameterService.getParameterValueAsString(KfsParameterConstants.PRE_DISBURSEMENT_ALL.class, PdpParameterConstants.MAX_NOTE_LINES);

        int maxNoteLines;
        try {
            maxNoteLines = Integer.parseInt(maxNoteLinesParam);
        } catch (NumberFormatException nfe) {
            throw new IllegalArgumentException("Invalid Max Notes Lines parameter, value: " + maxNoteLinesParam + " cannot be converted to an integer");
        }

        // The WordUtils should be sufficient for the majority of cases.  This method will
        // word wrap the whole string based on the MAX_NOTE_LINE_SIZE, separating each wrapped
        // word by a newline character.  The 'wrap' method adds line feeds to the end causing
        // the character length to exceed the max length by 1, hence the need for the replace
        // method before splitting.
        String wrappedText = WordUtils.wrap(text, KFSConstants.MAX_NOTE_LINE_SIZE);
        String[] noteLines = wrappedText.replaceAll("[\r]", "").split("\\n");

        // Loop through all the note lines.
        for (String noteLine : noteLines) {
            if (previousLineCount < (maxNoteLines - 3) && !StringUtils.isEmpty(noteLine)) {

                // This should only happen if we encounter a word that is greater than the max length.
                // The only concern I have for this occurring is with URLs/email addresses.
                if (noteLine.length() > KFSConstants.MAX_NOTE_LINE_SIZE) {
                    for (String choppedWord : chopWord(noteLine, KFSConstants.MAX_NOTE_LINE_SIZE)) {

                        // Make sure we're still under the maximum number of note lines.
                        if (previousLineCount < (maxNoteLines - 3) && !StringUtils.isEmpty(choppedWord)) {
                            pnt = new PaymentNoteText();
                            pnt.setCustomerNoteLineNbr(new KualiInteger(previousLineCount++));
                            pnt.setCustomerNoteText(choppedWord.replaceAll("\\n", "").trim());
                        }
                        // We can't add any additional note lines, or we'll exceed the maximum, therefore
                        // just break out of the loop early - there's nothing left to do.
                        else {
                            break;
                        }
                    }
                }
                // This should be the most common case.  Simply create a new PaymentNoteText,
                // add the line at the correct line location.
                else {
                    pnt = new PaymentNoteText();
                    pnt.setCustomerNoteLineNbr(new KualiInteger(previousLineCount++));
                    pnt.setCustomerNoteText(noteLine.replaceAll("\\n", "").trim());
                }
            }
        }
        return pnt;
    }

    /**
     * This method will take a word and simply chop into smaller
     * text segments that satisfy the limit requirements.  All words
     * brute force chopped, with no regard to preserving whole words.
     * <p>
     * For example:
     * <p>
     * "Java is a fun programming language!"
     * <p>
     * Might be chopped into:
     * <p>
     * "Java is a fun prog"
     * "ramming language!"
     *
     * @param word  The word that needs chopping
     * @param limit Number of character that should represent a chopped word
     * @return String [] of chopped words
     */
    protected String[] chopWord(String word, int limit) {
        StringBuilder builder = new StringBuilder();
        if (word != null && word.trim().length() > 0) {

            char[] chars = word.toCharArray();
            int index = 0;

            // First process all the words that fit into the limit.
            for (int i = 0; i < chars.length / limit; i++) {
                builder.append(String.copyValueOf(chars, index, limit));
                builder.append("\n");

                index += limit;
            }

            // Not all words will fit perfectly into the limit amount, so
            // calculate the modulus value to determine any remaining characters.
            int modValue = chars.length % limit;
            if (modValue > 0) {
                builder.append(String.copyValueOf(chars, index, modValue));
            }

        }

        // Split the chopped words into individual segments.
        return builder.toString().split("\\n");
    }

    /**
     * @see org.kuali.kfs.sys.document.service.PaymentSourceHelperService#oppositifyAndSaveEntry(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
     */
    @Override
    public void oppositifyAndSaveEntry(GeneralLedgerPendingEntry glpe, GeneralLedgerPendingEntrySequenceHelper glpeSeqHelper) {
        if (glpe.getTransactionDebitCreditCode().equals(KFSConstants.GL_CREDIT_CODE)) {
            glpe.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
        } else if (glpe.getTransactionDebitCreditCode().equals(KFSConstants.GL_DEBIT_CODE)) {
            glpe.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
        }
        if (glpeSeqHelper != null) {
            glpe.setTransactionLedgerEntrySequenceNumber(glpeSeqHelper.getSequenceCounter());
            glpeSeqHelper.increment();
        }
        glpe.setFinancialDocumentApprovedCode(KFSConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.APPROVED);
        businessObjectService.save(glpe);
    }

    /**
     * @see org.kuali.kfs.sys.document.service.PaymentSourceHelperService#handleEntryCancellation(org.kuali.kfs.sys.document.PaymentSource)
     */
    @Override
    public void handleEntryCancellation(PaymentSource paymentSource, PaymentSourceToExtractService<?> extractionService) {
        if (ObjectUtils.isNull(paymentSource.getGeneralLedgerPendingEntries()) || paymentSource.getGeneralLedgerPendingEntries().size() == 0) {
            // generate all the pending entries for the document
            getGeneralLedgerPendingEntryService().generateGeneralLedgerPendingEntries(paymentSource);
            // for each pending entry, opposite-ify it and reattach it to the document
            for (GeneralLedgerPendingEntry glpe : paymentSource.getGeneralLedgerPendingEntries()) {
                if (extractionService.shouldRollBackPendingEntry(glpe)) {
                    oppositifyAndSaveEntry(glpe, null);
                }
            }
        } else {
            List<GeneralLedgerPendingEntry> newGLPEs = new ArrayList<GeneralLedgerPendingEntry>();
            GeneralLedgerPendingEntrySequenceHelper glpeSeqHelper = new GeneralLedgerPendingEntrySequenceHelper(paymentSource.getGeneralLedgerPendingEntries().size() + 1);
            for (GeneralLedgerPendingEntry glpe : paymentSource.getGeneralLedgerPendingEntries()) {
                glpe.refresh();
                if (extractionService.shouldRollBackPendingEntry(glpe)) {
                    if (glpe.getFinancialDocumentApprovedCode().equals(KFSConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.PROCESSED)) {
                        // damn! it got processed! well, make a copy, oppositify, and save
                        GeneralLedgerPendingEntry undoer = new GeneralLedgerPendingEntry(glpe);
                        oppositifyAndSaveEntry(undoer, glpeSeqHelper);
                        newGLPEs.add(undoer);
                    } else {
                        // just delete the GLPE before anything happens to it
                        businessObjectService.delete(glpe);
                    }
                } else {
                    newGLPEs.add(glpe);
                }
            }
            paymentSource.setGeneralLedgerPendingEntries(newGLPEs);
        }
    }

    /**
     * @return the implementation of the UniversityDateService to use
     */
    public UniversityDateService getUniversityDateService() {
        return universityDateService;
    }

    /**
     * Sets the implementation of the UniversityDateService to use
     *
     * @param universityDateService the implementation of the UniversityDateService to use
     */
    public void setUniversityDateService(UniversityDateService universityDateService) {
        this.universityDateService = universityDateService;
    }

    /**
     * @return the implementation of the BusinessObjectService to use
     */
    public BusinessObjectService getBusinessObjectService() {
        return businessObjectService;
    }

    /**
     * Sets the implementation of the BusinessObjectService to use
     *
     * @param businessObjectService the implementation of the BusinessObjectService to use
     */
    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
        this.businessObjectService = businessObjectService;
    }

    /**
     * @return the implementation of the GeneralLedgerPendingEntryService to use
     */
    public GeneralLedgerPendingEntryService getGeneralLedgerPendingEntryService() {
        return generalLedgerPendingEntryService;
    }

    /**
     * Sets the implementation of the GeneralLedgerPendingEntryService to use
     *
     * @param generalLedgerPendingEntryService the implementation of the GeneralLedgerPendingEntryService to use
     */
    public void setGeneralLedgerPendingEntryService(GeneralLedgerPendingEntryService generalLedgerPendingEntryService) {
        this.generalLedgerPendingEntryService = generalLedgerPendingEntryService;
    }

    /**
     * @return the implementation of the BankService to use
     */
    public BankService getBankService() {
        return bankService;
    }

    /**
     * Sets the implementation of the BankService to use
     *
     * @param bankService the implementation of the BankService to use
     */
    public void setBankService(BankService bankService) {
        this.bankService = bankService;
    }

    /**
     * @return the implementation of the AccountingDocumentRuleHelperService to use
     */
    public AccountingDocumentRuleHelperService getAccountingDocumentRuleHelperService() {
        return accountingDocumentRuleHelperService;
    }

    /**
     * Sets the implementation of the AccountingDocumentRuleHelperService to use
     *
     * @param accountingDocumentRuleService the implementation of the AccountingDocumentRuleHelperService to use
     */
    public void setAccountingDocumentRuleHelperService(AccountingDocumentRuleHelperService accountingDocumentRuleService) {
        this.accountingDocumentRuleHelperService = accountingDocumentRuleService;
    }

    /**
     * @return the implementation of the ParameterService to use
     */
    public ParameterService getParameterService() {
        return parameterService;
    }

    /**
     * Sets the implementation of the ParameterService to use
     *
     * @param parameterService the implementation of the ParameterService to use
     */
    public void setParameterService(ParameterService parameterService) {
        this.parameterService = parameterService;
    }

    /**
     * @return the implementation of the ConfigurationService to use
     */
    public ConfigurationService getConfigurationService() {
        return configurationService;
    }

    /**
     * Sets the implementation of the ConfigurationService to use
     *
     * @param configurationService the implementation of the ConfigurationService to use
     */
    public void setConfigurationService(ConfigurationService configurationService) {
        this.configurationService = configurationService;
    }

    /**
     * @return the injected implementation of the DocumentService to use
     */
    public DocumentService getDocumentService() {
        return documentService;
    }

    /**
     * Injects the implementation of DocumentService to use
     *
     * @param documentService an implementation of DocumentService
     */
    public void setDocumentService(DocumentService documentService) {
        this.documentService = documentService;
    }

    /**
     * @return the injected implementation of OptionsService to use
     */
    public OptionsService getOptionsService() {
        return optionsService;
    }

    /**
     * Injects the implementation of OptionsService to use
     *
     * @param optionsService an implementation of OptionsService
     */
    public void setOptionsService(OptionsService optionsService) {
        this.optionsService = optionsService;
    }

    /**
     * @return the injected implementation of HomeOriginationSerice to use
     */
    public HomeOriginationService getHomeOriginationService() {
        return homeOriginationService;
    }

    /**
     * Injects the implementation of HomeOriginationService to use
     *
     * @param homeOriginationService an implementation of HomeOriginationService
     */
    public void setHomeOriginationService(HomeOriginationService homeOriginationService) {
        this.homeOriginationService = homeOriginationService;
    }
}
