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

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.Account;
import org.kuali.kfs.integration.cg.ContractsAndGrantsModuleService;
import org.kuali.kfs.integration.ld.LaborLedgerExpenseTransferAccountingLine;
import org.kuali.kfs.integration.ld.LaborLedgerExpenseTransferSourceAccountingLine;
import org.kuali.kfs.integration.ld.LaborLedgerExpenseTransferTargetAccountingLine;
import org.kuali.kfs.integration.ld.LaborModuleService;
import org.kuali.kfs.krad.UserSession;
import org.kuali.kfs.krad.bo.AdHocRoutePerson;
import org.kuali.kfs.krad.service.BusinessObjectService;
import org.kuali.kfs.krad.service.DocumentService;
import org.kuali.kfs.krad.service.KualiModuleService;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.module.ec.EffortConstants;
import org.kuali.kfs.module.ec.EffortKeyConstants;
import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail;
import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetailBuild;
import org.kuali.kfs.module.ec.businessobject.EffortCertificationDocumentBuild;
import org.kuali.kfs.module.ec.businessobject.EffortCertificationReportDefinition;
import org.kuali.kfs.module.ec.document.EffortCertificationDocument;
import org.kuali.kfs.module.ec.document.validation.impl.EffortCertificationDocumentRuleUtil;
import org.kuali.kfs.module.ec.service.EffortCertificationDocumentService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.MessageBuilder;
import org.kuali.kfs.sys.businessobject.AccountingLineOverride;
import org.kuali.kfs.sys.businessobject.AccountingLineOverride.COMPONENT;
import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kew.api.action.ActionRequestType;
import org.kuali.rice.kew.api.action.ActionTaken;
import org.kuali.rice.kew.api.action.ActionType;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.identity.PersonService;
import org.kuali.rice.kim.api.services.KimApiServiceLocator;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * To implement the services related to the effort certification document
 */
@Transactional
public class EffortCertificationDocumentServiceImpl implements EffortCertificationDocumentService {
    public static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(EffortCertificationDocumentServiceImpl.class);

    private LaborModuleService laborModuleService;
    private KualiModuleService kualiModuleService;
    private ContractsAndGrantsModuleService contractsAndGrantsModuleService;

    private DocumentService documentService;
    private BusinessObjectService businessObjectService;

    /**
     * @see org.kuali.kfs.module.ec.service.EffortCertificationDocumentService#processApprovedEffortCertificationDocument(org.kuali.kfs.module.ec.document.EffortCertificationDocument)
     */
    @Override
    public void processApprovedEffortCertificationDocument(EffortCertificationDocument effortCertificationDocument) {
        WorkflowDocument workflowDocument = effortCertificationDocument.getDocumentHeader().getWorkflowDocument();

        if (workflowDocument.isProcessed()) {
            GlobalVariables.setUserSession(new UserSession(KFSConstants.SYSTEM_USER));
            generateSalaryExpenseTransferDocument(effortCertificationDocument);
        }
    }

    /**
     * @see org.kuali.kfs.module.ec.service.EffortCertificationDocumentService#createAndRouteEffortCertificationDocument(org.kuali.kfs.module.ec.businessobject.EffortCertificationDocumentBuild)
     */

    @Override
    public boolean createAndRouteEffortCertificationDocument(EffortCertificationDocumentBuild effortCertificationDocumentBuild) {
        try {
            EffortCertificationDocument effortCertificationDocument = (EffortCertificationDocument) documentService.getNewDocument(EffortConstants.EffortDocumentTypes.EFFORT_CERTIFICATION_DOCUMENT);
            populateEffortCertificationDocument(effortCertificationDocument, effortCertificationDocumentBuild);
            documentService.routeDocument(effortCertificationDocument, KFSConstants.EMPTY_STRING, null);
        } catch (WorkflowException we) {
            LOG.error("Unable to route ECD document: " + effortCertificationDocumentBuild, we);
            throw new RuntimeException("Unable to route ECD document: " + effortCertificationDocumentBuild, we);
        }

        return true;
    }

