0001: /*
0002: * Copyright 2006-2007 The Kuali Foundation.
0003: *
0004: * Licensed under the Educational Community License, Version 1.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.opensource.org/licenses/ecl1.php
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.kuali.module.gl.service.impl;
0017:
0018: import java.sql.Date;
0019: import java.text.NumberFormat;
0020: import java.util.ArrayList;
0021: import java.util.Calendar;
0022: import java.util.Collection;
0023: import java.util.HashMap;
0024: import java.util.Iterator;
0025: import java.util.List;
0026: import java.util.Map;
0027:
0028: import org.kuali.core.bo.DocumentType;
0029: import org.kuali.core.service.DateTimeService;
0030: import org.kuali.core.service.DocumentTypeService;
0031: import org.kuali.core.service.KualiConfigurationService;
0032: import org.kuali.core.service.PersistenceService;
0033: import org.kuali.core.util.KualiDecimal;
0034: import org.kuali.core.util.ObjectUtils;
0035: import org.kuali.kfs.KFSConstants;
0036: import org.kuali.kfs.KFSKeyConstants;
0037: import org.kuali.kfs.KFSPropertyConstants;
0038: import org.kuali.kfs.bo.Options;
0039: import org.kuali.kfs.context.SpringContext;
0040: import org.kuali.kfs.service.ParameterEvaluator;
0041: import org.kuali.kfs.service.ParameterService;
0042: import org.kuali.module.chart.bo.A21SubAccount;
0043: import org.kuali.module.chart.bo.Account;
0044: import org.kuali.module.chart.bo.Chart;
0045: import org.kuali.module.chart.bo.ObjectCode;
0046: import org.kuali.module.chart.bo.OffsetDefinition;
0047: import org.kuali.module.chart.bo.codes.BalanceTyp;
0048: import org.kuali.module.chart.service.ObjectCodeService;
0049: import org.kuali.module.chart.service.OffsetDefinitionService;
0050: import org.kuali.module.financial.exceptions.InvalidFlexibleOffsetException;
0051: import org.kuali.module.financial.service.FlexibleOffsetAccountService;
0052: import org.kuali.module.gl.GLConstants;
0053: import org.kuali.module.gl.batch.ScrubberStep;
0054: import org.kuali.module.gl.batch.collector.CollectorBatch;
0055: import org.kuali.module.gl.bo.OriginEntry;
0056: import org.kuali.module.gl.bo.OriginEntryFull;
0057: import org.kuali.module.gl.bo.OriginEntryGroup;
0058: import org.kuali.module.gl.bo.OriginEntryLite;
0059: import org.kuali.module.gl.bo.OriginEntrySource;
0060: import org.kuali.module.gl.bo.Transaction;
0061: import org.kuali.module.gl.bo.UniversityDate;
0062: import org.kuali.module.gl.dao.UniversityDateDao;
0063: import org.kuali.module.gl.service.OriginEntryGroupService;
0064: import org.kuali.module.gl.service.OriginEntryLiteService;
0065: import org.kuali.module.gl.service.OriginEntryLookupService;
0066: import org.kuali.module.gl.service.OriginEntryService;
0067: import org.kuali.module.gl.service.ReportService;
0068: import org.kuali.module.gl.service.RunDateService;
0069: import org.kuali.module.gl.service.ScrubberProcessObjectCodeOverride;
0070: import org.kuali.module.gl.service.ScrubberValidator;
0071: import org.kuali.module.gl.service.impl.scrubber.DemergerReportData;
0072: import org.kuali.module.gl.service.impl.scrubber.ScrubberReportData;
0073: import org.kuali.module.gl.util.CachingLookup;
0074: import org.kuali.module.gl.util.CollectorReportData;
0075: import org.kuali.module.gl.util.Message;
0076: import org.kuali.module.gl.util.ObjectHelper;
0077: import org.kuali.module.gl.util.OriginEntryStatistics;
0078: import org.kuali.module.gl.util.ScrubberStatus;
0079: import org.kuali.module.gl.util.StringHelper;
0080: import org.springframework.util.StringUtils;
0081:
0082: /**
0083: * This class has the logic for the scrubber. It is required because the scrubber process needs instance variables. Instance
0084: * variables in a spring service are shared between all code calling the service. This will make sure each run of the scrubber has
0085: * it's own instance variables instead of being shared.
0086: */
0087: public class ScrubberProcess {
0088: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
0089: .getLogger(ScrubberProcess.class);
0090:
0091: private static final String TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE = "CE";
0092: private static final String TRANSACTION_TYPE_OFFSET = "O";
0093: private static final String TRANSACTION_TYPE_CAPITALIZATION = "C";
0094: private static final String TRANSACTION_TYPE_LIABILITY = "L";
0095: private static final String TRANSACTION_TYPE_TRANSFER = "T";
0096: private static final String TRANSACTION_TYPE_COST_SHARE = "CS";
0097: private static final String TRANSACTION_TYPE_OTHER = "X";
0098:
0099: private static final String COST_SHARE_CODE = "CSHR";
0100:
0101: private static final String COST_SHARE_TRANSFER_ENTRY_IND = "***";
0102:
0103: // These lengths are different then database field lengths, hence they are not from the DD
0104: private static final int COST_SHARE_ENCUMBRANCE_ENTRY_MAXLENGTH = 28;
0105: private static final int DEMERGER_TRANSACTION_LEDGET_ENTRY_DESCRIPTION = 33;
0106: private static final int OFFSET_MESSAGE_MAXLENGTH = 33;
0107:
0108: /* Services required */
0109: private FlexibleOffsetAccountService flexibleOffsetAccountService;
0110: private DocumentTypeService documentTypeService;
0111: private OriginEntryService originEntryService;
0112: private OriginEntryLiteService originEntryLiteService;
0113: private OriginEntryGroupService originEntryGroupService;
0114: private DateTimeService dateTimeService;
0115: private OffsetDefinitionService offsetDefinitionService;
0116: private ObjectCodeService objectCodeService;
0117: private KualiConfigurationService configurationService;
0118: private UniversityDateDao universityDateDao;
0119: private PersistenceService persistenceService;
0120: private ReportService reportService;
0121: private ScrubberValidator scrubberValidator;
0122: private ScrubberProcessObjectCodeOverride scrubberProcessObjectCodeOverride;
0123: private RunDateService runDateService;
0124: private ThreadLocal<OriginEntryLookupService> referenceLookup = new ThreadLocal<OriginEntryLookupService>();
0125:
0126: // this will only be populated when in collector mode, otherwise the memory requirements will be huge
0127: private Map<OriginEntry, OriginEntry> unscrubbedToUnscrubbedEntries;
0128:
0129: /* These are all different forms of the run date for this job */
0130: private Date runDate;
0131: private Calendar runCal;
0132: private UniversityDate universityRunDate;
0133: private String offsetString;
0134:
0135: /* These are the output groups */
0136: private OriginEntryGroup validGroup;
0137: private OriginEntryGroup errorGroup;
0138: private OriginEntryGroup expiredGroup;
0139:
0140: /* Unit Of Work info */
0141: private UnitOfWorkInfo unitOfWork;
0142: private KualiDecimal scrubCostShareAmount;
0143:
0144: /* Statistics for the reports */
0145: private ScrubberReportData scrubberReport;
0146: private Map<Transaction, List<Message>> scrubberReportErrors;
0147: private List<Message> transactionErrors;
0148: private DemergerReportData demergerReport;
0149:
0150: /* Description names */
0151: private String offsetDescription;
0152: private String capitalizationDescription;
0153: private String liabilityDescription;
0154: private String transferDescription;
0155: private String costShareDescription;
0156:
0157: private ParameterService parameterService;
0158:
0159: /* Misc stuff */
0160: private boolean reportOnlyMode;
0161: /**
0162: * Whether this instance is being used to support the scrubbing of a collector batch
0163: */
0164: private boolean collectorMode;
0165:
0166: /**
0167: * These parameters are all the dependencies.
0168: */
0169: public ScrubberProcess(
0170: FlexibleOffsetAccountService flexibleOffsetAccountService,
0171: DocumentTypeService documentTypeService,
0172: OriginEntryService originEntryService,
0173: OriginEntryGroupService originEntryGroupService,
0174: DateTimeService dateTimeService,
0175: OffsetDefinitionService offsetDefinitionService,
0176: ObjectCodeService objectCodeService,
0177: KualiConfigurationService configurationService,
0178: UniversityDateDao universityDateDao,
0179: PersistenceService persistenceService,
0180: ReportService reportService,
0181: ScrubberValidator scrubberValidator,
0182: ScrubberProcessObjectCodeOverride scrubberProcessObjectCodeOverride,
0183: RunDateService runDateService,
0184: OriginEntryLiteService originEntryLiteService) {
0185: super ();
0186: this .flexibleOffsetAccountService = flexibleOffsetAccountService;
0187: this .documentTypeService = documentTypeService;
0188: this .originEntryService = originEntryService;
0189: this .originEntryLiteService = originEntryLiteService;
0190: this .originEntryGroupService = originEntryGroupService;
0191: this .dateTimeService = dateTimeService;
0192: this .offsetDefinitionService = offsetDefinitionService;
0193: this .objectCodeService = objectCodeService;
0194: this .configurationService = configurationService;
0195: this .universityDateDao = universityDateDao;
0196: this .persistenceService = persistenceService;
0197: this .reportService = reportService;
0198: this .scrubberValidator = scrubberValidator;
0199: this .unscrubbedToUnscrubbedEntries = new HashMap<OriginEntry, OriginEntry>();
0200: this .scrubberProcessObjectCodeOverride = scrubberProcessObjectCodeOverride;
0201: this .runDateService = runDateService;
0202: collectorMode = false;
0203: parameterService = SpringContext
0204: .getBean(ParameterService.class);
0205: }
0206:
0207: /**
0208: * Scrub this single group read only. This will only output the scrubber report. It won't output any other groups.
0209: *
0210: * @param group the origin entry group that should be scrubbed
0211: * @param the document number of any specific entries to scrub
0212: */
0213: public void scrubGroupReportOnly(OriginEntryGroup group,
0214: String documentNumber) {
0215: LOG.debug("scrubGroupReportOnly() started");
0216:
0217: scrubEntries(group, documentNumber);
0218: }
0219:
0220: /**
0221: * Scrubs all entries in all groups and documents.
0222: */
0223: public void scrubEntries() {
0224: scrubEntries(null, null);
0225: }
0226:
0227: /**
0228: * Scrubs the origin entry and ID billing details if the given batch. Store all scrubber output into the collectorReportData
0229: * parameter. NOTE: DO NOT CALL ANY OF THE scrub* METHODS OF THIS CLASS AFTER CALLING THIS METHOD FOR EVERY UNIQUE INSTANCE OF
0230: * THIS CLASS, OR THE COLLECTOR REPORTS MAY BE CORRUPTED
0231: *
0232: * @param batch the data gathered from a Collector file
0233: * @param collectorReportData the statistics generated by running the Collector
0234: */
0235: public ScrubberStatus scrubCollectorBatch(CollectorBatch batch,
0236: CollectorReportData collectorReportData) {
0237: collectorMode = true;
0238:
0239: OriginEntryGroup group = originEntryGroupService.createGroup(
0240: batch.getTransmissionDate(),
0241: OriginEntrySource.COLLECTOR, false, false, false);
0242: for (OriginEntryFull originEntry : batch.getOriginEntries()) {
0243: originEntry.setGroup(group);
0244: originEntryService.save(originEntry);
0245: }
0246:
0247: // first, scrub the origin entries
0248: scrubEntries(group, null);
0249: // the scrubber process has just updated several member variables of this class. Store these values for the collector report
0250: collectorReportData.setBatchOriginEntryScrubberErrors(batch,
0251: scrubberReportErrors);
0252: collectorReportData
0253: .setScrubberReportData(batch, scrubberReport);
0254: collectorReportData
0255: .setDemergerReportData(batch, demergerReport);
0256:
0257: ScrubberStatus scrubberStatus = new ScrubberStatus();
0258: scrubberStatus.setInputGroup(group);
0259: scrubberStatus.setValidGroup(validGroup);
0260: scrubberStatus.setErrorGroup(errorGroup);
0261: scrubberStatus.setExpiredGroup(expiredGroup);
0262: scrubberStatus
0263: .setUnscrubbedToScrubbedEntries(unscrubbedToUnscrubbedEntries);
0264: return scrubberStatus;
0265: }
0266:
0267: /**
0268: * Scrub all entries that need it in origin entry. Put valid scrubbed entries in a scrubber valid group, put errors in a
0269: * scrubber error group, and transactions with an expired account in the scrubber expired account group.
0270: * @param group the specific origin entry group to scrub
0271: * @param documentNumber the number of the document with entries to scrub
0272: */
0273: public void scrubEntries(OriginEntryGroup group,
0274: String documentNumber) {
0275: LOG.debug("scrubEntries() started");
0276:
0277: // We are in report only mode if we pass a group to this method.
0278: // if not, we are in batch mode and we scrub the backup group
0279: reportOnlyMode = (group != null) && !collectorMode;
0280:
0281: scrubberReportErrors = new HashMap<Transaction, List<Message>>();
0282:
0283: // setup an object to hold the "default" date information
0284: runDate = calculateRunDate(dateTimeService.getCurrentDate());
0285: runCal = Calendar.getInstance();
0286: runCal.setTime(runDate);
0287:
0288: universityRunDate = universityDateDao.getByPrimaryKey(runDate);
0289: if (universityRunDate == null) {
0290: throw new IllegalStateException(
0291: configurationService
0292: .getPropertyString(KFSKeyConstants.ERROR_UNIV_DATE_NOT_FOUND));
0293: }
0294:
0295: setOffsetString();
0296: setDescriptions();
0297:
0298: // Create the groups that will store the valid and error entries that come out of the scrubber
0299: // We don't need groups for the reportOnlyMode
0300: if (!reportOnlyMode) {
0301: if (collectorMode) {
0302: // for collector mode, these groups are not meant to be permanently persisted.
0303: // after the collector is done, it will delete these groups, but in case there's a failure and the following groups
0304: // aren't created,
0305: // we set all of the group flags to false to prevent these groups from entering the nightly cycle
0306: validGroup = originEntryGroupService.createGroup(
0307: runDate, OriginEntrySource.SCRUBBER_VALID,
0308: false, false, false);
0309: errorGroup = originEntryGroupService.createGroup(
0310: runDate, OriginEntrySource.SCRUBBER_ERROR,
0311: false, false, false);
0312: expiredGroup = originEntryGroupService.createGroup(
0313: runDate, OriginEntrySource.SCRUBBER_EXPIRED,
0314: false, false, false);
0315: } else {
0316: validGroup = originEntryGroupService.createGroup(
0317: runDate, OriginEntrySource.SCRUBBER_VALID,
0318: true, true, false);
0319: errorGroup = originEntryGroupService.createGroup(
0320: runDate, OriginEntrySource.SCRUBBER_ERROR,
0321: false, true, false);
0322: expiredGroup = originEntryGroupService.createGroup(
0323: runDate, OriginEntrySource.SCRUBBER_EXPIRED,
0324: false, true, false);
0325: }
0326: }
0327:
0328: // get the origin entry groups to be processed by Scrubber
0329: Collection<OriginEntryGroup> groupsToScrub = null;
0330: if (reportOnlyMode || collectorMode) {
0331: groupsToScrub = new ArrayList<OriginEntryGroup>();
0332: groupsToScrub.add(group);
0333: } else {
0334: groupsToScrub = originEntryGroupService
0335: .getAllScrubbableBackupGroups();
0336: }
0337: LOG.debug("scrubEntries() number of groups to scrub: "
0338: + groupsToScrub.size());
0339:
0340: // generate the reports based on the origin entries to be processed by scrubber
0341: if (reportOnlyMode) {
0342: reportService.generateScrubberLedgerSummaryReportOnline(
0343: runDate, group, documentNumber);
0344: } else if (collectorMode) {
0345: // defer report generation for later
0346: } else {
0347: reportService.generateScrubberLedgerSummaryReportBatch(
0348: runDate, groupsToScrub);
0349: }
0350:
0351: // Scrub all of the OriginEntryGroups waiting to be scrubbed as of runDate.
0352: scrubberReport = new ScrubberReportData();
0353: for (Iterator iteratorOverGroups = groupsToScrub.iterator(); iteratorOverGroups
0354: .hasNext();) {
0355: OriginEntryGroup originEntryGroup = (OriginEntryGroup) iteratorOverGroups
0356: .next();
0357: LOG.debug("scrubEntries() Scrubbing group "
0358: + originEntryGroup.getId());
0359:
0360: processGroup(originEntryGroup);
0361:
0362: if (!reportOnlyMode && !collectorMode) {
0363: // Mark the origin entry group as being processed ...
0364: originEntryGroup.setProcess(Boolean.FALSE);
0365:
0366: // ... and save the origin entry group with the new process flag.
0367: originEntryGroupService.save(originEntryGroup);
0368: }
0369: }
0370:
0371: // generate the scrubber status summary report
0372: if (reportOnlyMode) {
0373: reportService.generateOnlineScrubberStatisticsReport(group
0374: .getId(), runDate, scrubberReport,
0375: scrubberReportErrors, documentNumber);
0376: } else if (collectorMode) {
0377: // defer report generation for later
0378: } else {
0379: reportService.generateBatchScrubberStatisticsReport(
0380: runDate, scrubberReport, scrubberReportErrors);
0381: }
0382:
0383: // run the demerger if during regular nightly processing and collector processing
0384: if (!reportOnlyMode) {
0385: performDemerger(errorGroup, validGroup);
0386: }
0387:
0388: // Run the reports
0389: if (reportOnlyMode) {
0390: // Run transaction list
0391: reportService.generateScrubberTransactionsOnline(runDate,
0392: group, documentNumber);
0393: } else if (collectorMode) {
0394: // defer report generation for later
0395: } else {
0396: // Run bad balance type report and removed transaction report
0397: reportService.generateScrubberBadBalanceTypeListingReport(
0398: runDate, groupsToScrub);
0399: reportService.generateScrubberRemovedTransactions(runDate,
0400: errorGroup);
0401: }
0402: }
0403:
0404: /**
0405: * The demerger process reads all of the documents in the error group, then moves all of the original entries for that document
0406: * from the valid group to the error group. It does not move generated entries to the error group. Those are deleted. It also
0407: * modifies the doc number and origin code of cost share transfers.
0408: *
0409: * @param errorGroup this scrubber run's error group
0410: * @param validGroup this scrubber run's valid group
0411: */
0412: private void performDemerger(OriginEntryGroup errorGroup,
0413: OriginEntryGroup validGroup) {
0414: LOG.debug("performDemerger() started");
0415:
0416: // Without this step, the job fails with Optimistic Lock Exceptions
0417: persistenceService.clearCache();
0418:
0419: demergerReport = new DemergerReportData();
0420:
0421: OriginEntryStatistics eOes = originEntryService
0422: .getStatistics(errorGroup.getId());
0423: demergerReport.setErrorTransactionsRead(eOes.getRowCount());
0424:
0425: // Read all the documents from the error group and move all non-generated
0426: // transactions for these documents from the valid group into the error group
0427: Collection<OriginEntryFull> errorDocuments = originEntryService
0428: .getDocumentsByGroup(errorGroup);
0429: Iterator<OriginEntryFull> i = errorDocuments.iterator();
0430: while (i.hasNext()) {
0431: OriginEntryFull document = i.next();
0432:
0433: // Get all the transactions for the document in the valid group
0434: Iterator<OriginEntryLite> transactions = originEntryLiteService
0435: .getEntriesByDocument(validGroup, document
0436: .getDocumentNumber(), document
0437: .getFinancialDocumentTypeCode(), document
0438: .getFinancialSystemOriginationCode());
0439:
0440: while (transactions.hasNext()) {
0441: OriginEntryLite transaction = transactions.next();
0442:
0443: String transactionType = getTransactionType(transaction);
0444:
0445: if (TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE
0446: .equals(transactionType)) {
0447: demergerReport
0448: .incrementCostShareEncumbranceTransactionsBypassed();
0449: originEntryLiteService.delete(transaction);
0450: } else if (TRANSACTION_TYPE_OFFSET
0451: .equals(transactionType)) {
0452: demergerReport
0453: .incrementOffsetTransactionsBypassed();
0454: originEntryLiteService.delete(transaction);
0455: } else if (TRANSACTION_TYPE_CAPITALIZATION
0456: .equals(transactionType)) {
0457: demergerReport
0458: .incrementCapitalizationTransactionsBypassed();
0459: originEntryLiteService.delete(transaction);
0460: } else if (TRANSACTION_TYPE_LIABILITY
0461: .equals(transactionType)) {
0462: demergerReport
0463: .incrementLiabilityTransactionsBypassed();
0464: originEntryLiteService.delete(transaction);
0465: } else if (TRANSACTION_TYPE_TRANSFER
0466: .equals(transactionType)) {
0467: demergerReport
0468: .incrementTransferTransactionsBypassed();
0469: originEntryLiteService.delete(transaction);
0470: } else if (TRANSACTION_TYPE_COST_SHARE
0471: .equals(transactionType)) {
0472: demergerReport
0473: .incrementCostShareTransactionsBypassed();
0474: originEntryLiteService.delete(transaction);
0475: } else {
0476: demergerReport.incrementErrorTransactionsSaved();
0477: transaction.setEntryGroupId(errorGroup.getId());
0478: originEntryLiteService.save(transaction);
0479: }
0480: }
0481: }
0482:
0483: // Read all the transactions in the error group and delete the generated ones
0484: Iterator<OriginEntryLite> ie = originEntryLiteService
0485: .getEntriesByGroup(errorGroup);
0486: while (ie.hasNext()) {
0487: OriginEntryLite transaction = ie.next();
0488:
0489: String transactionType = getTransactionType(transaction);
0490:
0491: if (TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE
0492: .equals(transactionType)) {
0493: demergerReport
0494: .incrementCostShareEncumbranceTransactionsBypassed();
0495: originEntryLiteService.delete(transaction);
0496: } else if (TRANSACTION_TYPE_OFFSET.equals(transactionType)) {
0497: demergerReport.incrementOffsetTransactionsBypassed();
0498: originEntryLiteService.delete(transaction);
0499: } else if (TRANSACTION_TYPE_CAPITALIZATION
0500: .equals(transactionType)) {
0501: demergerReport
0502: .incrementCapitalizationTransactionsBypassed();
0503: originEntryLiteService.delete(transaction);
0504: } else if (TRANSACTION_TYPE_LIABILITY
0505: .equals(transactionType)) {
0506: demergerReport.incrementLiabilityTransactionsBypassed();
0507: originEntryLiteService.delete(transaction);
0508: } else if (TRANSACTION_TYPE_TRANSFER
0509: .equals(transactionType)) {
0510: demergerReport.incrementTransferTransactionsBypassed();
0511: originEntryLiteService.delete(transaction);
0512: } else if (TRANSACTION_TYPE_COST_SHARE
0513: .equals(transactionType)) {
0514: demergerReport.incrementCostShareTransactionsBypassed();
0515: originEntryLiteService.delete(transaction);
0516: }
0517: }
0518:
0519: // Read all the transactions in the valid group and update the cost share transactions
0520: Iterator<OriginEntryLite> it = originEntryLiteService
0521: .getEntriesByGroup(validGroup);
0522: while (it.hasNext()) {
0523: OriginEntryLite transaction = it.next();
0524: demergerReport.incrementValidTransactionsSaved();
0525:
0526: String transactionType = getTransactionType(transaction);
0527: if (TRANSACTION_TYPE_COST_SHARE.equals(transactionType)) {
0528: transaction
0529: .setFinancialDocumentTypeCode(KFSConstants.TRANSFER_FUNDS);
0530: transaction
0531: .setFinancialSystemOriginationCode(KFSConstants.COST_SHARE);
0532: StringBuffer docNbr = new StringBuffer(COST_SHARE_CODE);
0533:
0534: String desc = transaction
0535: .getTransactionLedgerEntryDescription();
0536:
0537: docNbr.append(desc.substring(36, 38));
0538: docNbr.append("/");
0539: docNbr.append(desc.substring(38, 40));
0540: transaction.setDocumentNumber(docNbr.toString());
0541:
0542: transaction
0543: .setTransactionLedgerEntryDescription(desc
0544: .substring(0,
0545: DEMERGER_TRANSACTION_LEDGET_ENTRY_DESCRIPTION));
0546:
0547: originEntryLiteService.save(transaction);
0548: }
0549: }
0550:
0551: eOes = originEntryService.getStatistics(errorGroup.getId());
0552: demergerReport.setErrorTransactionWritten(eOes.getRowCount());
0553:
0554: if (!collectorMode) {
0555: reportService.generateScrubberDemergerStatisticsReports(
0556: runDate, demergerReport);
0557: }
0558: }
0559:
0560: /**
0561: * Determine the type of the transaction by looking at attributes
0562: *
0563: * @param transaction Transaction to identify
0564: * @return CE (Cost share encumbrance, O (Offset), C (apitalization), L (Liability), T (Transfer), CS (Cost Share), X (Other)
0565: */
0566: private String getTransactionType(OriginEntry transaction) {
0567: if (TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE.equals(transaction
0568: .getFinancialBalanceTypeCode())) {
0569: return TRANSACTION_TYPE_COST_SHARE_ENCUMBRANCE;
0570: }
0571: String desc = transaction
0572: .getTransactionLedgerEntryDescription();
0573:
0574: if (desc == null) {
0575: return TRANSACTION_TYPE_OTHER;
0576: }
0577:
0578: if (desc.startsWith(offsetDescription)
0579: && desc.indexOf(COST_SHARE_TRANSFER_ENTRY_IND) > -1) {
0580: return TRANSACTION_TYPE_COST_SHARE;
0581: }
0582: if (desc.startsWith(costShareDescription)
0583: && desc.indexOf(COST_SHARE_TRANSFER_ENTRY_IND) > -1) {
0584: return TRANSACTION_TYPE_COST_SHARE;
0585: }
0586: if (desc.startsWith(offsetDescription)) {
0587: return TRANSACTION_TYPE_OFFSET;
0588: }
0589: if (desc.startsWith(capitalizationDescription)) {
0590: return TRANSACTION_TYPE_CAPITALIZATION;
0591: }
0592: if (desc.startsWith(liabilityDescription)) {
0593: return TRANSACTION_TYPE_LIABILITY;
0594: }
0595: if (desc.startsWith(transferDescription)) {
0596: return TRANSACTION_TYPE_TRANSFER;
0597: }
0598: return TRANSACTION_TYPE_OTHER;
0599: }
0600:
0601: /**
0602: * This will process a group of origin entries. The COBOL code was refactored a lot to get this so there isn't a 1 to 1 section
0603: * of Cobol relating to this.
0604: *
0605: * @param originEntryGroup Group to process
0606: */
0607: private void processGroup(OriginEntryGroup originEntryGroup) {
0608: this .referenceLookup.get().setLookupService(
0609: SpringContext.getBean(CachingLookup.class));
0610:
0611: OriginEntry lastEntry = null;
0612: scrubCostShareAmount = KualiDecimal.ZERO;
0613: unitOfWork = new UnitOfWorkInfo();
0614:
0615: LOG.info("Starting Scrubber Process process group...");
0616: Iterator entries = originEntryLiteService
0617: .getEntriesByGroup(originEntryGroup);
0618: while (entries.hasNext()) {
0619: OriginEntry unscrubbedEntry = (OriginEntry) entries.next();
0620: scrubberReport.incrementUnscrubbedRecordsRead();
0621:
0622: transactionErrors = new ArrayList<Message>();
0623:
0624: // This is done so if the code modifies this row, then saves it, it will be an insert,
0625: // and it won't touch the original. The Scrubber never modifies input rows/groups.
0626: unscrubbedEntry.setEntryGroupId(null);
0627: unscrubbedEntry.resetEntryId();
0628:
0629: boolean saveErrorTransaction = false;
0630: boolean saveValidTransaction = false;
0631:
0632: // Build a scrubbed entry
0633: OriginEntry scrubbedEntry = new OriginEntryLite();
0634: scrubbedEntry.setDocumentNumber(unscrubbedEntry
0635: .getDocumentNumber());
0636: scrubbedEntry.setOrganizationDocumentNumber(unscrubbedEntry
0637: .getOrganizationDocumentNumber());
0638: scrubbedEntry.setOrganizationReferenceId(unscrubbedEntry
0639: .getOrganizationReferenceId());
0640: scrubbedEntry
0641: .setReferenceFinancialDocumentNumber(unscrubbedEntry
0642: .getReferenceFinancialDocumentNumber());
0643:
0644: Integer transactionNumber = unscrubbedEntry
0645: .getTransactionLedgerEntrySequenceNumber();
0646: scrubbedEntry
0647: .setTransactionLedgerEntrySequenceNumber(null == transactionNumber ? new Integer(
0648: 0)
0649: : transactionNumber);
0650: scrubbedEntry
0651: .setTransactionLedgerEntryDescription(unscrubbedEntry
0652: .getTransactionLedgerEntryDescription());
0653: scrubbedEntry
0654: .setTransactionLedgerEntryAmount(unscrubbedEntry
0655: .getTransactionLedgerEntryAmount());
0656: scrubbedEntry.setTransactionDebitCreditCode(unscrubbedEntry
0657: .getTransactionDebitCreditCode());
0658:
0659: // For Labor Scrubber
0660: boolean laborIndicator = false;
0661:
0662: List<Message> tmperrors = scrubberValidator
0663: .validateTransaction(unscrubbedEntry,
0664: scrubbedEntry, universityRunDate,
0665: laborIndicator);
0666: transactionErrors.addAll(tmperrors);
0667:
0668: // Expired account?
0669: Account unscrubbedEntryAccount = referenceLookup.get()
0670: .getAccount(unscrubbedEntry);
0671: if ((unscrubbedEntryAccount != null)
0672: && (unscrubbedEntryAccount
0673: .isAccountClosedIndicator())) {
0674: // Make a copy of it so OJB doesn't just update the row in the original
0675: // group. It needs to make a new one in the expired group
0676: OriginEntryFull expiredEntry = OriginEntryFull
0677: .copyFromOriginEntryable(scrubbedEntry);
0678:
0679: createOutputEntry(expiredEntry, expiredGroup);
0680: scrubberReport.incrementExpiredAccountFound();
0681: }
0682:
0683: if (!isFatal(transactionErrors)) {
0684: saveValidTransaction = true;
0685:
0686: if (collectorMode) {
0687: // only populate this map in collector mode because it's only needed for the collector
0688:
0689: // the collector scrubber uses this map to apply the same changes made on an origin entry during scrubbing to
0690: // the collector detail record
0691: unscrubbedToUnscrubbedEntries.put(unscrubbedEntry,
0692: scrubbedEntry);
0693:
0694: // for the collector, we don't need further processing, since we're going to rescrub all of the origin entries
0695: // anyways during the nightly process
0696: } else {
0697:
0698: // See if unit of work has changed
0699: if (!unitOfWork.isSameUnitOfWork(scrubbedEntry)) {
0700: // Generate offset for last unit of work
0701: generateOffset(lastEntry);
0702:
0703: unitOfWork = new UnitOfWorkInfo(scrubbedEntry);
0704: }
0705:
0706: KualiDecimal transactionAmount = scrubbedEntry
0707: .getTransactionLedgerEntryAmount();
0708:
0709: ParameterEvaluator offsetFiscalPeriods = parameterService
0710: .getParameterEvaluator(
0711: ScrubberStep.class,
0712: GLConstants.GlScrubberGroupRules.OFFSET_FISCAL_PERIOD_CODES,
0713: scrubbedEntry
0714: .getUniversityFiscalPeriodCode());
0715:
0716: BalanceTyp scrubbedEntryBalanceType = referenceLookup
0717: .get().getBalanceType(scrubbedEntry);
0718: if (scrubbedEntryBalanceType
0719: .isFinancialOffsetGenerationIndicator()
0720: && offsetFiscalPeriods.evaluationSucceeds()) {
0721: if (scrubbedEntry.isDebit()) {
0722: unitOfWork.offsetAmount = unitOfWork.offsetAmount
0723: .add(transactionAmount);
0724: } else {
0725: unitOfWork.offsetAmount = unitOfWork.offsetAmount
0726: .subtract(transactionAmount);
0727: }
0728: }
0729:
0730: // The sub account type code will only exist if there is a valid sub account
0731: String subAccountTypeCode = GLConstants
0732: .getSpaceSubAccountTypeCode();
0733: // major assumption: the a21 subaccount is proxied, so we don't want to query the database if the subacct
0734: // number is dashes
0735: A21SubAccount scrubbedEntryA21SubAccount = referenceLookup
0736: .get().getA21SubAccount(scrubbedEntry);
0737: if (!KFSConstants.getDashSubAccountNumber().equals(
0738: scrubbedEntry.getSubAccountNumber())
0739: && scrubbedEntryA21SubAccount != null) {
0740: subAccountTypeCode = scrubbedEntryA21SubAccount
0741: .getSubAccountTypeCode();
0742:
0743: }
0744:
0745: ParameterEvaluator costShareObjectTypeCodes = parameterService
0746: .getParameterEvaluator(
0747: ScrubberStep.class,
0748: GLConstants.GlScrubberGroupRules.COST_SHARE_OBJ_TYPE_CODES,
0749: scrubbedEntry
0750: .getFinancialObjectTypeCode());
0751: ParameterEvaluator costShareEncBalanceTypeCodes = parameterService
0752: .getParameterEvaluator(
0753: ScrubberStep.class,
0754: GLConstants.GlScrubberGroupRules.COST_SHARE_ENC_BAL_TYP_CODES,
0755: scrubbedEntry
0756: .getFinancialBalanceTypeCode());
0757: ParameterEvaluator costShareEncFiscalPeriodCodes = parameterService
0758: .getParameterEvaluator(
0759: ScrubberStep.class,
0760: GLConstants.GlScrubberGroupRules.COST_SHARE_ENC_FISCAL_PERIOD_CODES,
0761: scrubbedEntry
0762: .getUniversityFiscalPeriodCode());
0763: ParameterEvaluator costShareEncDocTypeCodes = parameterService
0764: .getParameterEvaluator(
0765: ScrubberStep.class,
0766: GLConstants.GlScrubberGroupRules.COST_SHARE_ENC_DOC_TYPE_CODES,
0767: scrubbedEntry
0768: .getFinancialDocumentTypeCode()
0769: .trim());
0770: ParameterEvaluator costShareFiscalPeriodCodes = parameterService
0771: .getParameterEvaluator(
0772: ScrubberStep.class,
0773: GLConstants.GlScrubberGroupRules.COST_SHARE_FISCAL_PERIOD_CODES,
0774: scrubbedEntry
0775: .getUniversityFiscalPeriodCode());
0776:
0777: Account scrubbedEntryAccount = referenceLookup
0778: .get().getAccount(scrubbedEntry);
0779: if (costShareObjectTypeCodes.evaluationSucceeds()
0780: && costShareEncBalanceTypeCodes
0781: .evaluationSucceeds()
0782: && scrubbedEntryAccount
0783: .isForContractsAndGrants()
0784: && KFSConstants.COST_SHARE
0785: .equals(subAccountTypeCode)
0786: && costShareEncFiscalPeriodCodes
0787: .evaluationSucceeds()
0788: && costShareEncDocTypeCodes
0789: .evaluationSucceeds()) {
0790: TransactionError te1 = generateCostShareEncumbranceEntries(scrubbedEntry);
0791: if (te1 != null) {
0792: List errors = new ArrayList();
0793: errors.add(te1.message);
0794: scrubberReportErrors.put(te1.transaction,
0795: errors);
0796:
0797: saveValidTransaction = false;
0798: saveErrorTransaction = true;
0799: }
0800: }
0801:
0802: Options scrubbedEntryOption = referenceLookup.get()
0803: .getOption(scrubbedEntry);
0804: if (costShareObjectTypeCodes.evaluationSucceeds()
0805: && scrubbedEntryOption
0806: .getActualFinancialBalanceTypeCd()
0807: .equals(
0808: scrubbedEntry
0809: .getFinancialBalanceTypeCode())
0810: && scrubbedEntryAccount
0811: .isForContractsAndGrants()
0812: && KFSConstants.COST_SHARE
0813: .equals(subAccountTypeCode)
0814: && costShareFiscalPeriodCodes
0815: .evaluationSucceeds()
0816: && costShareEncDocTypeCodes
0817: .evaluationSucceeds()) {
0818: if (scrubbedEntry.isDebit()) {
0819: scrubCostShareAmount = scrubCostShareAmount
0820: .subtract(transactionAmount);
0821: } else {
0822: scrubCostShareAmount = scrubCostShareAmount
0823: .add(transactionAmount);
0824: }
0825: }
0826:
0827: ParameterEvaluator otherDocTypeCodes = parameterService
0828: .getParameterEvaluator(
0829: ScrubberStep.class,
0830: GLConstants.GlScrubberGroupRules.OFFSET_DOC_TYPE_CODES,
0831: scrubbedEntry
0832: .getFinancialDocumentTypeCode());
0833:
0834: if (otherDocTypeCodes.evaluationSucceeds()) {
0835: String m = processCapitalization(scrubbedEntry);
0836: if (m != null) {
0837: saveValidTransaction = false;
0838: saveErrorTransaction = false;
0839: addTransactionError(m, "",
0840: Message.TYPE_FATAL);
0841: }
0842:
0843: m = processLiabilities(scrubbedEntry);
0844: if (m != null) {
0845: saveValidTransaction = false;
0846: saveErrorTransaction = false;
0847: addTransactionError(m, "",
0848: Message.TYPE_FATAL);
0849: }
0850:
0851: m = processPlantIndebtedness(scrubbedEntry);
0852: if (m != null) {
0853: saveValidTransaction = false;
0854: saveErrorTransaction = false;
0855: addTransactionError(m, "",
0856: Message.TYPE_FATAL);
0857: }
0858: }
0859:
0860: if (!scrubCostShareAmount.isZero()) {
0861: TransactionError te = generateCostShareEntries(scrubbedEntry);
0862:
0863: if (te != null) {
0864: saveValidTransaction = false;
0865: saveErrorTransaction = false;
0866:
0867: // Make a copy of it so OJB doesn't just update the row in the original
0868: // group. It needs to make a new one in the error group
0869: OriginEntryFull errorEntry = new OriginEntryFull(
0870: te.transaction);
0871: errorEntry
0872: .setTransactionScrubberOffsetGenerationIndicator(false);
0873: createOutputEntry(errorEntry, errorGroup);
0874: scrubberReport
0875: .incrementErrorRecordWritten();
0876:
0877: List messages = new ArrayList();
0878: messages.add(te.message);
0879: scrubberReportErrors.put(errorEntry,
0880: messages);
0881: }
0882: scrubCostShareAmount = KualiDecimal.ZERO;
0883: }
0884:
0885: if (transactionErrors.size() > 0) {
0886: scrubberReportErrors
0887: .put(
0888: OriginEntryFull
0889: .copyFromOriginEntryable(scrubbedEntry),
0890: transactionErrors);
0891: }
0892:
0893: lastEntry = scrubbedEntry;
0894:
0895: }
0896: } else {
0897: // Error transaction
0898: saveErrorTransaction = true;
0899:
0900: scrubberReportErrors.put(OriginEntryFull
0901: .copyFromOriginEntryable(unscrubbedEntry),
0902: transactionErrors);
0903: }
0904:
0905: if (saveValidTransaction) {
0906: scrubbedEntry
0907: .setTransactionScrubberOffsetGenerationIndicator(false);
0908: createOutputEntry(scrubbedEntry, validGroup);
0909: scrubberReport.incrementScrubbedRecordWritten();
0910: }
0911:
0912: if (saveErrorTransaction) {
0913: // Make a copy of it so OJB doesn't just update the row in the original
0914: // group. It needs to make a new one in the error group
0915: OriginEntryFull errorEntry = OriginEntryFull
0916: .copyFromOriginEntryable(unscrubbedEntry);
0917: errorEntry
0918: .setTransactionScrubberOffsetGenerationIndicator(false);
0919: createOutputEntry(errorEntry, errorGroup);
0920: scrubberReport.incrementErrorRecordWritten();
0921: }
0922: }
0923:
0924: if (!collectorMode) {
0925: // Generate last offset (if necessary)
0926: generateOffset(lastEntry);
0927: }
0928:
0929: this .referenceLookup.get().setLookupService(null);
0930: }
0931:
0932: /**
0933: * Determines if a given error is fatal and should stop this scrubber run
0934: *
0935: * @param errors errors from a scrubber run
0936: * @return true if the run should be abended, false otherwise
0937: */
0938: private boolean isFatal(List<Message> errors) {
0939: for (Iterator<Message> iter = errors.iterator(); iter.hasNext();) {
0940: Message element = iter.next();
0941: if (element.getType() == Message.TYPE_FATAL) {
0942: return true;
0943: }
0944: }
0945: return false;
0946: }
0947:
0948: /**
0949: * Generates a cost share entry and offset for the given entry and saves both to the valid group
0950: *
0951: * @param scrubbedEntry the originEntry that was scrubbed
0952: * @return a TransactionError initialized with any error encounted during entry generation, or (hopefully) null
0953: */
0954: private TransactionError generateCostShareEntries(
0955: OriginEntry scrubbedEntry) {
0956: // 3000-COST-SHARE to 3100-READ-OFSD in the cobol Generate Cost Share Entries
0957: LOG.debug("generateCostShareEntries() started");
0958:
0959: OriginEntryFull costShareEntry = OriginEntryFull
0960: .copyFromOriginEntryable(scrubbedEntry);
0961:
0962: Options scrubbedEntryOption = referenceLookup.get().getOption(
0963: scrubbedEntry);
0964: A21SubAccount scrubbedEntryA21SubAccount = referenceLookup
0965: .get().getA21SubAccount(scrubbedEntry);
0966:
0967: costShareEntry
0968: .setFinancialObjectCode(parameterService
0969: .getParameterValue(
0970: ScrubberStep.class,
0971: GLConstants.GlScrubberGroupParameters.COST_SHARE_OBJECT_CODE_PARM_NM));
0972: costShareEntry.setFinancialSubObjectCode(KFSConstants
0973: .getDashFinancialSubObjectCode());
0974: costShareEntry.setFinancialObjectTypeCode(scrubbedEntryOption
0975: .getFinancialObjectTypeTransferExpenseCd());
0976: costShareEntry
0977: .setTransactionLedgerEntrySequenceNumber(new Integer(0));
0978:
0979: StringBuffer description = new StringBuffer();
0980: description.append(costShareDescription);
0981: description.append(" ")
0982: .append(scrubbedEntry.getAccountNumber());
0983: description.append(offsetString);
0984: costShareEntry.setTransactionLedgerEntryDescription(description
0985: .toString());
0986:
0987: costShareEntry
0988: .setTransactionLedgerEntryAmount(scrubCostShareAmount);
0989: if (scrubCostShareAmount.isPositive()) {
0990: costShareEntry
0991: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
0992: } else {
0993: costShareEntry
0994: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
0995: costShareEntry
0996: .setTransactionLedgerEntryAmount(scrubCostShareAmount
0997: .negated());
0998: }
0999:
1000: costShareEntry.setTransactionDate(runDate);
1001: costShareEntry.setOrganizationDocumentNumber(null);
1002: costShareEntry
1003: .setProjectCode(KFSConstants.getDashProjectCode());
1004: costShareEntry.setOrganizationReferenceId(null);
1005: costShareEntry.setReferenceFinancialDocumentTypeCode(null);
1006: costShareEntry.setReferenceFinancialSystemOriginationCode(null);
1007: costShareEntry.setReferenceFinancialDocumentNumber(null);
1008: costShareEntry.setFinancialDocumentReversalDate(null);
1009: costShareEntry.setTransactionEncumbranceUpdateCode(null);
1010:
1011: createOutputEntry(costShareEntry, validGroup);
1012: scrubberReport.incrementCostShareEntryGenerated();
1013:
1014: OriginEntryFull costShareOffsetEntry = new OriginEntryFull(
1015: costShareEntry);
1016: costShareOffsetEntry
1017: .setTransactionLedgerEntryDescription(getOffsetMessage());
1018:
1019: OffsetDefinition offsetDefinition = offsetDefinitionService
1020: .getByPrimaryId(
1021: scrubbedEntry.getUniversityFiscalYear(),
1022: scrubbedEntry.getChartOfAccountsCode(),
1023: KFSConstants.TRANSFER_FUNDS, scrubbedEntry
1024: .getFinancialBalanceTypeCode());
1025: if (offsetDefinition != null) {
1026: if (offsetDefinition.getFinancialObject() == null) {
1027: StringBuffer objectCodeKey = new StringBuffer();
1028: objectCodeKey.append(offsetDefinition
1029: .getUniversityFiscalYear());
1030: objectCodeKey.append("-").append(
1031: offsetDefinition.getChartOfAccountsCode());
1032: objectCodeKey.append("-").append(
1033: offsetDefinition.getFinancialObjectCode());
1034:
1035: Message m = new Message(
1036: configurationService
1037: .getPropertyString(KFSKeyConstants.ERROR_OFFSET_DEFINITION_OBJECT_CODE_NOT_FOUND)
1038: + " (" + objectCodeKey.toString() + ")",
1039: Message.TYPE_FATAL);
1040: LOG
1041: .debug("generateCostShareEntries() Error 1 object not found");
1042: return new TransactionError(costShareEntry, m);
1043: }
1044:
1045: costShareOffsetEntry
1046: .setFinancialObjectCode(offsetDefinition
1047: .getFinancialObjectCode());
1048: costShareOffsetEntry.setFinancialObject(offsetDefinition
1049: .getFinancialObject());
1050: costShareOffsetEntry.setFinancialSubObjectCode(KFSConstants
1051: .getDashFinancialSubObjectCode());
1052:
1053: } else {
1054: Map<Transaction, List<Message>> errors = new HashMap<Transaction, List<Message>>();
1055:
1056: StringBuffer offsetKey = new StringBuffer(
1057: "cost share transfer ");
1058: offsetKey.append(scrubbedEntry.getUniversityFiscalYear());
1059: offsetKey.append("-");
1060: offsetKey.append(scrubbedEntry.getChartOfAccountsCode());
1061: offsetKey.append("-TF-");
1062: offsetKey.append(scrubbedEntry
1063: .getFinancialBalanceTypeCode());
1064:
1065: Message m = new Message(
1066: configurationService
1067: .getPropertyString(KFSKeyConstants.ERROR_OFFSET_DEFINITION_NOT_FOUND)
1068: + " (" + offsetKey.toString() + ")",
1069: Message.TYPE_FATAL);
1070:
1071: LOG
1072: .debug("generateCostShareEntries() Error 2 offset not found");
1073: return new TransactionError(costShareEntry, m);
1074: }
1075:
1076: costShareOffsetEntry
1077: .setFinancialObjectTypeCode(offsetDefinition
1078: .getFinancialObject()
1079: .getFinancialObjectTypeCode());
1080:
1081: if (costShareEntry.isCredit()) {
1082: costShareOffsetEntry
1083: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
1084: } else {
1085: costShareOffsetEntry
1086: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
1087: }
1088:
1089: try {
1090: flexibleOffsetAccountService
1091: .updateOffset(costShareOffsetEntry);
1092: } catch (InvalidFlexibleOffsetException e) {
1093: Message m = new Message(e.getMessage(), Message.TYPE_FATAL);
1094: LOG
1095: .debug("generateCostShareEntries() Cost Share Transfer Flexible Offset Error: "
1096: + e.getMessage());
1097: return new TransactionError(costShareEntry, m);
1098: }
1099:
1100: createOutputEntry(costShareOffsetEntry, validGroup);
1101: scrubberReport.incrementCostShareEntryGenerated();
1102:
1103: OriginEntryFull costShareSourceAccountEntry = new OriginEntryFull(
1104: costShareEntry);
1105:
1106: description = new StringBuffer();
1107: description.append(costShareDescription);
1108: description.append(" ")
1109: .append(scrubbedEntry.getAccountNumber());
1110: description.append(offsetString);
1111: costShareSourceAccountEntry
1112: .setTransactionLedgerEntryDescription(description
1113: .toString());
1114:
1115: costShareSourceAccountEntry
1116: .setChartOfAccountsCode(scrubbedEntryA21SubAccount
1117: .getCostShareChartOfAccountCode());
1118: costShareSourceAccountEntry
1119: .setAccountNumber(scrubbedEntryA21SubAccount
1120: .getCostShareSourceAccountNumber());
1121:
1122: setCostShareObjectCode(costShareSourceAccountEntry,
1123: scrubbedEntry);
1124: costShareSourceAccountEntry
1125: .setSubAccountNumber(scrubbedEntryA21SubAccount
1126: .getCostShareSourceSubAccountNumber());
1127:
1128: if (StringHelper.isNullOrEmpty(costShareSourceAccountEntry
1129: .getSubAccountNumber())) {
1130: costShareSourceAccountEntry
1131: .setSubAccountNumber(KFSConstants
1132: .getDashSubAccountNumber());
1133: }
1134:
1135: costShareSourceAccountEntry
1136: .setFinancialSubObjectCode(KFSConstants
1137: .getDashFinancialSubObjectCode());
1138: costShareSourceAccountEntry
1139: .setFinancialObjectTypeCode(scrubbedEntryOption
1140: .getFinancialObjectTypeTransferExpenseCd());
1141: costShareSourceAccountEntry
1142: .setTransactionLedgerEntrySequenceNumber(new Integer(0));
1143:
1144: costShareSourceAccountEntry
1145: .setTransactionLedgerEntryAmount(scrubCostShareAmount);
1146: if (scrubCostShareAmount.isPositive()) {
1147: costShareSourceAccountEntry
1148: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
1149: } else {
1150: costShareSourceAccountEntry
1151: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
1152: costShareSourceAccountEntry
1153: .setTransactionLedgerEntryAmount(scrubCostShareAmount
1154: .negated());
1155: }
1156:
1157: costShareSourceAccountEntry.setTransactionDate(runDate);
1158: costShareSourceAccountEntry.setOrganizationDocumentNumber(null);
1159: costShareSourceAccountEntry.setProjectCode(KFSConstants
1160: .getDashProjectCode());
1161: costShareSourceAccountEntry.setOrganizationReferenceId(null);
1162: costShareSourceAccountEntry
1163: .setReferenceFinancialDocumentTypeCode(null);
1164: costShareSourceAccountEntry
1165: .setReferenceFinancialSystemOriginationCode(null);
1166: costShareSourceAccountEntry
1167: .setReferenceFinancialDocumentNumber(null);
1168: costShareSourceAccountEntry
1169: .setFinancialDocumentReversalDate(null);
1170: costShareSourceAccountEntry
1171: .setTransactionEncumbranceUpdateCode(null);
1172:
1173: createOutputEntry(costShareSourceAccountEntry, validGroup);
1174: scrubberReport.incrementCostShareEntryGenerated();
1175:
1176: OriginEntryFull costShareSourceAccountOffsetEntry = new OriginEntryFull(
1177: costShareSourceAccountEntry);
1178: costShareSourceAccountOffsetEntry
1179: .setTransactionLedgerEntryDescription(getOffsetMessage());
1180:
1181: // Lookup the new offset definition.
1182: offsetDefinition = offsetDefinitionService.getByPrimaryId(
1183: scrubbedEntry.getUniversityFiscalYear(), scrubbedEntry
1184: .getChartOfAccountsCode(),
1185: KFSConstants.TRANSFER_FUNDS, scrubbedEntry
1186: .getFinancialBalanceTypeCode());
1187: if (offsetDefinition != null) {
1188: if (offsetDefinition.getFinancialObject() == null) {
1189: Map<Transaction, List<Message>> errors = new HashMap<Transaction, List<Message>>();
1190:
1191: StringBuffer objectCodeKey = new StringBuffer();
1192: objectCodeKey.append(costShareEntry
1193: .getUniversityFiscalYear());
1194: objectCodeKey.append("-").append(
1195: scrubbedEntry.getChartOfAccountsCode());
1196: objectCodeKey.append("-").append(
1197: scrubbedEntry.getFinancialObjectCode());
1198:
1199: Message m = new Message(
1200: configurationService
1201: .getPropertyString(KFSKeyConstants.ERROR_OFFSET_DEFINITION_OBJECT_CODE_NOT_FOUND)
1202: + " (" + objectCodeKey.toString() + ")",
1203: Message.TYPE_FATAL);
1204:
1205: LOG
1206: .debug("generateCostShareEntries() Error 3 object not found");
1207: return new TransactionError(
1208: costShareSourceAccountEntry, m);
1209: }
1210:
1211: costShareSourceAccountOffsetEntry
1212: .setFinancialObjectCode(offsetDefinition
1213: .getFinancialObjectCode());
1214: costShareSourceAccountOffsetEntry
1215: .setFinancialObject(offsetDefinition
1216: .getFinancialObject());
1217: costShareSourceAccountOffsetEntry
1218: .setFinancialSubObjectCode(KFSConstants
1219: .getDashFinancialSubObjectCode());
1220: } else {
1221: Map<Transaction, List<Message>> errors = new HashMap<Transaction, List<Message>>();
1222:
1223: StringBuffer offsetKey = new StringBuffer(
1224: "cost share transfer source ");
1225: offsetKey.append(scrubbedEntry.getUniversityFiscalYear());
1226: offsetKey.append("-");
1227: offsetKey.append(scrubbedEntry.getChartOfAccountsCode());
1228: offsetKey.append("-TF-");
1229: offsetKey.append(scrubbedEntry
1230: .getFinancialBalanceTypeCode());
1231:
1232: Message m = new Message(
1233: configurationService
1234: .getPropertyString(KFSKeyConstants.ERROR_OFFSET_DEFINITION_NOT_FOUND)
1235: + " (" + offsetKey.toString() + ")",
1236: Message.TYPE_FATAL);
1237:
1238: LOG
1239: .debug("generateCostShareEntries() Error 4 offset not found");
1240: return new TransactionError(costShareSourceAccountEntry, m);
1241: }
1242:
1243: costShareSourceAccountOffsetEntry
1244: .setFinancialObjectTypeCode(offsetDefinition
1245: .getFinancialObject()
1246: .getFinancialObjectTypeCode());
1247:
1248: if (scrubbedEntry.isCredit()) {
1249: costShareSourceAccountOffsetEntry
1250: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
1251: } else {
1252: costShareSourceAccountOffsetEntry
1253: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
1254: }
1255:
1256: try {
1257: flexibleOffsetAccountService
1258: .updateOffset(costShareSourceAccountOffsetEntry);
1259: } catch (InvalidFlexibleOffsetException e) {
1260: Message m = new Message(e.getMessage(), Message.TYPE_FATAL);
1261: LOG
1262: .debug("generateCostShareEntries() Cost Share Transfer Account Flexible Offset Error: "
1263: + e.getMessage());
1264: return new TransactionError(costShareEntry, m);
1265: }
1266:
1267: createOutputEntry(costShareSourceAccountOffsetEntry, validGroup);
1268: scrubberReport.incrementCostShareEntryGenerated();
1269:
1270: scrubCostShareAmount = KualiDecimal.ZERO;
1271:
1272: LOG.debug("generateCostShareEntries() successful");
1273: return null;
1274: }
1275:
1276: /**
1277: * Get all the transaction descriptions from the param table
1278: */
1279: private void setDescriptions() {
1280: offsetDescription = configurationService
1281: .getPropertyString(KFSKeyConstants.MSG_GENERATED_OFFSET);
1282: capitalizationDescription = configurationService
1283: .getPropertyString(KFSKeyConstants.MSG_GENERATED_CAPITALIZATION);
1284: liabilityDescription = configurationService
1285: .getPropertyString(KFSKeyConstants.MSG_GENERATED_LIABILITY);
1286: costShareDescription = configurationService
1287: .getPropertyString(KFSKeyConstants.MSG_GENERATED_COST_SHARE);
1288: transferDescription = configurationService
1289: .getPropertyString(KFSKeyConstants.MSG_GENERATED_TRANSFER);
1290: }
1291:
1292: /**
1293: * Generate the flag for the end of specific descriptions. This will be used in the demerger step
1294: */
1295: private void setOffsetString() {
1296:
1297: NumberFormat nf = NumberFormat.getInstance();
1298: nf.setMaximumFractionDigits(0);
1299: nf.setMaximumIntegerDigits(2);
1300: nf.setMinimumFractionDigits(0);
1301: nf.setMinimumIntegerDigits(2);
1302:
1303: offsetString = COST_SHARE_TRANSFER_ENTRY_IND
1304: + nf.format(runCal.get(Calendar.MONTH) + 1)
1305: + nf.format(runCal.get(Calendar.DAY_OF_MONTH));
1306: }
1307:
1308: /**
1309: * Generate the offset message with the flag at the end
1310: *
1311: * @return a generated offset message
1312: */
1313: private String getOffsetMessage() {
1314: String msg = offsetDescription
1315: + GLConstants
1316: .getSpaceTransactionLedgetEntryDescription();
1317:
1318: return msg.substring(0, OFFSET_MESSAGE_MAXLENGTH)
1319: + offsetString;
1320: }
1321:
1322: /**
1323: * Generates capitalization entries if necessary
1324: *
1325: * @param scrubbedEntry the entry to generate capitalization entries (possibly) for
1326: * @return null if no error, message if error
1327: */
1328: private String processCapitalization(OriginEntry scrubbedEntry) {
1329: // Lines 4694 - 4798 of the Pro Cobol listing on Confluence
1330: if (!parameterService
1331: .getIndicatorParameter(
1332: ScrubberStep.class,
1333: GLConstants.GlScrubberGroupParameters.CAPITALIZATION_IND)) {
1334: return null;
1335: }
1336:
1337: OriginEntryFull capitalizationEntry = OriginEntryFull
1338: .copyFromOriginEntryable(scrubbedEntry);
1339:
1340: Options scrubbedEntryOption = referenceLookup.get().getOption(
1341: scrubbedEntry);
1342: ObjectCode scrubbedEntryObjectCode = referenceLookup.get()
1343: .getFinancialObject(scrubbedEntry);
1344: Chart scrubbedEntryChart = referenceLookup.get().getChart(
1345: scrubbedEntry);
1346: Account scrubbedEntryAccount = referenceLookup.get()
1347: .getAccount(scrubbedEntry);
1348:
1349: ParameterEvaluator documentTypeCodes = parameterService
1350: .getParameterEvaluator(
1351: ScrubberStep.class,
1352: GLConstants.GlScrubberGroupRules.CAPITALIZATION_DOC_TYPE_CODES,
1353: scrubbedEntry.getFinancialDocumentTypeCode());
1354: ParameterEvaluator fiscalPeriodCodes = parameterService
1355: .getParameterEvaluator(
1356: ScrubberStep.class,
1357: GLConstants.GlScrubberGroupRules.CAPITALIZATION_FISCAL_PERIOD_CODES,
1358: scrubbedEntry.getUniversityFiscalPeriodCode());
1359: ParameterEvaluator objectSubTypeCodes = parameterService
1360: .getParameterEvaluator(
1361: ScrubberStep.class,
1362: GLConstants.GlScrubberGroupRules.CAPITALIZATION_OBJ_SUB_TYPE_CODES,
1363: scrubbedEntryObjectCode
1364: .getFinancialObjectSubTypeCode());
1365: ParameterEvaluator subFundGroupCodes = parameterService
1366: .getParameterEvaluator(
1367: ScrubberStep.class,
1368: GLConstants.GlScrubberGroupRules.CAPITALIZATION_SUB_FUND_GROUP_CODES,
1369: scrubbedEntryAccount.getSubFundGroupCode());
1370: ParameterEvaluator chartCodes = parameterService
1371: .getParameterEvaluator(
1372: ScrubberStep.class,
1373: GLConstants.GlScrubberGroupRules.CAPITALIZATION_CHART_CODES,
1374: scrubbedEntry.getChartOfAccountsCode());
1375:
1376: if (scrubbedEntry.getFinancialBalanceTypeCode().equals(
1377: scrubbedEntryOption.getActualFinancialBalanceTypeCd())
1378: && scrubbedEntry.getUniversityFiscalYear().intValue() > 1995
1379: && documentTypeCodes.evaluationSucceeds()
1380: && fiscalPeriodCodes.evaluationSucceeds()
1381: && objectSubTypeCodes.evaluationSucceeds()
1382: && subFundGroupCodes.evaluationSucceeds()
1383: && chartCodes.evaluationSucceeds()) {
1384:
1385: String objectSubTypeCode = scrubbedEntryObjectCode
1386: .getFinancialObjectSubTypeCode();
1387:
1388: String capitalizationObjectCode = parameterService
1389: .getParameterValue(
1390: ScrubberStep.class,
1391: GLConstants.GlScrubberGroupParameters.CAPITALIZATION_SUBTYPE_OBJECT,
1392: objectSubTypeCode);
1393: if (capitalizationObjectCode != null) {
1394: capitalizationEntry
1395: .setFinancialObjectCode(capitalizationObjectCode);
1396: persistenceService.retrieveReferenceObject(
1397: capitalizationEntry,
1398: KFSPropertyConstants.FINANCIAL_OBJECT);
1399: }
1400:
1401: capitalizationEntry
1402: .setFinancialObjectTypeCode(scrubbedEntryOption
1403: .getFinancialObjectTypeAssetsCd());
1404: persistenceService.retrieveReferenceObject(
1405: capitalizationEntry,
1406: KFSPropertyConstants.OBJECT_TYPE);
1407: capitalizationEntry
1408: .setTransactionLedgerEntryDescription(capitalizationDescription);
1409:
1410: plantFundAccountLookup(scrubbedEntry, capitalizationEntry);
1411:
1412: capitalizationEntry
1413: .setUniversityFiscalPeriodCode(scrubbedEntry
1414: .getUniversityFiscalPeriodCode());
1415:
1416: createOutputEntry(capitalizationEntry, validGroup);
1417: scrubberReport.incrementCapitalizationEntryGenerated();
1418:
1419: // Clear out the id & the ojb version number to make sure we do an insert on the next one
1420: capitalizationEntry.setVersionNumber(null);
1421: capitalizationEntry.setEntryId(null);
1422:
1423: capitalizationEntry
1424: .setFinancialObjectCode(scrubbedEntryChart
1425: .getFundBalanceObjectCode());
1426: if (ObjectUtils.isNotNull(scrubbedEntryChart
1427: .getFundBalanceObject())) {
1428: capitalizationEntry
1429: .setFinancialObjectTypeCode(scrubbedEntryChart
1430: .getFundBalanceObject()
1431: .getFinancialObjectTypeCode());
1432: } else {
1433: capitalizationEntry
1434: .setFinancialObjectTypeCode(scrubbedEntryOption
1435: .getFinObjectTypeFundBalanceCd());
1436: }
1437:
1438: if (scrubbedEntry.isDebit()) {
1439: capitalizationEntry
1440: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
1441: } else {
1442: capitalizationEntry
1443: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
1444: }
1445:
1446: try {
1447: flexibleOffsetAccountService
1448: .updateOffset(capitalizationEntry);
1449: } catch (InvalidFlexibleOffsetException e) {
1450: LOG
1451: .debug("processCapitalization() Capitalization Flexible Offset Error: "
1452: + e.getMessage());
1453: return e.getMessage();
1454: }
1455:
1456: createOutputEntry(capitalizationEntry, validGroup);
1457: scrubberReport.incrementCapitalizationEntryGenerated();
1458: }
1459: return null;
1460: }
1461:
1462: /**
1463: * Generates the plant indebtedness entries
1464: *
1465: * @param scrubbedEntry the entry to generated plant indebtedness entries for if necessary
1466: * @return null if no error, message if error
1467: */
1468: private String processPlantIndebtedness(OriginEntry scrubbedEntry) {
1469: // Lines 4855 - 4979 of the Pro Cobol listing on Confluence
1470: if (!parameterService
1471: .getIndicatorParameter(
1472: ScrubberStep.class,
1473: GLConstants.GlScrubberGroupParameters.PLANT_INDEBTEDNESS_IND)) {
1474: return null;
1475: }
1476:
1477: OriginEntryFull plantIndebtednessEntry = OriginEntryFull
1478: .copyFromOriginEntryable(scrubbedEntry);
1479:
1480: Options scrubbedEntryOption = referenceLookup.get().getOption(
1481: scrubbedEntry);
1482: ObjectCode scrubbedEntryObjectCode = referenceLookup.get()
1483: .getFinancialObject(scrubbedEntry);
1484: Account scrubbedEntryAccount = referenceLookup.get()
1485: .getAccount(scrubbedEntry);
1486: Chart scrubbedEntryChart = referenceLookup.get().getChart(
1487: scrubbedEntry);
1488:
1489: ParameterEvaluator objectSubTypeCodes = parameterService
1490: .getParameterEvaluator(
1491: ScrubberStep.class,
1492: GLConstants.GlScrubberGroupRules.PLANT_INDEBTEDNESS_OBJ_SUB_TYPE_CODES,
1493: scrubbedEntryObjectCode
1494: .getFinancialObjectSubTypeCode());
1495: ParameterEvaluator subFundGroupCodes = parameterService
1496: .getParameterEvaluator(
1497: ScrubberStep.class,
1498: GLConstants.GlScrubberGroupRules.PLANT_INDEBTEDNESS_SUB_FUND_GROUP_CODES,
1499: scrubbedEntryAccount.getSubFundGroupCode());
1500:
1501: if (scrubbedEntry.getFinancialBalanceTypeCode().equals(
1502: scrubbedEntryOption.getActualFinancialBalanceTypeCd())
1503: && subFundGroupCodes.evaluationSucceeds()
1504: && objectSubTypeCodes.evaluationSucceeds()) {
1505:
1506: plantIndebtednessEntry
1507: .setTransactionLedgerEntryDescription(KFSConstants.PLANT_INDEBTEDNESS_ENTRY_DESCRIPTION);
1508:
1509: if (scrubbedEntry.isDebit()) {
1510: plantIndebtednessEntry
1511: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
1512: } else {
1513: plantIndebtednessEntry
1514: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
1515: }
1516:
1517: plantIndebtednessEntry
1518: .setTransactionScrubberOffsetGenerationIndicator(true);
1519: createOutputEntry(plantIndebtednessEntry, validGroup);
1520: scrubberReport.incrementPlantIndebtednessEntryGenerated();
1521:
1522: // Clear out the id & the ojb version number to make sure we do an insert on the next one
1523: plantIndebtednessEntry.setVersionNumber(null);
1524: plantIndebtednessEntry.setEntryId(null);
1525:
1526: plantIndebtednessEntry
1527: .setFinancialObjectCode(scrubbedEntryChart
1528: .getFundBalanceObjectCode());
1529: plantIndebtednessEntry
1530: .setFinancialObjectTypeCode(scrubbedEntryOption
1531: .getFinObjectTypeFundBalanceCd());
1532: plantIndebtednessEntry
1533: .setTransactionDebitCreditCode(scrubbedEntry
1534: .getTransactionDebitCreditCode());
1535:
1536: plantIndebtednessEntry
1537: .setTransactionScrubberOffsetGenerationIndicator(true);
1538: plantIndebtednessEntry
1539: .setFinancialSubObjectCode(KFSConstants
1540: .getDashFinancialSubObjectCode());
1541:
1542: try {
1543: flexibleOffsetAccountService
1544: .updateOffset(plantIndebtednessEntry);
1545: } catch (InvalidFlexibleOffsetException e) {
1546: LOG
1547: .error(
1548: "processPlantIndebtedness() Flexible Offset Exception (1)",
1549: e);
1550: LOG
1551: .debug("processPlantIndebtedness() Plant Indebtedness Flexible Offset Error: "
1552: + e.getMessage());
1553: return e.getMessage();
1554: }
1555:
1556: createOutputEntry(plantIndebtednessEntry, validGroup);
1557: scrubberReport.incrementPlantIndebtednessEntryGenerated();
1558:
1559: // Clear out the id & the ojb version number to make sure we do an insert on the next one
1560: plantIndebtednessEntry.setVersionNumber(null);
1561: plantIndebtednessEntry.setEntryId(null);
1562:
1563: plantIndebtednessEntry.setFinancialObjectCode(scrubbedEntry
1564: .getFinancialObjectCode());
1565: plantIndebtednessEntry
1566: .setFinancialObjectTypeCode(scrubbedEntry
1567: .getFinancialObjectTypeCode());
1568: plantIndebtednessEntry
1569: .setTransactionDebitCreditCode(scrubbedEntry
1570: .getTransactionDebitCreditCode());
1571:
1572: plantIndebtednessEntry
1573: .setTransactionLedgerEntryDescription(scrubbedEntry
1574: .getTransactionLedgerEntryDescription());
1575:
1576: plantIndebtednessEntry.setAccountNumber(scrubbedEntry
1577: .getAccountNumber());
1578: plantIndebtednessEntry.setSubAccountNumber(scrubbedEntry
1579: .getSubAccountNumber());
1580:
1581: if (scrubbedEntry.getChartOfAccountsCode().equals(
1582: scrubbedEntryAccount.getOrganization()
1583: .getChartOfAccountsCode())
1584: && scrubbedEntryAccount.getOrganizationCode()
1585: .equals(
1586: scrubbedEntryAccount
1587: .getOrganizationCode())
1588: && scrubbedEntry.getAccountNumber().equals(
1589: scrubbedEntryAccount.getAccountNumber())
1590: && scrubbedEntry.getChartOfAccountsCode().equals(
1591: scrubbedEntryAccount
1592: .getChartOfAccountsCode())) {
1593: plantIndebtednessEntry
1594: .setAccountNumber(scrubbedEntryAccount
1595: .getOrganization()
1596: .getCampusPlantAccountNumber());
1597: plantIndebtednessEntry
1598: .setChartOfAccountsCode(scrubbedEntryAccount
1599: .getOrganization()
1600: .getCampusPlantChartCode());
1601: }
1602:
1603: plantIndebtednessEntry.setSubAccountNumber(KFSConstants
1604: .getDashSubAccountNumber());
1605: plantIndebtednessEntry
1606: .setFinancialSubObjectCode(KFSConstants
1607: .getDashFinancialSubObjectCode());
1608:
1609: StringBuffer litGenPlantXferFrom = new StringBuffer();
1610: litGenPlantXferFrom.append(transferDescription + " ");
1611: litGenPlantXferFrom.append(
1612: scrubbedEntry.getChartOfAccountsCode()).append(" ");
1613: litGenPlantXferFrom
1614: .append(scrubbedEntry.getAccountNumber());
1615: plantIndebtednessEntry
1616: .setTransactionLedgerEntryDescription(litGenPlantXferFrom
1617: .toString());
1618:
1619: createOutputEntry(plantIndebtednessEntry, validGroup);
1620: scrubberReport.incrementPlantIndebtednessEntryGenerated();
1621:
1622: // Clear out the id & the ojb version number to make sure we do an insert on the next one
1623: plantIndebtednessEntry.setVersionNumber(null);
1624: plantIndebtednessEntry.setEntryId(null);
1625:
1626: plantIndebtednessEntry
1627: .setFinancialObjectCode(scrubbedEntryChart
1628: .getFundBalanceObjectCode());
1629: plantIndebtednessEntry
1630: .setFinancialObjectTypeCode(scrubbedEntryOption
1631: .getFinObjectTypeFundBalanceCd());
1632: plantIndebtednessEntry
1633: .setFinancialSubObjectCode(KFSConstants
1634: .getDashFinancialSubObjectCode());
1635:
1636: if (scrubbedEntry.isDebit()) {
1637: plantIndebtednessEntry
1638: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
1639: } else {
1640: plantIndebtednessEntry
1641: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
1642: }
1643:
1644: try {
1645: flexibleOffsetAccountService
1646: .updateOffset(plantIndebtednessEntry);
1647: } catch (InvalidFlexibleOffsetException e) {
1648: LOG
1649: .error(
1650: "processPlantIndebtedness() Flexible Offset Exception (2)",
1651: e);
1652: LOG
1653: .debug("processPlantIndebtedness() Plant Indebtedness Flexible Offset Error: "
1654: + e.getMessage());
1655: return e.getMessage();
1656: }
1657:
1658: createOutputEntry(plantIndebtednessEntry, validGroup);
1659: scrubberReport.incrementPlantIndebtednessEntryGenerated();
1660: }
1661:
1662: return null;
1663: }
1664:
1665: /**
1666: * Generate the liability entries for the entry if necessary
1667: *
1668: * @param scrubbedEntry the entry to generate liability entries for if necessary
1669: * @return null if no error, message if error
1670: */
1671: private String processLiabilities(OriginEntry scrubbedEntry) {
1672: // Lines 4799 to 4839 of the Pro Cobol list of the scrubber on Confluence
1673: if (!parameterService.getIndicatorParameter(ScrubberStep.class,
1674: GLConstants.GlScrubberGroupParameters.LIABILITY_IND)) {
1675: return null;
1676: }
1677:
1678: OriginEntryFull liabilityEntry = OriginEntryFull
1679: .copyFromOriginEntryable(scrubbedEntry);
1680:
1681: Chart scrubbedEntryChart = referenceLookup.get().getChart(
1682: scrubbedEntry);
1683: Options scrubbedEntryOption = referenceLookup.get().getOption(
1684: scrubbedEntry);
1685: ObjectCode scrubbedEntryFinancialObject = referenceLookup.get()
1686: .getFinancialObject(scrubbedEntry);
1687: Account scrubbedEntryAccount = referenceLookup.get()
1688: .getAccount(scrubbedEntry);
1689:
1690: ParameterEvaluator chartCodes = parameterService
1691: .getParameterEvaluator(
1692: ScrubberStep.class,
1693: GLConstants.GlScrubberGroupRules.LIABILITY_CHART_CODES,
1694: scrubbedEntry.getChartOfAccountsCode());
1695: ParameterEvaluator docTypeCodes = parameterService
1696: .getParameterEvaluator(
1697: ScrubberStep.class,
1698: GLConstants.GlScrubberGroupRules.LIABILITY_DOC_TYPE_CODES,
1699: scrubbedEntry.getFinancialDocumentTypeCode());
1700: ParameterEvaluator fiscalPeriods = parameterService
1701: .getParameterEvaluator(
1702: ScrubberStep.class,
1703: GLConstants.GlScrubberGroupRules.LIABILITY_FISCAL_PERIOD_CODES,
1704: scrubbedEntry.getUniversityFiscalPeriodCode());
1705: ParameterEvaluator objSubTypeCodes = parameterService
1706: .getParameterEvaluator(
1707: ScrubberStep.class,
1708: GLConstants.GlScrubberGroupRules.LIABILITY_OBJ_SUB_TYPE_CODES,
1709: scrubbedEntryFinancialObject
1710: .getFinancialObjectSubTypeCode());
1711: ParameterEvaluator subFundGroupCodes = parameterService
1712: .getParameterEvaluator(
1713: ScrubberStep.class,
1714: GLConstants.GlScrubberGroupRules.LIABILITY_SUB_FUND_GROUP_CODES,
1715: scrubbedEntryAccount.getSubFundGroupCode());
1716:
1717: if (scrubbedEntry.getFinancialBalanceTypeCode().equals(
1718: scrubbedEntryOption.getActualFinancialBalanceTypeCd())
1719: && scrubbedEntry.getUniversityFiscalYear().intValue() > 1995
1720: && docTypeCodes.evaluationSucceeds()
1721: && fiscalPeriods.evaluationSucceeds()
1722: && objSubTypeCodes.evaluationSucceeds()
1723: && subFundGroupCodes.evaluationSucceeds()
1724: && chartCodes.evaluationSucceeds()) {
1725:
1726: liabilityEntry
1727: .setFinancialObjectCode(parameterService
1728: .getParameterValue(
1729: ScrubberStep.class,
1730: GLConstants.GlScrubberGroupParameters.LIABILITY_OBJECT_CODE));
1731: liabilityEntry
1732: .setFinancialObjectTypeCode(scrubbedEntryOption
1733: .getFinObjectTypeLiabilitiesCode());
1734:
1735: liabilityEntry.setTransactionDebitCreditCode(scrubbedEntry
1736: .getTransactionDebitCreditCode());
1737: liabilityEntry
1738: .setTransactionLedgerEntryDescription(liabilityDescription);
1739: plantFundAccountLookup(scrubbedEntry, liabilityEntry);
1740:
1741: createOutputEntry(liabilityEntry, validGroup);
1742: scrubberReport.incrementLiabilityEntryGenerated();
1743:
1744: // Clear out the id & the ojb version number to make sure we do an insert on the next one
1745: liabilityEntry.setVersionNumber(null);
1746: liabilityEntry.setEntryId(null);
1747:
1748: // ... and now generate the offset half of the liability entry
1749: liabilityEntry.setFinancialObjectCode(scrubbedEntryChart
1750: .getFundBalanceObjectCode());
1751: if (ObjectUtils.isNotNull(scrubbedEntryChart
1752: .getFundBalanceObject())) {
1753: liabilityEntry
1754: .setFinancialObjectTypeCode(scrubbedEntryChart
1755: .getFundBalanceObject()
1756: .getFinancialObjectTypeCode());
1757: } else {
1758: liabilityEntry
1759: .setFinancialObjectTypeCode(scrubbedEntryOption
1760: .getFinObjectTypeFundBalanceCd());
1761: }
1762:
1763: if (liabilityEntry.isDebit()) {
1764: liabilityEntry
1765: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
1766: } else {
1767: liabilityEntry
1768: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
1769: }
1770:
1771: try {
1772: flexibleOffsetAccountService
1773: .updateOffset(liabilityEntry);
1774: } catch (InvalidFlexibleOffsetException e) {
1775: LOG
1776: .debug("processLiabilities() Liability Flexible Offset Error: "
1777: + e.getMessage());
1778: return e.getMessage();
1779: }
1780:
1781: createOutputEntry(liabilityEntry, validGroup);
1782: scrubberReport.incrementLiabilityEntryGenerated();
1783: }
1784: return null;
1785: }
1786:
1787: /**
1788: * Updates the entries with the proper chart and account for the plant fund
1789: *
1790: * @param scrubbedEntry basis for plant fund entry
1791: * @param liabilityEntry liability entry
1792: */
1793: private void plantFundAccountLookup(OriginEntry scrubbedEntry,
1794: OriginEntryFull liabilityEntry) {
1795: // 4000-PLANT-FUND-ACCT to 4000-PLANT-FUND-ACCT-EXIT in cobol
1796:
1797: liabilityEntry.setSubAccountNumber(KFSConstants
1798: .getDashSubAccountNumber());
1799: persistenceService.retrieveReferenceObject(liabilityEntry,
1800: KFSPropertyConstants.ACCOUNT);
1801:
1802: ObjectCode scrubbedEntryObjectCode = referenceLookup.get()
1803: .getFinancialObject(scrubbedEntry);
1804: Account scrubbedEntryAccount = referenceLookup.get()
1805: .getAccount(scrubbedEntry);
1806:
1807: if (liabilityEntry.getChartOfAccountsCode().equals(
1808: liabilityEntry.getAccount().getOrganization()
1809: .getChartOfAccountsCode())
1810: && scrubbedEntryAccount.getOrganizationCode().equals(
1811: liabilityEntry.getAccount().getOrganization()
1812: .getOrganizationCode())
1813: && liabilityEntry.getAccountNumber().equals(
1814: liabilityEntry.getAccount().getAccountNumber())
1815: && liabilityEntry.getChartOfAccountsCode().equals(
1816: liabilityEntry.getAccount()
1817: .getChartOfAccountsCode())) {
1818: persistenceService.retrieveReferenceObject(liabilityEntry,
1819: KFSPropertyConstants.FINANCIAL_OBJECT);
1820: persistenceService.retrieveReferenceObject(liabilityEntry
1821: .getAccount(), KFSPropertyConstants.ORGANIZATION);
1822:
1823: String objectSubTypeCode = scrubbedEntryObjectCode
1824: .getFinancialObjectSubTypeCode();
1825: ParameterEvaluator campusObjSubTypeCodes = parameterService
1826: .getParameterEvaluator(
1827: ScrubberStep.class,
1828: GLConstants.GlScrubberGroupRules.PLANT_FUND_CAMPUS_OBJECT_SUB_TYPE_CODES,
1829: objectSubTypeCode);
1830: ParameterEvaluator orgObjSubTypeCodes = parameterService
1831: .getParameterEvaluator(
1832: ScrubberStep.class,
1833: GLConstants.GlScrubberGroupRules.PLANT_FUND_ORG_OBJECT_SUB_TYPE_CODES,
1834: objectSubTypeCode);
1835:
1836: if (campusObjSubTypeCodes.evaluationSucceeds()) {
1837: liabilityEntry.setAccountNumber(scrubbedEntryAccount
1838: .getOrganization()
1839: .getCampusPlantAccountNumber());
1840: liabilityEntry
1841: .setChartOfAccountsCode(scrubbedEntryAccount
1842: .getOrganization()
1843: .getCampusPlantChartCode());
1844:
1845: persistenceService.retrieveReferenceObject(
1846: liabilityEntry, KFSPropertyConstants.ACCOUNT);
1847: persistenceService.retrieveReferenceObject(
1848: liabilityEntry, KFSPropertyConstants.CHART);
1849: } else if (orgObjSubTypeCodes.evaluationSucceeds()) {
1850: liabilityEntry.setAccountNumber(scrubbedEntryAccount
1851: .getOrganization()
1852: .getOrganizationPlantAccountNumber());
1853: liabilityEntry
1854: .setChartOfAccountsCode(scrubbedEntryAccount
1855: .getOrganization()
1856: .getOrganizationPlantChartCode());
1857:
1858: persistenceService.retrieveReferenceObject(
1859: liabilityEntry, KFSPropertyConstants.ACCOUNT);
1860: persistenceService.retrieveReferenceObject(
1861: liabilityEntry, KFSPropertyConstants.CHART);
1862: }
1863: }
1864: }
1865:
1866: /**
1867: * The purpose of this method is to generate a "Cost Share Encumbrance"
1868: * transaction for the current transaction and its offset. The cost share chart and account for current transaction are obtained
1869: * from the CA_A21_SUB_ACCT_T table. This method calls the method SET-OBJECT-2004 to get the Cost Share Object Code. It then
1870: * writes out the cost share transaction. Next it read the GL_OFFSET_DEFN_T table for the offset object code that corresponds to
1871: * the cost share object code. In addition to the object code it needs to get subobject code. It then reads the CA_OBJECT_CODE_T
1872: * table to make sure the offset object code found in the GL_OFFSET_DEFN_T is valid and to get the object type code associated
1873: * with this object code. It writes out the offset transaction and returns.
1874: *
1875: * @param scrubbedEntry the entry to perhaps create a cost share encumbrance for
1876: * @return a message if there was an error encountered generating the entries, or (hopefully) null if no errors were encountered
1877: */
1878: private TransactionError generateCostShareEncumbranceEntries(
1879: OriginEntry scrubbedEntry) {
1880: // 3200-COST-SHARE-ENC to 3200-CSE-EXIT in the COBOL
1881: LOG.debug("generateCostShareEncumbranceEntries() started");
1882:
1883: OriginEntryFull costShareEncumbranceEntry = OriginEntryFull
1884: .copyFromOriginEntryable(scrubbedEntry);
1885:
1886: // First 28 characters of the description, padding to 28 if shorter)
1887: StringBuffer buffer = new StringBuffer((scrubbedEntry
1888: .getTransactionLedgerEntryDescription() + GLConstants
1889: .getSpaceTransactionLedgetEntryDescription())
1890: .substring(0, COST_SHARE_ENCUMBRANCE_ENTRY_MAXLENGTH));
1891:
1892: buffer.append("FR-");
1893: buffer.append(costShareEncumbranceEntry
1894: .getChartOfAccountsCode());
1895: buffer.append(costShareEncumbranceEntry.getAccountNumber());
1896:
1897: costShareEncumbranceEntry
1898: .setTransactionLedgerEntryDescription(buffer.toString());
1899:
1900: A21SubAccount scrubbedEntryA21SubAccount = referenceLookup
1901: .get().getA21SubAccount(scrubbedEntry);
1902: Options scrubbedEntryOption = referenceLookup.get().getOption(
1903: scrubbedEntry);
1904:
1905: costShareEncumbranceEntry
1906: .setChartOfAccountsCode(scrubbedEntryA21SubAccount
1907: .getCostShareChartOfAccountCode());
1908: costShareEncumbranceEntry
1909: .setAccountNumber(scrubbedEntryA21SubAccount
1910: .getCostShareSourceAccountNumber());
1911: costShareEncumbranceEntry
1912: .setSubAccountNumber(scrubbedEntryA21SubAccount
1913: .getCostShareSourceSubAccountNumber());
1914:
1915: if (!StringUtils.hasText(costShareEncumbranceEntry
1916: .getSubAccountNumber())) {
1917: costShareEncumbranceEntry.setSubAccountNumber(KFSConstants
1918: .getDashSubAccountNumber());
1919: }
1920:
1921: costShareEncumbranceEntry
1922: .setFinancialBalanceTypeCode(scrubbedEntryOption
1923: .getCostShareEncumbranceBalanceTypeCd());
1924: setCostShareObjectCode(costShareEncumbranceEntry, scrubbedEntry);
1925: costShareEncumbranceEntry
1926: .setFinancialSubObjectCode(KFSConstants
1927: .getDashFinancialSubObjectCode());
1928: costShareEncumbranceEntry
1929: .setTransactionLedgerEntrySequenceNumber(new Integer(0));
1930:
1931: if (!StringUtils.hasText(scrubbedEntry
1932: .getTransactionDebitCreditCode())) {
1933: if (scrubbedEntry.getTransactionLedgerEntryAmount()
1934: .isPositive()) {
1935: costShareEncumbranceEntry
1936: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
1937: } else {
1938: costShareEncumbranceEntry
1939: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
1940: costShareEncumbranceEntry
1941: .setTransactionLedgerEntryAmount(scrubbedEntry
1942: .getTransactionLedgerEntryAmount()
1943: .negated());
1944: }
1945: }
1946:
1947: costShareEncumbranceEntry.setTransactionDate(runDate);
1948:
1949: costShareEncumbranceEntry
1950: .setTransactionScrubberOffsetGenerationIndicator(true);
1951: createOutputEntry(costShareEncumbranceEntry, validGroup);
1952: scrubberReport.incrementCostShareEncumbranceGenerated();
1953:
1954: OriginEntryFull costShareEncumbranceOffsetEntry = new OriginEntryFull(
1955: costShareEncumbranceEntry);
1956:
1957: costShareEncumbranceOffsetEntry
1958: .setTransactionLedgerEntryDescription(offsetDescription);
1959:
1960: OffsetDefinition offset = offsetDefinitionService
1961: .getByPrimaryId(costShareEncumbranceEntry
1962: .getUniversityFiscalYear(),
1963: costShareEncumbranceEntry
1964: .getChartOfAccountsCode(),
1965: costShareEncumbranceEntry
1966: .getFinancialDocumentTypeCode(),
1967: costShareEncumbranceEntry
1968: .getFinancialBalanceTypeCode());
1969:
1970: if (offset != null) {
1971: if (offset.getFinancialObject() == null) {
1972: StringBuffer offsetKey = new StringBuffer();
1973: offsetKey.append(offset.getUniversityFiscalYear());
1974: offsetKey.append("-");
1975: offsetKey.append(offset.getChartOfAccountsCode());
1976: offsetKey.append("-");
1977: offsetKey.append(offset.getFinancialObjectCode());
1978:
1979: LOG
1980: .debug("generateCostShareEncumbranceEntries() object code not found");
1981: return new TransactionError(
1982: costShareEncumbranceEntry,
1983: new Message(
1984: configurationService
1985: .getPropertyString(KFSKeyConstants.ERROR_NO_OBJECT_FOR_OBJECT_ON_OFSD)
1986: + "("
1987: + offsetKey.toString()
1988: + ")", Message.TYPE_FATAL));
1989: }
1990: costShareEncumbranceOffsetEntry
1991: .setFinancialObjectCode(offset
1992: .getFinancialObjectCode());
1993: costShareEncumbranceOffsetEntry.setFinancialObject(offset
1994: .getFinancialObject());
1995: costShareEncumbranceOffsetEntry
1996: .setFinancialSubObjectCode(KFSConstants
1997: .getDashFinancialSubObjectCode());
1998: } else {
1999: StringBuffer offsetKey = new StringBuffer(
2000: "Cost share encumbrance ");
2001: offsetKey.append(costShareEncumbranceEntry
2002: .getUniversityFiscalYear());
2003: offsetKey.append("-");
2004: offsetKey.append(costShareEncumbranceEntry
2005: .getChartOfAccountsCode());
2006: offsetKey.append("-");
2007: offsetKey.append(costShareEncumbranceEntry
2008: .getFinancialDocumentTypeCode());
2009: offsetKey.append("-");
2010: offsetKey.append(costShareEncumbranceEntry
2011: .getFinancialBalanceTypeCode());
2012:
2013: LOG
2014: .debug("generateCostShareEncumbranceEntries() offset not found");
2015: return new TransactionError(
2016: costShareEncumbranceEntry,
2017: new Message(
2018: configurationService
2019: .getPropertyString(KFSKeyConstants.ERROR_OFFSET_DEFINITION_NOT_FOUND)
2020: + "(" + offsetKey.toString() + ")",
2021: Message.TYPE_FATAL));
2022: }
2023:
2024: costShareEncumbranceOffsetEntry
2025: .setFinancialObjectTypeCode(offset.getFinancialObject()
2026: .getFinancialObjectTypeCode());
2027:
2028: if (costShareEncumbranceEntry.isCredit()) {
2029: costShareEncumbranceOffsetEntry
2030: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
2031: } else {
2032: costShareEncumbranceOffsetEntry
2033: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
2034: }
2035:
2036: costShareEncumbranceOffsetEntry.setTransactionDate(runDate);
2037: costShareEncumbranceOffsetEntry
2038: .setOrganizationDocumentNumber(null);
2039: costShareEncumbranceOffsetEntry.setProjectCode(KFSConstants
2040: .getDashProjectCode());
2041: costShareEncumbranceOffsetEntry
2042: .setOrganizationReferenceId(null);
2043: costShareEncumbranceOffsetEntry
2044: .setReferenceFinancialDocumentTypeCode(null);
2045: costShareEncumbranceOffsetEntry
2046: .setReferenceFinancialSystemOriginationCode(null);
2047: costShareEncumbranceOffsetEntry
2048: .setReferenceFinancialDocumentNumber(null);
2049: costShareEncumbranceOffsetEntry.setReversalDate(null);
2050: costShareEncumbranceOffsetEntry
2051: .setTransactionEncumbranceUpdateCode(null);
2052:
2053: costShareEncumbranceOffsetEntry
2054: .setTransactionScrubberOffsetGenerationIndicator(true);
2055:
2056: try {
2057: flexibleOffsetAccountService
2058: .updateOffset(costShareEncumbranceOffsetEntry);
2059: } catch (InvalidFlexibleOffsetException e) {
2060: Message m = new Message(e.getMessage(), Message.TYPE_FATAL);
2061: LOG
2062: .debug("generateCostShareEncumbranceEntries() Cost Share Encumbrance Flexible Offset Error: "
2063: + e.getMessage());
2064: return new TransactionError(
2065: costShareEncumbranceOffsetEntry, m);
2066: }
2067:
2068: createOutputEntry(costShareEncumbranceOffsetEntry, validGroup);
2069: scrubberReport.incrementCostShareEncumbranceGenerated();
2070:
2071: LOG
2072: .debug("generateCostShareEncumbranceEntries() returned successfully");
2073: return null;
2074: }
2075:
2076: /**
2077: * Sets the proper cost share object code in an entry and its offset
2078: *
2079: * @param costShareEntry GL Entry for cost share
2080: * @param originEntry Scrubbed GL Entry that this is based on
2081: */
2082: private void setCostShareObjectCode(OriginEntryFull costShareEntry,
2083: OriginEntry originEntry) {
2084: // This code is SET-OBJECT-2004 to 2520-INIT-SCRB-AREA in the Cobol
2085: ObjectCode originEntryFinancialObject = referenceLookup.get()
2086: .getFinancialObject(originEntry);
2087:
2088: if (originEntryFinancialObject == null) {
2089: addTransactionError(
2090: configurationService
2091: .getPropertyString(KFSKeyConstants.ERROR_OBJECT_CODE_NOT_FOUND),
2092: originEntry.getFinancialObjectCode(),
2093: Message.TYPE_FATAL);
2094: }
2095:
2096: String originEntryObjectLevelCode = (originEntryFinancialObject == null) ? ""
2097: : originEntryFinancialObject
2098: .getFinancialObjectLevelCode();
2099:
2100: String financialOriginEntryObjectCode = originEntry
2101: .getFinancialObjectCode();
2102: String originEntryObjectCode = scrubberProcessObjectCodeOverride
2103: .getOriginEntryObjectCode(originEntryObjectLevelCode,
2104: financialOriginEntryObjectCode);
2105:
2106: // General rules
2107: if (originEntryObjectCode
2108: .equals(financialOriginEntryObjectCode)) {
2109: String param = parameterService
2110: .getParameterValue(
2111: ScrubberStep.class,
2112: GLConstants.GlScrubberGroupParameters.COST_SHARE_OBJECT_CODE_BY_LEVEL_PARM_NM,
2113: originEntryObjectLevelCode);
2114: if (param == null) {
2115: param = parameterService
2116: .getParameterValue(
2117: ScrubberStep.class,
2118: GLConstants.GlScrubberGroupParameters.COST_SHARE_OBJECT_CODE_BY_LEVEL_PARM_NM,
2119: "DEFAULT");
2120: if (param == null) {
2121: throw new RuntimeException(
2122: "Unable to determine cost sharing object code from object level. Default entry missing.");
2123: }
2124: }
2125: originEntryObjectCode = param;
2126: }
2127:
2128: // Lookup the new object code
2129: ObjectCode objectCode = objectCodeService.getByPrimaryId(
2130: costShareEntry.getUniversityFiscalYear(),
2131: costShareEntry.getChartOfAccountsCode(),
2132: originEntryObjectCode);
2133: if (objectCode != null) {
2134: costShareEntry.setFinancialObjectTypeCode(objectCode
2135: .getFinancialObjectTypeCode());
2136: costShareEntry
2137: .setFinancialObjectCode(originEntryObjectCode);
2138: } else {
2139: addTransactionError(
2140: configurationService
2141: .getPropertyString(KFSKeyConstants.ERROR_COST_SHARE_OBJECT_NOT_FOUND),
2142: costShareEntry.getFinancialObjectCode(),
2143: Message.TYPE_FATAL);
2144: }
2145: }
2146:
2147: /**
2148: * The purpose of this method is to build the actual offset transaction. It does this by performing the following steps: 1.
2149: * Getting the offset object code and offset subobject code from the GL Offset Definition Table. 2. For the offset object code
2150: * it needs to get the associated object type, object subtype, and object active code.
2151: *
2152: * @param scrubbedEntry entry to determine if an offset is needed for
2153: * @return true if an offset would be needed for this entry, false otherwise
2154: */
2155: private boolean generateOffset(OriginEntry scrubbedEntry) {
2156: // This code is 3000-OFFSET to SET-OBJECT-2004 in the Cobol
2157: LOG.debug("generateOffset() started");
2158:
2159: // There was no previous unit of work so we need no offset
2160: if (scrubbedEntry == null) {
2161: return true;
2162: }
2163:
2164: // If the offset amount is zero, don't bother to lookup the offset definition ...
2165: if (unitOfWork.offsetAmount.isZero()) {
2166: return true;
2167: }
2168:
2169: ParameterEvaluator docTypeRule = parameterService
2170: .getParameterEvaluator(
2171: ScrubberStep.class,
2172: GLConstants.GlScrubberGroupRules.OFFSET_DOC_TYPE_CODES,
2173: scrubbedEntry.getFinancialDocumentTypeCode());
2174: if (!docTypeRule.evaluationSucceeds()) {
2175: return true;
2176: }
2177:
2178: // do nothing if flexible offset is enabled and scrubber offset indicator of the document
2179: // type code is turned off in the document type table
2180: String documentTypeCode = scrubbedEntry
2181: .getFinancialDocumentTypeCode();
2182: DocumentType documentType = documentTypeService
2183: .getDocumentTypeByCode(documentTypeCode);
2184: if ((!documentType
2185: .isTransactionScrubberOffsetGenerationIndicator())
2186: && flexibleOffsetAccountService.getEnabled()) {
2187: return true;
2188: }
2189:
2190: // Create an offset
2191: OriginEntryFull offsetEntry = OriginEntryFull
2192: .copyFromOriginEntryable(scrubbedEntry);
2193: offsetEntry
2194: .setTransactionLedgerEntryDescription(offsetDescription);
2195:
2196: OffsetDefinition offsetDefinition = offsetDefinitionService
2197: .getByPrimaryId(
2198: scrubbedEntry.getUniversityFiscalYear(),
2199: scrubbedEntry.getChartOfAccountsCode(),
2200: scrubbedEntry.getFinancialDocumentTypeCode(),
2201: scrubbedEntry.getFinancialBalanceTypeCode());
2202: if (offsetDefinition != null) {
2203: if (offsetDefinition.getFinancialObject() == null) {
2204: StringBuffer offsetKey = new StringBuffer(
2205: offsetDefinition.getUniversityFiscalYear());
2206: offsetKey.append("-");
2207: offsetKey.append(offsetDefinition
2208: .getChartOfAccountsCode());
2209: offsetKey.append("-");
2210: offsetKey.append(offsetDefinition
2211: .getFinancialObjectCode());
2212:
2213: putTransactionError(
2214: offsetEntry,
2215: configurationService
2216: .getPropertyString(KFSKeyConstants.ERROR_OFFSET_DEFINITION_OBJECT_CODE_NOT_FOUND),
2217: offsetKey.toString(), Message.TYPE_FATAL);
2218:
2219: createOutputEntry(offsetEntry, errorGroup);
2220: scrubberReport.incrementErrorRecordWritten();
2221: return false;
2222: }
2223:
2224: offsetEntry.setFinancialObject(offsetDefinition
2225: .getFinancialObject());
2226: offsetEntry.setFinancialObjectCode(offsetDefinition
2227: .getFinancialObjectCode());
2228:
2229: offsetEntry.setFinancialSubObject(null);
2230: offsetEntry.setFinancialSubObjectCode(KFSConstants
2231: .getDashFinancialSubObjectCode());
2232: } else {
2233: StringBuffer sb = new StringBuffer("Unit of work offset ");
2234: sb.append(scrubbedEntry.getUniversityFiscalYear());
2235: sb.append("-");
2236: sb.append(scrubbedEntry.getChartOfAccountsCode());
2237: sb.append("-");
2238: sb.append(scrubbedEntry.getFinancialDocumentTypeCode());
2239: sb.append("-");
2240: sb.append(scrubbedEntry.getFinancialBalanceTypeCode());
2241:
2242: putTransactionError(
2243: offsetEntry,
2244: configurationService
2245: .getPropertyString(KFSKeyConstants.ERROR_OFFSET_DEFINITION_NOT_FOUND),
2246: sb.toString(), Message.TYPE_FATAL);
2247:
2248: createOutputEntry(offsetEntry, errorGroup);
2249: scrubberReport.incrementErrorRecordWritten();
2250: return false;
2251: }
2252:
2253: offsetEntry.setFinancialObjectTypeCode(offsetEntry
2254: .getFinancialObject().getFinancialObjectTypeCode());
2255: offsetEntry
2256: .setTransactionLedgerEntryAmount(unitOfWork.offsetAmount);
2257:
2258: if (unitOfWork.offsetAmount.isPositive()) {
2259: offsetEntry
2260: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
2261: } else {
2262: offsetEntry
2263: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
2264: offsetEntry
2265: .setTransactionLedgerEntryAmount(unitOfWork.offsetAmount
2266: .negated());
2267: }
2268:
2269: offsetEntry.setOrganizationDocumentNumber(null);
2270: offsetEntry.setOrganizationReferenceId(null);
2271: offsetEntry.setReferenceFinancialDocumentTypeCode(null);
2272: offsetEntry.setReferenceDocumentType(null);
2273: offsetEntry.setReferenceFinancialSystemOriginationCode(null);
2274: offsetEntry.setReferenceFinancialDocumentNumber(null);
2275: offsetEntry.setTransactionEncumbranceUpdateCode(null);
2276: offsetEntry.setProjectCode(KFSConstants.getDashProjectCode());
2277: offsetEntry.setTransactionDate(runDate);
2278:
2279: try {
2280: flexibleOffsetAccountService.updateOffset(offsetEntry);
2281: } catch (InvalidFlexibleOffsetException e) {
2282: LOG.debug("generateOffset() Offset Flexible Offset Error: "
2283: + e.getMessage());
2284: putTransactionError(offsetEntry, e.getMessage(), "",
2285: Message.TYPE_FATAL);
2286: return true;
2287: }
2288:
2289: createOutputEntry(offsetEntry, validGroup);
2290: scrubberReport.incrementOffsetEntryGenerated();
2291: return true;
2292: }
2293:
2294: /**
2295: * Save an entry in origin entry
2296: *
2297: * @param entry Entry to save
2298: * @param group Group to save it in
2299: */
2300: private void createOutputEntry(OriginEntry entry,
2301: OriginEntryGroup group) {
2302: // Write the entry if we aren't running in report only or collector mode.
2303: if (reportOnlyMode || collectorMode) {
2304: // If the group is null don't write it because the error and expired groups aren't created in reportOnlyMode
2305: if (group != null) {
2306: entry.setEntryGroupId(group.getId());
2307: originEntryLiteService.save((OriginEntryLite) entry);
2308: }
2309: } else {
2310: entry.setEntryGroupId(group.getId());
2311: originEntryLiteService.save((OriginEntryLite) entry);
2312: }
2313: }
2314:
2315: /**
2316: * If object is null, generate an error
2317: *
2318: * @param glObject object to test
2319: * @param errorMessage error message if glObject is null
2320: * @param errorValue value of glObject to print in the error message
2321: * @param type Type of message (fatal or warning)
2322: * @return true of glObject is null
2323: */
2324: private boolean ifNullAddTransactionErrorAndReturnFalse(
2325: Object glObject, String errorMessage, String errorValue,
2326: int type) {
2327: if (glObject == null) {
2328: if (StringUtils.hasText(errorMessage)) {
2329: addTransactionError(errorMessage, errorValue, type);
2330: } else {
2331: addTransactionError("Unexpected null object", glObject
2332: .getClass().getName(), type);
2333: }
2334: return false;
2335: }
2336: return true;
2337: }
2338:
2339: /**
2340: * Add an error message to the list of messages for this transaction
2341: *
2342: * @param errorMessage Error message
2343: * @param errorValue Value that is in error
2344: * @param type Type of error (Fatal or Warning)
2345: */
2346: private void addTransactionError(String errorMessage,
2347: String errorValue, int type) {
2348: transactionErrors.add(new Message(errorMessage + " ("
2349: + errorValue + ")", type));
2350: }
2351:
2352: /**
2353: * Puts a transaction error into this instance's collection of errors
2354: *
2355: * @param s a transaction that caused a scrubber error
2356: * @param errorMessage the message of what caused the error
2357: * @param errorValue the value in error
2358: * @param type the type of error
2359: */
2360: private void putTransactionError(Transaction s,
2361: String errorMessage, String errorValue, int type) {
2362: List te = new ArrayList();
2363: te
2364: .add(new Message(errorMessage + "(" + errorValue + ")",
2365: type));
2366: scrubberReportErrors.put(s, te);
2367: }
2368:
2369: /**
2370: * A class to hold the current unit of work the scrubber is using
2371: */
2372: class UnitOfWorkInfo {
2373: // Unit of work key
2374: public Integer univFiscalYr = 0;
2375: public String finCoaCd = "";
2376: public String accountNbr = "";
2377: public String subAcctNbr = "";
2378: public String finBalanceTypCd = "";
2379: public String fdocTypCd = "";
2380: public String fsOriginCd = "";
2381: public String fdocNbr = "";
2382: public Date fdocReversalDt = new Date(dateTimeService
2383: .getCurrentDate().getTime());
2384: public String univFiscalPrdCd = "";
2385:
2386: // Data about unit of work
2387: public boolean entryMode = true;
2388: public KualiDecimal offsetAmount = KualiDecimal.ZERO;
2389: public String scrbFinCoaCd;
2390: public String scrbAccountNbr;
2391:
2392: /**
2393: * Constructs a ScrubberProcess.UnitOfWorkInfo instance
2394: */
2395: public UnitOfWorkInfo() {
2396: }
2397:
2398: /**
2399: * Constructs a ScrubberProcess.UnitOfWorkInfo instance
2400: * @param e an origin entry belonging to this unit of work
2401: */
2402: public UnitOfWorkInfo(OriginEntry e) {
2403: univFiscalYr = e.getUniversityFiscalYear();
2404: finCoaCd = e.getChartOfAccountsCode();
2405: accountNbr = e.getAccountNumber();
2406: subAcctNbr = e.getSubAccountNumber();
2407: finBalanceTypCd = e.getFinancialBalanceTypeCode();
2408: fdocTypCd = e.getFinancialDocumentTypeCode();
2409: fsOriginCd = e.getFinancialSystemOriginationCode();
2410: fdocNbr = e.getDocumentNumber();
2411: fdocReversalDt = e.getFinancialDocumentReversalDate();
2412: univFiscalPrdCd = e.getUniversityFiscalPeriodCode();
2413: }
2414:
2415: /**
2416: * Determines if an entry belongs to this unit of work
2417: *
2418: * @param e the entry to check
2419: * @return true if it belongs to this unit of work, false otherwise
2420: */
2421: public boolean isSameUnitOfWork(OriginEntry e) {
2422: // Compare the key fields
2423: return univFiscalYr.equals(e.getUniversityFiscalYear())
2424: && finCoaCd.equals(e.getChartOfAccountsCode())
2425: && accountNbr.equals(e.getAccountNumber())
2426: && subAcctNbr.equals(e.getSubAccountNumber())
2427: && finBalanceTypCd.equals(e
2428: .getFinancialBalanceTypeCode())
2429: && fdocTypCd.equals(e
2430: .getFinancialDocumentTypeCode())
2431: && fsOriginCd.equals(e
2432: .getFinancialSystemOriginationCode())
2433: && fdocNbr.equals(e.getDocumentNumber())
2434: && ObjectHelper.isEqual(fdocReversalDt, e
2435: .getFinancialDocumentReversalDate())
2436: && univFiscalPrdCd.equals(e
2437: .getUniversityFiscalPeriodCode());
2438: }
2439:
2440: /**
2441: * Converts this unit of work info to a String
2442: * @return a String representation of this UnitOfWorkInfo
2443: * @see java.lang.Object#toString()
2444: */
2445: public String toString() {
2446: return univFiscalYr + finCoaCd + accountNbr + subAcctNbr
2447: + finBalanceTypCd + fdocTypCd + fsOriginCd
2448: + fdocNbr + fdocReversalDt + univFiscalPrdCd;
2449: }
2450:
2451: /**
2452: * Generates the beginning of an OriginEntryFull, based on the unit of work info
2453: *
2454: * @return a partially initialized OriginEntryFull
2455: */
2456: public OriginEntryFull getOffsetTemplate() {
2457: OriginEntryFull e = new OriginEntryFull();
2458: e.setUniversityFiscalYear(univFiscalYr);
2459: e.setChartOfAccountsCode(finCoaCd);
2460: e.setAccountNumber(accountNbr);
2461: e.setSubAccountNumber(subAcctNbr);
2462: e.setFinancialBalanceTypeCode(finBalanceTypCd);
2463: e.setFinancialDocumentTypeCode(fdocTypCd);
2464: e.setFinancialSystemOriginationCode(fsOriginCd);
2465: e.setDocumentNumber(fdocNbr);
2466: e.setFinancialDocumentReversalDate(fdocReversalDt);
2467: e.setUniversityFiscalPeriodCode(univFiscalPrdCd);
2468: return e;
2469: }
2470: }
2471:
2472: /**
2473: * An internal class to hold errors encountered by the scrubber
2474: */
2475: class TransactionError {
2476: public Transaction transaction;
2477: public Message message;
2478:
2479: /**
2480: * Constructs a ScrubberProcess.TransactionError instance
2481: * @param t the transaction that had the error
2482: * @param m a message about the error
2483: */
2484: public TransactionError(Transaction t, Message m) {
2485: transaction = t;
2486: message = m;
2487: }
2488: }
2489:
2490: /**
2491: * This method modifies the run date if it is before the cutoff time specified by the RunTimeService See
2492: * KULRNE-70 This method is public to facilitate unit testing
2493: *
2494: * @param currentDate the date the scrubber should report as having run on
2495: * @return the run date
2496: */
2497: public Date calculateRunDate(java.util.Date currentDate) {
2498: return new Date(runDateService.calculateRunDate(currentDate)
2499: .getTime());
2500: }
2501:
2502: /**
2503: * Sets the referenceLookup attribute value.
2504: *
2505: * @param referenceLookup The referenceLookup to set.
2506: */
2507: public void setReferenceLookup(
2508: OriginEntryLookupService referenceLookup) {
2509: this.referenceLookup.set(referenceLookup);
2510: this.scrubberValidator.setReferenceLookup(referenceLookup);
2511: }
2512:
2513: }
|