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.fp.document.validation.impl;
017    
018    import static org.kuali.kfs.sys.KFSConstants.AMOUNT_PROPERTY_NAME;
019    import static org.kuali.kfs.sys.KFSConstants.BALANCE_TYPE_BASE_BUDGET;
020    import static org.kuali.kfs.sys.KFSConstants.BALANCE_TYPE_CURRENT_BUDGET;
021    import static org.kuali.kfs.sys.KFSConstants.BALANCE_TYPE_MONTHLY_BUDGET;
022    import static org.kuali.kfs.sys.KFSConstants.CREDIT_AMOUNT_PROPERTY_NAME;
023    import static org.kuali.kfs.sys.KFSConstants.DEBIT_AMOUNT_PROPERTY_NAME;
024    import static org.kuali.kfs.sys.KFSConstants.GL_DEBIT_CODE;
025    import static org.kuali.kfs.sys.KFSConstants.JOURNAL_LINE_HELPER_PROPERTY_NAME;
026    import static org.kuali.kfs.sys.KFSConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME;
027    import static org.kuali.kfs.sys.KFSConstants.SQUARE_BRACKET_LEFT;
028    import static org.kuali.kfs.sys.KFSConstants.SQUARE_BRACKET_RIGHT;
029    import static org.kuali.kfs.sys.KFSConstants.VOUCHER_LINE_HELPER_CREDIT_PROPERTY_NAME;
030    import static org.kuali.kfs.sys.KFSConstants.VOUCHER_LINE_HELPER_DEBIT_PROPERTY_NAME;
031    import static org.kuali.kfs.sys.KFSKeyConstants.ERROR_ZERO_AMOUNT;
032    import static org.kuali.kfs.sys.KFSKeyConstants.ERROR_ZERO_OR_NEGATIVE_AMOUNT;
033    import static org.kuali.kfs.sys.KFSKeyConstants.JournalVoucher.ERROR_NEGATIVE_NON_BUDGET_AMOUNTS;
034    import static org.kuali.kfs.sys.KFSPropertyConstants.BALANCE_TYPE;
035    
036    import org.apache.commons.lang.StringUtils;
037    import org.kuali.kfs.fp.document.JournalVoucherDocument;
038    import org.kuali.kfs.sys.businessobject.AccountingLine;
039    import org.kuali.kfs.sys.document.validation.GenericValidation;
040    import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
041    import org.kuali.rice.kns.util.GlobalVariables;
042    import org.kuali.rice.kns.util.KualiDecimal;
043    
044    /**
045     * The Journal Voucher's version of the accounting line amount validation
046     */
047    public class JournalVoucherAccountingLineAmountValidation extends GenericValidation {
048        private JournalVoucherDocument journalVoucherForValidation;
049        private AccountingLine accountingLineForValidation;
050    
051        /**
052         * Accounting lines for Journal Vouchers can be positive or negative, just not "$0.00".  
053         * 
054         * Additionally, accounting lines cannot have negative dollar amounts if the balance type of the 
055         * journal voucher allows for general ledger pending entry offset generation or the balance type 
056         * is not a budget type code.
057         * @see org.kuali.kfs.sys.document.validation.Validation#validate(org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent)
058         */
059        public boolean validate(AttributedDocumentEvent event) {
060            KualiDecimal amount = getAccountingLineForValidation().getAmount();
061    
062            getJournalVoucherForValidation().refreshReferenceObject(BALANCE_TYPE);
063    
064            if (getJournalVoucherForValidation().getBalanceType().isFinancialOffsetGenerationIndicator()) {
065                // check for negative or zero amounts
066                if (amount.isZero()) { // if 0
067                    GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(buildErrorMapKeyPathForDebitCreditAmount(true), ERROR_ZERO_OR_NEGATIVE_AMOUNT, "an accounting line");
068                    GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(buildErrorMapKeyPathForDebitCreditAmount(false), ERROR_ZERO_OR_NEGATIVE_AMOUNT, "an accounting line");
069    
070                    return false;
071                }
072                else if (amount.isNegative()) { // entered a negative number
073                    String debitCreditCode = getAccountingLineForValidation().getDebitCreditCode();
074                    if (StringUtils.isNotBlank(debitCreditCode) && GL_DEBIT_CODE.equals(debitCreditCode)) {
075                        GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(buildErrorMapKeyPathForDebitCreditAmount(true), ERROR_ZERO_OR_NEGATIVE_AMOUNT, "an accounting line");
076                    }
077                    else {
078                        GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(buildErrorMapKeyPathForDebitCreditAmount(false), ERROR_ZERO_OR_NEGATIVE_AMOUNT, "an accounting line");
079                    }
080    
081                    return false;
082                }
083            }
084            else {
085                // Check for zero amounts
086                if (amount.isZero()) { // amount == 0
087                    GlobalVariables.getMessageMap().putError(AMOUNT_PROPERTY_NAME, ERROR_ZERO_AMOUNT, "an accounting line");
088                    return false;
089                }
090                else if (amount.isNegative()) {
091                    if (!getAccountingLineForValidation().getBalanceTypeCode().equals(BALANCE_TYPE_BASE_BUDGET) && !getAccountingLineForValidation().getBalanceTypeCode().equals(BALANCE_TYPE_CURRENT_BUDGET) && !getAccountingLineForValidation().getBalanceTypeCode().equals(BALANCE_TYPE_MONTHLY_BUDGET)) {
092                        GlobalVariables.getMessageMap().putError(AMOUNT_PROPERTY_NAME, ERROR_NEGATIVE_NON_BUDGET_AMOUNTS);
093                    }
094                }
095            }
096    
097            return true;
098        }
099        
100        /**
101         * This method looks at the current full key path that exists in the ErrorMap structure to determine how to build 
102         * the error map for the special journal voucher credit and debit fields since they don't conform to the standard 
103         * pattern of accounting lines.
104         * 
105         * The error map key path is also dependent on whether or not the accounting line containing an error is a new 
106         * accounting line or an existing line that is being updated.  This determination is made by searching for 
107         * NEW_SOURCE_ACCT_LINE_PROPERTY_NAME in the error path of the global error map.
108         * 
109         * @param isDebit Identifies whether or not the line we are returning an error path for is a debit accounting line or not.
110         * @return The full error map key path for the appropriate amount type.
111         */
112        protected String buildErrorMapKeyPathForDebitCreditAmount(boolean isDebit) {
113            // determine if we are looking at a new line add or an update
114            boolean isNewLineAdd = GlobalVariables.getMessageMap().getErrorPath().contains(NEW_SOURCE_ACCT_LINE_PROPERTY_NAME);
115            isNewLineAdd |= GlobalVariables.getMessageMap().getErrorPath().contains(NEW_SOURCE_ACCT_LINE_PROPERTY_NAME);
116    
117            if (isNewLineAdd) {
118                return isDebit ? DEBIT_AMOUNT_PROPERTY_NAME : CREDIT_AMOUNT_PROPERTY_NAME;
119            }
120            else {
121                String index = StringUtils.substringBetween(GlobalVariables.getMessageMap().getKeyPath("", true), SQUARE_BRACKET_LEFT, SQUARE_BRACKET_RIGHT);
122                String indexWithParams = SQUARE_BRACKET_LEFT + index + SQUARE_BRACKET_RIGHT;
123                return isDebit ? (JOURNAL_LINE_HELPER_PROPERTY_NAME + indexWithParams + VOUCHER_LINE_HELPER_DEBIT_PROPERTY_NAME) : (JOURNAL_LINE_HELPER_PROPERTY_NAME + indexWithParams + VOUCHER_LINE_HELPER_CREDIT_PROPERTY_NAME);
124            }
125        }
126    
127        /**
128         * Gets the accountingLineForValidation attribute. 
129         * @return Returns the accountingLineForValidation.
130         */
131        public AccountingLine getAccountingLineForValidation() {
132            return accountingLineForValidation;
133        }
134    
135        /**
136         * Sets the accountingLineForValidation attribute value.
137         * @param accountingLineForValidation The accountingLineForValidation to set.
138         */
139        public void setAccountingLineForValidation(AccountingLine accountingLineForValidation) {
140            this.accountingLineForValidation = accountingLineForValidation;
141        }
142    
143        /**
144         * Gets the journalVoucherForValidation attribute. 
145         * @return Returns the journalVoucherForValidation.
146         */
147        public JournalVoucherDocument getJournalVoucherForValidation() {
148            return journalVoucherForValidation;
149        }
150    
151        /**
152         * Sets the journalVoucherForValidation attribute value.
153         * @param journalVoucherForValidation The journalVoucherForValidation to set.
154         */
155        public void setJournalVoucherForValidation(JournalVoucherDocument journalVoucherForValidation) {
156            this.journalVoucherForValidation = journalVoucherForValidation;
157        }
158    }