    /**
     * @see org.kuali.kfs.module.ec.service.EffortCertificationDocumentService#populateEffortCertificationDocument(org.kuali.kfs.module.ec.document.EffortCertificationDocument,
     * org.kuali.kfs.module.ec.businessobject.EffortCertificationDocumentBuild)
     */

    @Override
    public boolean populateEffortCertificationDocument(EffortCertificationDocument effortCertificationDocument, EffortCertificationDocumentBuild effortCertificationDocumentBuild) {
        // populate the fields of the document
        effortCertificationDocument.setUniversityFiscalYear(effortCertificationDocumentBuild.getUniversityFiscalYear());
        effortCertificationDocument.setEmplid(effortCertificationDocumentBuild.getEmplid());
        effortCertificationDocument.setEffortCertificationReportNumber(effortCertificationDocumentBuild.getEffortCertificationReportNumber());
        effortCertificationDocument.setEffortCertificationDocumentCode(effortCertificationDocumentBuild.getEffortCertificationDocumentCode());

        // populate the detail line of the document
        List<EffortCertificationDetail> detailLines = effortCertificationDocument.getEffortCertificationDetailLines();
        detailLines.clear();

        List<EffortCertificationDetailBuild> detailLinesBuild = effortCertificationDocumentBuild.getEffortCertificationDetailLinesBuild();
        for (EffortCertificationDetailBuild detailLineBuild : detailLinesBuild) {
            detailLines.add(new EffortCertificationDetail(detailLineBuild));
        }

        // populate the document header of the document
        FinancialSystemDocumentHeader documentHeader = effortCertificationDocument.getFinancialSystemDocumentHeader();
        documentHeader.setDocumentDescription(effortCertificationDocumentBuild.getEmplid());
        documentHeader.setFinancialDocumentTotalAmount(EffortCertificationDocument.getDocumentTotalAmount(effortCertificationDocument));

        return true;
    }

    /**
     * @see org.kuali.kfs.module.ec.service.EffortCertificationDocumentService#resetEffortCertificationDetailLines(org.kuali.kfs.module.ec.document.EffortCertificationDocument)
     */

    @Override
    public void removeEffortCertificationDetailLines(EffortCertificationDocument effortCertificationDocument) {
        Map<String, String> fieldValues = new HashMap<String, String>();
        fieldValues.put(KFSPropertyConstants.DOCUMENT_NUMBER, effortCertificationDocument.getDocumentNumber());

        businessObjectService.deleteMatching(EffortCertificationDetail.class, fieldValues);
    }

    /**
     * @see org.kuali.kfs.module.ec.service.EffortCertificationDocumentService#generateSalaryExpenseTransferDocument(org.kuali.kfs.module.ec.document.EffortCertificationDocument)
     */

    @Override
    public boolean generateSalaryExpenseTransferDocument(EffortCertificationDocument effortCertificationDocument) {
        List<LaborLedgerExpenseTransferAccountingLine> sourceAccoutingLines = this.buildSourceAccountingLines(effortCertificationDocument);
        List<LaborLedgerExpenseTransferAccountingLine> targetAccoutingLines = this.buildTargetAccountingLines(effortCertificationDocument);

        if (sourceAccoutingLines.isEmpty() || targetAccoutingLines.isEmpty()) {
            return true;
        }

        String description = effortCertificationDocument.getEmplid();
        String explanation = MessageBuilder.buildMessageWithPlaceHolder(EffortKeyConstants.MESSAGE_CREATE_SET_DOCUMENT_DESCRIPTION, effortCertificationDocument.getDocumentNumber()).toString();

        String annotation = KFSConstants.EMPTY_STRING;
        List<String> adHocRecipients = new ArrayList<String>();
        adHocRecipients.addAll(this.getFiscalOfficersIfAmountChanged(effortCertificationDocument));

        try {
            laborModuleService.createAndBlankApproveSalaryExpenseTransferDocument(description, explanation, annotation, adHocRecipients, sourceAccoutingLines, targetAccoutingLines);
        } catch (WorkflowException we) {
            LOG.error("Error while routing SET document created from ECD: " + effortCertificationDocument, we);
            throw new RuntimeException("Error while routing SET document created from ECD: " + effortCertificationDocument, we);
        }
        return true;
    }

