001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.module.financial.rules;
017:
018: import static org.kuali.core.util.AssertionUtils.assertThat;
019: import static org.kuali.kfs.KFSConstants.BALANCE_TYPE_PRE_ENCUMBRANCE;
020: import static org.kuali.kfs.KFSPropertyConstants.REFERENCE_NUMBER;
021: import static org.kuali.kfs.KFSPropertyConstants.REVERSAL_DATE;
022: import static org.kuali.kfs.rules.AccountingDocumentRuleBaseConstants.ERROR_PATH.DOCUMENT_ERROR_PREFIX;
023:
024: import org.apache.commons.lang.StringUtils;
025: import org.kuali.core.datadictionary.BusinessObjectEntry;
026: import org.kuali.core.document.Document;
027: import org.kuali.core.service.DataDictionaryService;
028: import org.kuali.core.util.GlobalVariables;
029: import org.kuali.kfs.KFSConstants;
030: import org.kuali.kfs.KFSKeyConstants;
031: import org.kuali.kfs.bo.AccountingLine;
032: import org.kuali.kfs.bo.GeneralLedgerPendingEntry;
033: import org.kuali.kfs.bo.TargetAccountingLine;
034: import org.kuali.kfs.context.SpringContext;
035: import org.kuali.kfs.document.AccountingDocument;
036: import org.kuali.kfs.rules.AccountingDocumentRuleBase;
037: import org.kuali.kfs.rules.AccountingDocumentRuleUtil;
038: import org.kuali.kfs.service.HomeOriginationService;
039: import org.kuali.module.financial.document.PreEncumbranceDocument;
040:
041: /**
042: * Business rule(s) applicable to PreEncumbrance documents.
043: */
044: public class PreEncumbranceDocumentRule extends
045: AccountingDocumentRuleBase {
046:
047: /**
048: * This method performs business rule checks on the accounting line being added to the document to ensure the accounting line
049: * is valid and appropriate for the document. Currently, this method calls isRequiredReferenceFieldsValid()
050: * associated with the new accounting line.
051: *
052: * @param financialDocument The document the new line is being added to.
053: * @param accountingLine The new accounting line being added.
054: * @return True if the business rules all pass, false otherwise.
055: *
056: * @see FinancialDocumentRuleBase#processCustomAddAccountingLineBusinessRules(org.kuali.core.document.FinancialDocument,
057: * org.kuali.core.bo.AccountingLine)
058: */
059: @Override
060: public boolean processCustomAddAccountingLineBusinessRules(
061: AccountingDocument financialDocument,
062: AccountingLine accountingLine) {
063: return isRequiredReferenceFieldsValid(accountingLine);
064: }
065:
066: /**
067: * This method performs business rule checks on the accounting line being updated to the document to ensure the accounting line
068: * is valid and appropriate for the document. Currently, this method calls isRequiredReferenceFieldsValid()
069: * associated with the updated accounting line.
070: *
071: * @param transactionalDocument The document the accounting line being updated resides within.
072: * @param accountingLine The original accounting line.
073: * @param updatedAccoutingLine The updated version of the accounting line.
074: * @return True if the business rules all pass for the update, false otherwise.
075: *
076: * @see FinancialDocumentRuleBase#processCustomUpdateAccountingLineBusinessRules(org.kuali.core.document.FinancialDocument,
077: * org.kuali.core.bo.AccountingLine, org.kuali.core.bo.AccountingLine)
078: */
079: @Override
080: protected boolean processCustomUpdateAccountingLineBusinessRules(
081: AccountingDocument financialDocument,
082: AccountingLine originalAccountingLine,
083: AccountingLine updatedAccountingLine) {
084: return isRequiredReferenceFieldsValid(updatedAccountingLine);
085: }
086:
087: /**
088: * This method performs business rule checks on the accounting line being provided to ensure the accounting line
089: * is valid and appropriate for the document.
090: *
091: * @param transactionalDocument The document associated with the accounting line being validated.
092: * @param accountingLine The accounting line being validated.
093: * @return True if the business rules all pass, false otherwise.
094: *
095: * @see FinancialDocumentRuleBase#processCustomReviewAccountingLineBusinessRules(org.kuali.core.document.FinancialDocument,
096: * org.kuali.core.bo.AccountingLine)
097: */
098: @Override
099: protected boolean processCustomReviewAccountingLineBusinessRules(
100: AccountingDocument financialDocument,
101: AccountingLine accountingLine) {
102: return isRequiredReferenceFieldsValid(accountingLine);
103: }
104:
105: /**
106: * This method checks that there is a value in a disencumbrance line's reference number field. This cannot be
107: * done by the DataDictionary validation because not all documents require it. It does not validate the
108: * existence of the referenced document.
109: *
110: * @param accountingLine The accounting line the reference field is being retrieved from.
111: * @return True if the required external encumbrance reference field is valid, false otherwise.
112: */
113: private boolean isRequiredReferenceFieldsValid(
114: AccountingLine accountingLine) {
115: boolean valid = true;
116:
117: if (accountingLine.isTargetAccountingLine()) {
118: BusinessObjectEntry boe = SpringContext.getBean(
119: DataDictionaryService.class).getDataDictionary()
120: .getBusinessObjectEntry(
121: TargetAccountingLine.class.getName());
122: if (StringUtils
123: .isEmpty(accountingLine.getReferenceNumber())) {
124: putRequiredPropertyError(boe, REFERENCE_NUMBER);
125: valid = false;
126: }
127: }
128: return valid;
129: }
130:
131: /**
132: * This method performs custom route business rule checks on the document being routed. The rules include
133: * confirming that the reversal date is valid for routing.
134: *
135: * @param document The document being routed.
136: * @return True if all the business rules pass, false otherwise.
137: *
138: * @see org.kuali.kfs.rules.AccountingDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.core.document.Document)
139: */
140: @Override
141: protected boolean processCustomRouteDocumentBusinessRules(
142: Document document) {
143: boolean valid = isReversalDateValidForRouting((PreEncumbranceDocument) document);
144: // this short-circuiting is just because it's the current eDocs policy (altho I think it's user-unfriendly)
145: if (valid) {
146: // super is to call isAccountingLinesRequiredNumberForRoutingMet()
147: valid &= super
148: .processCustomRouteDocumentBusinessRules(document);
149: }
150: return valid;
151: }
152:
153: /**
154: * If a PreEncumbrance document has a reversal date, it must not be earlier than the current date to route.
155: *
156: * @param preEncumbranceDocument The document the reversal date is retrieved from.
157: * @return True if this document does not have a reversal date earlier than the current date, false otherwise.
158: */
159: private boolean isReversalDateValidForRouting(
160: PreEncumbranceDocument preEncumbranceDocument) {
161: java.sql.Date reversalDate = preEncumbranceDocument
162: .getReversalDate();
163: return AccountingDocumentRuleUtil.isValidReversalDate(
164: reversalDate, DOCUMENT_ERROR_PREFIX + REVERSAL_DATE);
165: }
166:
167: /**
168: * PreEncumbrance documents require at least one source and one target accounting line for routing.
169: *
170: * @param financialDocument The document being routed.
171: * @return True if the number of source and target accounting lines are both greater than zero, false otherwise.
172: *
173: * @see org.kuali.kfs.rules.AccountingDocumentRuleBase#isAccountingLinesRequiredNumberForRoutingMet(org.kuali.kfs.document.AccountingDocument)
174: */
175: @Override
176: protected boolean isAccountingLinesRequiredNumberForRoutingMet(
177: AccountingDocument financialDocument) {
178: if (0 == financialDocument.getSourceAccountingLines().size()
179: && 0 == financialDocument.getTargetAccountingLines()
180: .size()) {
181: GlobalVariables.getErrorMap().putError(
182: KFSConstants.ACCOUNTING_LINE_ERRORS,
183: KFSKeyConstants.ERROR_DOCUMENT_NO_ACCOUNTING_LINES);
184: return false;
185: } else {
186: return true;
187: }
188: }
189:
190: /**
191: * PreEncumbrance documents don't need to balance in any way.
192: *
193: * @param financialDocument The document whose bablance is being validated.
194: * @return Always returns true.
195: *
196: * @see FinancialDocumentRuleBase#isDocumentBalanceValid(org.kuali.core.document.FinancialDocument)
197: */
198: @Override
199: protected boolean isDocumentBalanceValid(
200: AccountingDocument financialDocument) {
201: return true;
202: }
203:
204: /**
205: * This method contains PreEncumbrance document specific general ledger pending entry explicit entry
206: * attribute assignments. These attributes include financial balance type code, reversal date and
207: * transaction encumbrance update code.
208: *
209: * @param financialDocument The document which contains the explicit entry.
210: * @param accountingLine The accounting line the explicit entry is generated from.
211: * @param explicitEntry The explicit entry being updated.
212: *
213: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(org.kuali.core.document.FinancialDocument,
214: * org.kuali.core.bo.AccountingLine, org.kuali.module.gl.bo.GeneralLedgerPendingEntry)
215: */
216: protected void customizeExplicitGeneralLedgerPendingEntry(
217: AccountingDocument financialDocument,
218: AccountingLine accountingLine,
219: GeneralLedgerPendingEntry explicitEntry) {
220: explicitEntry
221: .setFinancialBalanceTypeCode(BALANCE_TYPE_PRE_ENCUMBRANCE);
222:
223: // set the reversal date to what was chosen by the user in the interface
224: PreEncumbranceDocument peDoc = (PreEncumbranceDocument) financialDocument;
225: if (peDoc.getReversalDate() != null) {
226: explicitEntry.setFinancialDocumentReversalDate(peDoc
227: .getReversalDate());
228: }
229: explicitEntry.setTransactionEntryProcessedTs(null);
230: if (accountingLine.isSourceAccountingLine()) {
231: explicitEntry
232: .setTransactionEncumbranceUpdateCode(KFSConstants.ENCUMB_UPDT_DOCUMENT_CD);
233: } else {
234: assertThat(accountingLine.isTargetAccountingLine(),
235: accountingLine);
236: explicitEntry
237: .setTransactionEncumbranceUpdateCode(KFSConstants.ENCUMB_UPDT_REFERENCE_DOCUMENT_CD);
238: explicitEntry
239: .setReferenceFinancialSystemOriginationCode(SpringContext
240: .getBean(HomeOriginationService.class)
241: .getHomeOrigination()
242: .getFinSystemHomeOriginationCode());
243: explicitEntry
244: .setReferenceFinancialDocumentNumber(accountingLine
245: .getReferenceNumber());
246: explicitEntry
247: .setReferenceFinancialDocumentTypeCode(explicitEntry
248: .getFinancialDocumentTypeCode()); // "PE"
249: }
250: }
251:
252: /**
253: * This method limits valid debits to only expense object type codes. Additionally, an
254: * IllegalStateException will be thrown if the accounting line passed in is not an expense,
255: * is an error correction with a positive dollar amount or is not an error correction and
256: * has a negative amount.
257: *
258: * @param transactionalDocument The document the accounting line being checked is located in.
259: * @param accountingLine The accounting line being analyzed.
260: * @return True if the accounting line given is a debit accounting line, false otherwise.
261: *
262: * @see IsDebitUtils#isDebitConsideringSection(FinancialDocumentRuleBase, FinancialDocument, AccountingLine)
263: * @see org.kuali.core.rule.AccountingLineRule#isDebit(org.kuali.core.document.FinancialDocument,
264: * org.kuali.core.bo.AccountingLine)
265: */
266: public boolean isDebit(AccountingDocument financialDocument,
267: AccountingLine accountingLine) {
268: // if not expense, or positive amount on an error-correction, or negative amount on a non-error-correction, throw exception
269: if (!isExpense(accountingLine)
270: || (isErrorCorrection(financialDocument) == accountingLine
271: .getAmount().isPositive())) {
272: throw new IllegalStateException(
273: IsDebitUtils.isDebitCalculationIllegalStateExceptionMessage);
274: }
275:
276: return !IsDebitUtils.isDebitConsideringSection(this,
277: financialDocument, accountingLine);
278: }
279: }
|