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.ld.batch.service.impl;
017    
018    import java.io.File;
019    import java.io.FilenameFilter;
020    import java.math.BigDecimal;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    
026    import org.kuali.kfs.gl.GeneralLedgerConstants;
027    import org.kuali.kfs.gl.batch.service.BalancingService;
028    import org.kuali.kfs.gl.batch.service.PosterService;
029    import org.kuali.kfs.gl.batch.service.impl.BalancingServiceBaseImpl;
030    import org.kuali.kfs.gl.businessobject.Balance;
031    import org.kuali.kfs.gl.businessobject.Entry;
032    import org.kuali.kfs.gl.businessobject.LedgerBalanceHistory;
033    import org.kuali.kfs.gl.businessobject.OriginEntryInformation;
034    import org.kuali.kfs.module.ld.LaborConstants;
035    import org.kuali.kfs.module.ld.LaborKeyConstants;
036    import org.kuali.kfs.module.ld.batch.LaborBalancingStep;
037    import org.kuali.kfs.module.ld.businessobject.LaborBalanceHistory;
038    import org.kuali.kfs.module.ld.businessobject.LaborEntryHistory;
039    import org.kuali.kfs.module.ld.businessobject.LaborOriginEntry;
040    import org.kuali.kfs.module.ld.businessobject.LedgerBalance;
041    import org.kuali.kfs.module.ld.businessobject.LedgerEntry;
042    import org.kuali.kfs.sys.FileUtil;
043    import org.kuali.kfs.sys.KFSConstants;
044    import org.kuali.kfs.sys.KFSKeyConstants;
045    import org.kuali.kfs.sys.KFSPropertyConstants;
046    import org.kuali.kfs.sys.Message;
047    import org.kuali.rice.kns.util.KualiDecimal;
048    import org.kuali.rice.kns.util.ObjectUtils;
049    import org.springframework.transaction.annotation.Transactional;
050    
051    /**
052     * Service implementation of BalancingService for Labor balancing
053     */
054    @Transactional
055    public class LaborBalancingServiceImpl extends BalancingServiceBaseImpl<LaborEntryHistory, LaborBalanceHistory> implements BalancingService {
056        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LaborBalancingServiceImpl.class);
057        
058        protected File laborPosterInputFile = null;
059        protected File laborPosterErrorOutputFile = null;
060        
061        /**
062         * @see org.kuali.kfs.gl.batch.service.BalancingService#getPosterInputFile()
063         */
064        public File getPosterInputFile() {
065            // avoid running scanning logic on file system
066            if (laborPosterInputFile != null) {
067                return laborPosterInputFile;
068            }
069            
070            FilenameFilter filenameFilter = new FilenameFilter() {
071                public boolean accept(File dir, String name) {
072                    return (name.startsWith(LaborConstants.BatchFileSystem.POSTER_INPUT_FILE) &&
073                            name.endsWith(GeneralLedgerConstants.BatchFileSystem.EXTENSION));
074                }
075            };
076            
077            laborPosterInputFile = FileUtil.getNewestFile(new File(batchFileDirectoryName), filenameFilter);
078            
079            return laborPosterInputFile;
080        }
081    
082        /**
083         * @see org.kuali.kfs.gl.batch.service.BalancingService#getPosterErrorOutputFile()
084         */
085        public File getPosterErrorOutputFile() {
086            // avoid running scanning logic on file system
087            if (laborPosterErrorOutputFile != null) {
088                return laborPosterErrorOutputFile;
089            }
090            
091            FilenameFilter filenameFilter = new FilenameFilter() {
092                public boolean accept(File dir, String name) {
093                    return (name.startsWith(LaborConstants.BatchFileSystem.POSTER_ERROR_OUTPUT_FILE) &&
094                            name.endsWith(GeneralLedgerConstants.BatchFileSystem.EXTENSION));
095                }
096            };
097            
098            laborPosterErrorOutputFile = FileUtil.getNewestFile(new File(batchFileDirectoryName), filenameFilter);
099            
100            return laborPosterErrorOutputFile;
101        }
102        
103        
104        /**
105         * @see org.kuali.kfs.gl.batch.service.BalancingService#getPastFiscalYearsToConsider()
106         */
107        public int getPastFiscalYearsToConsider() {
108            return Integer.parseInt(parameterService.getParameterValue(LaborBalancingStep.class, LaborConstants.Balancing.NUMBER_OF_PAST_FISCAL_YEARS_TO_INCLUDE));
109        }
110        
111        /**
112         * @see org.kuali.kfs.gl.batch.service.BalancingService#getComparisonFailuresToPrintPerReport()
113         */
114        public int getComparisonFailuresToPrintPerReport() {
115            return Integer.parseInt(parameterService.getParameterValue(LaborBalancingStep.class, LaborConstants.Balancing.NUMBER_OF_COMPARISON_FAILURES_TO_PRINT_PER_REPORT));
116        }
117        
118        /**
119         * @see org.kuali.kfs.gl.batch.service.BalancingService#getShortTableLabel(java.lang.String)
120         */
121        public String getShortTableLabel(String businessObjectName) {
122            Map<String, String> names = new HashMap<String, String>();
123            names.put((Entry.class).getSimpleName(), kualiConfigurationService.getPropertyString(LaborKeyConstants.Balancing.REPORT_ENTRY_LABEL));
124            names.put((LaborEntryHistory.class).getSimpleName(), kualiConfigurationService.getPropertyString(LaborKeyConstants.Balancing.REPORT_ENTRY_LABEL));
125            names.put((Balance.class).getSimpleName(), kualiConfigurationService.getPropertyString(LaborKeyConstants.Balancing.REPORT_BALANCE_LABEL));
126            names.put((LaborBalanceHistory.class).getSimpleName(), kualiConfigurationService.getPropertyString(LaborKeyConstants.Balancing.REPORT_BALANCE_LABEL));
127            
128            return names.get(businessObjectName) == null ? kualiConfigurationService.getPropertyString(KFSKeyConstants.Balancing.REPORT_UNKNOWN_LABEL) : names.get(businessObjectName);
129        }
130        
131        /**
132         * @see org.kuali.kfs.gl.batch.service.BalancingService#getOriginEntry(java.lang.String, int)
133         */
134        public OriginEntryInformation getOriginEntry(String inputLine, int lineNumber) {
135            LaborOriginEntry originEntry = new LaborOriginEntry();
136            originEntry.setFromTextFileForBatch(inputLine, lineNumber);
137            
138            return originEntry;
139        }
140    
141        /**
142         * @see org.kuali.kfs.gl.batch.service.impl.BalancingServiceBaseImpl#updateHistoriesHelper(java.lang.Integer, java.lang.Integer, java.io.File, java.io.File)
143         */
144        protected int updateHistoriesHelper(Integer postMode, Integer startUniversityFiscalYear, File inputFile, File errorFile) {
145            if(postMode == PosterService.MODE_ENTRIES){
146                return super.updateHistoriesHelper(postMode, startUniversityFiscalYear, inputFile, errorFile);
147            }
148            return 0;
149        }
150        
151        /**
152         * @see org.kuali.kfs.gl.batch.service.BalancingService#updateEntryHistory(org.kuali.kfs.gl.businessobject.OriginEntryInformation)
153         * @see org.kuali.kfs.module.ld.batch.service.impl.LaborPosterServiceImpl#postAsLedgerEntry(org.kuali.kfs.gl.businessobject.Transaction, int, java.util.Date)
154         */
155        public void updateEntryHistory(Integer postMode, OriginEntryInformation originEntry) {
156            if(postMode == PosterService.MODE_ENTRIES){
157                // TODO Retrieve and update 1 by 1? Is a HashMap or cache better so that storing only occurs once at the end?
158                LaborOriginEntry laborOriginEntry = (LaborOriginEntry) originEntry;
159                LaborEntryHistory ledgerEntryHistory = new LaborEntryHistory(laborOriginEntry);
160        
161                LaborEntryHistory retrievedLedgerEntryHistory = (LaborEntryHistory) businessObjectService.retrieve(ledgerEntryHistory);
162                if(ObjectUtils.isNotNull(retrievedLedgerEntryHistory)) {
163                    ledgerEntryHistory = retrievedLedgerEntryHistory;
164                }
165                
166                ledgerEntryHistory.addAmount(laborOriginEntry.getTransactionLedgerEntryAmount());
167                
168                businessObjectService.save(ledgerEntryHistory);
169            }
170        }
171        
172        /**
173         * @see org.kuali.kfs.gl.batch.service.BalancingService#updateBalanceHistory(org.kuali.kfs.gl.businessobject.OriginEntryInformation)
174         * @see org.kuali.kfs.module.ld.batch.service.impl.LaborPosterServiceImpl#updateLedgerBalance(org.kuali.kfs.gl.businessobject.Transaction, int, java.util.Date)
175         */
176        public void updateBalanceHistory(Integer postMode, OriginEntryInformation originEntry) {
177            if(postMode == PosterService.MODE_ENTRIES){
178                // TODO Retrieve and update 1 by 1? Is a HashMap or cache better so that storing only occurs once at the end?
179                LaborOriginEntry laborOriginEntry = (LaborOriginEntry) originEntry;
180                LaborBalanceHistory ledgerBalanceHistory = new LaborBalanceHistory(laborOriginEntry);
181                
182                LaborBalanceHistory retrievedLedgerBalanceHistory = (LaborBalanceHistory) businessObjectService.retrieve(ledgerBalanceHistory);
183                if(ObjectUtils.isNotNull(retrievedLedgerBalanceHistory)) {
184                    ledgerBalanceHistory = retrievedLedgerBalanceHistory;
185                }
186                
187                // Make sure the amount update properly recognized debit / credit logic. This is modeled after LaborPosterServiceImpl#updateLedgerBalance
188                KualiDecimal amount = laborOriginEntry.getTransactionLedgerEntryAmount();
189                laborOriginEntry.refreshReferenceObject(KFSPropertyConstants.BALANCE_TYPE); 
190                laborOriginEntry.refreshReferenceObject(KFSPropertyConstants.OBJECT_TYPE); 
191                if (laborOriginEntry.getBalanceType().isFinancialOffsetGenerationIndicator()) { 
192                    if (!laborOriginEntry.getTransactionDebitCreditCode().equals(laborOriginEntry.getObjectType().getFinObjectTypeDebitcreditCd())) { 
193                        amount = amount.negated(); 
194                    } 
195                } 
196        
197                ledgerBalanceHistory.addAmount(laborOriginEntry.getUniversityFiscalPeriodCode(), amount);
198                
199                businessObjectService.save(ledgerBalanceHistory);
200            }
201        }
202        
203        /**
204         * Compares entries in the Balance and BalanceHistory tables to ensure the amounts match.
205         * @return count is compare failures
206         */
207        protected Integer compareBalanceHistory() {
208            Integer countComparisionFailures = 0;
209            
210            List data = ledgerEntryBalanceCachingDao.compareBalanceHistory(LedgerBalance.class, balanceHistoryPersistentClass, getPastFiscalYearsToConsider());
211    
212            
213            if (!data.isEmpty()) {
214                for (Iterator itr = data.iterator(); itr.hasNext();) {
215                    LaborBalanceHistory balance = createBalanceFromMap((Map)itr.next());
216                    countComparisionFailures++;
217                    if (countComparisionFailures <= this.getComparisonFailuresToPrintPerReport()) {
218                        reportWriterService.writeError(balance, new Message(kualiConfigurationService.getPropertyString(KFSKeyConstants.Balancing.MESSAGE_BATCH_BALANCING_RECORD_FAILED_BALANCING), Message.TYPE_WARNING, balance.getClass().getSimpleName()));
219                    }
220                }
221            }
222    
223            return countComparisionFailures;
224        }
225        
226        /**
227         * Compares entries in the Entry and EntryHistory tables to ensure the amounts match.
228         * @return count is compare failures
229         */
230        protected Integer compareEntryHistory() {
231            Integer countComparisionFailures = 0;
232            
233            List data = ledgerEntryBalanceCachingDao.compareEntryHistory(LedgerEntry.class, entryHistoryPersistentClass, getPastFiscalYearsToConsider());
234            
235            if (!data.isEmpty()) {
236                for (Iterator itr = data.iterator(); itr.hasNext();) {
237                    LaborEntryHistory entry = createEntryHistoryFromMap((Map)itr.next());
238                    countComparisionFailures++;
239                    if (countComparisionFailures <= this.getComparisonFailuresToPrintPerReport()) {
240                      reportWriterService.writeError(entry, new Message(kualiConfigurationService.getPropertyString(KFSKeyConstants.Balancing.MESSAGE_BATCH_BALANCING_RECORD_FAILED_BALANCING), Message.TYPE_WARNING, entry.getClass().getSimpleName()));
241                    }
242                    
243                }
244            }
245    
246            return countComparisionFailures;
247        }
248        
249        
250        /**
251         * 
252         * @see org.kuali.kfs.gl.batch.service.BalancingService#clearBalanceHistory()
253         */
254       
255        public void clearHistories() {
256            Map<String, Object> fieldValues = new HashMap<String, Object>();
257            businessObjectService.deleteMatching(LaborEntryHistory.class, fieldValues);
258            businessObjectService.deleteMatching(LaborBalanceHistory.class, fieldValues);
259            
260            reportWriterService.writeFormattedMessageLine(kualiConfigurationService.getPropertyString(KFSKeyConstants.Balancing.MESSAGE_BATCH_BALANCING_HISTORY_PURGED));
261    
262        }
263        
264        /**
265         * @see org.kuali.kfs.gl.batch.service.BalancingService#getBalance(org.kuali.kfs.gl.businessobject.LedgerBalanceHistory)
266         */
267        public Balance getBalance(LedgerBalanceHistory ledgerBalanceHistory) {
268            LedgerBalance ledgerBalance = new LedgerBalance((LaborBalanceHistory) ledgerBalanceHistory);
269            return (LedgerBalance) businessObjectService.retrieve(ledgerBalance);
270        }
271        
272        /**
273         * @see org.kuali.kfs.gl.batch.service.BalancingService#clearPosterFileCache()
274         */
275        public void clearPosterFileCache() {
276            this.laborPosterInputFile = null;
277            this.laborPosterErrorOutputFile = null;
278        }
279        
280        protected LaborBalanceHistory createBalanceFromMap(Map<String, Object> map) {
281            LaborBalanceHistory balance = new LaborBalanceHistory();
282            balance.setUniversityFiscalYear(((BigDecimal)(map.get(GeneralLedgerConstants.ColumnNames.UNIVERSITY_FISCAL_YEAR))).intValue());
283            balance.setChartOfAccountsCode((String)map.get(GeneralLedgerConstants.ColumnNames.CHART_OF_ACCOUNTS_CODE));
284            balance.setAccountNumber((String)map.get(GeneralLedgerConstants.ColumnNames.ACCOUNT_NUMBER));
285            balance.setSubAccountNumber((String)map.get(GeneralLedgerConstants.ColumnNames.SUB_ACCOUNT_NUMBER));
286            balance.setObjectCode((String)map.get(GeneralLedgerConstants.ColumnNames.OBJECT_CODE));
287            balance.setSubObjectCode((String)map.get(GeneralLedgerConstants.ColumnNames.SUB_OBJECT_CODE));
288            balance.setBalanceTypeCode((String)map.get(GeneralLedgerConstants.ColumnNames.BALANCE_TYPE_CODE));
289            balance.setObjectTypeCode((String)map.get(GeneralLedgerConstants.ColumnNames.OBJECT_TYPE_CODE));
290            balance.setEmplid((String)map.get(LaborConstants.ColumnNames.EMPLOYEE_IDENTIFIER));
291            balance.setPositionNumber((String)map.get(LaborConstants.ColumnNames.POSITION_NUMBER));
292            
293            balance.setAccountLineAnnualBalanceAmount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.ACCOUNTING_LINE_ACTUALS_BALANCE_AMOUNT)));
294            balance.setContractsGrantsBeginningBalanceAmount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.CONTRACT_AND_GRANTS_BEGINNING_BALANCE)));
295            balance.setBeginningBalanceLineAmount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.BEGINNING_BALANCE)));
296            balance.setMonth1Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_1_ACCT_AMT)));
297            balance.setMonth2Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_2_ACCT_AMT)));
298            balance.setMonth3Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_3_ACCT_AMT)));
299            balance.setMonth4Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_4_ACCT_AMT)));
300            balance.setMonth5Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_5_ACCT_AMT)));
301            balance.setMonth6Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_6_ACCT_AMT)));
302            balance.setMonth7Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_7_ACCT_AMT)));
303            balance.setMonth8Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_8_ACCT_AMT)));
304            balance.setMonth9Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_9_ACCT_AMT)));
305            balance.setMonth10Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_10_ACCT_AMT)));
306            balance.setMonth11Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_11_ACCT_AMT)));
307            balance.setMonth12Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_12_ACCT_AMT)));
308            balance.setMonth13Amount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.MONTH_13_ACCT_AMT)));
309            
310            return balance;
311            
312        }
313        
314        protected LaborEntryHistory createEntryHistoryFromMap(Map<String, Object> map) {
315            LaborEntryHistory entry = new LaborEntryHistory();
316            entry.setUniversityFiscalYear(((BigDecimal)(map.get(GeneralLedgerConstants.ColumnNames.UNIVERSITY_FISCAL_YEAR))).intValue());
317            entry.setChartOfAccountsCode((String)map.get(GeneralLedgerConstants.ColumnNames.CHART_OF_ACCOUNTS_CODE));
318            entry.setFinancialObjectCode((String)map.get(GeneralLedgerConstants.ColumnNames.OBJECT_CODE));
319            entry.setFinancialBalanceTypeCode((String)map.get(GeneralLedgerConstants.ColumnNames.BALANCE_TYPE_CODE));
320            entry.setUniversityFiscalPeriodCode((String)map.get(GeneralLedgerConstants.ColumnNames.FISCAL_PERIOD_CODE));
321            entry.setTransactionDebitCreditCode((String)map.get(GeneralLedgerConstants.ColumnNames.TRANSACTION_DEBIT_CREDIT_CD));
322            entry.setTransactionLedgerEntryAmount(convertBigDecimalToKualiDecimal((BigDecimal)map.get(GeneralLedgerConstants.ColumnNames.TRANSACTION_LEDGER_ENTRY_AMOUNT)));
323            
324            return entry;
325            
326        }
327        
328        protected KualiDecimal convertBigDecimalToKualiDecimal(BigDecimal biggy) {
329            if (ObjectUtils.isNull(biggy))
330                return new KualiDecimal(0);   
331            else 
332                return new KualiDecimal(biggy);
333        
334        }
335        
336        /**
337         * @see org.kuali.kfs.gl.batch.service.BalancingService#getReversalInputFile()
338         */
339        public File getReversalInputFile(){
340            return null;
341        }
342    
343        /**
344         * @see org.kuali.kfs.gl.batch.service.BalancingService#getReversalErrorOutputFile()
345         */
346        public File getReversalErrorOutputFile(){
347            return null;
348        }
349    
350        /**
351         * @see org.kuali.kfs.gl.batch.service.BalancingService#getICRInputFile()
352         */
353        public File getICRInputFile(){
354            return null;
355        }
356    
357        /**
358         * @see org.kuali.kfs.gl.batch.service.BalancingService#getICRErrorOutputFile()
359         */
360        public File getICRErrorOutputFile(){
361            return null;
362        }
363        
364    }