    /**
     * @see org.kuali.kfs.module.ec.service.EffortCertificationDocumentService#addRouteLooping(org.kuali.kfs.module.ec.document.EffortCertificationDocument)
     */

    @Override
    public void addRouteLooping(EffortCertificationDocument effortCertificationDocument) {
        WorkflowDocument workflowDocument = effortCertificationDocument.getDocumentHeader().getWorkflowDocument();
        Set<Person> priorApprovers = getPriorApprovers(workflowDocument);

        for (EffortCertificationDetail detailLine : effortCertificationDocument.getEffortCertificationDetailLines()) {
            boolean hasBeenChanged = EffortCertificationDocumentRuleUtil.isPayrollAmountChangedFromPersisted(detailLine);
            if (!hasBeenChanged) {
                continue;
            }
            boolean isNewLine = detailLine.isNewLineIndicator();
            if (LOG.isInfoEnabled()) {
                LOG.info("EC Detail Line has been changed: " + detailLine);
            }

            Account account = detailLine.getAccount();
            Person fiscalOfficer = account.getAccountFiscalOfficerUser();
            if (fiscalOfficer != null && StringUtils.isNotBlank(fiscalOfficer.getPrincipalName())) {
                // KULEFR-206
                // String actionRequestOfOfficer = this.getActionRequest(routeLevelName, KFSConstants.RouteLevelNames.ACCOUNT);
                AdHocRoutePerson adHocRoutePerson = buildAdHocRouteRecipient(fiscalOfficer.getPrincipalName(), ActionRequestType.APPROVE);

                addAdHocRoutePerson(effortCertificationDocument.getAdHocRoutePersons(), priorApprovers, adHocRoutePerson, isNewLine);
            } else {
                LOG.warn("Unable to obtain a fiscal officer for the detail line's account: " + account.getChartOfAccountsCode() + "-" + account.getAccountNumber());
            }

            Person projectDirector = contractsAndGrantsModuleService.getProjectDirectorForAccount(account);
            if (projectDirector != null) {
                String accountProjectDirectorPersonUserId = projectDirector.getPrincipalName();
                // KULEFR-206
                // String actionRequestOfDirector = this.getActionRequest(routeLevelName,
                // KFSConstants.RouteLevelNames.PROJECT_MANAGEMENT);
                AdHocRoutePerson adHocRoutePerson = buildAdHocRouteRecipient(accountProjectDirectorPersonUserId, ActionRequestType.APPROVE);

                addAdHocRoutePerson(effortCertificationDocument.getAdHocRoutePersons(), priorApprovers, adHocRoutePerson, isNewLine);
            }
        }
    }

    /**
     * add the given ad hoc route person in the list if the person is one of prior approvers and is not in the list
     *
     * @param adHocRoutePersonList Collection of adhoc route persons
     * @param priorApprovers       Set of prior approvers
     * @param adHocRoutePerson     person to adhoc route to
     */
    protected void addAdHocRoutePerson(Collection<AdHocRoutePerson> adHocRoutePersonList, Set<Person> priorApprovers, AdHocRoutePerson adHocRoutePerson) {
        addAdHocRoutePerson(adHocRoutePersonList, priorApprovers, adHocRoutePerson, false);
    }

