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.kfs.KFSPropertyConstants.REFERENCE_NUMBER;
019: import static org.kuali.kfs.rules.AccountingDocumentRuleBaseConstants.ERROR_PATH.DOCUMENT_ERROR_PREFIX;
020:
021: import org.apache.commons.lang.StringUtils;
022: import org.kuali.core.datadictionary.BusinessObjectEntry;
023: import org.kuali.core.service.DataDictionaryService;
024: import org.kuali.core.util.GlobalVariables;
025: import org.kuali.kfs.KFSConstants;
026: import org.kuali.kfs.KFSKeyConstants;
027: import org.kuali.kfs.KFSPropertyConstants;
028: import org.kuali.kfs.bo.AccountingLine;
029: import org.kuali.kfs.bo.GeneralLedgerPendingEntry;
030: import org.kuali.kfs.bo.SourceAccountingLine;
031: import org.kuali.kfs.context.SpringContext;
032: import org.kuali.kfs.document.AccountingDocument;
033: import org.kuali.kfs.rules.AccountingDocumentRuleBase;
034:
035: /**
036: * Business rule(s) applicable to NonCheckDisbursement documents.
037: */
038: public class NonCheckDisbursementDocumentRule extends
039: AccountingDocumentRuleBase {
040:
041: /**
042: * Overrides to consider the object types.<br/>
043: * <p>
044: * Note: This <code>{@link org.kuali.core.document.Document}</code> is always balanced because it only has From: lines.
045: *
046: * @param financialDocument The document whose balance is being validated.
047: * @return Always returns true, because this type of document is always balanced.
048: *
049: * @see FinancialDocumentRuleBase#isDocumentBalanceValid(FinancialDocument)
050: */
051: @Override
052: protected boolean isDocumentBalanceValid(
053: AccountingDocument financialDocument) {
054: return true;
055: }
056:
057: /**
058: * This method performs business rule checks on the accounting line being added to the document to ensure the accounting line
059: * is valid and appropriate for the document. Currently, this method calls isRequiredReferenceFieldsValid()
060: * associated with the new accounting line.
061: *
062: * @param financialDocument The document the new line is being added to.
063: * @param accountingLine The new accounting line being added.
064: * @return True if the business rules all pass, false otherwise.
065: *
066: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#processCustomAddAccountingLineBusinessRules(org.kuali.core.document.FinancialDocument,
067: * org.kuali.core.bo.AccountingLine)
068: */
069: @Override
070: public boolean processCustomAddAccountingLineBusinessRules(
071: AccountingDocument document, AccountingLine accountingLine) {
072: boolean retval = true;
073: retval = super .processCustomAddAccountingLineBusinessRules(
074: document, accountingLine);
075: if (retval) {
076: retval = isRequiredReferenceFieldsValid(accountingLine);
077: }
078: return retval;
079: }
080:
081: /**
082: * This method determines if a given accounting line is a debit accounting line. This is done by calling
083: * IsDebitUtiles.isDebitConsideringNothingPositiveOnly().
084: *
085: * An IllegalStateException will be thrown if the accounting line passed in is not an expense,
086: * is an error correction with a positive dollar amount or is not an error correction and
087: * has a negative amount.
088: *
089: * @param transactionalDocument The document the accounting line being checked is located in.
090: * @param accountingLine The accounting line being analyzed.
091: * @return True if the accounting line given is a debit accounting line, false otherwise.
092: * @throws IllegalStateException Thrown if accounting line attributes are invalid.
093: *
094: * @see IsDebitUtils#isDebitConsideringNothingPositiveOnly(FinancialDocumentRuleBase, FinancialDocument, AccountingLine)
095: * @see org.kuali.core.rule.AccountingLineRule#isDebit(org.kuali.core.document.FinancialDocument,
096: * org.kuali.core.bo.AccountingLine)
097: */
098: public boolean isDebit(AccountingDocument financialDocument,
099: AccountingLine accountingLine) throws IllegalStateException {
100: return IsDebitUtils.isDebitConsideringNothingPositiveOnly(this ,
101: financialDocument, accountingLine);
102: }
103:
104: /**
105: * Overrides the parent to display correct error message for a single sided document.
106: *
107: * @param financialDocument The document to be routed.
108: * @return True if the document contains source accounting lines, false otherwise.
109: *
110: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#isSourceAccountingLinesRequiredNumberForRoutingMet(org.kuali.core.document.FinancialDocument)
111: */
112: @Override
113: protected boolean isSourceAccountingLinesRequiredNumberForRoutingMet(
114: AccountingDocument financialDocument) {
115: if (0 == financialDocument.getSourceAccountingLines().size()) {
116: GlobalVariables
117: .getErrorMap()
118: .putError(
119: DOCUMENT_ERROR_PREFIX
120: + KFSPropertyConstants.SOURCE_ACCOUNTING_LINES,
121: KFSKeyConstants.ERROR_DOCUMENT_SINGLE_SECTION_NO_ACCOUNTING_LINES);
122: return false;
123: } else {
124: return true;
125: }
126: }
127:
128: /**
129: * Overrides the parent to return true, because NonCheckDisbursement documents only use the SourceAccountingLines data
130: * structures. The list that holds TargetAccountingLines should be empty. This will be checked when the document is
131: * "routed" or submitted to post - it's called automatically by the parent's processRouteDocument method.
132: *
133: * @param financialDocument The document to be routed.
134: * @return This method always returns true.
135: *
136: * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#isTargetAccountingLinesRequiredNumberForRoutingMet(org.kuali.core.document.FinancialDocument)
137: */
138: @Override
139: protected boolean isTargetAccountingLinesRequiredNumberForRoutingMet(
140: AccountingDocument financialDocument) {
141: return true;
142: }
143:
144: /**
145: * This method sets attributes on the explicitly general ledger pending entry specific to NonCheckDisbursement documents.
146: * This includes setting the transaction ledger entry description and blanking out the reference financial document number,
147: * the reference financial system origin code and the reference financial document type code. These values must be
148: * nullified because they don't belong in general ledger pending entries and if they aren't null, the general error
149: * corrections won't post properly.
150: *
151: * @param financialDocument The document which contains the general ledger pending entry being modified.
152: * @param accountingLine The accounting line the explicit entry was generated from.
153: * @param explicitEntry The explicit entry being updated.
154: *
155: * @see FinancialDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(FinancialDocument, AccountingLine,
156: * GeneralLedgerPendingEntry)
157: */
158: protected void customizeExplicitGeneralLedgerPendingEntry(
159: AccountingDocument financialDocument,
160: AccountingLine accountingLine,
161: GeneralLedgerPendingEntry explicitEntry) {
162: explicitEntry
163: .setTransactionLedgerEntryDescription(buildTransactionLedgerEntryDescriptionUsingRefOriginAndRefDocNumber(
164: financialDocument, accountingLine));
165:
166: // Clearing fields that are already handled by the parent algorithm - we don't actually want
167: // these to copy over from the accounting lines b/c they don't belong in the GLPEs
168: // if they aren't nulled, then GECs fail to post
169: explicitEntry.setReferenceFinancialDocumentNumber(null);
170: explicitEntry.setReferenceFinancialSystemOriginationCode(null);
171: explicitEntry.setReferenceFinancialDocumentTypeCode(null);
172: }
173:
174: /**
175: * Builds an appropriately formatted string to be used for the <code>transactionLedgerEntryDescription</code>. It is built
176: * using information from the <code>{@link AccountingLine}</code>. Format is "01-12345: blah blah blah".
177: *
178: * @param financialDocument The document the description will be pulled from, if the accounting line description is blank.
179: * @param line The accounting line that will be used for populating the transaction ledger entry description.
180: * @return The description to be applied to the transaction ledger entry.
181: */
182: private String buildTransactionLedgerEntryDescriptionUsingRefOriginAndRefDocNumber(
183: AccountingDocument financialDocument, AccountingLine line) {
184: String description = "";
185: if (StringUtils.isBlank(line.getReferenceNumber())) {
186: throw new IllegalStateException(
187: "Reference Document Number is required and should be validated before this point.");
188: }
189:
190: description = KFSConstants.ORIGIN_CODE_KUALI + "-"
191: + line.getReferenceNumber();
192:
193: if (StringUtils.isNotBlank(line
194: .getFinancialDocumentLineDescription())) {
195: description += ": "
196: + line.getFinancialDocumentLineDescription();
197: } else {
198: description += ": "
199: + financialDocument.getDocumentHeader()
200: .getFinancialDocumentDescription();
201: }
202:
203: if (description.length() > GENERAL_LEDGER_PENDING_ENTRY_CODE.GLPE_DESCRIPTION_MAX_LENGTH) {
204: description = description
205: .substring(
206: 0,
207: GENERAL_LEDGER_PENDING_ENTRY_CODE.GLPE_DESCRIPTION_MAX_LENGTH - 3)
208: + "...";
209: }
210:
211: return description;
212: }
213:
214: /**
215: * This method checks that values exist in the reference fields that are required. The reference field that
216: * is required is the 'reference number' field.
217: *
218: * @param accountingLine The accounting line being validated.
219: * @return True if all of the required reference fields are valid, false otherwise.
220: */
221: private boolean isRequiredReferenceFieldsValid(
222: AccountingLine accountingLine) {
223: boolean valid = true;
224:
225: BusinessObjectEntry boe = SpringContext.getBean(
226: DataDictionaryService.class).getDataDictionary()
227: .getBusinessObjectEntry(
228: SourceAccountingLine.class.getName());
229: if (StringUtils.isEmpty(accountingLine.getReferenceNumber())) {
230: putRequiredPropertyError(boe, REFERENCE_NUMBER);
231: valid = false;
232: }
233: return valid;
234: }
235: }
|