001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.ec.batch.service.impl;
017    
018    import java.util.ArrayList;
019    import java.util.Arrays;
020    import java.util.Collection;
021    import java.util.Date;
022    import java.util.HashMap;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Set;
026    
027    import org.apache.commons.lang.StringUtils;
028    import org.kuali.kfs.integration.ld.LaborLedgerBalance;
029    import org.kuali.kfs.integration.ld.LaborLedgerEntry;
030    import org.kuali.kfs.integration.ld.LaborModuleService;
031    import org.kuali.kfs.module.ec.EffortConstants;
032    import org.kuali.kfs.module.ec.EffortKeyConstants;
033    import org.kuali.kfs.module.ec.EffortPropertyConstants;
034    import org.kuali.kfs.module.ec.EffortConstants.ExtractProcess;
035    import org.kuali.kfs.module.ec.EffortConstants.SystemParameters;
036    import org.kuali.kfs.module.ec.batch.service.EffortCertificationExtractService;
037    import org.kuali.kfs.module.ec.businessobject.EffortCertificationDocumentBuild;
038    import org.kuali.kfs.module.ec.businessobject.EffortCertificationReportDefinition;
039    import org.kuali.kfs.module.ec.document.EffortCertificationDocument;
040    import org.kuali.kfs.module.ec.document.validation.impl.LedgerBalanceFieldValidator;
041    import org.kuali.kfs.module.ec.service.EffortCertificationDocumentBuildService;
042    import org.kuali.kfs.module.ec.service.EffortCertificationReportDefinitionService;
043    import org.kuali.kfs.module.ec.service.EffortCertificationReportService;
044    import org.kuali.kfs.module.ec.util.EffortCertificationParameterFinder;
045    import org.kuali.kfs.module.ec.util.ExtractProcessReportDataHolder;
046    import org.kuali.kfs.module.ec.util.LedgerBalanceConsolidationHelper;
047    import org.kuali.kfs.module.ec.util.LedgerBalanceWithMessage;
048    import org.kuali.kfs.sys.KFSPropertyConstants;
049    import org.kuali.kfs.sys.Message;
050    import org.kuali.kfs.sys.MessageBuilder;
051    import org.kuali.kfs.sys.ObjectUtil;
052    import org.kuali.kfs.sys.context.SpringContext;
053    import org.kuali.kfs.sys.service.OptionsService;
054    import org.kuali.kfs.sys.service.UniversityDateService;
055    import org.kuali.rice.kns.service.BusinessObjectService;
056    import org.kuali.rice.kns.service.DateTimeService;
057    import org.kuali.rice.kns.service.KualiModuleService;
058    import org.kuali.rice.kns.util.KualiDecimal;
059    import org.kuali.rice.kns.util.spring.Logged;
060    import org.springframework.transaction.annotation.Transactional;
061    
062    /**
063     * This is an implemeation of Effort Certification Extract process, which extracts Labor Ledger records of the employees who were
064     * paid on a grant or cost shared during the selected reporting period, and generates effort certification build/temporary document.
065     * Its major tasks include:
066     * 
067     * <li>Identify employees who were paid on a grant or cost shared;</li>
068     * <li>Select qualified Labor Ledger records for each identified employee;</li>
069     * <li>Generate effort certification build document from the selected Labor Ledger records for each employee.</li>
070     */
071    @Transactional
072    public class EffortCertificationExtractServiceImpl implements EffortCertificationExtractService {
073        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(EffortCertificationExtractServiceImpl.class);
074    
075        private BusinessObjectService businessObjectService;
076        private OptionsService optionsService;
077        private DateTimeService dateTimeService;
078        private UniversityDateService universityDateService;
079    
080        private LaborModuleService laborModuleService;
081        private KualiModuleService kualiModuleService;
082        
083        private EffortCertificationDocumentBuildService effortCertificationDocumentBuildService;
084        private EffortCertificationReportService effortCertificationReportService;
085        private EffortCertificationReportDefinitionService effortCertificationReportDefinitionService;
086    
087        /**
088         * @see org.kuali.kfs.module.ec.batch.service.EffortCertificationExtractService#extract()
089         */
090        @Logged
091        public void extract() {
092            Integer fiscalYear = EffortCertificationParameterFinder.getExtractReportFiscalYear();
093            String reportNumber = EffortCertificationParameterFinder.getExtractReportNumber();
094    
095            this.extract(fiscalYear, reportNumber);
096        }
097    
098        /**
099         * @see org.kuali.kfs.module.ec.batch.service.EffortCertificationExtractService#extract(java.lang.Integer, java.lang.String)
100         */
101        @Logged
102        public void extract(Integer fiscalYear, String reportNumber) {
103            Map<String, String> fieldValues = EffortCertificationReportDefinition.buildKeyMap(fiscalYear, reportNumber);
104    
105            // check if a report has been defined and its docuemnts have not been generated.
106            String errorMessage = this.validateReportDefintion(fiscalYear, reportNumber);
107            errorMessage = StringUtils.isNotEmpty(errorMessage) ? errorMessage : this.existEffortCertificationDocument(fieldValues);
108            if (StringUtils.isNotEmpty(errorMessage)) {
109                LOG.fatal(errorMessage);
110                throw new IllegalArgumentException(errorMessage);
111            }
112    
113            Map<String, List<String>> parameters = this.getSystemParameters();
114            parameters.put(ExtractProcess.EXPENSE_OBJECT_TYPE, getExpenseObjectTypeCodes(fiscalYear));
115    
116            EffortCertificationReportDefinition reportDefinition = effortCertificationReportDefinitionService.findReportDefinitionByPrimaryKey(fieldValues);
117            ExtractProcessReportDataHolder reportDataHolder = this.initializeReportData(reportDefinition);
118    
119            List<String> employees = this.findEmployeesEligibleForEffortCertification(reportDefinition);
120    
121            effortCertificationDocumentBuildService.removeExistingDocumentBuild(fieldValues);
122            this.generateDocumentBuild(reportDefinition, employees, reportDataHolder, parameters);
123    
124            Date runDate = dateTimeService.getCurrentSqlDate();
125            effortCertificationReportService.generateReportForExtractProcess(reportDataHolder, runDate);
126        }
127    
128        /**
129         * @see org.kuali.kfs.module.ec.batch.service.EffortCertificationExtractService#extract(java.lang.String,
130         *      org.kuali.kfs.module.ec.businessobject.EffortCertificationReportDefinition)
131         */
132        @Logged
133        public EffortCertificationDocumentBuild extract(String emplid, EffortCertificationReportDefinition reportDefinition) {
134            Map<String, List<String>> parameters = this.getSystemParameters();
135            parameters.put(ExtractProcess.EXPENSE_OBJECT_TYPE, getExpenseObjectTypeCodes(reportDefinition.getUniversityFiscalYear()));
136    
137            List<String> positionGroupCodes = effortCertificationReportDefinitionService.findPositionObjectGroupCodes(reportDefinition);
138            Integer postingYear = universityDateService.getCurrentFiscalYear();
139            ExtractProcessReportDataHolder reportDataHolder = this.initializeReportData(reportDefinition);
140    
141            return this.generateDocumentBuildByEmployee(postingYear, emplid, positionGroupCodes, reportDefinition, reportDataHolder, parameters);
142        }
143    
144        /**
145         * @see org.kuali.kfs.module.ec.batch.service.EffortCertificationExtractService#isEmployeesEligibleForEffortCertification(java.lang.String,
146         *      org.kuali.kfs.module.ec.businessobject.EffortCertificationReportDefinition)
147         */
148        @Logged
149        public boolean isEmployeeEligibleForEffortCertification(String emplid, EffortCertificationReportDefinition reportDefinition) {
150            Map<String, Set<String>> earnCodePayGroups = effortCertificationReportDefinitionService.findReportEarnCodePayGroups(reportDefinition);
151            List<String> balanceTypeList = EffortConstants.ELIGIBLE_BALANCE_TYPES_FOR_EFFORT_REPORT;
152            Map<Integer, Set<String>> reportPeriods = reportDefinition.getReportPeriods();
153    
154            return laborModuleService.isEmployeeWithPayType(emplid, reportPeriods, balanceTypeList, earnCodePayGroups);
155        }
156    
157        /**
158         * @see org.kuali.kfs.module.ec.batch.service.EffortCertificationExtractService#findEmployeesEligibleForEffortCertification(org.kuali.kfs.module.ec.businessobject.EffortCertificationReportDefinition)
159         */
160        @Logged
161        public List<String> findEmployeesEligibleForEffortCertification(EffortCertificationReportDefinition reportDefinition) {
162            Map<String, Set<String>> earnCodePayGroups = effortCertificationReportDefinitionService.findReportEarnCodePayGroups(reportDefinition);
163            List<String> balanceTypeList = EffortConstants.ELIGIBLE_BALANCE_TYPES_FOR_EFFORT_REPORT;
164            Map<Integer, Set<String>> reportPeriods = reportDefinition.getReportPeriods();
165    
166            return laborModuleService.findEmployeesWithPayType(reportPeriods, balanceTypeList, earnCodePayGroups);
167        }
168    
169        /**
170         * check if a report has been defined. The combination of fiscal year and report number can determine a report definition.
171         * 
172         * @param fiscalYear the the given fiscalYear
173         * @param reportNumber the the given report number
174         * @return a message if a report has not been defined or its documents have been gerenated; otherwise, return null
175         */
176        protected String validateReportDefintion(Integer fiscalYear, String reportNumber) {
177            EffortCertificationReportDefinition reportDefinition = new EffortCertificationReportDefinition();
178            reportDefinition.setUniversityFiscalYear(fiscalYear);
179            reportDefinition.setEffortCertificationReportNumber(reportNumber);
180    
181            String errorMessage = effortCertificationReportDefinitionService.validateEffortCertificationReportDefinition(reportDefinition);
182            return StringUtils.isNotEmpty(errorMessage) ? errorMessage : null;
183        }
184    
185        /**
186         * check if the docuemnts for the given report definition have not been generated. The combination of fiscal year and report
187         * number can determine a report definition.
188         * 
189         * @param fieldValues the map containing fiscalYear and report number
190         * @return a message if the documents have been gerenated; otherwise, return null
191         */
192        protected String existEffortCertificationDocument(Map<String, String> fieldValues) {
193            String fiscalYear = fieldValues.get(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR);
194            String reportNumber = fieldValues.get(EffortPropertyConstants.EFFORT_CERTIFICATION_REPORT_NUMBER);
195    
196            // check if any document has been generated for the selected report definition
197            int countOfDocuments = businessObjectService.countMatching(EffortCertificationDocument.class, fieldValues);
198            if (countOfDocuments > 0) {
199                return MessageBuilder.buildMessageWithPlaceHolder(EffortKeyConstants.ERROR_REPORT_DOCUMENT_EXIST, reportNumber, fiscalYear).getMessage();
200            }
201    
202            return null;
203        }
204    
205        /**
206         * generate a document (build) as well as their detail lines for the given employees
207         * 
208         * @param reportDefinition the given report definition
209         * @param employees the given employees
210         * @param reportDataHolder the holder of report data
211         * @param parameters the given system parameters
212         */
213        protected void generateDocumentBuild(EffortCertificationReportDefinition reportDefinition, List<String> employees, ExtractProcessReportDataHolder reportDataHolder, Map<String, List<String>> parameters) {
214            List<String> positionGroupCodes = effortCertificationReportDefinitionService.findPositionObjectGroupCodes(reportDefinition);
215            Integer postingYear = universityDateService.getCurrentFiscalYear();
216    
217            for (String emplid : employees) {
218                EffortCertificationDocumentBuild document = this.generateDocumentBuildByEmployee(postingYear, emplid, positionGroupCodes, reportDefinition, reportDataHolder, parameters);
219    
220                if (document != null) {
221                    List<EffortCertificationDocumentBuild> documents = new ArrayList<EffortCertificationDocumentBuild>();
222                    documents.add(document);
223    
224                    businessObjectService.save(documents);
225    
226                    reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_DETAIL_LINES_WRITTEN, this.getCountOfDetailLines(documents));
227                    reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_CERTIFICATIONS_WRITTEN, documents.size());
228                }
229            }
230            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_EMPLOYEES_SELECTED, employees.size());
231            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_ERRORS_FOUND, reportDataHolder.getLedgerBalancesWithMessage().size());
232        }
233    
234        /**
235         * extract the qualified labor ledger balance records of the given employee with the given report periods.
236         * 
237         * @param emplid the given employee id
238         * @param positionGroupCodes the specified position group codes
239         * @param reportDefinition the specified report definition
240         * @param parameters the system paramters setup in front
241         * @param reportDataHolder the given holder that contains any information to be written into the working report
242         * @return the qualified labor ledger balance records of the given employee
243         */
244        protected List<LaborLedgerBalance> getQualifiedLedgerBalances(String emplid, List<String> positionGroupCodes, EffortCertificationReportDefinition reportDefinition, ExtractProcessReportDataHolder reportDataHolder, Map<String, List<String>> parameters) {
245            List<LedgerBalanceWithMessage> ledgerBalancesWithMessage = reportDataHolder.getLedgerBalancesWithMessage();
246            Map<Integer, Set<String>> reportPeriods = reportDefinition.getReportPeriods();
247    
248            // select ledger balances by the given employee and other criteria
249            Collection<LaborLedgerBalance> ledgerBalances = this.selectLedgerBalanceForEmployee(emplid, positionGroupCodes, reportDefinition, parameters);
250            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_BALANCES_READ, ledgerBalances.size());
251    
252            // clear up the ledger balance collection
253            List<LaborLedgerBalance> validLedgerBalances = this.removeUnqualifiedLedgerBalances(ledgerBalances, reportDefinition, reportDataHolder);
254            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_BALANCES_SELECTED, validLedgerBalances.size());
255    
256            // consolidate the pre-qualified ledger balances
257            List<LaborLedgerBalance> consolidatedLedgerBalances = this.cosolidateLedgerBalances(validLedgerBalances, reportDefinition);
258    
259            // check the employee according to the pre-qualified ledger balances
260            boolean isQualifiedEmployee = this.checkEmployeeBasedOnLedgerBalances(emplid, consolidatedLedgerBalances, reportDefinition, reportDataHolder, parameters);
261    
262            // abort all ledger balances if the employee is not qualified; otherwise, adopt the consolidated balances
263            List<LaborLedgerBalance> qualifiedLedgerBalances = isQualifiedEmployee ? consolidatedLedgerBalances : null;
264    
265            return qualifiedLedgerBalances;
266        }
267    
268        /**
269         * remove the ledger balances without valid account, and nonzero total amount
270         * 
271         * @param ledgerBalances the given ledger balances
272         * @param reportDefinition the given report definition
273         * @param reportDataHolder the given holder that contains any information to be written into the working report
274         */
275        protected List<LaborLedgerBalance> removeUnqualifiedLedgerBalances(Collection<LaborLedgerBalance> ledgerBalances, EffortCertificationReportDefinition reportDefinition, ExtractProcessReportDataHolder reportDataHolder) {
276            Map<Integer, Set<String>> reportPeriods = reportDefinition.getReportPeriods();
277            List<LedgerBalanceWithMessage> ledgerBalancesWithMessage = reportDataHolder.getLedgerBalancesWithMessage();
278    
279            List<LaborLedgerBalance> validLedgerBalances = new ArrayList<LaborLedgerBalance>();
280    
281            for (LaborLedgerBalance balance : ledgerBalances) {
282                // within the given periods, the total amount of a single balance cannot be ZERO
283                Message errorAmountMessage = LedgerBalanceFieldValidator.isNonZeroAmountBalanceWithinReportPeriod(balance, reportPeriods);
284    
285                // every balance record must be associated with a valid account
286                Message invalidAccountMessage = LedgerBalanceFieldValidator.hasValidAccount(balance);
287                if (invalidAccountMessage != null) {
288                    this.reportInvalidLedgerBalance(ledgerBalancesWithMessage, balance, invalidAccountMessage);
289                }
290    
291                if (errorAmountMessage == null && invalidAccountMessage == null) {
292                    validLedgerBalances.add(balance);
293                }
294            }
295    
296            ledgerBalances = null;
297            return validLedgerBalances;
298        }
299    
300        /**
301         * check all ledger balances of the given employee and see if they can meet certain requiremnets. If not, the employee would be
302         * unqualified for effort reporting
303         * 
304         * @param emplid the given employee id
305         * @param ledgerBalances the all pre-qualified ledger balances of the employee
306         * @param reportDefinition the specified report definition
307         * @param reportDataHolder the given holder that contains any information to be written into the working report
308         * @param parameters the system paramters setup in front
309         * @return true if all ledger balances as whole meet requirements; otherwise, return false
310         */
311        protected boolean checkEmployeeBasedOnLedgerBalances(String emplid, List<LaborLedgerBalance> ledgerBalances, EffortCertificationReportDefinition reportDefinition, ExtractProcessReportDataHolder reportDataHolder, Map<String, List<String>> parameters) {
312            if (ledgerBalances == null || ledgerBalances.isEmpty()) {
313                return false;
314            }
315    
316            Map<Integer, Set<String>> reportPeriods = reportDefinition.getReportPeriods();
317            List<LedgerBalanceWithMessage> ledgerBalancesWithMessage = reportDataHolder.getLedgerBalancesWithMessage();
318    
319            // the total amount of all balances must be positive; otherwise, not to generate effort report for the employee
320            Message nonpositiveTotalError = LedgerBalanceFieldValidator.isTotalAmountPositive(ledgerBalances, reportPeriods);
321            if (nonpositiveTotalError != null) {
322                this.reportEmployeeWithoutValidBalances(ledgerBalancesWithMessage, nonpositiveTotalError, emplid);
323                return false;
324            }
325    
326            // the specified employee must have at least one grant account
327            Message grantAccountNotFoundError = LedgerBalanceFieldValidator.hasGrantAccount(ledgerBalances);
328            if (grantAccountNotFoundError != null) {
329                // exclude the error message according to the request in KULEFR-55
330                // this.reportEmployeeWithoutValidBalances(ledgerBalancesWithMessage, grantAccountNotFoundError, emplid);
331                return false;
332            }
333    
334            // check if there is at least one account funded by federal grants when an effort report can only be generated for an
335            // employee with pay by federal grant
336            boolean isFederalFundsOnly = Boolean.parseBoolean(parameters.get(SystemParameters.FEDERAL_ONLY_BALANCE_IND).get(0));
337            if (isFederalFundsOnly) {
338                List<String> federalAgencyTypeCodes = parameters.get(SystemParameters.FEDERAL_AGENCY_TYPE_CODE);
339    
340                Message federalFundsNotFoundError = LedgerBalanceFieldValidator.hasFederalFunds(ledgerBalances, federalAgencyTypeCodes);
341                if (federalFundsNotFoundError != null) {
342                    this.reportEmployeeWithoutValidBalances(ledgerBalancesWithMessage, federalFundsNotFoundError, emplid);
343                    return false;
344                }
345            }
346    
347            return true;
348        }
349    
350        /**
351         * select the labor ledger balances for the specifed employee
352         * 
353         * @param emplid the given empolyee id
354         * @param positionObjectGroupCodes the specified position object group codes
355         * @param reportDefinition the specified report definition
356         * @return the labor ledger balances for the specifed employee
357         */
358        protected Collection<LaborLedgerBalance> selectLedgerBalanceForEmployee(String emplid, List<String> positionObjectGroupCodes, EffortCertificationReportDefinition reportDefinition, Map<String, List<String>> parameters) {
359            List<String> expenseObjectTypeCodes = parameters.get(ExtractProcess.EXPENSE_OBJECT_TYPE);
360            List<String> excludedAccountTypeCode = parameters.get(SystemParameters.ACCOUNT_TYPE_CODE_BALANCE_SELECT);
361            List<String> emplids = Arrays.asList(emplid);
362            List<String> laborObjectCodes = Arrays.asList(EffortConstants.LABOR_OBJECT_SALARY_CODE);
363    
364            Map<String, List<String>> fieldValues = new HashMap<String, List<String>>();
365            fieldValues.put(KFSPropertyConstants.EMPLID, emplids);
366            fieldValues.put(KFSPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE, expenseObjectTypeCodes);
367            fieldValues.put(EffortPropertyConstants.LABOR_OBJECT_FRINGE_OR_SALARY_CODE, laborObjectCodes);
368    
369            Map<String, List<String>> excludedFieldValues = new HashMap<String, List<String>>();
370            excludedFieldValues.put(EffortPropertyConstants.ACCOUNT_ACCOUNT_TYPE_CODE, excludedAccountTypeCode);
371    
372            Set<Integer> fiscalYears = reportDefinition.getReportPeriods().keySet();
373            List<String> balanceTypes = EffortConstants.ELIGIBLE_BALANCE_TYPES_FOR_EFFORT_REPORT;
374    
375            return laborModuleService.findLedgerBalances(fieldValues, excludedFieldValues, fiscalYears, balanceTypes, positionObjectGroupCodes);
376        }
377    
378        /**
379         * consolidate the given labor ledger balances and determine whether they are qualified for effort reporting
380         * 
381         * @param ledgerBalances the given labor ledger balances
382         * @param reportDefinition the specified report definition
383         * @return a collection of ledger balances if they are qualified; otherwise, return null
384         */
385        protected List<LaborLedgerBalance> cosolidateLedgerBalances(List<LaborLedgerBalance> ledgerBalances, EffortCertificationReportDefinition reportDefinition) {
386            List<LaborLedgerBalance> cosolidatedLedgerBalances = new ArrayList<LaborLedgerBalance>();
387    
388            Map<Integer, Set<String>> reportPeriods = reportDefinition.getReportPeriods();
389            Map<String, LaborLedgerBalance> ledgerBalanceMap = new HashMap<String, LaborLedgerBalance>();
390            LedgerBalanceConsolidationHelper.consolidateLedgerBalances(ledgerBalanceMap, ledgerBalances, this.getConsolidationKeys());
391    
392            for (String key : ledgerBalanceMap.keySet()) {
393                LaborLedgerBalance ledgerBalance = ledgerBalanceMap.get(key);
394    
395                KualiDecimal totalAmount = LedgerBalanceConsolidationHelper.calculateTotalAmountWithinReportPeriod(ledgerBalance, reportPeriods);
396                if (totalAmount.isNonZero()) {
397                    cosolidatedLedgerBalances.add(ledgerBalance);
398                }
399            }
400    
401            return cosolidatedLedgerBalances;
402        }
403    
404        // generate the effort certification document build for the given employee
405        protected EffortCertificationDocumentBuild generateDocumentBuildByEmployee(Integer postingYear, String emplid, List<String> positionGroupCodes, EffortCertificationReportDefinition reportDefinition, ExtractProcessReportDataHolder reportDataHolder, Map<String, List<String>> parameters) {
406            List<LaborLedgerBalance> qualifiedLedgerBalance = this.getQualifiedLedgerBalances(emplid, positionGroupCodes, reportDefinition, reportDataHolder, parameters);
407    
408            if (qualifiedLedgerBalance == null || qualifiedLedgerBalance.isEmpty()) {
409                return null;
410            }
411    
412            return effortCertificationDocumentBuildService.generateDocumentBuild(postingYear, reportDefinition, qualifiedLedgerBalance);
413        }
414    
415        // add an error entry into error map
416        protected void reportInvalidLedgerBalance(List<LedgerBalanceWithMessage> ledgerBalancesWithMessage, LaborLedgerBalance ledgerBalance, Message message) {
417            ledgerBalancesWithMessage.add(new LedgerBalanceWithMessage(ledgerBalance, message.toString()));
418        }
419    
420        // add an error entry into error map
421        protected void reportEmployeeWithoutValidBalances(List<LedgerBalanceWithMessage> ledgerBalancesWithMessage, Message message, String emplid) {
422            LaborLedgerBalance ledgerBalance = kualiModuleService.getResponsibleModuleService(LaborLedgerBalance.class).createNewObjectFromExternalizableClass(LaborLedgerBalance.class);   
423            ledgerBalance.setEmplid(emplid);
424            this.reportInvalidLedgerBalance(ledgerBalancesWithMessage, ledgerBalance, message);
425        }
426    
427        // store and cache relating system parameters in a Map for the future use
428        protected Map<String, List<String>> getSystemParameters() {
429            Map<String, List<String>> parameters = new HashMap<String, List<String>>();
430    
431            parameters.put(SystemParameters.ACCOUNT_TYPE_CODE_BALANCE_SELECT, EffortCertificationParameterFinder.getAccountTypeCodes());
432            parameters.put(SystemParameters.FEDERAL_ONLY_BALANCE_IND, EffortCertificationParameterFinder.getFederalOnlyBalanceIndicatorAsString());
433            parameters.put(SystemParameters.FEDERAL_AGENCY_TYPE_CODE, EffortCertificationParameterFinder.getFederalAgencyTypeCodes());
434    
435            return parameters;
436        }
437    
438        // get the expense object code setup in System Options
439        protected List<String> getExpenseObjectTypeCodes(Integer fiscalYear) {
440            List<String> expenseObjectTypeCodes = new ArrayList<String>();
441            expenseObjectTypeCodes.add(optionsService.getOptions(fiscalYear).getFinObjTypeExpenditureexpCd());
442    
443            return expenseObjectTypeCodes;
444        }
445    
446        // get the count of detail lines associated with the given documents
447        protected int getCountOfDetailLines(List<EffortCertificationDocumentBuild> documents) {
448            int numOfDetailLines = 0;
449            for (EffortCertificationDocumentBuild document : documents) {
450                numOfDetailLines += document.getEffortCertificationDetailLinesBuild().size();
451            }
452            return numOfDetailLines;
453        }
454    
455        // get the field names used to build the keys for record consolidation
456        protected List<String> getConsolidationKeys() {
457            List<String> consolidationKeys = new ArrayList<String>();
458    
459            consolidationKeys.add(KFSPropertyConstants.EMPLID);
460            consolidationKeys.add(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE);
461            consolidationKeys.add(KFSPropertyConstants.ACCOUNT_NUMBER);
462            consolidationKeys.add(KFSPropertyConstants.SUB_ACCOUNT_NUMBER);
463            consolidationKeys.add(KFSPropertyConstants.FINANCIAL_OBJECT_CODE);
464            consolidationKeys.add(KFSPropertyConstants.POSITION_NUMBER);
465    
466            return consolidationKeys;
467        }
468    
469        // initialize the report data hold with default values
470        protected ExtractProcessReportDataHolder initializeReportData(EffortCertificationReportDefinition reportDefinition) {
471            ExtractProcessReportDataHolder reportDataHolder = new ExtractProcessReportDataHolder(reportDefinition);
472    
473            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_BALANCES_READ, 0);
474            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_BALANCES_SELECTED, 0);
475            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_DETAIL_LINES_WRITTEN, 0);
476            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_CERTIFICATIONS_WRITTEN, 0);
477            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_EMPLOYEES_SELECTED, 0);
478            reportDataHolder.updateBasicStatistics(ExtractProcess.NUM_ERRORS_FOUND, 0);
479    
480            return reportDataHolder;
481        }
482    
483        /**
484         * Sets the businessObjectService attribute value.
485         * 
486         * @param businessObjectService The businessObjectService to set.
487         */
488        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
489            this.businessObjectService = businessObjectService;
490        }
491    
492        /**
493         * Sets the optionsService attribute value.
494         * 
495         * @param optionsService The optionsService to set.
496         */
497        public void setOptionsService(OptionsService optionsService) {
498            this.optionsService = optionsService;
499        }
500    
501        /**
502         * Sets the dateTimeService attribute value.
503         * 
504         * @param dateTimeService The dateTimeService to set.
505         */
506        public void setDateTimeService(DateTimeService dateTimeService) {
507            this.dateTimeService = dateTimeService;
508        }
509    
510        /**
511         * Sets the laborModuleService attribute value.
512         * 
513         * @param laborModuleService The laborModuleService to set.
514         */
515        public void setLaborModuleService(LaborModuleService laborModuleService) {
516            this.laborModuleService = laborModuleService;
517        }
518    
519        /**
520         * Sets the effortCertificationDocumentBuildService attribute value.
521         * 
522         * @param effortCertificationDocumentBuildService The effortCertificationDocumentBuildService to set.
523         */
524        public void setEffortCertificationDocumentBuildService(EffortCertificationDocumentBuildService effortCertificationDocumentBuildService) {
525            this.effortCertificationDocumentBuildService = effortCertificationDocumentBuildService;
526        }
527    
528        /**
529         * Sets the effortCertificationReportService attribute value.
530         * 
531         * @param effortCertificationReportService The effortCertificationReportService to set.
532         */
533        public void setEffortCertificationReportService(EffortCertificationReportService effortCertificationReportService) {
534            this.effortCertificationReportService = effortCertificationReportService;
535        }
536    
537        /**
538         * Sets the universityDateService attribute value.
539         * 
540         * @param universityDateService The universityDateService to set.
541         */
542        public void setUniversityDateService(UniversityDateService universityDateService) {
543            this.universityDateService = universityDateService;
544        }
545    
546        /**
547         * Sets the effortCertificationReportDefinitionService attribute value.
548         * 
549         * @param effortCertificationReportDefinitionService The effortCertificationReportDefinitionService to set.
550         */
551        public void setEffortCertificationReportDefinitionService(EffortCertificationReportDefinitionService effortCertificationReportDefinitionService) {
552            this.effortCertificationReportDefinitionService = effortCertificationReportDefinitionService;
553        }
554    
555        /**
556         * Sets the kualiModuleService attribute value.
557         * @param kualiModuleService The kualiModuleService to set.
558         */
559        public void setKualiModuleService(KualiModuleService kualiModuleService) {
560            this.kualiModuleService = kualiModuleService;
561        }
562    }