    /**
     * add the given ad hoc route person in the list if the person is one of prior approvers, or the change was a new line, and the person is not in the list
     *
     * @param adHocRoutePersonList Collection of adhoc route persons
     * @param priorApprovers       Set of prior approvers
     * @param adHocRoutePerson     person to adhoc route to
     * @param isNewLine            whether the change was a new line
     */
    protected void addAdHocRoutePerson(Collection<AdHocRoutePerson> adHocRoutePersonList, Set<Person> priorApprovers, AdHocRoutePerson adHocRoutePerson, boolean isNewLine) {
        boolean canBeAdded = false;

        // if it's a new line, person can be added even if they weren't a prior approver
        if (priorApprovers == null || isNewLine) {
            canBeAdded = true;
        } else {
            // we only want to ad-hoc if the user previously approved this document
            for (Person approver : priorApprovers) {
                if (StringUtils.equals(approver.getPrincipalName(), adHocRoutePerson.getId())) {
                    canBeAdded = true;
                    break;
                }
            }
        }

        if (canBeAdded) {
            // check that we have not already added them for the same action
            for (AdHocRoutePerson person : adHocRoutePersonList) {
                if (isSameAdHocRoutePerson(person, adHocRoutePerson)) {
                    canBeAdded = false;
                    break;
                }
            }
        }

        if (canBeAdded) {
            adHocRoutePersonList.add(adHocRoutePerson);
        }
    }

    protected boolean isSameAdHocRoutePerson(AdHocRoutePerson person1, AdHocRoutePerson person2) {
        if (person1 == null || person2 == null) {
            return false;
        }

        boolean isSameAdHocRoutePerson = StringUtils.equals(person1.getId(), person2.getId());
        isSameAdHocRoutePerson &= person1.getType().equals(person2.getType());
        isSameAdHocRoutePerson &= StringUtils.equals(person1.getActionRequested(), person2.getActionRequested());

        return isSameAdHocRoutePerson;
    }

    protected Set<Person> getPriorApprovers(WorkflowDocument workflowDocument) {
        PersonService personService = KimApiServiceLocator.getPersonService();
        List<ActionTaken> actionsTaken = workflowDocument.getActionsTaken();
        Set<String> principalIds = new HashSet<String>();
        Set<Person> persons = new HashSet<Person>();

        for (ActionTaken actionTaken : actionsTaken) {
            if (ActionType.APPROVE.equals(actionTaken.getActionTaken())) {
                String principalId = actionTaken.getPrincipalId();
                if (!principalIds.contains(principalId)) {
                    principalIds.add(principalId);
                    persons.add(personService.getPerson(principalId));
                }
            }
        }
        return persons;
    }

    /**
     * build an adhoc route recipient from the given person user id and action request
     *
     * @param personUserId  the given person user id
     * @param actionRequest the given action request
     * @return an adhoc route recipient built from the given information
     */
    protected AdHocRoutePerson buildAdHocRouteRecipient(String personUserId, ActionRequestType actionRequest) {
        AdHocRoutePerson adHocRoutePerson = new AdHocRoutePerson();
        adHocRoutePerson.setActionRequested(actionRequest.getCode());
        adHocRoutePerson.setId(personUserId);

        return adHocRoutePerson;
    }

    /**
     * build the source accounting lines for a salary expense transfer document from the given effort certification document. In the
     * holder, the first item is source accounting line list and the second the target accounting line list.
     *
     * @param effortCertificationDocument the given effort certification document
     * @return the source accounting lines for a salary expense transfer document built from the given effort certification document
     */
    protected List<LaborLedgerExpenseTransferAccountingLine> buildSourceAccountingLines(EffortCertificationDocument effortCertificationDocument) {
        List<LaborLedgerExpenseTransferAccountingLine> sourceAccountingLines = new ArrayList<LaborLedgerExpenseTransferAccountingLine>();

        List<EffortCertificationDetail> effortCertificationDetailLines = effortCertificationDocument.getEffortCertificationDetailLines();
        for (EffortCertificationDetail detailLine : effortCertificationDetailLines) {
            if (this.getDifference(detailLine).isPositive()) {
                LaborLedgerExpenseTransferSourceAccountingLine sourceLine = kualiModuleService.getResponsibleModuleService(LaborLedgerExpenseTransferSourceAccountingLine.class).createNewObjectFromExternalizableClass(LaborLedgerExpenseTransferSourceAccountingLine.class);
                this.addAccountingLineIntoList(sourceAccountingLines, sourceLine, effortCertificationDocument, detailLine);
            }
        }
        return sourceAccountingLines;
    }

