/*
 * 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.tem.document;

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.fp.document.DisbursementVoucherDocument;
import org.kuali.kfs.integration.purap.PurchasingAccountsPayableModuleService;
import org.kuali.kfs.krad.document.Document;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.module.tem.TemConstants;
import org.kuali.kfs.module.tem.TemKeyConstants;
import org.kuali.kfs.module.tem.businessobject.ActualExpense;
import org.kuali.kfs.module.tem.businessobject.ImportedExpense;
import org.kuali.kfs.module.tem.businessobject.PerDiemExpense;
import org.kuali.kfs.module.tem.businessobject.TemExpense;
import org.kuali.kfs.module.tem.businessobject.TravelPayment;
import org.kuali.kfs.module.tem.document.service.AccountingDocumentRelationshipService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.batch.service.PaymentSourceExtractionService;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
import org.kuali.kfs.sys.businessobject.PaymentSourceWireTransfer;
import org.kuali.kfs.sys.businessobject.WireCharge;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.PaymentSource;
import org.kuali.kfs.sys.document.service.PaymentSourceHelperService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.identity.PersonService;

import javax.persistence.Column;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.kuali.kfs.module.tem.TemConstants.DISBURSEMENT_VOUCHER_DOCTYPE;

public abstract class TEMReimbursementDocument extends TravelDocumentBase implements PaymentSource {
    private String paymentMethod = KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_CHECK;
    private TravelPayment travelPayment;
    private PaymentSourceWireTransfer wireTransfer;
    private volatile transient Person initiator;
    private Date corporateCardPaymentExtractDate;
    private Date corporateCardPaymentPaidDate;
    private Date corporateCardPaymentCancelDate;

    private static transient volatile PaymentSourceHelperService paymentSourceHelperService;
    private static transient volatile PaymentSourceExtractionService paymentSourceExtractionService;
    private static transient volatile AccountingDocumentRelationshipService accountingDocumentRelationshipService;
    private static transient volatile PurchasingAccountsPayableModuleService purapModuleService;


    @Column(name = "PAYMENT_METHOD", nullable = true, length = 15)
    public String getPaymentMethod() {
        return paymentMethod;
    }

    public void setPaymentMethod(String paymentMethod) {
        this.paymentMethod = paymentMethod;
    }

    /**
     * @return the travel payment associated with this document
     */
    public TravelPayment getTravelPayment() {
        return travelPayment;
    }

    /**
     * Sets the travel payment to be associated with this document
     *
     * @param travelPayment a travel payment for this document
     */
    public void setTravelPayment(TravelPayment travelPayment) {
        this.travelPayment = travelPayment;
    }

    /**
     * @return the wire transfer associated with this document
     */
    @Override
    public PaymentSourceWireTransfer getWireTransfer() {
        return wireTransfer;
    }

    /**
     * Sets the wire transfer associated with this document
     *
     * @param wireTransfer the wire transfer for this document
     */
    public void setWireTransfer(PaymentSourceWireTransfer wireTransfer) {
        this.wireTransfer = wireTransfer;
    }

    /**
     * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#initiateDocument()
     */
    @Override
    public void initiateDocument() {
        super.initiateDocument();

        //clear expenses
        setActualExpenses(new ArrayList<ActualExpense>());
        setPerDiemExpenses(new ArrayList<PerDiemExpense>());

        //default dates if null
        Calendar calendar = getDateTimeService().getCurrentCalendar();
        if (getTripBegin() == null) {
            calendar.add(Calendar.DAY_OF_MONTH, 1);
            setTripBegin(new Timestamp(calendar.getTimeInMillis()));

        }
        if (getTripEnd() == null) {
            calendar.add(Calendar.DAY_OF_MONTH, 2);
            setTripEnd(new Timestamp(calendar.getTimeInMillis()));
        }

        // initiate payment and wire transfer
        this.travelPayment = new TravelPayment();
        this.wireTransfer = new PaymentSourceWireTransfer();
        // set due date to the next day
        calendar = getDateTimeService().getCurrentCalendar();
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        travelPayment.setDueDate(new java.sql.Date(calendar.getTimeInMillis()));
        travelPayment.setCheckStubText(getConfigurationService().getPropertyValueAsString(TemKeyConstants.MESSAGE_TEM_REIMBURSEMENT_PAYMENT_HOLD_TEXT));

        // set up the default bank
        setDefaultBankCode();
        updatePayeeTypeForReimbursable();
    }

    /**
     * For reimbursable documents, sets the proper payee type code and profile id after a profile lookup
     */
    public void updatePayeeTypeForReimbursable() {
        if (!ObjectUtils.isNull(getTraveler()) && !ObjectUtils.isNull(getTravelPayment()) && !StringUtils.isBlank(getTraveler().getTravelerTypeCode())) {
            if (getTravelerService().isEmployee(getTraveler())) {
                getTravelPayment().setPayeeTypeCode(KFSConstants.PaymentPayeeTypes.EMPLOYEE);
            } else {
                getTravelPayment().setPayeeTypeCode(KFSConstants.PaymentPayeeTypes.CUSTOMER);
            }
        }
    }

    /**
     * change to generate the GLPE on ACTUAL EXPENSE based on Reimbursable minus Advance and its only ONE entry to
     * DV clearning account
     *
     * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#generateDocumentGeneralLedgerPendingEntries
     * (org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
     */
    @Override
    public boolean generateDocumentGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
        LOG.info("processDocumentGenerateGeneralLedgerPendingEntries for TEMReimbursementDocument - start");

        boolean success = true;
        boolean doGenerate = true;

        success = super.generateDocumentGeneralLedgerPendingEntries(sequenceHelper);

        KualiDecimal reimbursableToTraveler = getAmountForTravelClearing();

        if (KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_WIRE.equals(getTravelPayment().getPaymentMethodCode()) && !getWireTransfer().isWireTransferFeeWaiverIndicator()) {
            LOG.debug("generating wire charge gl pending entries.");

            // retrieve wire charge
            WireCharge wireCharge = getPaymentSourceHelperService().retrieveCurrentYearWireCharge();

            // generate debits
            GeneralLedgerPendingEntry chargeEntry = getPaymentSourceHelperService().processWireChargeDebitEntries(this, sequenceHelper, wireCharge);

            // generate credits
            getPaymentSourceHelperService().processWireChargeCreditEntries(this, sequenceHelper, wireCharge, chargeEntry);
        }

        // for wire or drafts generate bank offset entry (if enabled), for ACH and checks offset will be generated by PDP
        if (KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_WIRE.equals(getTravelPayment().getPaymentMethodCode()) || KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_DRAFT.equals(getTravelPayment().getPaymentMethodCode())) {
            getPaymentSourceHelperService().generateDocumentBankOffsetEntries(this, sequenceHelper, getWireTransferOrForeignDraftDocumentType());
        }

        LOG.info("processDocumentGenerateGeneralLedgerPendingEntries for TEMReimbursementDocument - end");

        return success;
    }

    /**
     * @return the amount that should be used in the travel clearing pending entries
     */
    protected KualiDecimal getAmountForTravelClearing() {
        KualiDecimal reimbursableToTraveler = getTravelReimbursementService().getReimbursableToTraveler(this);
        return reimbursableToTraveler;
    }

    /**
     * @see org.kuali.kfs.sys.document.AccountingDocumentBase#customizeExplicitGeneralLedgerPendingEntry(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry)
     */
    @Override
    public void customizeExplicitGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail postable, GeneralLedgerPendingEntry explicitEntry) {
        /* change document type based on payment method to pick up different offsets; if payment method code is blank, use the normal doc type name */
        explicitEntry.setFinancialDocumentTypeCode(getPaymentDocumentType());
        if (LOG.isDebugEnabled()) {
            LOG.debug("changing doc type on pending entry " + explicitEntry.getTransactionLedgerEntrySequenceNumber() + " to " + explicitEntry.getFinancialDocumentTypeCode());
        }
    }

    /**
     * @return the document type to use for entries associated with this document, which is based, if possible, on the payment type
     */
    public String getPaymentDocumentType() {
        if (!StringUtils.isBlank(getTravelPayment().getPaymentMethodCode())) {
            if (KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_CHECK.equals(getTravelPayment().getPaymentMethodCode())) {
                return getAchCheckDocumentType();
            }
            return getWireTransferOrForeignDraftDocumentType();
        }
        return getDocumentTypeName();
    }

    /**
     * @return the FSLO document type associated with ach/check entries for this document type
     */
    public abstract String getAchCheckDocumentType();

    /**
     * @return the FSLO document type associated with wire transfer or foreign draft entries for this document type
     */
    public abstract String getWireTransferOrForeignDraftDocumentType();

    /**
     * @return RCCA, the corporate card payment ach/check document type
     */
    public String getCorporateCardPaymentAchCheckDocumentType() {
        return TemConstants.TravelDocTypes.REIMBURSABLE_CORPORATE_CARD_CHECK_ACH_DOCUMENT;
    }

    /**
     * @see org.kuali.kfs.module.tem.document.TravelDocument#getReimbursableTotal()
     */
    @Override
    public KualiDecimal getReimbursableTotal() {
        KualiDecimal eligible = getEligibleAmount();
        final KualiDecimal expenseLimit = getExpenseLimit();

        if (expenseLimit != null && expenseLimit.doubleValue() > 0) {
            return eligible.isGreaterThan(expenseLimit) ? expenseLimit : eligible;
        }

        return eligible;
    }

    /**
     * Get eligible amount
     *
     * @return
     */
    public KualiDecimal getEligibleAmount() {
        return getApprovedAmount().subtract(getCTSTotal()).subtract(getCorporateCardTotal());
    }

    /**
     * Total amount to be paid to Vendor
     *
     * @return
     */
    public KualiDecimal getTotalPaidAmountToVendor() {
        KualiDecimal totalPaidAmountToVendor = KualiDecimal.ZERO;
        List<Document> relatedDisbursementList = getTravelDocumentService().getDocumentsRelatedTo(this, DISBURSEMENT_VOUCHER_DOCTYPE);
        for (Document document : relatedDisbursementList) {
            if (document instanceof DisbursementVoucherDocument) {
                DisbursementVoucherDocument dv = (DisbursementVoucherDocument) document;
                if (dv.getDocumentHeader().getWorkflowDocument().isFinal()) {
                    totalPaidAmountToVendor = totalPaidAmountToVendor.add(dv.getDisbVchrCheckTotalAmount());
                }
            }
        }
        return totalPaidAmountToVendor;
    }

    /**
     * Total requested to be paid
     *
     * @return
     */
    public KualiDecimal getTotalPaidAmountToRequests() {
        KualiDecimal totalPaidAmountToRequests = KualiDecimal.ZERO;

        Set<String> documentNumbers = getAccountingDocumentRelationshipService().huntForRelatedDocumentNumbersWithDocumentType(documentNumber, TemConstants.REQUISITION_DOCTYPE);
        if (documentNumbers != null && !documentNumbers.isEmpty()) {
            List<String> relatedRequisitionDocumentNumbers = new ArrayList<String>();
            relatedRequisitionDocumentNumbers.addAll(documentNumbers);
            totalPaidAmountToRequests = getPurchasingAccountsPayableModuleService().getTotalPaidAmountToRequisitions(relatedRequisitionDocumentNumbers);
        }
        return totalPaidAmountToRequests;
    }

    /**
     * @return
     */
    public KualiDecimal getReimbursableGrandTotal() {
        KualiDecimal grandTotal = KualiDecimal.ZERO;
        grandTotal = getApprovedAmount().add(getTotalPaidAmountToVendor()).add(getTotalPaidAmountToRequests());

        if (!grandTotal.isPositive()) {
            return KualiDecimal.ZERO;
        }

        return grandTotal;
    }

    /**
     * @return the amount that should be disbursed to the traveler for this reimbursement
     */
    public KualiDecimal getPaymentAmount() {
        final KualiDecimal reimbursableExpenseTotal = getEligibleAmount();
        if (reimbursableExpenseTotal.isLessThan(KualiDecimal.ZERO)) {
            return KualiDecimal.ZERO;
        }

        final KualiDecimal reimbursableExpenseTotalWithExpenseLimit = applyExpenseLimit(reimbursableExpenseTotal);
        return reimbursableExpenseTotalWithExpenseLimit;
    }

    /**
     * @return
     */
    public boolean requiresTravelerApprovalRouting() {
        return getTravelDocumentService().requiresTravelerApproval(this);
    }

    /**
     * @see org.kuali.kfs.module.tem.document.TravelDocument#getPerDiemAdjustment()
     */
    @Override
    public KualiDecimal getPerDiemAdjustment() {
        //do not use per diem adjustment
        return null;
    }

    /**
     * @see org.kuali.kfs.module.tem.document.TravelDocument#setPerDiemAdjustment(org.kuali.rice.kns.util.KualiDecimal)
     */
    @Override
    public void setPerDiemAdjustment(KualiDecimal perDiemAdjustment) {
    }

    /**
     * @see org.kuali.kfs.module.tem.document.TravelDocument#getExpenseTypeCode()
     */
    @Override
    public String getDefaultCardTypeCode() {
        return TemConstants.ACTUAL_EXPENSE;
    }

    /**
     * @return the value of the travelPayment's attachmentCode
     */
    @Override
    public boolean hasAttachment() {
        return getTravelPayment().isAttachmentCode();
    }

    /**
     * @return the payment method code from the travelPayment
     */
    @Override
    public String getPaymentMethodCode() {
        return getTravelPayment().getPaymentMethodCode();
    }

    public Date getCorporateCardPaymentExtractDate() {
        return corporateCardPaymentExtractDate;
    }

    public void setCorporateCardPaymentExtractDate(Date corporateCardPaymentExtractDate) {
        this.corporateCardPaymentExtractDate = corporateCardPaymentExtractDate;
    }

    public Date getCorporateCardPaymentPaidDate() {
        return corporateCardPaymentPaidDate;
    }

    public void setCorporateCardPaymentPaidDate(Date corporateCardPaymentPaidDate) {
        this.corporateCardPaymentPaidDate = corporateCardPaymentPaidDate;
    }

    public Date getCorporateCardPaymentCancelDate() {
        return corporateCardPaymentCancelDate;
    }

    public void setCorporateCardPaymentCancelDate(Date corporateCardPaymentCancelDate) {
        this.corporateCardPaymentCancelDate = corporateCardPaymentCancelDate;
    }

    /*
     *  Used for searching on ENT and RELO.
     *
     */
    public String getTemProfileName() {
        if (!ObjectUtils.isNull(getTraveler()) && !StringUtils.isBlank(getTraveler().getName())) {
            return getTraveler().getName().toUpperCase();
        }
        return KFSConstants.EMPTY_STRING;
    }

    /**
     * Returns the campus code of the initiator
     *
     * @see org.kuali.kfs.sys.document.PaymentSource#getCampusCode()
     */
    @Override
    public String getCampusCode() {
        return getInitiator().getCampusCode();
    }

    /**
     * Caches and returns the Person record of the initiator of this document
     *
     * @return a Person record for the initiator of this document
     */
    protected Person getInitiator() {
        if (initiator == null) {
            initiator = SpringContext.getBean(PersonService.class).getPerson(getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId());
        }
        return initiator;
    }

    /**
     * Overridden to set document number on travel payment and wire transfer
     *
     * @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#prepareForSave()
     */
    @Override
    public void prepareForSave() {
        super.prepareForSave();

        if (wireTransfer != null) {
            wireTransfer.setDocumentNumber(this.documentNumber);
        }
        if (travelPayment != null) {
            travelPayment.setDocumentNumber(this.documentNumber);
        }
    }

    /**
     * Overridden to extract immediate payment if necerssary
     *
     * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#doRouteStatusChange(org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange)
     */
    @Override
    public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
        super.doRouteStatusChange(statusChangeEvent);
        if (getDocumentHeader().getWorkflowDocument().isProcessed()) {
            if (getTravelPayment().isImmediatePaymentIndicator()) {
                getPaymentSourceExtractionService().extractSingleImmediatePayment(this);
            }
        }
    }

    /**
     * Every reimbursable document debits on entry
     *
     * @see org.kuali.kfs.sys.document.AccountingDocumentBase#isDebit(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
     */
    @Override
    public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) {
        return true;
    }

    /**
     * Determines if any imported expenses on the document have a card type of CORP
     *
     * @return true if there are CORP imported expenses on the document, false otherwise
     */
    public boolean isCorporateCardPayable() {
        if (getImportedExpenses() != null && !getImportedExpenses().isEmpty()) {
            for (ImportedExpense expense : getImportedExpenses()) {
                if (StringUtils.equals(expense.getCardType(), TemConstants.TRAVEL_TYPE_CORP)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Based on which pdp dates are present (extract, paid, canceled), determines a String for the status
     *
     * @return a String representation of the status
     */
    public String getCorporateCardPaymentPdpStatus() {
        if (corporateCardPaymentCancelDate != null) {
            return "Canceled";
        }
        if (corporateCardPaymentPaidDate != null) {
            return "Paid";
        }
        if (corporateCardPaymentExtractDate != null) {
            return "Extracted";
        }
        return "Pre-Extraction";
    }

    /**
     * Reimbursable documents route by profile account if they have no actual expenses, per diem expenses, or reimbursable imported expenses
     *
     * @see org.kuali.kfs.module.tem.document.TravelDocumentBase#shouldRouteByProfileAccount()
     */
    @Override
    protected boolean shouldRouteByProfileAccount() {
        final boolean shouldRouteByProfileAccount = !hasReimbursableExpenses() || hasOnlyPrepaidExpenses();
        return shouldRouteByProfileAccount;
    }

    /**
     * @return true if there are reimbursable expenses on this document and false if there are not
     */
    public boolean hasReimbursableExpenses() {
        final boolean actualExpenses = hasReimbursableExpenses(getActualExpenses());
        final boolean perDiemExpenses = getPerDiemExpenses() != null && !getPerDiemExpenses().isEmpty();
        final boolean reimbursableImportedExpenses = hasReimbursableExpenses(getImportedExpenses());
        return (actualExpenses || perDiemExpenses || reimbursableImportedExpenses);
    }

    /**
     * @return true if there are reimbursable imported expenses; false otherwise
     */
    protected boolean hasReimbursableExpenses(List<? extends TemExpense> expenses) {
        if (expenses == null || expenses.isEmpty()) {
            return false; // no imported expenses; let's just skip out now...
        }
        for (TemExpense expense : expenses) {
            if (!expense.getNonReimbursable()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Return the expense date if it exists
     *
     * @see org.kuali.kfs.module.tem.document.TravelDocument#getEffectiveDateForMileageRate(org.kuali.kfs.module.tem.businessobject.ActualExpense)
     */
    @Override
    public Date getEffectiveDateForMileageRate(ActualExpense expense) {
        if (expense != null && expense.getExpenseDate() != null) {
            return expense.getExpenseDate();
        }
        return null;
    }

    /**
     * Returns the "mileage date" on the expense if it exists
     *
     * @see org.kuali.kfs.module.tem.document.TravelDocument#getEffectiveDateForMileageRate(org.kuali.kfs.module.tem.businessobject.PerDiemExpense)
     */
    @Override
    public Date getEffectiveDateForMileageRate(PerDiemExpense expense) {
        if (expense != null && expense.getMileageDate() != null) {
            return new java.sql.Date(expense.getMileageDate().getTime());
        }
        return null;
    }

    /**
     * Returns the mileage expense date if it exists
     *
     * @see org.kuali.kfs.module.tem.document.TravelDocument#getEffectiveDateForPerDiem(org.kuali.kfs.module.tem.businessobject.PerDiemExpense)
     */
    @Override
    public Date getEffectiveDateForPerDiem(PerDiemExpense expense) {
        if (expense != null && expense.getMileageDate() != null) {
            return getEffectiveDateForPerDiem(expense.getMileageDate());
        }
        return null;
    }

    /**
     * Just returns the date back
     *
     * @see org.kuali.kfs.module.tem.document.TravelDocument#getEffectiveDateForPerDiem(java.sql.Date)
     */
    @Override
    public Date getEffectiveDateForPerDiem(java.sql.Timestamp expenseDate) {
        return new java.sql.Date(expenseDate.getTime());
    }

    @Override
    public Map<String, String> getDisapprovedAppDocStatusMap() {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * @return the default implementation of the PaymentSourceHelperService
     */
    public static PaymentSourceHelperService getPaymentSourceHelperService() {
        if (paymentSourceHelperService == null) {
            paymentSourceHelperService = SpringContext.getBean(PaymentSourceHelperService.class);
        }
        return paymentSourceHelperService;
    }

    /**
     * @return the default implementation of the PaymentSourceHelperService
     */
    public static PaymentSourceExtractionService getPaymentSourceExtractionService() {
        if (paymentSourceExtractionService == null) {
            paymentSourceExtractionService = SpringContext.getBean(PaymentSourceExtractionService.class, TemConstants.REIMBURSABLE_PAYMENT_SOURCE_EXTRACTION_SERVICE);
        }
        return paymentSourceExtractionService;
    }

    public static AccountingDocumentRelationshipService getAccountingDocumentRelationshipService() {
        if (accountingDocumentRelationshipService == null) {
            accountingDocumentRelationshipService = SpringContext.getBean(AccountingDocumentRelationshipService.class);
        }
        return accountingDocumentRelationshipService;
    }

    public static PurchasingAccountsPayableModuleService getPurchasingAccountsPayableModuleService() {
        if (purapModuleService == null) {
            purapModuleService = SpringContext.getBean(PurchasingAccountsPayableModuleService.class);
        }
        return purapModuleService;
    }

}
