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 }