001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.labor.service.impl;
017:
018: import static org.kuali.module.gl.bo.OriginEntrySource.LABOR_MAIN_POSTER_ERROR;
019: import static org.kuali.module.gl.bo.OriginEntrySource.LABOR_MAIN_POSTER_VALID;
020: import static org.kuali.module.gl.bo.OriginEntrySource.LABOR_SCRUBBER_VALID;
021: import static org.kuali.module.labor.LaborConstants.DestinationNames.ORIGN_ENTRY;
022:
023: import java.sql.Date;
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.HashMap;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030:
031: import org.kuali.core.service.DateTimeService;
032: import org.kuali.kfs.KFSConstants;
033: import org.kuali.kfs.service.ParameterService;
034: import org.kuali.module.gl.batch.poster.PostTransaction;
035: import org.kuali.module.gl.batch.poster.VerifyTransaction;
036: import org.kuali.module.gl.bo.OriginEntryGroup;
037: import org.kuali.module.gl.bo.Transaction;
038: import org.kuali.module.gl.service.OriginEntryGroupService;
039: import org.kuali.module.gl.util.Message;
040: import org.kuali.module.gl.util.Summary;
041: import org.kuali.module.labor.LaborConstants.Poster;
042: import org.kuali.module.labor.batch.LaborPosterStep;
043: import org.kuali.module.labor.bo.LaborOriginEntry;
044: import org.kuali.module.labor.rules.TransactionFieldValidator;
045: import org.kuali.module.labor.service.LaborOriginEntryService;
046: import org.kuali.module.labor.service.LaborPosterService;
047: import org.kuali.module.labor.service.LaborReportService;
048: import org.kuali.module.labor.util.MessageBuilder;
049: import org.kuali.module.labor.util.ObjectUtil;
050: import org.kuali.module.labor.util.ReportRegistry;
051: import org.springframework.transaction.annotation.Transactional;
052:
053: /**
054: * The Labor Ledger Poster accepts pending entries generated by Labor Ledger e-docs (such as Salary Expense Transfer and Benefit
055: * Expense Transfer), and combines them with entries from external systems. It edits the entries for validity. Invalid entries can
056: * be marked for Labor Ledger Error Correction process. The Poster writes valid entries to the Labor Ledger Entry table, updates
057: * balances in the Labor Ledger Balance table, and summarizes the entries for posting to the General Ledger.
058: */
059: @Transactional
060: public class LaborPosterServiceImpl implements LaborPosterService {
061: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
062: .getLogger(LaborPosterServiceImpl.class);
063:
064: private LaborOriginEntryService laborOriginEntryService;
065: private OriginEntryGroupService originEntryGroupService;
066:
067: private LaborReportService laborReportService;
068: private DateTimeService dateTimeService;
069: private VerifyTransaction laborPosterTransactionValidator;
070: private ParameterService parameterService;
071:
072: private PostTransaction laborLedgerEntryPoster;
073: private PostTransaction laborLedgerBalancePoster;
074: private PostTransaction laborGLLedgerEntryPoster;
075:
076: private final static int STEP = 1;
077: private final static int LINE_INTERVAL = 2;
078:
079: /**
080: * @see org.kuali.module.labor.service.LaborPosterService#postMainEntries()
081: */
082: public void postMainEntries() {
083: LOG.info("postMainEntries() started");
084:
085: Date runDate = dateTimeService.getCurrentSqlDate();
086: OriginEntryGroup validGroup = originEntryGroupService
087: .createGroup(runDate, LABOR_MAIN_POSTER_VALID, true,
088: false, false);
089: OriginEntryGroup invalidGroup = originEntryGroupService
090: .createGroup(runDate, LABOR_MAIN_POSTER_ERROR, false,
091: true, false);
092:
093: this .postLaborLedgerEntries(validGroup, invalidGroup, runDate);
094: this .postLaborGLEntries(validGroup, runDate);
095: }
096:
097: /**
098: * post the qualified origin entries into Labor Ledger tables
099: *
100: * @param validGroup the origin entry group that holds the valid transactions
101: * @param invalidGroup the origin entry group that holds the invalid transactions
102: * @param runDate the data when the process is running
103: */
104: private void postLaborLedgerEntries(OriginEntryGroup validGroup,
105: OriginEntryGroup invalidGroup, Date runDate) {
106: LOG.info("postLaborLedgerEntries() started");
107:
108: String reportsDirectory = ReportRegistry.getReportsDirectory();
109: Map<Transaction, List<Message>> errorMap = new HashMap<Transaction, List<Message>>();
110: List<Summary> reportSummary = this
111: .buildReportSummaryForLaborLedgerPosting();
112:
113: Collection<OriginEntryGroup> postingGroups = originEntryGroupService
114: .getGroupsToPost(LABOR_SCRUBBER_VALID);
115: laborReportService.generateInputSummaryReport(postingGroups,
116: ReportRegistry.LABOR_POSTER_INPUT, reportsDirectory,
117: runDate);
118:
119: int numberOfOriginEntry = laborOriginEntryService
120: .getCountOfEntriesInGroups(postingGroups);
121: int numberOfSelectedOriginEntry = 0;
122:
123: for (OriginEntryGroup entryGroup : postingGroups) {
124: Iterator<LaborOriginEntry> entries = laborOriginEntryService
125: .getEntriesByGroup(entryGroup);
126: while (entries != null && entries.hasNext()) {
127: LaborOriginEntry originEntry = entries.next();
128: if (postSingleEntryIntoLaborLedger(originEntry,
129: reportSummary, errorMap, validGroup,
130: invalidGroup, runDate)) {
131: numberOfSelectedOriginEntry++;
132: originEntry = null;
133: }
134: }
135: // reset the process flag of the group so that it cannot be handled any more
136: entryGroup.setProcess(Boolean.FALSE);
137: originEntryGroupService.save(entryGroup);
138: }
139: Summary
140: .updateReportSummary(reportSummary, ORIGN_ENTRY,
141: KFSConstants.OperationType.READ,
142: numberOfOriginEntry, 0);
143: Summary.updateReportSummary(reportSummary, ORIGN_ENTRY,
144: KFSConstants.OperationType.SELECT,
145: numberOfSelectedOriginEntry, 0);
146: Summary.updateReportSummary(reportSummary, ORIGN_ENTRY,
147: KFSConstants.OperationType.REPORT_ERROR, errorMap
148: .size(), 0);
149:
150: laborReportService.generateStatisticsReport(reportSummary,
151: errorMap, ReportRegistry.LABOR_POSTER_STATISTICS,
152: reportsDirectory, runDate);
153: laborReportService.generateErrorTransactionListing(
154: invalidGroup, ReportRegistry.LABOR_POSTER_ERROR,
155: reportsDirectory, runDate);
156: }
157:
158: /**
159: * post the given entry into the labor ledger tables if the entry is qualified; otherwise report error
160: *
161: * @param originEntry the given origin entry, a transaction
162: * @param reportSummary the report summary object that need to be update when a transaction is posted
163: * @param errorMap a map that holds the invalid transaction and corresponding error message
164: * @param validGroup the origin entry group that holds the valid transactions
165: * @param invalidGroup the origin entry group that holds the invalid transactions
166: * @param runDate the data when the process is running
167: * @return true if the given transaction is posted into ledger tables; otherwise, return false
168: */
169: private boolean postSingleEntryIntoLaborLedger(
170: LaborOriginEntry originEntry, List<Summary> reportSummary,
171: Map<Transaction, List<Message>> errorMap,
172: OriginEntryGroup validGroup, OriginEntryGroup invalidGroup,
173: Date runDate) {
174: try {
175: // reject the entry that is not postable
176: if (!isPostableEntry(originEntry)) {
177: return false;
178: }
179:
180: // reject the invalid entry so that it can be available for error correction
181: List<Message> errors = this .validateEntry(originEntry);
182: if (errors != null && !errors.isEmpty()) {
183: errorMap.put(originEntry, errors);
184: postAsProcessedOriginEntry(originEntry, invalidGroup,
185: runDate);
186: return false;
187: }
188:
189: // post the current origin entry as a valid origin entry, ledger entry and ledger balance
190: postAsProcessedOriginEntry(originEntry, validGroup, runDate);
191:
192: String operationOnLedgerEntry = postAsLedgerEntry(
193: originEntry, runDate);
194: Summary.updateReportSummary(reportSummary,
195: laborLedgerEntryPoster.getDestinationName(),
196: operationOnLedgerEntry, STEP, 0);
197:
198: String operationOnLedgerBalance = updateLedgerBalance(
199: originEntry, runDate);
200: Summary.updateReportSummary(reportSummary,
201: laborLedgerBalancePoster.getDestinationName(),
202: operationOnLedgerBalance, STEP, 0);
203: } catch (Exception e) {
204: LOG.error("Cannot post the input transaction: "
205: + originEntry + "\n" + e);
206: return false;
207: }
208: return true;
209: }
210:
211: /**
212: * determine if the given origin entry need to be posted
213: *
214: * @param originEntry the given origin entry, a transcation
215: * @return true if the transaction is eligible for poster process; otherwise; return false
216: */
217: private boolean isPostableEntry(LaborOriginEntry originEntry) {
218: if (TransactionFieldValidator.checkZeroTotalAmount(originEntry) != null) {
219: return false;
220: } else if (TransactionFieldValidator.checkPostableObjectCode(
221: originEntry, this .getObjectsNotProcessed()) != null) {
222: return false;
223: }
224: return true;
225: }
226:
227: /**
228: * validate the given entry, and generate an error list if the entry cannot meet the business rules
229: *
230: * @param originEntry the given origin entry, a transcation
231: * @return error message list. If the given transaction is invalid, the list has message(s); otherwise, it is empty
232: */
233: private List<Message> validateEntry(LaborOriginEntry originEntry) {
234: return laborPosterTransactionValidator
235: .verifyTransaction(originEntry);
236: }
237:
238: /**
239: * post the processed entry into the approperiate group, either valid or invalid group
240: *
241: * @param originEntry the given origin entry, a transaction
242: * @param entryGroup the origin entry group that the transaction will be assigned
243: * @param postDate the data when the transaction is processes
244: */
245: private void postAsProcessedOriginEntry(
246: LaborOriginEntry originEntry, OriginEntryGroup entryGroup,
247: Date postDate) {
248: LaborOriginEntry newOriginEntry = new LaborOriginEntry();
249:
250: ObjectUtil.buildObject(newOriginEntry, originEntry);
251: newOriginEntry.setEntryId(null);
252: newOriginEntry.setEntryGroupId(entryGroup.getId());
253: newOriginEntry.setTransactionPostingDate(postDate);
254:
255: laborOriginEntryService.save(newOriginEntry);
256: }
257:
258: /**
259: * post the given entry to the labor entry table
260: *
261: * @param originEntry the given origin entry, a transaction
262: * @param postDate the data when the transaction is processes return the operation type of the process
263: */
264: private String postAsLedgerEntry(LaborOriginEntry originEntry,
265: Date postDate) {
266: return laborLedgerEntryPoster.post(originEntry, 0, postDate);
267: }
268:
269: /**
270: * update the labor ledger balance for the given entry
271: *
272: * @param originEntry the given origin entry, a transaction
273: * @param postDate the data when the transaction is processes return the operation type of the process
274: */
275: private String updateLedgerBalance(LaborOriginEntry originEntry,
276: Date postDate) {
277: return laborLedgerBalancePoster.post(originEntry, 0, postDate);
278: }
279:
280: /**
281: * post the valid origin entries in the given group into General Ledger
282: *
283: * @param validGroup the origin entry group that contains the valid transactions determined in the Labor Poster
284: * @param runDate the data when the process is running
285: */
286: private void postLaborGLEntries(OriginEntryGroup validGroup,
287: Date runDate) {
288: LOG.info("postLaborGLEntries() started");
289:
290: String reportsDirectory = ReportRegistry.getReportsDirectory();
291: List<Summary> reportSummary = this
292: .buildReportSummaryForLaborGLPosting();
293: Map<Transaction, List<Message>> errorMap = new HashMap<Transaction, List<Message>>();
294:
295: Collection<LaborOriginEntry> entries = laborOriginEntryService
296: .getConsolidatedEntryCollectionByGroup(validGroup);
297: int numberOfOriginEntries = laborOriginEntryService
298: .getCountOfEntriesInSingleGroup(validGroup);
299: int numberOfSelectedOriginEntry = 0;
300:
301: for (LaborOriginEntry originEntry : entries) {
302:
303: List<Message> errors = this
304: .isPostableForLaborGLEntry(originEntry);
305: if (!errors.isEmpty()) {
306: continue;
307: }
308: String operationType = laborGLLedgerEntryPoster.post(
309: originEntry, 0, runDate);
310: Summary.updateReportSummary(reportSummary,
311: laborGLLedgerEntryPoster.getDestinationName(),
312: operationType, STEP, 0);
313:
314: numberOfSelectedOriginEntry++;
315: }
316: Summary.updateReportSummary(reportSummary, ORIGN_ENTRY,
317: KFSConstants.OperationType.READ, numberOfOriginEntries,
318: 0);
319: Summary.updateReportSummary(reportSummary, ORIGN_ENTRY,
320: KFSConstants.OperationType.SELECT,
321: numberOfSelectedOriginEntry, 0);
322: Summary.updateReportSummary(reportSummary, ORIGN_ENTRY,
323: KFSConstants.OperationType.REPORT_ERROR, errorMap
324: .size(), 0);
325: laborReportService.generateStatisticsReport(reportSummary,
326: errorMap, ReportRegistry.LABOR_POSTER_GL_SUMMARY,
327: reportsDirectory, runDate);
328: }
329:
330: /**
331: * determine if the given origin entry can be posted back to Labor GL entry
332: *
333: * @param originEntry the given origin entry, atransaction
334: * @return a message list. The list has message(s) if the given origin entry cannot be posted back to Labor GL entry; otherwise,
335: * it is empty
336: */
337: private List<Message> isPostableForLaborGLEntry(
338: LaborOriginEntry originEntry) {
339: List<Message> errors = new ArrayList<Message>();
340: MessageBuilder.addMessageIntoList(errors,
341: TransactionFieldValidator.checkPostablePeridCode(
342: originEntry, getPeriodCodesNotProcessed()));
343: MessageBuilder.addMessageIntoList(errors,
344: TransactionFieldValidator.checkPostableBalanceTypeCode(
345: originEntry, getBalanceTypesNotProcessed()));
346: MessageBuilder.addMessageIntoList(errors,
347: TransactionFieldValidator
348: .checkZeroTotalAmount(originEntry));
349: return errors;
350: }
351:
352: /**
353: * build a report summary list for labor ledger posting
354: *
355: * @return a report summary list for labor ledger posting
356: */
357: private List<Summary> buildReportSummaryForLaborLedgerPosting() {
358: List<Summary> reportSummary = new ArrayList<Summary>();
359:
360: String destination = laborLedgerEntryPoster
361: .getDestinationName();
362: reportSummary.add(new Summary(reportSummary.size()
363: + LINE_INTERVAL, "", 0));
364: reportSummary.addAll(Summary.buildDefualtReportSummary(
365: destination, reportSummary.size() + LINE_INTERVAL));
366:
367: destination = laborLedgerBalancePoster.getDestinationName();
368: reportSummary.add(new Summary(reportSummary.size()
369: + LINE_INTERVAL, "", 0));
370: reportSummary.addAll(Summary.buildDefualtReportSummary(
371: destination, reportSummary.size() + LINE_INTERVAL));
372:
373: return reportSummary;
374: }
375:
376: /**
377: * build a report summary list for labor general ledger posting
378: *
379: * @return a report summary list for labor general ledger posting
380: */
381: private List<Summary> buildReportSummaryForLaborGLPosting() {
382: List<Summary> reportSummary = new ArrayList<Summary>();
383:
384: String destination = laborGLLedgerEntryPoster
385: .getDestinationName();
386: reportSummary.add(new Summary(reportSummary.size()
387: + LINE_INTERVAL, "", 0));
388: Summary.updateReportSummary(reportSummary, destination,
389: KFSConstants.OperationType.INSERT, 0, reportSummary
390: .size()
391: + LINE_INTERVAL);
392:
393: return reportSummary;
394: }
395:
396: /**
397: * Get a set of the balance type codes that are bypassed by Labor Poster
398: *
399: * @return a set of the balance type codes that are bypassed by Labor Poster
400: */
401: public List<String> getBalanceTypesNotProcessed() {
402: return parameterService.getParameterValues(
403: LaborPosterStep.class,
404: Poster.BALANCE_TYPES_NOT_PROCESSED);
405: }
406:
407: /**
408: * Get a set of the object codes that are bypassed by Labor Poster
409: *
410: * @return a set of the object codes that are bypassed by Labor Poster
411: */
412: public List<String> getObjectsNotProcessed() {
413: return parameterService.getParameterValues(
414: LaborPosterStep.class,
415: Poster.OBJECT_CODES_NOT_PROCESSED);
416: }
417:
418: /**
419: * Get a set of the fiscal period codes that are bypassed by Labor Poster
420: *
421: * @return a set of the fiscal period codes that are bypassed by Labor Poster
422: */
423: public List<String> getPeriodCodesNotProcessed() {
424: return parameterService.getParameterValues(
425: LaborPosterStep.class,
426: Poster.PERIOD_CODES_NOT_PROCESSED);
427: }
428:
429: /**
430: * Sets the dateTimeService attribute value.
431: *
432: * @param dateTimeService The dateTimeService to set.
433: */
434: public void setDateTimeService(DateTimeService dateTimeService) {
435: this .dateTimeService = dateTimeService;
436: }
437:
438: /**
439: * Sets the laborLedgerBalancePoster attribute value.
440: *
441: * @param laborLedgerBalancePoster The laborLedgerBalancePoster to set.
442: */
443: public void setLaborLedgerBalancePoster(
444: PostTransaction laborLedgerBalancePoster) {
445: this .laborLedgerBalancePoster = laborLedgerBalancePoster;
446: }
447:
448: /**
449: * Sets the laborGLLedgerEntryPoster attribute value.
450: *
451: * @param laborGLLedgerEntryPoster The laborGLLedgerEntryPoster to set.
452: */
453: public void setLaborGLLedgerEntryPoster(
454: PostTransaction laborGLLedgerEntryPoster) {
455: this .laborGLLedgerEntryPoster = laborGLLedgerEntryPoster;
456: }
457:
458: /**
459: * Sets the laborLedgerEntryPoster attribute value.
460: *
461: * @param laborLedgerEntryPoster The laborLedgerEntryPoster to set.
462: */
463: public void setLaborLedgerEntryPoster(
464: PostTransaction laborLedgerEntryPoster) {
465: this .laborLedgerEntryPoster = laborLedgerEntryPoster;
466: }
467:
468: /**
469: * Sets the laborOriginEntryService attribute value.
470: *
471: * @param laborOriginEntryService The laborOriginEntryService to set.
472: */
473: public void setLaborOriginEntryService(
474: LaborOriginEntryService laborOriginEntryService) {
475: this .laborOriginEntryService = laborOriginEntryService;
476: }
477:
478: /**
479: * Sets the originEntryGroupService attribute value.
480: *
481: * @param originEntryGroupService The originEntryGroupService to set.
482: */
483: public void setOriginEntryGroupService(
484: OriginEntryGroupService originEntryGroupService) {
485: this .originEntryGroupService = originEntryGroupService;
486: }
487:
488: /**
489: * Sets the laborReportService attribute value.
490: *
491: * @param laborReportService The laborReportService to set.
492: */
493: public void setLaborReportService(
494: LaborReportService laborReportService) {
495: this .laborReportService = laborReportService;
496: }
497:
498: /**
499: * Sets the laborPosterTransactionValidator attribute value.
500: *
501: * @param laborPosterTransactionValidator The laborPosterTransactionValidator to set.
502: */
503: public void setLaborPosterTransactionValidator(
504: VerifyTransaction laborPosterTransactionValidator) {
505: this .laborPosterTransactionValidator = laborPosterTransactionValidator;
506: }
507:
508: public void setParameterService(ParameterService parameterService) {
509: this.parameterService = parameterService;
510: }
511: }
|