    /**
     * build the target accounting lines for a salary expense transfer document from the given effort certification document. In the
     * holder, the first item is source accounting line list and the second the target accounting line list.
     *
     * @param effortCertificationDocument the given effort certification document
     * @return the target accounting lines for a salary expense transfer document built from the given effort certification document
     */
    protected List<LaborLedgerExpenseTransferAccountingLine> buildTargetAccountingLines(EffortCertificationDocument effortCertificationDocument) {
        List<LaborLedgerExpenseTransferAccountingLine> targetAccountingLines = new ArrayList<LaborLedgerExpenseTransferAccountingLine>();

        List<EffortCertificationDetail> effortCertificationDetailLines = effortCertificationDocument.getEffortCertificationDetailLines();
        for (EffortCertificationDetail detailLine : effortCertificationDetailLines) {
            if (this.getDifference(detailLine).isNegative()) {
                LaborLedgerExpenseTransferTargetAccountingLine targetLine = kualiModuleService.getResponsibleModuleService(LaborLedgerExpenseTransferTargetAccountingLine.class).createNewObjectFromExternalizableClass(LaborLedgerExpenseTransferTargetAccountingLine.class);
                this.addAccountingLineIntoList(targetAccountingLines, targetLine, effortCertificationDocument, detailLine);
            }
        }
        return targetAccountingLines;
    }

    /**
     * get all fiscal officers of the detail line accounts where the salary amounts are changed
     *
     * @param effortCertificationDocument the given document that contains the detail lines
     * @return all fiscal officers of the detail line accounts where the salary amounts are changed
     */
    protected Set<String> getFiscalOfficersIfAmountChanged(EffortCertificationDocument effortCertificationDocument) {
        Set<String> fiscalOfficers = new HashSet<String>();

        List<EffortCertificationDetail> effortCertificationDetailLines = effortCertificationDocument.getEffortCertificationDetailLines();
        for (EffortCertificationDetail detailLine : effortCertificationDetailLines) {
            if (this.getDifference(detailLine).isNonZero()) {
                Account account = detailLine.getAccount();
                String accountFiscalOfficerPersonUserId = account.getAccountFiscalOfficerUser().getPrincipalName();

                if (StringUtils.isEmpty(accountFiscalOfficerPersonUserId)) {
                    fiscalOfficers.add(accountFiscalOfficerPersonUserId);
                }
            }
        }
        return fiscalOfficers;
    }

    /**
     * add a new accounting line into the given accounting line list. The accounting line is generated from the given detail line
     *
     * @param accountingLines             a list of accounting lines
     * @param clazz                       the specified class of the accounting line
     * @param effortCertificationDocument the given effort certification document that contains the given detail line
     * @param detailLine                  the given detail line that is used to generate an accounting line
     */
    protected void addAccountingLineIntoList(List<LaborLedgerExpenseTransferAccountingLine> accountingLineList, LaborLedgerExpenseTransferAccountingLine accountingLine, EffortCertificationDocument effortCertificationDocument, EffortCertificationDetail detailLine) {
        accountingLine.setSequenceNumber(accountingLineList.size() + 1);

        this.populateAccountingLine(effortCertificationDocument, detailLine, accountingLine);
        accountingLineList.add(accountingLine);
    }

