001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.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/ecl2.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.sys.document.service.impl;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.apache.log4j.Logger;
020    import org.kuali.kfs.sys.KFSConstants;
021    import org.kuali.kfs.sys.businessobject.AccountingLine;
022    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
023    import org.kuali.kfs.sys.document.AccountingDocument;
024    import org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource;
025    import org.kuali.kfs.sys.document.service.AccountingDocumentRuleHelperService;
026    import org.kuali.kfs.sys.document.service.DebitDeterminerService;
027    import org.kuali.kfs.sys.service.OptionsService;
028    import org.kuali.rice.kns.util.KualiDecimal;
029    
030    /**
031     * Default implementation of the DebitDeterminerService.
032     */
033    public class DebitDeterminerServiceImpl implements DebitDeterminerService {
034        private static Logger LOG = Logger.getLogger(DebitDeterminerServiceImpl.class);
035        protected static final String isDebitCalculationIllegalStateExceptionMessage = "an invalid debit/credit check state was detected";
036        protected static final String isErrorCorrectionIllegalStateExceptionMessage = "invalid (error correction) document not allowed";
037        protected static final String isInvalidLineTypeIllegalArgumentExceptionMessage = "invalid accounting line type";
038        
039        private AccountingDocumentRuleHelperService accountingDocumentRuleUtil;
040        private OptionsService optionsService;
041    
042        /**
043         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#disallowErrorCorrectionDocumentCheck(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource)
044         */
045        public void disallowErrorCorrectionDocumentCheck(GeneralLedgerPendingEntrySource poster) {
046            LOG.debug("disallowErrorCorrectionDocumentCheck(AccountingDocumentRuleBase, AccountingDocument) - start");
047    
048            if (isErrorCorrection(poster)) {
049                throw new IllegalStateException(isErrorCorrectionIllegalStateExceptionMessage);
050            }
051    
052            LOG.debug("disallowErrorCorrectionDocumentCheck(AccountingDocumentRuleBase, AccountingDocument) - end");
053        }
054    
055        /**
056         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isAsset(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
057         */
058        public boolean isAsset(GeneralLedgerPendingEntrySourceDetail postable) {
059            LOG.debug("isAsset(AccountingLine) - start");
060    
061            boolean returnboolean = isAssetTypeCode(accountingDocumentRuleUtil.getObjectCodeTypeCodeWithoutSideEffects(postable));
062            LOG.debug("isAsset(AccountingLine) - end");
063            return returnboolean;
064        }
065    
066        /**
067         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isAssetTypeCode(java.lang.String)
068         */
069        public boolean isAssetTypeCode(String objectTypeCode) {
070            LOG.debug("isAssetTypeCode(String) - start");
071    
072            boolean returnboolean = optionsService.getCurrentYearOptions().getFinancialObjectTypeAssetsCd().equals(objectTypeCode);
073            LOG.debug("isAssetTypeCode(String) - end");
074            return returnboolean;
075        }
076    
077        /**
078         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitCode(java.lang.String)
079         */
080        public boolean isDebitCode(String debitCreditCode) {
081            LOG.debug("isDebitCode(String) - start");
082    
083            boolean returnboolean = StringUtils.equals(KFSConstants.GL_DEBIT_CODE, debitCreditCode);
084            LOG.debug("isDebitCode(String) - end");
085            return returnboolean;
086        }
087    
088        /**
089         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitConsideringNothingPositiveOnly(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
090         */
091        public boolean isDebitConsideringNothingPositiveOnly(GeneralLedgerPendingEntrySource poster, GeneralLedgerPendingEntrySourceDetail postable) {
092            LOG.debug("isDebitConsideringNothingPositiveOnly(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - start");
093    
094            boolean isDebit = false;
095            KualiDecimal amount = postable.getAmount();
096            boolean isPositiveAmount = amount.isPositive();
097            // isDebit if income/liability/expense/asset and line amount is positive
098            if (isPositiveAmount && (isIncomeOrLiability(postable) || isExpenseOrAsset(postable))) {
099                isDebit = true;
100            }
101            else {
102                // non error correction
103                if (!isErrorCorrection(poster)) {
104                    throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage);
105    
106                }
107                // error correction
108                else {
109                    isDebit = false;
110                }
111            }
112    
113            LOG.debug("isDebitConsideringNothingPositiveOnly(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - end");
114            return isDebit;
115        }
116    
117        /**
118         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitConsideringSection(org.kuali.kfs.sys.document.AccountingDocument, org.kuali.kfs.sys.businessobject.AccountingLine)
119         */
120        public boolean isDebitConsideringSection(AccountingDocument accountingDocument, AccountingLine accountingLine) {
121            LOG.debug("isDebitConsideringSection(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - start");
122    
123            KualiDecimal amount = accountingLine.getAmount();
124            // zero amounts are not allowed
125            if (amount.isZero()) {
126                throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage);
127            }
128            boolean isDebit = false;
129            boolean isPositiveAmount = accountingLine.getAmount().isPositive();
130            // source line
131            if (accountingLine.isSourceAccountingLine()) {
132                // income/liability/expense/asset
133                if (isIncomeOrLiability(accountingLine) || isExpenseOrAsset(accountingLine)) {
134                    isDebit = !isPositiveAmount;
135                }
136                else {
137                    throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage);
138                }
139            }
140            // target line
141            else {
142                if (accountingLine.isTargetAccountingLine()) {
143                    if (isIncomeOrLiability(accountingLine) || isExpenseOrAsset(accountingLine)) {
144                        isDebit = isPositiveAmount;
145                    }
146                    else {
147                        throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage);
148                    }
149                }
150                else {
151                    throw new IllegalArgumentException(isInvalidLineTypeIllegalArgumentExceptionMessage);
152                }
153            }
154    
155            LOG.debug("isDebitConsideringSection(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - end");
156            return isDebit;
157        }
158    
159        /**
160         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitConsideringSectionAndTypePositiveOnly(org.kuali.kfs.sys.document.AccountingDocument, org.kuali.kfs.sys.businessobject.AccountingLine)
161         */
162        public boolean isDebitConsideringSectionAndTypePositiveOnly(AccountingDocument accountingDocument, AccountingLine accountingLine) {
163            LOG.debug("isDebitConsideringSectionAndTypePositiveOnly(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - start");
164    
165            boolean isDebit = false;
166            KualiDecimal amount = accountingLine.getAmount();
167            boolean isPositiveAmount = amount.isPositive();
168            // non error correction - only allow amount >0
169            if (!isPositiveAmount && !isErrorCorrection(accountingDocument)) {
170                throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage);
171            }
172            // source line
173            if (accountingLine.isSourceAccountingLine()) {
174                // could write below block in one line using == as XNOR operator, but that's confusing to read:
175                // isDebit = (rule.isIncomeOrLiability(accountingLine) == isPositiveAmount);
176                if (isPositiveAmount) {
177                    isDebit = isIncomeOrLiability(accountingLine);
178                }
179                else {
180                    isDebit = isExpenseOrAsset(accountingLine);
181                }
182            }
183            // target line
184            else {
185                if (accountingLine.isTargetAccountingLine()) {
186                    if (isPositiveAmount) {
187                        isDebit = isExpenseOrAsset(accountingLine);
188                    }
189                    else {
190                        isDebit = isIncomeOrLiability(accountingLine);
191                    }
192                }
193                else {
194                    throw new IllegalArgumentException(isInvalidLineTypeIllegalArgumentExceptionMessage);
195                }
196            }
197    
198            LOG.debug("isDebitConsideringSectionAndTypePositiveOnly(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - end");
199            return isDebit;
200        }
201    
202        /**
203         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitConsideringType(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
204         */
205        public boolean isDebitConsideringType(GeneralLedgerPendingEntrySource poster, GeneralLedgerPendingEntrySourceDetail postable) {
206            LOG.debug("isDebitConsideringType(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - start");
207    
208            KualiDecimal amount = postable.getAmount();
209            // zero amounts are not allowed
210            if (amount.isZero()) {
211                throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage);
212            }
213            boolean isDebit = false;
214            boolean isPositiveAmount = postable.getAmount().isPositive();
215    
216            // income/liability
217            if (isIncomeOrLiability(postable)) {
218                isDebit = !isPositiveAmount;
219            }
220            // expense/asset
221            else {
222                if (isExpenseOrAsset(postable)) {
223                    isDebit = isPositiveAmount;
224                }
225                else {
226                    throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage);
227                }
228            }
229    
230            LOG.debug("isDebitConsideringType(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - end");
231            return isDebit;
232        }
233    
234        /**
235         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isErrorCorrection(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource)
236         */
237        public boolean isErrorCorrection(GeneralLedgerPendingEntrySource poster) {
238            return StringUtils.isNotBlank(poster.getDocumentHeader().getFinancialDocumentInErrorNumber());
239        }
240    
241        /**
242         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isExpense(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
243         */
244        public boolean isExpense(GeneralLedgerPendingEntrySourceDetail postable) {
245            LOG.debug("isExpense(AccountingLine) - start");
246    
247            boolean returnboolean = accountingDocumentRuleUtil.isExpense(postable);
248            LOG.debug("isExpense(AccountingLine) - end");
249            return returnboolean;
250        }
251    
252        /**
253         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isExpenseOrAsset(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
254         */
255        public boolean isExpenseOrAsset(GeneralLedgerPendingEntrySourceDetail postable) {
256            LOG.debug("isExpenseOrAsset(AccountingLine) - start");
257    
258            boolean returnboolean = isAsset(postable) || isExpense(postable);
259            LOG.debug("isExpenseOrAsset(AccountingLine) - end");
260            return returnboolean;
261        }
262    
263        /**
264         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isIncome(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
265         */
266        public boolean isIncome(GeneralLedgerPendingEntrySourceDetail postable) {
267            LOG.debug("isIncome(AccountingLine) - start");
268    
269            boolean returnboolean = accountingDocumentRuleUtil.isIncome(postable);
270            LOG.debug("isIncome(AccountingLine) - end");
271            return returnboolean;
272        }
273    
274        /**
275         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isIncomeOrLiability(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
276         */
277        public boolean isIncomeOrLiability(GeneralLedgerPendingEntrySourceDetail postable) {
278            LOG.debug("isIncomeOrLiability(AccountingLine) - start");
279    
280            boolean returnboolean = isLiability(postable) || isIncome(postable);
281            LOG.debug("isIncomeOrLiability(AccountingLine) - end");
282            return returnboolean;
283        }
284    
285        /**
286         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isLiability(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
287         */
288        public boolean isLiability(GeneralLedgerPendingEntrySourceDetail postable) {
289            LOG.debug("isLiability(AccountingLine) - start");
290    
291            boolean returnboolean = isLiabilityTypeCode(accountingDocumentRuleUtil.getObjectCodeTypeCodeWithoutSideEffects(postable));
292            LOG.debug("isLiability(AccountingLine) - end");
293            return returnboolean;
294        }
295    
296        /**
297         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isLiabilityTypeCode(java.lang.String)
298         */
299        public boolean isLiabilityTypeCode(String objectTypeCode) {
300            LOG.debug("isLiabilityTypeCode(String) - start");
301    
302            boolean returnboolean = optionsService.getCurrentYearOptions().getFinObjectTypeLiabilitiesCode().equals(objectTypeCode);
303            LOG.debug("isLiabilityTypeCode(String) - end");
304            return returnboolean;
305        }
306    
307        /**
308         * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isRevenue(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail)
309         */
310        public boolean isRevenue(GeneralLedgerPendingEntrySourceDetail postable) {
311            LOG.debug("isRevenue(AccountingLine) - start");
312    
313            boolean returnboolean = !isExpense(postable);
314            LOG.debug("isRevenue(AccountingLine) - end");
315            return returnboolean;
316        }
317    
318        /**
319         * Sets the accountingDocumentRuleUtils attribute value.
320         * @param accountingDocumentRuleUtils The accountingDocumentRuleUtils to set.
321         */
322        public void setAccountingDocumentRuleUtils(AccountingDocumentRuleHelperService accountingDocumentRuleUtil) {
323            this.accountingDocumentRuleUtil = accountingDocumentRuleUtil;
324        }
325    
326        /**
327         * Sets the optionsService attribute value.
328         * @param optionsService The optionsService to set.
329         */
330        public void setOptionsService(OptionsService optionsService) {
331            this.optionsService = optionsService;
332        }
333    
334        /**
335         * Gets the isDebitCalculationIllegalStateExceptionMessage attribute. 
336         * @return Returns the isDebitCalculationIllegalStateExceptionMessage.
337         */
338        public String getDebitCalculationIllegalStateExceptionMessage() {
339            return isDebitCalculationIllegalStateExceptionMessage;
340        }
341    
342        /**
343         * Gets the isErrorCorrectionIllegalStateExceptionMessage attribute. 
344         * @return Returns the isErrorCorrectionIllegalStateExceptionMessage.
345         */
346        public String getErrorCorrectionIllegalStateExceptionMessage() {
347            return isErrorCorrectionIllegalStateExceptionMessage;
348        }
349    
350        /**
351         * Gets the isInvalidLineTypeIllegalArgumentExceptionMessage attribute. 
352         * @return Returns the isInvalidLineTypeIllegalArgumentExceptionMessage.
353         */
354        public String getInvalidLineTypeIllegalArgumentExceptionMessage() {
355            return isInvalidLineTypeIllegalArgumentExceptionMessage;
356        }
357    
358        
359    }