View Javadoc
1   /*
2    * The Kuali Financial System, a comprehensive financial management system for higher education.
3    * 
4    * Copyright 2005-2014 The Kuali Foundation
5    * 
6    * This program is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Affero General Public License as
8    * published by the Free Software Foundation, either version 3 of the
9    * License, or (at your option) any later version.
10   * 
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Affero General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Affero General Public License
17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package org.kuali.kfs.fp.document.validation.impl;
20  
21  import static org.kuali.kfs.sys.KFSConstants.AMOUNT_PROPERTY_NAME;
22  import static org.kuali.kfs.sys.KFSConstants.CREDIT_AMOUNT_PROPERTY_NAME;
23  import static org.kuali.kfs.sys.KFSConstants.DEBIT_AMOUNT_PROPERTY_NAME;
24  import static org.kuali.kfs.sys.KFSConstants.GL_DEBIT_CODE;
25  import static org.kuali.kfs.sys.KFSConstants.JOURNAL_LINE_HELPER_PROPERTY_NAME;
26  import static org.kuali.kfs.sys.KFSConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME;
27  import static org.kuali.kfs.sys.KFSConstants.SQUARE_BRACKET_LEFT;
28  import static org.kuali.kfs.sys.KFSConstants.SQUARE_BRACKET_RIGHT;
29  import static org.kuali.kfs.sys.KFSConstants.VOUCHER_LINE_HELPER_CREDIT_PROPERTY_NAME;
30  import static org.kuali.kfs.sys.KFSConstants.VOUCHER_LINE_HELPER_DEBIT_PROPERTY_NAME;
31  import static org.kuali.kfs.sys.KFSKeyConstants.ERROR_ZERO_AMOUNT;
32  import static org.kuali.kfs.sys.KFSKeyConstants.ERROR_ZERO_OR_NEGATIVE_AMOUNT;
33  import static org.kuali.kfs.sys.KFSKeyConstants.JournalVoucher.ERROR_NEGATIVE_NON_BUDGET_AMOUNTS;
34  import static org.kuali.kfs.sys.KFSPropertyConstants.BALANCE_TYPE;
35  
36  import java.util.Collection;
37  
38  import org.apache.commons.lang.StringUtils;
39  import org.kuali.kfs.fp.document.JournalVoucherDocument;
40  import org.kuali.kfs.sys.KFSParameterKeyConstants;
41  import org.kuali.kfs.sys.businessobject.AccountingLine;
42  import org.kuali.kfs.sys.context.SpringContext;
43  import org.kuali.kfs.sys.document.validation.GenericValidation;
44  import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
45  import org.kuali.rice.core.api.util.type.KualiDecimal;
46  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
47  import org.kuali.rice.krad.util.GlobalVariables;
48  
49  /**
50   * The Journal Voucher's version of the accounting line amount validation
51   */
52  public class JournalVoucherAccountingLineAmountValidation extends GenericValidation {
53      private JournalVoucherDocument journalVoucherForValidation;
54      private AccountingLine accountingLineForValidation;
55  
56      /**
57       * Accounting lines for Journal Vouchers can be positive or negative, just not "$0.00".
58       *
59       * Additionally, accounting lines cannot have negative dollar amounts if the balance type of the
60       * journal voucher allows for general ledger pending entry offset generation or the balance type
61       * is not a budget type code.
62       * @see org.kuali.kfs.sys.document.validation.Validation#validate(org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent)
63       */
64      @Override
65      public boolean validate(AttributedDocumentEvent event) {
66          KualiDecimal amount = getAccountingLineForValidation().getAmount();
67  
68          getJournalVoucherForValidation().refreshReferenceObject(BALANCE_TYPE);
69  
70          if (getJournalVoucherForValidation().getBalanceType().isFinancialOffsetGenerationIndicator()) {
71              // check for negative or zero amounts
72              if (amount.isZero()) { // if 0
73                  GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(buildMessageMapKeyPathForDebitCreditAmount(true), ERROR_ZERO_OR_NEGATIVE_AMOUNT, "an accounting line");
74                  GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(buildMessageMapKeyPathForDebitCreditAmount(false), ERROR_ZERO_OR_NEGATIVE_AMOUNT, "an accounting line");
75  
76                  return false;
77              }
78              else if (amount.isNegative()) { // entered a negative number
79                  String debitCreditCode = getAccountingLineForValidation().getDebitCreditCode();
80                  if (StringUtils.isNotBlank(debitCreditCode) && GL_DEBIT_CODE.equals(debitCreditCode)) {
81                      GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(buildMessageMapKeyPathForDebitCreditAmount(true), ERROR_ZERO_OR_NEGATIVE_AMOUNT, "an accounting line");
82                  }
83                  else {
84                      GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(buildMessageMapKeyPathForDebitCreditAmount(false), ERROR_ZERO_OR_NEGATIVE_AMOUNT, "an accounting line");
85                  }
86  
87                  return false;
88              }
89          }
90          else {
91              // Check for zero amounts
92              if (amount.isZero()) { // amount == 0
93                  GlobalVariables.getMessageMap().putError(AMOUNT_PROPERTY_NAME, ERROR_ZERO_AMOUNT, "an accounting line");
94                  return false;
95              }
96              else if (amount.isNegative()) {
97              	if (!allowNegativeAmounts(getAccountingLineForValidation())) {
98                      GlobalVariables.getMessageMap().putError(AMOUNT_PROPERTY_NAME, ERROR_NEGATIVE_NON_BUDGET_AMOUNTS);
99                  }
100             }
101         }
102 
103         return true;
104     }
105 
106     /**
107      * This method retrieves the parameter values that define the allowable balance type codes and determines if negative amounts
108      * are allowed for the associated accounting line.
109      *
110      * @param acctLine The accounting line which will be used to determine if negative amounts are allowed.
111      * @return True if the accounting line has a balance type found in the associated parameter, false otherwise.
112      */
113     private boolean allowNegativeAmounts(AccountingLine acctLine) {
114     	Collection<String> budgetTypes = SpringContext.getBean(ParameterService.class).getParameterValuesAsString(JournalVoucherDocument.class, KFSParameterKeyConstants.FpParameterConstants.FP_BUDGET_BALANCE_TYPES);
115     	return budgetTypes.contains(acctLine.getBalanceTypeCode());
116     }
117 
118     /**
119      * This method looks at the current full key path that exists in the MessageMap structure to determine how to build
120      * the error map for the special journal voucher credit and debit fields since they don't conform to the standard
121      * pattern of accounting lines.
122      *
123      * The error map key path is also dependent on whether or not the accounting line containing an error is a new
124      * accounting line or an existing line that is being updated.  This determination is made by searching for
125      * NEW_SOURCE_ACCT_LINE_PROPERTY_NAME in the error path of the global error map.
126      *
127      * @param isDebit Identifies whether or not the line we are returning an error path for is a debit accounting line or not.
128      * @return The full error map key path for the appropriate amount type.
129      */
130     protected String buildMessageMapKeyPathForDebitCreditAmount(boolean isDebit) {
131         // determine if we are looking at a new line add or an update
132         boolean isNewLineAdd = GlobalVariables.getMessageMap().getErrorPath().contains(NEW_SOURCE_ACCT_LINE_PROPERTY_NAME);
133         isNewLineAdd |= GlobalVariables.getMessageMap().getErrorPath().contains(NEW_SOURCE_ACCT_LINE_PROPERTY_NAME);
134 
135         if (isNewLineAdd) {
136             return isDebit ? DEBIT_AMOUNT_PROPERTY_NAME : CREDIT_AMOUNT_PROPERTY_NAME;
137         }
138         else {
139             String index = StringUtils.substringBetween(GlobalVariables.getMessageMap().getKeyPath("", true), SQUARE_BRACKET_LEFT, SQUARE_BRACKET_RIGHT);
140             String indexWithParams = SQUARE_BRACKET_LEFT + index + SQUARE_BRACKET_RIGHT;
141             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);
142         }
143     }
144 
145     /**
146      * Gets the accountingLineForValidation attribute.
147      * @return Returns the accountingLineForValidation.
148      */
149     public AccountingLine getAccountingLineForValidation() {
150         return accountingLineForValidation;
151     }
152 
153     /**
154      * Sets the accountingLineForValidation attribute value.
155      * @param accountingLineForValidation The accountingLineForValidation to set.
156      */
157     public void setAccountingLineForValidation(AccountingLine accountingLineForValidation) {
158         this.accountingLineForValidation = accountingLineForValidation;
159     }
160 
161     /**
162      * Gets the journalVoucherForValidation attribute.
163      * @return Returns the journalVoucherForValidation.
164      */
165     public JournalVoucherDocument getJournalVoucherForValidation() {
166         return journalVoucherForValidation;
167     }
168 
169     /**
170      * Sets the journalVoucherForValidation attribute value.
171      * @param journalVoucherForValidation The journalVoucherForValidation to set.
172      */
173     public void setJournalVoucherForValidation(JournalVoucherDocument journalVoucherForValidation) {
174         this.journalVoucherForValidation = journalVoucherForValidation;
175     }
176 }