001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.kfs.rules;
017:
018: import org.apache.commons.lang.StringUtils;
019: import org.apache.log4j.Logger;
020: import org.kuali.core.exceptions.ReferentialIntegrityException;
021: import org.kuali.core.rules.LedgerPostingDocumentRuleBase;
022: import org.kuali.core.util.GeneralLedgerPendingEntrySequenceHelper;
023: import org.kuali.core.util.GlobalVariables;
024: import org.kuali.core.util.ObjectUtils;
025: import org.kuali.kfs.KFSConstants;
026: import org.kuali.kfs.KFSKeyConstants;
027: import org.kuali.kfs.KFSPropertyConstants;
028: import org.kuali.kfs.bo.GeneralLedgerPendingEntry;
029: import org.kuali.kfs.context.SpringContext;
030: import org.kuali.module.chart.bo.ObjectCode;
031: import org.kuali.module.chart.bo.OffsetDefinition;
032: import org.kuali.module.chart.service.OffsetDefinitionService;
033: import org.kuali.module.financial.bo.OffsetAccount;
034: import org.kuali.module.financial.service.FlexibleOffsetAccountService;
035: import org.kuali.module.gl.service.SufficientFundsService;
036:
037: /**
038: * This class contains a helper method used to implement a rule for the CashManagementDocument (a FinancialDocument) as well as to
039: * implement rules for TransactionalDocuments.
040: */
041: public class GeneralLedgerPostingDocumentRuleBase extends
042: LedgerPostingDocumentRuleBase {
043: /**
044: * Logger for this class
045: */
046: private static final Logger LOG = Logger
047: .getLogger(GeneralLedgerPostingDocumentRuleBase.class);
048:
049: /**
050: * This populates an GeneralLedgerPendingEntry offsetEntry object instance with values that differ from the values supplied in
051: * the explicit entry that it was cloned from. Note that the entries do not contain BOs now.
052: *
053: * @param universityFiscalYear
054: * @param explicitEntry
055: * @param sequenceHelper
056: * @param offsetEntry Cloned from the explicit entry
057: * @return whether the offset generation is successful
058: */
059: protected boolean populateOffsetGeneralLedgerPendingEntry(
060: Integer universityFiscalYear,
061: GeneralLedgerPendingEntry explicitEntry,
062: GeneralLedgerPendingEntrySequenceHelper sequenceHelper,
063: GeneralLedgerPendingEntry offsetEntry) {
064: LOG
065: .debug("populateOffsetGeneralLedgerPendingEntry(Integer, GeneralLedgerPendingEntry, GeneralLedgerPendingEntrySequenceHelper, GeneralLedgerPendingEntry) - start");
066:
067: boolean success = true;
068:
069: // lookup offset object info
070: OffsetDefinition offsetDefinition = SpringContext.getBean(
071: OffsetDefinitionService.class).getByPrimaryId(
072: universityFiscalYear,
073: explicitEntry.getChartOfAccountsCode(),
074: explicitEntry.getFinancialDocumentTypeCode(),
075: explicitEntry.getFinancialBalanceTypeCode());
076: if (ObjectUtils.isNull(offsetDefinition)) {
077: success = false;
078: GlobalVariables
079: .getErrorMap()
080: .putError(
081: KFSConstants.GENERAL_LEDGER_PENDING_ENTRIES_TAB_ERRORS,
082: KFSKeyConstants.ERROR_DOCUMENT_NO_OFFSET_DEFINITION,
083: universityFiscalYear.toString(),
084: explicitEntry.getChartOfAccountsCode(),
085: explicitEntry
086: .getFinancialDocumentTypeCode(),
087: explicitEntry.getFinancialBalanceTypeCode());
088: } else {
089: OffsetAccount flexibleOffsetAccount = SpringContext
090: .getBean(FlexibleOffsetAccountService.class)
091: .getByPrimaryIdIfEnabled(
092: explicitEntry.getChartOfAccountsCode(),
093: explicitEntry.getAccountNumber(),
094: getOffsetFinancialObjectCode(offsetDefinition));
095: flexOffsetAccountIfNecessary(flexibleOffsetAccount,
096: offsetEntry);
097: }
098:
099: // update offset entry fields that are different from the explicit entry that it was created from
100: offsetEntry
101: .setTransactionLedgerEntrySequenceNumber(new Integer(
102: sequenceHelper.getSequenceCounter()));
103: offsetEntry
104: .setTransactionDebitCreditCode(getOffsetEntryDebitCreditCode(explicitEntry));
105:
106: String offsetObjectCode = getOffsetFinancialObjectCode(offsetDefinition);
107: offsetEntry.setFinancialObjectCode(offsetObjectCode);
108: if (offsetObjectCode
109: .equals(AccountingDocumentRuleBaseConstants.GENERAL_LEDGER_PENDING_ENTRY_CODE
110: .getBlankFinancialObjectCode())) {
111: // no BO, so punt
112: offsetEntry
113: .setAcctSufficientFundsFinObjCd(AccountingDocumentRuleBaseConstants.GENERAL_LEDGER_PENDING_ENTRY_CODE
114: .getBlankFinancialObjectCode());
115: } else {
116: // Need current ObjectCode and Account BOs to get sufficient funds code. (Entries originally have no BOs.)
117: // todo: private or other methods to get these BOs, instead of using the entry and leaving some BOs filled in?
118: offsetEntry
119: .refreshReferenceObject(KFSPropertyConstants.FINANCIAL_OBJECT);
120: offsetEntry
121: .refreshReferenceObject(KFSPropertyConstants.ACCOUNT);
122: ObjectCode financialObject = offsetEntry
123: .getFinancialObject();
124: // The ObjectCode reference may be invalid because a flexible offset account changed its chart code.
125: if (ObjectUtils.isNull(financialObject)) {
126: throw new ReferentialIntegrityException(
127: "offset object code "
128: + offsetEntry.getUniversityFiscalYear()
129: + "-"
130: + offsetEntry.getChartOfAccountsCode()
131: + "-"
132: + offsetEntry.getFinancialObjectCode());
133: }
134: offsetEntry.setAcctSufficientFundsFinObjCd(SpringContext
135: .getBean(SufficientFundsService.class)
136: .getSufficientFundsObjectCode(
137: financialObject,
138: offsetEntry.getAccount()
139: .getAccountSufficientFundsCode()));
140: }
141:
142: offsetEntry
143: .setFinancialObjectTypeCode(getOffsetFinancialObjectTypeCode(offsetDefinition));
144: offsetEntry.setFinancialSubObjectCode(KFSConstants
145: .getDashFinancialSubObjectCode());
146: offsetEntry.setTransactionEntryOffsetIndicator(true);
147: offsetEntry
148: .setTransactionLedgerEntryDescription(KFSConstants.GL_PE_OFFSET_STRING);
149:
150: LOG
151: .debug("populateOffsetGeneralLedgerPendingEntry(Integer, GeneralLedgerPendingEntry, GeneralLedgerPendingEntrySequenceHelper, GeneralLedgerPendingEntry) - end");
152: return success;
153: }
154:
155: /**
156: * Applies the given flexible offset account to the given offset entry. Does nothing if flexibleOffsetAccount is null or its COA
157: * and account number are the same as the offset entry's.
158: *
159: * @param flexibleOffsetAccount may be null
160: * @param offsetEntry may be modified
161: */
162: private static void flexOffsetAccountIfNecessary(
163: OffsetAccount flexibleOffsetAccount,
164: GeneralLedgerPendingEntry offsetEntry) {
165: LOG
166: .debug("flexOffsetAccountIfNecessary(OffsetAccount, GeneralLedgerPendingEntry) - start");
167:
168: if (flexibleOffsetAccount == null) {
169: LOG
170: .debug("flexOffsetAccountIfNecessary(OffsetAccount, GeneralLedgerPendingEntry) - end");
171: return; // They are not required and may also be disabled.
172: }
173: String flexCoa = flexibleOffsetAccount
174: .getFinancialOffsetChartOfAccountCode();
175: String flexAccountNumber = flexibleOffsetAccount
176: .getFinancialOffsetAccountNumber();
177: if (flexCoa.equals(offsetEntry.getChartOfAccountsCode())
178: && flexAccountNumber.equals(offsetEntry
179: .getAccountNumber())) {
180: LOG
181: .debug("flexOffsetAccountIfNecessary(OffsetAccount, GeneralLedgerPendingEntry) - end");
182: return; // no change, so leave sub-account as is
183: }
184: if (ObjectUtils.isNull(flexibleOffsetAccount
185: .getFinancialOffsetAccount())) {
186: throw new ReferentialIntegrityException(
187: "flexible offset account " + flexCoa + "-"
188: + flexAccountNumber);
189: }
190: offsetEntry.setChartOfAccountsCode(flexCoa);
191: offsetEntry.setAccountNumber(flexAccountNumber);
192: // COA and account number are part of the sub-account's key, so the original sub-account would be invalid.
193: offsetEntry.setSubAccountNumber(KFSConstants
194: .getDashSubAccountNumber());
195:
196: LOG
197: .debug("flexOffsetAccountIfNecessary(OffsetAccount, GeneralLedgerPendingEntry) - end");
198: }
199:
200: /**
201: * Helper method that determines the offset entry's financial object code.
202: *
203: * @param offsetDefinition
204: * @return String
205: */
206: protected String getOffsetFinancialObjectCode(
207: OffsetDefinition offsetDefinition) {
208: LOG
209: .debug("getOffsetFinancialObjectCode(OffsetDefinition) - start");
210:
211: if (null != offsetDefinition) {
212: String returnString = getEntryValue(
213: offsetDefinition.getFinancialObjectCode(),
214: AccountingDocumentRuleBaseConstants.GENERAL_LEDGER_PENDING_ENTRY_CODE
215: .getBlankFinancialObjectCode());
216: LOG
217: .debug("getOffsetFinancialObjectCode(OffsetDefinition) - end");
218: return returnString;
219: } else {
220: LOG
221: .debug("getOffsetFinancialObjectCode(OffsetDefinition) - end");
222: return AccountingDocumentRuleBaseConstants.GENERAL_LEDGER_PENDING_ENTRY_CODE
223: .getBlankFinancialObjectCode();
224: }
225:
226: }
227:
228: /**
229: * Helper method that determines the offset entry's financial object type code.
230: *
231: * @param offsetDefinition
232: * @return String
233: */
234: protected String getOffsetFinancialObjectTypeCode(
235: OffsetDefinition offsetDefinition) {
236: LOG
237: .debug("getOffsetFinancialObjectTypeCode(OffsetDefinition) - start");
238:
239: if (null != offsetDefinition
240: && null != offsetDefinition.getFinancialObject()) {
241: String returnString = getEntryValue(
242: offsetDefinition.getFinancialObject()
243: .getFinancialObjectTypeCode(),
244: AccountingDocumentRuleBaseConstants.GENERAL_LEDGER_PENDING_ENTRY_CODE
245: .getBlankFinancialObjectType());
246: LOG
247: .debug("getOffsetFinancialObjectTypeCode(OffsetDefinition) - end");
248: return returnString;
249: } else {
250: LOG
251: .debug("getOffsetFinancialObjectTypeCode(OffsetDefinition) - end");
252: return AccountingDocumentRuleBaseConstants.GENERAL_LEDGER_PENDING_ENTRY_CODE
253: .getBlankFinancialObjectType();
254: }
255:
256: }
257:
258: /**
259: * Helper method that determines the debit/credit code for the offset entry. If the explicit was a debit, the offset is a
260: * credit. Otherwise, it's opposite.
261: *
262: * @param explicitEntry
263: * @return String
264: */
265: protected String getOffsetEntryDebitCreditCode(
266: GeneralLedgerPendingEntry explicitEntry) {
267: LOG
268: .debug("getOffsetEntryDebitCreditCode(GeneralLedgerPendingEntry) - start");
269:
270: String offsetDebitCreditCode = KFSConstants.GL_BUDGET_CODE;
271: if (KFSConstants.GL_DEBIT_CODE.equals(explicitEntry
272: .getTransactionDebitCreditCode())) {
273: offsetDebitCreditCode = KFSConstants.GL_CREDIT_CODE;
274: } else if (KFSConstants.GL_CREDIT_CODE.equals(explicitEntry
275: .getTransactionDebitCreditCode())) {
276: offsetDebitCreditCode = KFSConstants.GL_DEBIT_CODE;
277: }
278:
279: LOG
280: .debug("getOffsetEntryDebitCreditCode(GeneralLedgerPendingEntry) - end");
281: return offsetDebitCreditCode;
282: }
283:
284: /**
285: * A helper method that checks the intended target value for null and empty strings. If the intended target value is not null or
286: * an empty string, it returns that value, ohterwise, it returns a backup value.
287: *
288: * @param targetValue
289: * @param backupValue
290: * @return String
291: */
292: protected final String getEntryValue(String targetValue,
293: String backupValue) {
294: LOG.debug("getEntryValue(String, String) - start");
295:
296: if (StringUtils.isNotBlank(targetValue)) {
297: LOG.debug("getEntryValue(String, String) - end");
298: return targetValue;
299: } else {
300: LOG.debug("getEntryValue(String, String) - end");
301: return backupValue;
302: }
303: }
304: }
|