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.module.purap.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.module.purap.PurapConstants.PaymentRequestStatuses;
025 import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
026 import org.kuali.kfs.module.purap.businessobject.PurApItem;
027 import org.kuali.kfs.module.purap.document.PaymentRequestDocument;
028 import org.kuali.kfs.sys.businessobject.AccountingLine;
029 import org.kuali.kfs.sys.document.AccountingDocument;
030 import org.kuali.kfs.sys.document.validation.BranchingValidation;
031 import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
032 import org.kuali.rice.kns.bo.PersistableBusinessObject;
033 import org.kuali.rice.kns.service.ParameterService;
034 import org.kuali.rice.kns.util.ObjectUtils;
035
036 /**
037 * A validation which uses parameters to determine if a value on an accounting line is valid.
038 */
039 public class PurchasingAccountsPayableObjectCodeOverrideBranchingValidation extends BranchingValidation {
040 protected String propertyPath;
041 protected String parameterToCheckAgainst;
042 protected ParameterService parameterService;
043 protected String responsibleProperty;
044 protected AccountingDocument accountingDocumentForValidation;
045 protected AccountingLine accountingLineForValidation;
046
047 protected final static String OBJECT_CODE_OVERRIDEN = "ObjectCodeOverriden";
048 protected final static String OBJECT_CODE_NOT_OVERRIDEN = "ObjectCodeNotOverriden";
049
050 @Override
051 protected String determineBranch(AttributedDocumentEvent event) {
052 if (!StringUtils.isBlank(propertyPath)) {
053 refreshByPath(accountingLineForValidation);
054 }
055
056 boolean isTaxApproval = false;
057 //if payment request, skip object code check when this is a tax approval,
058 // or if this accounting line is from a Tax Charge line.
059 if (accountingDocumentForValidation instanceof PaymentRequestDocument) {
060 PaymentRequestDocument preq = (PaymentRequestDocument)accountingDocumentForValidation;
061 PurApAccountingLine purapAccountingLine = (PurApAccountingLine)accountingLineForValidation;
062 PurApItem item = purapAccountingLine.getPurapItem();
063
064 if (StringUtils.equals(PaymentRequestStatuses.AWAITING_TAX_REVIEW, preq.getStatusCode())){
065 isTaxApproval = true;
066 }else if(StringUtils.equals(PaymentRequestStatuses.DEPARTMENT_APPROVED, preq.getStatusCode()) &&
067 (ObjectUtils.isNotNull(item) && item.getItemType().getIsTaxCharge()) ){
068 isTaxApproval = true;
069 }
070 }
071
072 if (isTaxApproval) {
073 return null;
074 } else if (isAccountingLineValueAllowed(accountingDocumentForValidation.getClass(), accountingLineForValidation, parameterToCheckAgainst, propertyPath, (responsibleProperty != null ? responsibleProperty : propertyPath))){
075 return OBJECT_CODE_OVERRIDEN;
076 } else {
077 return OBJECT_CODE_NOT_OVERRIDEN;
078 }
079 }
080
081 /**
082 * Checks that a value on an accounting line is valid, based on parameters, for a document of the given class
083 * @param documentClass the class of the document to check
084 * @param accountingLine the accounting line to check
085 * @param parameterName the name of the parameter to check
086 * @param propertyName the name of the property to check
087 * @param userEnteredPropertyName the value the user entered on the line
088 * @return true if this passes validation, false otherwise
089 */
090 protected boolean isAccountingLineValueAllowed(Class documentClass, AccountingLine accountingLine, String parameterName, String propertyName, String userEnteredPropertyName) {
091 boolean isAllowed = false;
092 String exceptionMessage = "Invalid property name provided to PurchasingAccountsPayableObjectCodeOverrideBranchingValidation isAccountingLineValueAllowed method: " + propertyName;
093 try {
094 String propertyValue = (String) PropertyUtils.getProperty(accountingLine, propertyName);
095 if (getParameterService().parameterExists(documentClass, parameterName)) {
096 isAllowed = getParameterService().getParameterEvaluator(documentClass, parameterName, propertyValue).evaluationSucceeds();
097 }
098 }
099 catch (IllegalAccessException e) {
100 throw new RuntimeException(exceptionMessage, e);
101 }
102 catch (InvocationTargetException e) {
103 throw new RuntimeException(exceptionMessage, e);
104 }
105 catch (NoSuchMethodException e) {
106 throw new RuntimeException(exceptionMessage, e);
107 }
108 return isAllowed;
109 }
110
111 /**
112 * Refreshes a value on the accounting line, using the propertyPath to decided what to refresh
113 * @param line the accounting line to refresh a property on
114 */
115 public void refreshByPath(AccountingLine line) {
116 refreshByQueue(line, convertPathToQueue(propertyPath));
117 }
118
119 /**
120 * Creates a Queue which represents a FIFO path of what properties to visit, based on the given property path
121 * @param path the path to convert to a Queue
122 * @return a Queue representing the path
123 */
124 protected Queue<String> convertPathToQueue(String path) {
125 Queue<String> pathQueue = new LinkedList<String>();
126 for (String property: path.split("\\.")) {
127 pathQueue.add(property);
128 }
129 return pathQueue;
130 }
131
132 /**
133 * Recursively refreshes a property given by the queue path
134 * @param bo the business object to refresh
135 * @param path the path, in Queue form, of properties to refresh
136 */
137 protected void refreshByQueue(PersistableBusinessObject bo, Queue<String> path) {
138 if (path.size() > 1) { // we know that the last thing on our list is a code. why refresh that?
139 String currentProperty = path.remove();
140 bo.refreshReferenceObject(currentProperty);
141 PersistableBusinessObject childBO = (PersistableBusinessObject)ObjectUtils.getPropertyValue(bo, currentProperty);
142 if (!ObjectUtils.isNull(childBO)) {
143 refreshByQueue(childBO, path);
144 }
145 }
146 }
147
148 /**
149 * Gets the propertyPath attribute. This is the path to the value to check, e. g. "accountNumber.subFundGroup.fundGroupCode"
150 * @return Returns the propertyPath.
151 */
152 public String getPropertyPath() {
153 return propertyPath;
154 }
155
156 /**
157 * Sets the propertyPath attribute value. This is the path to the value to check, e. g. "accountNumber.subFundGroup.fundGroupCode"
158 * @param propertyPath The propertyPath to set.
159 */
160 public void setPropertyPath(String refreshPath) {
161 this.propertyPath = refreshPath;
162 }
163
164 /**
165 * Gets the parameterService attribute.
166 * @return Returns the parameterService.
167 */
168 public ParameterService getParameterService() {
169 return parameterService;
170 }
171
172 /**
173 * Sets the parameterService attribute value.
174 * @param parameterService The parameterService to set.
175 */
176 public void setParameterService(ParameterService parameterService) {
177 this.parameterService = parameterService;
178 }
179
180 /**
181 * Gets the parameterToCheckAgainst attribute. This is the name of the parameter which has the values to validate against.
182 * @return Returns the parameterToCheckAgainst.
183 */
184 public String getParameterToCheckAgainst() {
185 return parameterToCheckAgainst;
186 }
187
188 /**
189 * Sets the parameterToCheckAgainst attribute value. This is the name of the parameter which has the values to validate against.
190 * @param parameterToCheckAgainst The parameterToCheckAgainst to set.
191 */
192 public void setParameterToCheckAgainst(String parameterToCheckAgainst) {
193 this.parameterToCheckAgainst = parameterToCheckAgainst;
194 }
195
196 /**
197 * Gets the responsibleProperty attribute. This is the property on the accounting line to show the error on.
198 * @return Returns the responsibleProperty.
199 */
200 public String getResponsibleProperty() {
201 return responsibleProperty;
202 }
203
204 /**
205 * Sets the responsibleProperty attribute value. This is the property on the accounting line to show the error on.
206 * @param responsibleProperty The responsibleProperty to set.
207 */
208 public void setResponsibleProperty(String responsibleProperty) {
209 this.responsibleProperty = responsibleProperty;
210 }
211
212 /**
213 * Gets the accountingDocumentForValidation attribute.
214 * @return Returns the accountingDocumentForValidation.
215 */
216 public AccountingDocument getAccountingDocumentForValidation() {
217 return accountingDocumentForValidation;
218 }
219
220 /**
221 * Sets the accountingDocumentForValidation attribute value.
222 * @param accountingDocumentForValidation The accountingDocumentForValidation to set.
223 */
224 public void setAccountingDocumentForValidation(AccountingDocument accountingDocumentForValidation) {
225 this.accountingDocumentForValidation = accountingDocumentForValidation;
226 }
227
228 /**
229 * Gets the accountingLineForValidation attribute.
230 * @return Returns the accountingLineForValidation.
231 */
232 public AccountingLine getAccountingLineForValidation() {
233 return accountingLineForValidation;
234 }
235
236 /**
237 * Sets the accountingLineForValidation attribute value.
238 * @param accountingLineForValidation The accountingLineForValidation to set.
239 */
240 public void setAccountingLineForValidation(AccountingLine accountingLineForValidation) {
241 this.accountingLineForValidation = accountingLineForValidation;
242 }
243
244 }