    /**
     * populate an accounting line from the given detail line
     *
     * @param effortCertificationDocument the given effort certification document that contains the given detail line
     * @param detailLine                  the given detail line
     * @param accountingLine              the accounting line needed to be populated
     */
    protected void populateAccountingLine(EffortCertificationDocument effortCertificationDocument, EffortCertificationDetail detailLine, LaborLedgerExpenseTransferAccountingLine accountingLine) {
        accountingLine.setChartOfAccountsCode(detailLine.getChartOfAccountsCode());
        accountingLine.setAccountNumber(detailLine.getAccountNumber());
        accountingLine.setSubAccountNumber(detailLine.getSubAccountNumber());

        accountingLine.setPostingYear(detailLine.getUniversityFiscalYear());
        accountingLine.setFinancialObjectCode(detailLine.getFinancialObjectCode());
        accountingLine.setBalanceTypeCode(KFSConstants.BALANCE_TYPE_ACTUAL);

        accountingLine.setAmount(this.getDifference(detailLine).abs());

        accountingLine.setFinancialSubObjectCode(null);
        accountingLine.setProjectCode(null);
        accountingLine.setOrganizationReferenceId(null);

        accountingLine.setEmplid(effortCertificationDocument.getEmplid());
        accountingLine.setPositionNumber(detailLine.getPositionNumber());
        accountingLine.setPayrollTotalHours(BigDecimal.ZERO);

        EffortCertificationReportDefinition reportDefinition = effortCertificationDocument.getEffortCertificationReportDefinition();
        accountingLine.setPayrollEndDateFiscalYear(reportDefinition.getExpenseTransferFiscalYear());
        accountingLine.setPayrollEndDateFiscalPeriodCode(reportDefinition.getExpenseTransferFiscalPeriodCode());

        accountingLine.refreshNonUpdateableReferences();

        AccountingLineOverride override = laborModuleService.determineNeededOverrides(null, accountingLine);

        // if an expired account override is needed, set it, otherwise validations on the downstream ST doc could fail
        accountingLine.setAccountExpiredOverrideNeeded(override.hasComponent(COMPONENT.EXPIRED_ACCOUNT));
        accountingLine.setAccountExpiredOverride(accountingLine.getAccountExpiredOverrideNeeded());

        // if a non-budgeted object code override is needed, set it, otherwise validations on the downstream ST doc could fail
        accountingLine.setObjectBudgetOverrideNeeded(override.hasComponent(COMPONENT.NON_BUDGETED_OBJECT));
        accountingLine.setObjectBudgetOverride(accountingLine.isObjectBudgetOverrideNeeded());

        if (accountingLine.getAccountExpiredOverrideNeeded() || accountingLine.isObjectBudgetOverrideNeeded()) {
            accountingLine.setOverrideCode(override.getCode());
        }

    }

    /**
     * get the difference between the original amount and updated amount of the given detail line
     *
     * @param detailLine the given detail line
     * @return the difference between the original amount and updated amount of the given detail line
     */
    protected KualiDecimal getDifference(EffortCertificationDetail detailLine) {
        return detailLine.getEffortCertificationOriginalPayrollAmount().subtract(detailLine.getEffortCertificationPayrollAmount());
    }

    /**
     * Sets the laborModuleService attribute value.
     *
     * @param laborModuleService The laborModuleService to set.
     */
    public void setLaborModuleService(LaborModuleService laborModuleService) {
        this.laborModuleService = laborModuleService;
    }

    /**
     * Sets the documentService attribute value.
     *
     * @param documentService The documentService to set.
     */
    public void setDocumentService(DocumentService documentService) {
        this.documentService = documentService;
    }

    /**
     * Sets the businessObjectService attribute value.
     *
     * @param businessObjectService The businessObjectService to set.
     */
    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
        this.businessObjectService = businessObjectService;
    }

    /**
     * Sets the contractsAndGrantsModuleService attribute value.
     *
     * @param contractsAndGrantsModuleService The contractsAndGrantsModuleService to set.
     */
    public void setContractsAndGrantsModuleService(ContractsAndGrantsModuleService contractsAndGrantsModuleService) {
        this.contractsAndGrantsModuleService = contractsAndGrantsModuleService;
    }

    /**
     * Sets the kualiModuleService attribute value.
     *
     * @param kualiModuleService The kualiModuleService to set.
     */
    public void setKualiModuleService(KualiModuleService kualiModuleService) {
        this.kualiModuleService = kualiModuleService;
    }
}
