001: /*
002: * Copyright 2005-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.gl.service.impl;
017:
018: import java.math.BigDecimal;
019: import java.sql.Date;
020: import java.text.DecimalFormat;
021: import java.text.SimpleDateFormat;
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Map;
028:
029: import org.apache.ojb.broker.metadata.MetadataManager;
030: import org.kuali.core.service.DateTimeService;
031: import org.kuali.core.service.KualiConfigurationService;
032: import org.kuali.core.service.PersistenceService;
033: import org.kuali.core.util.KualiDecimal;
034: import org.kuali.kfs.KFSConstants;
035: import org.kuali.kfs.KFSKeyConstants;
036: import org.kuali.kfs.context.SpringContext;
037: import org.kuali.kfs.service.ParameterService;
038: import org.kuali.kfs.service.impl.ParameterConstants;
039: import org.kuali.module.chart.bo.AccountingPeriod;
040: import org.kuali.module.chart.bo.IcrAutomatedEntry;
041: import org.kuali.module.chart.bo.ObjectCode;
042: import org.kuali.module.chart.dao.IcrAutomatedEntryDao;
043: import org.kuali.module.chart.service.AccountingPeriodService;
044: import org.kuali.module.chart.service.ObjectCodeService;
045: import org.kuali.module.financial.exceptions.InvalidFlexibleOffsetException;
046: import org.kuali.module.financial.service.FlexibleOffsetAccountService;
047: import org.kuali.module.gl.GLConstants;
048: import org.kuali.module.gl.batch.PosterIndirectCostRecoveryEntriesStep;
049: import org.kuali.module.gl.batch.poster.PostTransaction;
050: import org.kuali.module.gl.batch.poster.VerifyTransaction;
051: import org.kuali.module.gl.bo.ExpenditureTransaction;
052: import org.kuali.module.gl.bo.OriginEntryFull;
053: import org.kuali.module.gl.bo.OriginEntryGroup;
054: import org.kuali.module.gl.bo.OriginEntrySource;
055: import org.kuali.module.gl.bo.Reversal;
056: import org.kuali.module.gl.bo.Transaction;
057: import org.kuali.module.gl.bo.UniversityDate;
058: import org.kuali.module.gl.dao.ExpenditureTransactionDao;
059: import org.kuali.module.gl.dao.ReversalDao;
060: import org.kuali.module.gl.dao.UniversityDateDao;
061: import org.kuali.module.gl.service.OriginEntryGroupService;
062: import org.kuali.module.gl.service.OriginEntryService;
063: import org.kuali.module.gl.service.PosterService;
064: import org.kuali.module.gl.service.ReportService;
065: import org.kuali.module.gl.service.RunDateService;
066: import org.kuali.module.gl.util.Message;
067: import org.springframework.transaction.annotation.Transactional;
068:
069: /**
070: * The base implementation of PosterService
071: */
072: @Transactional
073: public class PosterServiceImpl implements PosterService {
074: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
075: .getLogger(PosterServiceImpl.class);
076:
077: public static final KualiDecimal WARNING_MAX_DIFFERENCE = new KualiDecimal(
078: "0.03");
079: public static final String DATE_FORMAT_STRING = "yyyyMMdd";
080:
081: private List transactionPosters;
082: private VerifyTransaction verifyTransaction;
083: private OriginEntryService originEntryService;
084: private OriginEntryGroupService originEntryGroupService;
085: private DateTimeService dateTimeService;
086: private ReversalDao reversalDao;
087: private UniversityDateDao universityDateDao;
088: private AccountingPeriodService accountingPeriodService;
089: private ExpenditureTransactionDao expenditureTransactionDao;
090: private IcrAutomatedEntryDao icrAutomatedEntryDao;
091: private ObjectCodeService objectCodeService;
092: private ReportService reportService;
093: private ParameterService parameterService;
094: private KualiConfigurationService configurationService;
095: private FlexibleOffsetAccountService flexibleOffsetAccountService;
096: private RunDateService runDateService;
097:
098: /**
099: * Post scrubbed GL entries to GL tables.
100: */
101: public void postMainEntries() {
102: LOG.debug("postMainEntries() started");
103: postEntries(PosterService.MODE_ENTRIES);
104: }
105:
106: /**
107: * Post reversal GL entries to GL tables.
108: */
109: public void postReversalEntries() {
110: LOG.debug("postReversalEntries() started");
111: postEntries(PosterService.MODE_REVERSAL);
112: }
113:
114: /**
115: * Post ICR GL entries to GL tables.
116: */
117: public void postIcrEntries() {
118: LOG.debug("postIcrEntries() started");
119: postEntries(PosterService.MODE_ICR);
120: }
121:
122: /**
123: * Actually post the entries. The mode variable decides which entries to post.
124: *
125: * @param mode the poster's current run mode
126: */
127: private void postEntries(int mode) {
128: LOG.debug("postEntries() started");
129:
130: String validEntrySourceCode = OriginEntrySource.MAIN_POSTER_VALID;
131: String invalidEntrySourceCode = OriginEntrySource.MAIN_POSTER_ERROR;
132: OriginEntryGroup validGroup = null;
133: OriginEntryGroup invalidGroup = null;
134:
135: Date executionDate = new Date(dateTimeService.getCurrentDate()
136: .getTime());
137: Date runDate = new Date(runDateService.calculateRunDate(
138: executionDate).getTime());
139:
140: UniversityDate runUniversityDate = universityDateDao
141: .getByPrimaryKey(runDate);
142:
143: Collection groups = null;
144: Iterator reversalTransactions = null;
145: switch (mode) {
146: case PosterService.MODE_ENTRIES:
147: validEntrySourceCode = OriginEntrySource.MAIN_POSTER_VALID;
148: invalidEntrySourceCode = OriginEntrySource.MAIN_POSTER_ERROR;
149: groups = originEntryGroupService.getGroupsToPost();
150: reportService.generatePosterMainLedgerSummaryReport(
151: executionDate, runDate, groups);
152: break;
153: case PosterService.MODE_REVERSAL:
154: validEntrySourceCode = OriginEntrySource.REVERSAL_POSTER_VALID;
155: invalidEntrySourceCode = OriginEntrySource.REVERSAL_POSTER_ERROR;
156: reversalTransactions = reversalDao.getByDate(runDate);
157: reportService.generatePosterReversalLedgerSummaryReport(
158: executionDate, runDate, reversalTransactions);
159: break;
160: case PosterService.MODE_ICR:
161: validEntrySourceCode = OriginEntrySource.ICR_POSTER_VALID;
162: invalidEntrySourceCode = OriginEntrySource.ICR_POSTER_ERROR;
163: groups = originEntryGroupService.getIcrGroupsToPost();
164: reportService.generatePosterIcrLedgerSummaryReport(
165: executionDate, runDate, groups);
166: break;
167: default:
168: throw new IllegalArgumentException("Invalid poster mode "
169: + mode);
170: }
171:
172: // Create new Groups for output transactions
173: validGroup = originEntryGroupService.createGroup(runDate,
174: validEntrySourceCode, true, true, false);
175: invalidGroup = originEntryGroupService.createGroup(runDate,
176: invalidEntrySourceCode, false, true, false);
177:
178: Map reportError = new HashMap();
179:
180: // Build the summary map so all the possible combinations of destination &
181: // operation
182: // are included in the summary part of the report.
183: Map reportSummary = new HashMap();
184: for (Iterator posterIter = transactionPosters.iterator(); posterIter
185: .hasNext();) {
186: PostTransaction poster = (PostTransaction) posterIter
187: .next();
188: reportSummary.put(poster.getDestinationName() + ","
189: + GLConstants.DELETE_CODE, new Integer(0));
190: reportSummary.put(poster.getDestinationName() + ","
191: + GLConstants.INSERT_CODE, new Integer(0));
192: reportSummary.put(poster.getDestinationName() + ","
193: + GLConstants.UPDATE_CODE, new Integer(0));
194: }
195:
196: int ecount = 0;
197: try {
198: if ((mode == PosterService.MODE_ENTRIES)
199: || (mode == PosterService.MODE_ICR)) {
200: LOG.debug("postEntries() Processing groups");
201: for (Iterator iter = groups.iterator(); iter.hasNext();) {
202: OriginEntryGroup group = (OriginEntryGroup) iter
203: .next();
204:
205: Iterator entries = originEntryService
206: .getEntriesByGroup(group);
207: while (entries.hasNext()) {
208: Transaction tran = (Transaction) entries.next();
209:
210: postTransaction(tran, mode, reportSummary,
211: reportError, invalidGroup, validGroup,
212: runUniversityDate);
213:
214: if (++ecount % 1000 == 0) {
215: LOG.info("postEntries() Posted Entry "
216: + ecount);
217: }
218: }
219:
220: // Mark this group so we don't process it again next time the poster runs
221: group.setProcess(Boolean.FALSE);
222: originEntryGroupService.save(group);
223: }
224: } else {
225: LOG
226: .debug("postEntries() Processing reversal transactions");
227:
228: final String GL_REVERSAL_T = MetadataManager
229: .getInstance().getGlobalRepository()
230: .getDescriptorFor(Reversal.class)
231: .getFullTableName();
232:
233: while (reversalTransactions.hasNext()) {
234: Transaction tran = (Transaction) reversalTransactions
235: .next();
236: addReporting(reportSummary, GL_REVERSAL_T,
237: GLConstants.SELECT_CODE);
238:
239: postTransaction(tran, mode, reportSummary,
240: reportError, invalidGroup, validGroup,
241: runUniversityDate);
242:
243: if (++ecount % 1000 == 0) {
244: LOG
245: .info("postEntries() Posted Entry "
246: + ecount);
247: }
248: }
249:
250: // Report Reversal poster valid transactions
251: reportService
252: .generatePosterReversalTransactionsListing(
253: executionDate, runDate, validGroup);
254: }
255: } catch (RuntimeException e) {
256: LOG.info("postEntries() failed posting Entry " + ecount);
257: throw e;
258: }
259:
260: LOG.info("postEntries() done, total count = " + ecount);
261:
262: // Generate the reports
263: reportService.generatePosterStatisticsReport(executionDate,
264: runDate, reportSummary, transactionPosters,
265: reportError, mode);
266: reportService.generatePosterErrorTransactionListing(
267: executionDate, runDate, invalidGroup, mode);
268: }
269:
270: /**
271: * Runs the given transaction through each transaction posting algorithms associated with this instance
272: *
273: * @param tran a transaction to post
274: * @param mode the mode the poster is running in
275: * @param reportSummary a Map of summary counts generated by the posting process
276: * @param reportError a Map of errors encountered during posting
277: * @param invalidGroup the group to save invalid entries to
278: * @param validGroup the gorup to save valid posted entries into
279: * @param runUniversityDate the university date of this poster run
280: */
281: private void postTransaction(Transaction tran, int mode,
282: Map reportSummary, Map reportError,
283: OriginEntryGroup invalidGroup, OriginEntryGroup validGroup,
284: UniversityDate runUniversityDate) {
285:
286: List errors = new ArrayList();
287:
288: Transaction originalTransaction = tran;
289:
290: final String GL_ORIGIN_ENTRY_T = MetadataManager.getInstance()
291: .getGlobalRepository().getDescriptorFor(
292: OriginEntryFull.class).getFullTableName();
293:
294: // Update select count in the report
295: if (mode == PosterService.MODE_ENTRIES) {
296: addReporting(reportSummary, GL_ORIGIN_ENTRY_T,
297: GLConstants.SELECT_CODE);
298: } else {
299: addReporting(reportSummary, GL_ORIGIN_ENTRY_T + " (ICR)",
300: GLConstants.SELECT_CODE);
301: }
302:
303: // If these are reversal entries, we need to reverse the entry and
304: // modify a few fields
305: if (mode == PosterService.MODE_REVERSAL) {
306: Reversal reversal = new Reversal(tran);
307:
308: // Reverse the debit/credit code
309: if (KFSConstants.GL_DEBIT_CODE.equals(reversal
310: .getTransactionDebitCreditCode())) {
311: reversal
312: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
313: } else if (KFSConstants.GL_CREDIT_CODE.equals(reversal
314: .getTransactionDebitCreditCode())) {
315: reversal
316: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
317: }
318:
319: UniversityDate udate = universityDateDao
320: .getByPrimaryKey(reversal
321: .getFinancialDocumentReversalDate());
322: if (udate != null) {
323: reversal.setUniversityFiscalYear(udate
324: .getUniversityFiscalYear());
325: reversal.setUniversityFiscalPeriodCode(udate
326: .getUniversityFiscalAccountingPeriod());
327:
328: AccountingPeriod ap = accountingPeriodService
329: .getByPeriod(reversal
330: .getUniversityFiscalPeriodCode(),
331: reversal.getUniversityFiscalYear());
332: if (ap != null) {
333: if (KFSConstants.ACCOUNTING_PERIOD_STATUS_CLOSED
334: .equals(ap
335: .getUniversityFiscalPeriodStatusCode())) {
336: reversal
337: .setUniversityFiscalYear(runUniversityDate
338: .getUniversityFiscalYear());
339: reversal
340: .setUniversityFiscalPeriodCode(runUniversityDate
341: .getUniversityFiscalAccountingPeriod());
342: }
343: reversal.setFinancialDocumentReversalDate(null);
344: String newDescription = KFSConstants.GL_REVERSAL_DESCRIPTION_PREFIX
345: + reversal
346: .getTransactionLedgerEntryDescription();
347: if (newDescription.length() > 40) {
348: newDescription = newDescription
349: .substring(0, 40);
350: }
351: reversal
352: .setTransactionLedgerEntryDescription(newDescription);
353: } else {
354: errors
355: .add(configurationService
356: .getPropertyString(KFSKeyConstants.ERROR_UNIV_DATE_NOT_IN_ACCOUNTING_PERIOD_TABLE));
357: }
358: } else {
359: errors
360: .add(configurationService
361: .getPropertyString(KFSKeyConstants.ERROR_REVERSAL_DATE_NOT_IN_UNIV_DATE_TABLE));
362: }
363:
364: PersistenceService ps = SpringContext
365: .getBean(PersistenceService.class);
366: ps.retrieveNonKeyFields(reversal);
367: tran = reversal;
368: }
369:
370: if (errors.size() == 0) {
371: errors = verifyTransaction.verifyTransaction(tran);
372: }
373:
374: // Now check each poster to see if it needs to verify the transaction. If
375: // it returns errors, we won't post it
376: for (Iterator posterIter = transactionPosters.iterator(); posterIter
377: .hasNext();) {
378: PostTransaction poster = (PostTransaction) posterIter
379: .next();
380: if (poster instanceof VerifyTransaction) {
381: VerifyTransaction vt = (VerifyTransaction) poster;
382:
383: errors.addAll(vt.verifyTransaction(tran));
384: }
385: }
386:
387: if (errors.size() > 0) {
388: // Error on this transaction
389: reportError.put(tran, errors);
390: addReporting(reportSummary, "WARNING",
391: GLConstants.SELECT_CODE);
392:
393: originEntryService.createEntry(tran, invalidGroup);
394: } else {
395: // No error so post it
396: for (Iterator posterIter = transactionPosters.iterator(); posterIter
397: .hasNext();) {
398: PostTransaction poster = (PostTransaction) posterIter
399: .next();
400: String actionCode = poster.post(tran, mode,
401: runUniversityDate.getUniversityDate());
402:
403: if (actionCode.startsWith(GLConstants.ERROR_CODE)) {
404: errors = new ArrayList();
405: errors.add(actionCode);
406: reportError.put(tran, errors);
407: } else if (actionCode.indexOf(GLConstants.INSERT_CODE) >= 0) {
408: addReporting(reportSummary, poster
409: .getDestinationName(),
410: GLConstants.INSERT_CODE);
411: } else if (actionCode.indexOf(GLConstants.UPDATE_CODE) >= 0) {
412: addReporting(reportSummary, poster
413: .getDestinationName(),
414: GLConstants.UPDATE_CODE);
415: } else if (actionCode.indexOf(GLConstants.DELETE_CODE) >= 0) {
416: addReporting(reportSummary, poster
417: .getDestinationName(),
418: GLConstants.DELETE_CODE);
419: } else if (actionCode.indexOf(GLConstants.SELECT_CODE) >= 0) {
420: addReporting(reportSummary, poster
421: .getDestinationName(),
422: GLConstants.SELECT_CODE);
423: }
424: }
425:
426: if (errors.size() == 0) {
427: originEntryService.createEntry(tran, validGroup);
428:
429: // Delete the reversal entry
430: if (mode == PosterService.MODE_REVERSAL) {
431: reversalDao.delete((Reversal) originalTransaction);
432: addReporting(reportSummary, MetadataManager
433: .getInstance().getGlobalRepository()
434: .getDescriptorFor(Reversal.class)
435: .getFullTableName(),
436: GLConstants.DELETE_CODE);
437: }
438: }
439: }
440: }
441:
442: /**
443: * This step reads the expenditure table and uses the data to generate Indirect Cost Recovery transactions.
444: */
445: public void generateIcrTransactions() {
446: LOG.debug("generateIcrTransactions() started");
447:
448: Date executionDate = dateTimeService.getCurrentSqlDate();
449: Date runDate = new Date(runDateService.calculateRunDate(
450: executionDate).getTime());
451:
452: OriginEntryGroup group = originEntryGroupService.createGroup(
453: runDate, OriginEntrySource.ICR_TRANSACTIONS, true,
454: true, false);
455:
456: Map<ExpenditureTransaction, List<Message>> reportErrors = new HashMap();
457:
458: int reportExpendTranRetrieved = 0;
459: int reportExpendTranDeleted = 0;
460: int reportExpendTranKept = 0;
461: int reportOriginEntryGenerated = 0;
462:
463: Iterator expenditureTransactions = expenditureTransactionDao
464: .getAllExpenditureTransactions();
465: while (expenditureTransactions.hasNext()) {
466: ExpenditureTransaction et = (ExpenditureTransaction) expenditureTransactions
467: .next();
468: reportExpendTranRetrieved++;
469:
470: KualiDecimal transactionAmount = et
471: .getAccountObjectDirectCostAmount();
472: KualiDecimal distributionAmount = KualiDecimal.ZERO;
473:
474: Collection automatedEntries = icrAutomatedEntryDao
475: .getEntriesBySeries(et.getUniversityFiscalYear(),
476: et.getAccount()
477: .getFinancialIcrSeriesIdentifier(),
478: et.getBalanceTypeCode());
479: int automatedEntriesCount = automatedEntries.size();
480:
481: if (automatedEntriesCount > 0) {
482: for (Iterator icrIter = automatedEntries.iterator(); icrIter
483: .hasNext();) {
484: IcrAutomatedEntry icrEntry = (IcrAutomatedEntry) icrIter
485: .next();
486: KualiDecimal generatedTransactionAmount = null;
487:
488: if (!icrIter.hasNext()) {
489: generatedTransactionAmount = distributionAmount;
490:
491: // Log differences that are over WARNING_MAX_DIFFERENCE
492: if (getPercentage(transactionAmount,
493: icrEntry.getAwardIndrCostRcvyRatePct())
494: .subtract(distributionAmount).abs()
495: .isGreaterThan(WARNING_MAX_DIFFERENCE)) {
496: List warnings = new ArrayList();
497: warnings.add("ADJUSTMENT GREATER THAN "
498: + WARNING_MAX_DIFFERENCE);
499: reportErrors.put(et, warnings);
500: }
501: } else if (icrEntry.getTransactionDebitIndicator()
502: .equals(KFSConstants.GL_DEBIT_CODE)) {
503: generatedTransactionAmount = getPercentage(
504: transactionAmount, icrEntry
505: .getAwardIndrCostRcvyRatePct());
506: distributionAmount = distributionAmount
507: .add(generatedTransactionAmount);
508: } else if (icrEntry.getTransactionDebitIndicator()
509: .equals(KFSConstants.GL_CREDIT_CODE)) {
510: generatedTransactionAmount = getPercentage(
511: transactionAmount, icrEntry
512: .getAwardIndrCostRcvyRatePct());
513: distributionAmount = distributionAmount
514: .subtract(generatedTransactionAmount);
515: } else {
516: // Log if D / C code not found
517: List warnings = new ArrayList();
518: warnings.add("DEBIT OR CREDIT CODE NOT FOUND");
519: reportErrors.put(et, warnings);
520: }
521:
522: generateTransactions(et, icrEntry,
523: generatedTransactionAmount, runDate, group,
524: reportErrors);
525: reportOriginEntryGenerated = reportOriginEntryGenerated + 2;
526: }
527: }
528:
529: // Delete expenditure record
530: expenditureTransactionDao.delete(et);
531: reportExpendTranDeleted++;
532: }
533:
534: reportService.generatePosterIcrStatisticsReport(executionDate,
535: runDate, reportErrors, reportExpendTranRetrieved,
536: reportExpendTranDeleted, reportExpendTranKept,
537: reportOriginEntryGenerated);
538: }
539:
540: /**
541: * Generate a transfer transaction and an offset transaction
542: *
543: * @param et an expenditure transaction
544: * @param icrEntry the indirect cost recovery entry
545: * @param generatedTransactionAmount the amount of the transaction
546: * @param runDate the transaction date for the newly created origin entry
547: * @param group the group to save the origin entry to
548: */
549: private void generateTransactions(ExpenditureTransaction et,
550: IcrAutomatedEntry icrEntry,
551: KualiDecimal generatedTransactionAmount, Date runDate,
552: OriginEntryGroup group, Map reportErrors) {
553: BigDecimal pct = new BigDecimal(icrEntry
554: .getAwardIndrCostRcvyRatePct().toString());
555: pct = pct.divide(BDONEHUNDRED);
556:
557: OriginEntryFull e = new OriginEntryFull();
558: e.setTransactionLedgerEntrySequenceNumber(0);
559:
560: // SYMBOL_USE_EXPENDITURE_ENTRY means we use the field from the expenditure entry, SYMBOL_USE_IRC_FROM_ACCOUNT
561: // means we use the ICR field from the account record, otherwise, use the field in the icrEntry
562: if (GLConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY
563: .equals(icrEntry.getFinancialObjectCode())
564: || GLConstants.PosterService.SYMBOL_USE_IRC_FROM_ACCOUNT
565: .equals(icrEntry.getFinancialObjectCode())) {
566: e.setFinancialObjectCode(et.getObjectCode());
567: e.setFinancialSubObjectCode(et.getSubObjectCode());
568: } else {
569: e.setFinancialObjectCode(icrEntry.getFinancialObjectCode());
570: if (GLConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY
571: .equals(icrEntry.getFinancialSubObjectCode())) {
572: e.setFinancialSubObjectCode(et.getSubObjectCode());
573: } else {
574: e.setFinancialSubObjectCode(icrEntry
575: .getFinancialSubObjectCode());
576: }
577: }
578:
579: if (GLConstants.PosterService.SYMBOL_USE_EXPENDITURE_ENTRY
580: .equals(icrEntry.getAccountNumber())) {
581: e.setAccountNumber(et.getAccountNumber());
582: e.setChartOfAccountsCode(et.getChartOfAccountsCode());
583: e.setSubAccountNumber(et.getSubAccountNumber());
584: } else if (GLConstants.PosterService.SYMBOL_USE_IRC_FROM_ACCOUNT
585: .equals(icrEntry.getAccountNumber())) {
586: e.setAccountNumber(et.getAccount()
587: .getIndirectCostRecoveryAcctNbr());
588: e.setChartOfAccountsCode(et.getAccount()
589: .getIndirectCostRcvyFinCoaCode());
590: e.setSubAccountNumber(KFSConstants
591: .getDashSubAccountNumber());
592: } else {
593: e.setAccountNumber(icrEntry.getAccountNumber());
594: e.setSubAccountNumber(icrEntry.getSubAccountNumber());
595: e.setChartOfAccountsCode(icrEntry.getChartOfAccountsCode());
596: // TODO Reporting thing line 1946
597: }
598:
599: e
600: .setFinancialDocumentTypeCode(parameterService
601: .getParameterValue(
602: PosterIndirectCostRecoveryEntriesStep.class,
603: KFSConstants.SystemGroupParameterNames.GL_INDIRECT_COST_RECOVERY));
604: e
605: .setFinancialSystemOriginationCode(parameterService
606: .getParameterValue(
607: ParameterConstants.GENERAL_LEDGER_BATCH.class,
608: KFSConstants.SystemGroupParameterNames.GL_ORIGINATION_CODE));
609: SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_STRING);
610: e.setDocumentNumber(sdf.format(runDate));
611: if (KFSConstants.GL_DEBIT_CODE.equals(icrEntry
612: .getTransactionDebitIndicator())) {
613: e
614: .setTransactionLedgerEntryDescription(getChargeDescription(
615: pct, et.getObjectCode(), et.getAccount()
616: .getAcctIndirectCostRcvyTypeCd(),
617: et.getAccountObjectDirectCostAmount().abs()));
618: } else {
619: e
620: .setTransactionLedgerEntryDescription(getOffsetDescription(
621: pct, et.getAccountObjectDirectCostAmount()
622: .abs(),
623: et.getChartOfAccountsCode(), et
624: .getAccountNumber()));
625: }
626: e.setTransactionDate(new java.sql.Date(runDate.getTime()));
627: e.setTransactionDebitCreditCode(icrEntry
628: .getTransactionDebitIndicator());
629: e.setFinancialBalanceTypeCode(et.getBalanceTypeCode());
630: e.setUniversityFiscalYear(et.getUniversityFiscalYear());
631: e.setUniversityFiscalPeriodCode(et
632: .getUniversityFiscalAccountingPeriod());
633:
634: ObjectCode oc = objectCodeService.getByPrimaryId(e
635: .getUniversityFiscalYear(), e.getChartOfAccountsCode(),
636: e.getFinancialObjectCode());
637: if (oc == null) {
638: // TODO This should be a report thing, not an exception
639: throw new IllegalArgumentException(
640: configurationService
641: .getPropertyString(KFSKeyConstants.ERROR_OBJECT_CODE_NOT_FOUND_FOR)
642: + e.getUniversityFiscalYear()
643: + ","
644: + e.getChartOfAccountsCode()
645: + ","
646: + e.getFinancialObjectCode());
647: }
648: e.setFinancialObjectTypeCode(oc.getFinancialObjectTypeCode());
649:
650: if (generatedTransactionAmount.isNegative()) {
651: if (KFSConstants.GL_DEBIT_CODE.equals(icrEntry
652: .getTransactionDebitIndicator())) {
653: e
654: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
655: } else {
656: e
657: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
658: }
659: e
660: .setTransactionLedgerEntryAmount(generatedTransactionAmount
661: .negated());
662: } else {
663: e
664: .setTransactionLedgerEntryAmount(generatedTransactionAmount);
665: }
666:
667: if (et.getBalanceTypeCode().equals(
668: et.getOption().getExtrnlEncumFinBalanceTypCd())
669: || et.getBalanceTypeCode().equals(
670: et.getOption().getIntrnlEncumFinBalanceTypCd())
671: || et.getBalanceTypeCode().equals(
672: et.getOption().getPreencumbranceFinBalTypeCd())
673: || et
674: .getBalanceTypeCode()
675: .equals(
676: et
677: .getOption()
678: .getCostShareEncumbranceBalanceTypeCd())) {
679: e
680: .setDocumentNumber(parameterService
681: .getParameterValue(
682: PosterIndirectCostRecoveryEntriesStep.class,
683: KFSConstants.SystemGroupParameterNames.GL_INDIRECT_COST_RECOVERY));
684: }
685: e.setProjectCode(et.getProjectCode());
686: if (GLConstants.getDashOrganizationReferenceId().equals(
687: et.getOrganizationReferenceId())) {
688: e.setOrganizationReferenceId(null);
689: } else {
690: e.setOrganizationReferenceId(et
691: .getOrganizationReferenceId());
692: }
693:
694: // TODO 2031-2039
695: originEntryService.createEntry(e, group);
696:
697: // Now generate Offset
698: e = new OriginEntryFull(e);
699: if (KFSConstants.GL_DEBIT_CODE.equals(e
700: .getTransactionDebitCreditCode())) {
701: e
702: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
703: } else {
704: e.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
705: }
706: e.setFinancialSubObjectCode(KFSConstants
707: .getDashFinancialSubObjectCode());
708: e.setFinancialObjectCode(icrEntry
709: .getOffsetBalanceSheetObjectCodeNumber());
710:
711: ObjectCode balSheetObjectCode = objectCodeService
712: .getByPrimaryId(icrEntry.getUniversityFiscalYear(), e
713: .getChartOfAccountsCode(), icrEntry
714: .getOffsetBalanceSheetObjectCodeNumber());
715: if (balSheetObjectCode == null) {
716: List warnings = new ArrayList();
717: warnings
718: .add(configurationService
719: .getPropertyString(KFSKeyConstants.ERROR_INVALID_OFFSET_OBJECT_CODE)
720: + icrEntry.getUniversityFiscalYear()
721: + "-"
722: + e.getChartOfAccountsCode()
723: + "-"
724: + icrEntry
725: .getOffsetBalanceSheetObjectCodeNumber());
726: reportErrors.put(e, warnings);
727: } else {
728: e.setFinancialObjectTypeCode(balSheetObjectCode
729: .getFinancialObjectTypeCode());
730: }
731:
732: if (KFSConstants.GL_DEBIT_CODE.equals(icrEntry
733: .getTransactionDebitIndicator())) {
734: e
735: .setTransactionLedgerEntryDescription(getChargeDescription(
736: pct, et.getObjectCode(), et.getAccount()
737: .getAcctIndirectCostRcvyTypeCd(),
738: et.getAccountObjectDirectCostAmount().abs()));
739: } else {
740: e
741: .setTransactionLedgerEntryDescription(getOffsetDescription(
742: pct, et.getAccountObjectDirectCostAmount()
743: .abs(),
744: et.getChartOfAccountsCode(), et
745: .getAccountNumber()));
746: }
747:
748: try {
749: flexibleOffsetAccountService.updateOffset(e);
750: } catch (InvalidFlexibleOffsetException ex) {
751: List warnings = new ArrayList();
752: warnings.add("FAILED TO GENERATE FLEXIBLE OFFSETS "
753: + ex.getMessage());
754: reportErrors.put(e, warnings);
755: }
756:
757: originEntryService.createEntry(e, group);
758: }
759:
760: private static KualiDecimal ONEHUNDRED = new KualiDecimal("100");
761: private static DecimalFormat DFPCT = new DecimalFormat("#0.000");
762: private static DecimalFormat DFAMT = new DecimalFormat(
763: "##########.00");
764: private static BigDecimal BDONEHUNDRED = new BigDecimal("100");
765:
766: /**
767: * Generates a percent of a KualiDecimal amount (great for finding out how much of an origin entry should be recouped by indirect cost recovery)
768: *
769: * @param amount the original amount
770: * @param percent the percentage of that amount to calculate
771: * @return the percent of the amount
772: */
773: private KualiDecimal getPercentage(KualiDecimal amount,
774: BigDecimal percent) {
775: BigDecimal result = amount.bigDecimalValue().multiply(percent)
776: .divide(BDONEHUNDRED, 2, BigDecimal.ROUND_DOWN);
777: return new KualiDecimal(result);
778: }
779:
780: /**
781: * Generates the description of a charge
782: *
783: * @param rate the ICR rate for this entry
784: * @param objectCode the object code of this entry
785: * @param type the ICR type code of this entry's account
786: * @param amount the amount of this entry
787: * @return a description for the charge entry
788: */
789: private String getChargeDescription(BigDecimal rate,
790: String objectCode, String type, KualiDecimal amount) {
791: BigDecimal newRate = rate
792: .multiply(PosterServiceImpl.BDONEHUNDRED);
793:
794: StringBuffer desc = new StringBuffer("CHG ");
795: if (newRate.doubleValue() < 10) {
796: desc.append(" ");
797: }
798: desc.append(DFPCT.format(newRate));
799: desc.append("% ON ");
800: desc.append(objectCode);
801: desc.append(" (");
802: desc.append(type);
803: desc.append(") ");
804: String amt = DFAMT.format(amount);
805: while (amt.length() < 13) {
806: amt = " " + amt;
807: }
808: desc.append(amt);
809: return desc.toString();
810: }
811:
812: /**
813: * Returns the description of a debit origin entry created by generateTransactions
814: *
815: * @param rate the ICR rate that relates to this entry
816: * @param amount the amount of this entry
817: * @param chartOfAccountsCode the chart codce of the debit entry
818: * @param accountNumber the account number of the debit entry
819: * @return a description for the debit entry
820: */
821: private String getOffsetDescription(BigDecimal rate,
822: KualiDecimal amount, String chartOfAccountsCode,
823: String accountNumber) {
824: BigDecimal newRate = rate
825: .multiply(PosterServiceImpl.BDONEHUNDRED);
826:
827: StringBuffer desc = new StringBuffer("RCV ");
828: if (newRate.doubleValue() < 10) {
829: desc.append(" ");
830: }
831: desc.append(DFPCT.format(newRate));
832: desc.append("% ON ");
833: String amt = DFAMT.format(amount);
834: while (amt.length() < 13) {
835: amt = " " + amt;
836: }
837: desc.append(amt);
838: desc.append(" FRM ");
839: // desc.append(chartOfAccountsCode);
840: // desc.append("-");
841: desc.append(accountNumber);
842: return desc.toString();
843: }
844:
845: /**
846: * Increments a named count holding statistics about posted transactions
847: *
848: * @param reporting a Map of counts generated by this process
849: * @param destination the destination of a given transaction
850: * @param operation the operation being performed on the transaction
851: */
852: private void addReporting(Map reporting, String destination,
853: String operation) {
854: String key = destination + "," + operation;
855: if (reporting.containsKey(key)) {
856: Integer c = (Integer) reporting.get(key);
857: reporting.put(key, new Integer(c.intValue() + 1));
858: } else {
859: reporting.put(key, new Integer(1));
860: }
861: }
862:
863: public void setVerifyTransaction(VerifyTransaction vt) {
864: verifyTransaction = vt;
865: }
866:
867: public void setTransactionPosters(List p) {
868: transactionPosters = p;
869: }
870:
871: public void setOriginEntryService(OriginEntryService oes) {
872: originEntryService = oes;
873: }
874:
875: public void setOriginEntryGroupService(OriginEntryGroupService oes) {
876: originEntryGroupService = oes;
877: }
878:
879: public void setDateTimeService(DateTimeService dts) {
880: dateTimeService = dts;
881: }
882:
883: public void setReversalDao(ReversalDao red) {
884: reversalDao = red;
885: }
886:
887: public void setUniversityDateDao(UniversityDateDao udd) {
888: universityDateDao = udd;
889: }
890:
891: public void setAccountingPeriodService(AccountingPeriodService aps) {
892: accountingPeriodService = aps;
893: }
894:
895: public void setExpenditureTransactionDao(
896: ExpenditureTransactionDao etd) {
897: expenditureTransactionDao = etd;
898: }
899:
900: public void setIcrAutomatedEntryDao(IcrAutomatedEntryDao iaed) {
901: icrAutomatedEntryDao = iaed;
902: }
903:
904: public void setObjectCodeService(ObjectCodeService ocs) {
905: objectCodeService = ocs;
906: }
907:
908: public void setReportService(ReportService rs) {
909: reportService = rs;
910: }
911:
912: public void setConfigurationService(
913: KualiConfigurationService configurationService) {
914: this .configurationService = configurationService;
915: }
916:
917: public void setParameterService(ParameterService parameterService) {
918: this .parameterService = parameterService;
919: }
920:
921: public void setFlexibleOffsetAccountService(
922: FlexibleOffsetAccountService flexibleOffsetAccountService) {
923: this .flexibleOffsetAccountService = flexibleOffsetAccountService;
924: }
925:
926: public RunDateService getRunDateService() {
927: return runDateService;
928: }
929:
930: public void setRunDateService(RunDateService runDateService) {
931: this.runDateService = runDateService;
932: }
933: }
|