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.validation.impl;
017
018 import java.lang.reflect.InvocationTargetException;
019 import java.util.LinkedList;
020 import java.util.Queue;
021
022 import org.apache.commons.beanutils.PropertyUtils;
023 import org.apache.commons.lang.StringUtils;
024 import org.kuali.kfs.sys.businessobject.AccountingLine;
025 import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
026 import org.kuali.kfs.sys.document.AccountingDocument;
027 import org.kuali.kfs.sys.document.validation.GenericValidation;
028 import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
029 import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
030 import org.kuali.rice.kns.bo.PersistableBusinessObject;
031 import org.kuali.rice.kns.service.ParameterService;
032 import org.kuali.rice.kns.util.ObjectUtils;
033
034 /**
035 * A validation which uses parameters to determine if a value on an accounting line is valid.
036 */
037 public class AccountingLineValueAllowedValidation extends GenericValidation {
038 protected String propertyPath;
039 protected String parameterToCheckAgainst;
040 protected ParameterService parameterService;
041 protected String responsibleProperty;
042 protected AccountingDocument accountingDocumentForValidation;
043 protected AccountingLine accountingLineForValidation;
044
045 /**
046 * Checks if a value in a given accounting line is allowed, based on system parameters.
047 * <strong>Expects an accounting document as the first parameter and accounting line as the second</strong>
048 * @see org.kuali.kfs.sys.document.validation.GenericValidation#validate(java.lang.Object[])
049 */
050 public boolean validate(AttributedDocumentEvent event) {
051
052 if (!StringUtils.isBlank(propertyPath)) {
053 refreshByPath(accountingLineForValidation);
054 }
055
056 return isAccountingLineValueAllowed(accountingDocumentForValidation.getDocumentClassForAccountingLineValueAllowedValidation(), accountingLineForValidation, parameterToCheckAgainst, propertyPath, (responsibleProperty != null ? responsibleProperty : propertyPath));
057 }
058
059 /**
060 * Checks that a value on an accounting line is valid, based on parameters, for a document of the given class
061 * @param documentClass the class of the document to check
062 * @param accountingLine the accounting line to check
063 * @param parameterName the name of the parameter to check
064 * @param propertyName the name of the property to check
065 * @param userEnteredPropertyName the value the user entered on the line
066 * @return true if this passes validation, false otherwise
067 */
068 protected boolean isAccountingLineValueAllowed(Class documentClass, AccountingLine accountingLine, String parameterName, String propertyName, String userEnteredPropertyName) {
069 boolean isAllowed = true;
070 String exceptionMessage = "Invalue property name provided to AccountingDocumentRuleBase isAccountingLineValueAllowed method: " + propertyName;
071 try {
072 String propertyValue = (String) PropertyUtils.getProperty(accountingLine, propertyName);
073 if (getParameterService().parameterExists(KfsParameterConstants.FINANCIAL_PROCESSING_DOCUMENT.class, parameterName)) {
074 isAllowed = getParameterService().getParameterEvaluator(KfsParameterConstants.FINANCIAL_PROCESSING_DOCUMENT.class, parameterName, propertyValue).evaluateAndAddError(SourceAccountingLine.class, propertyName, userEnteredPropertyName);
075 }
076 if (getParameterService().parameterExists(documentClass, parameterName)) {
077 isAllowed = getParameterService().getParameterEvaluator(documentClass, parameterName, propertyValue).evaluateAndAddError(SourceAccountingLine.class, propertyName, userEnteredPropertyName);
078 }
079 }
080 catch (IllegalAccessException e) {
081 throw new RuntimeException(exceptionMessage, e);
082 }
083 catch (InvocationTargetException e) {
084 throw new RuntimeException(exceptionMessage, e);
085 }
086 catch (NoSuchMethodException e) {
087 throw new RuntimeException(exceptionMessage, e);
088 }
089 return isAllowed;
090 }
091
092 /**
093 * Refreshes a value on the accounting line, using the propertyPath to decided what to refresh
094 * @param line the accounting line to refresh a property on
095 */
096 public void refreshByPath(AccountingLine line) {
097 refreshByQueue(line, convertPathToQueue(propertyPath));
098 }
099
100 /**
101 * Creates a Queue which represents a FIFO path of what properties to visit, based on the given property path
102 * @param path the path to convert to a Queue
103 * @return a Queue representing the path
104 */
105 protected Queue<String> convertPathToQueue(String path) {
106 Queue<String> pathQueue = new LinkedList<String>();
107 for (String property: path.split("\\.")) {
108 pathQueue.add(property);
109 }
110 return pathQueue;
111 }
112
113 /**
114 * Recursively refreshes a property given by the queue path
115 * @param bo the business object to refresh
116 * @param path the path, in Queue form, of properties to refresh
117 */
118 protected void refreshByQueue(PersistableBusinessObject bo, Queue<String> path) {
119 if (path.size() > 1) { // we know that the last thing on our list is a code. why refresh that?
120 String currentProperty = path.remove();
121 bo.refreshReferenceObject(currentProperty);
122 PersistableBusinessObject childBO = (PersistableBusinessObject)ObjectUtils.getPropertyValue(bo, currentProperty);
123 if (!ObjectUtils.isNull(childBO)) {
124 refreshByQueue(childBO, path);
125 }
126 }
127 }
128
129 /**
130 * Gets the propertyPath attribute. This is the path to the value to check, e. g. "accountNumber.subFundGroup.fundGroupCode"
131 * @return Returns the propertyPath.
132 */
133 public String getPropertyPath() {
134 return propertyPath;
135 }
136
137 /**
138 * Sets the propertyPath attribute value. This is the path to the value to check, e. g. "accountNumber.subFundGroup.fundGroupCode"
139 * @param propertyPath The propertyPath to set.
140 */
141 public void setPropertyPath(String refreshPath) {
142 this.propertyPath = refreshPath;
143 }
144
145 /**
146 * Gets the parameterService attribute.
147 * @return Returns the parameterService.
148 */
149 public ParameterService getParameterService() {
150 return parameterService;
151 }
152
153 /**
154 * Sets the parameterService attribute value.
155 * @param parameterService The parameterService to set.
156 */
157 public void setParameterService(ParameterService parameterService) {
158 this.parameterService = parameterService;
159 }
160
161 /**
162 * Gets the parameterToCheckAgainst attribute. This is the name of the parameter which has the values to validate against.
163 * @return Returns the parameterToCheckAgainst.
164 */
165 public String getParameterToCheckAgainst() {
166 return parameterToCheckAgainst;
167 }
168
169 /**
170 * Sets the parameterToCheckAgainst attribute value. This is the name of the parameter which has the values to validate against.
171 * @param parameterToCheckAgainst The parameterToCheckAgainst to set.
172 */
173 public void setParameterToCheckAgainst(String parameterToCheckAgainst) {
174 this.parameterToCheckAgainst = parameterToCheckAgainst;
175 }
176
177 /**
178 * Gets the responsibleProperty attribute. This is the property on the accounting line to show the error on.
179 * @return Returns the responsibleProperty.
180 */
181 public String getResponsibleProperty() {
182 return responsibleProperty;
183 }
184
185 /**
186 * Sets the responsibleProperty attribute value. This is the property on the accounting line to show the error on.
187 * @param responsibleProperty The responsibleProperty to set.
188 */
189 public void setResponsibleProperty(String responsibleProperty) {
190 this.responsibleProperty = responsibleProperty;
191 }
192
193 /**
194 * Gets the accountingDocumentForValidation attribute.
195 * @return Returns the accountingDocumentForValidation.
196 */
197 public AccountingDocument getAccountingDocumentForValidation() {
198 return accountingDocumentForValidation;
199 }
200
201 /**
202 * Sets the accountingDocumentForValidation attribute value.
203 * @param accountingDocumentForValidation The accountingDocumentForValidation to set.
204 */
205 public void setAccountingDocumentForValidation(AccountingDocument accountingDocumentForValidation) {
206 this.accountingDocumentForValidation = accountingDocumentForValidation;
207 }
208
209 /**
210 * Gets the accountingLineForValidation attribute.
211 * @return Returns the accountingLineForValidation.
212 */
213 public AccountingLine getAccountingLineForValidation() {
214 return accountingLineForValidation;
215 }
216
217 /**
218 * Sets the accountingLineForValidation attribute value.
219 * @param accountingLineForValidation The accountingLineForValidation to set.
220 */
221 public void setAccountingLineForValidation(AccountingLine accountingLineForValidation) {
222 this.accountingLineForValidation = accountingLineForValidation;
223 }
224 }