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 java.text.MessageFormat;
019 import java.util.ArrayList;
020 import java.util.Arrays;
021 import java.util.List;
022
023 import org.apache.commons.lang.StringUtils;
024 import org.kuali.kfs.fp.businessobject.DisbursementVoucherNonEmployeeTravel;
025 import org.kuali.kfs.fp.businessobject.DisbursementVoucherWireTransfer;
026 import org.kuali.kfs.fp.businessobject.options.PaymentReasonValuesFinder;
027 import org.kuali.kfs.fp.document.DisbursementVoucherConstants;
028 import org.kuali.kfs.fp.document.DisbursementVoucherDocument;
029 import org.kuali.kfs.sys.KFSConstants;
030 import org.kuali.kfs.sys.KFSKeyConstants;
031 import org.kuali.kfs.sys.KFSPropertyConstants;
032 import org.kuali.kfs.sys.businessobject.Bank;
033 import org.kuali.kfs.sys.context.SpringContext;
034 import org.kuali.kfs.sys.service.BankService;
035 import org.kuali.rice.core.util.KeyLabelPair;
036 import org.kuali.rice.kew.exception.WorkflowException;
037 import org.kuali.rice.kns.document.Document;
038 import org.kuali.rice.kns.rules.PromptBeforeValidationBase;
039 import org.kuali.rice.kns.service.KualiConfigurationService;
040 import org.kuali.rice.kns.service.ParameterService;
041 import org.kuali.rice.kns.util.ObjectUtils;
042
043 /**
044 * Checks warnings and prompt conditions for dv document.
045 */
046 public class DisbursementVoucherDocumentPreRules extends PromptBeforeValidationBase implements DisbursementVoucherConstants {
047
048 /**
049 * Executes pre-rules for Disbursement Voucher Document
050 *
051 * @param document submitted document
052 * @return true if pre-rules execute successfully
053 * @see org.kuali.rice.kns.rules.PromptBeforeValidationBase#doRules(org.kuali.rice.kns.document.MaintenanceDocument)
054 */
055 @Override
056 public boolean doPrompts(Document document) {
057 boolean preRulesOK = true;
058
059 DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) document;
060 checkSpecialHandlingIndicator(dvDocument);
061
062 preRulesOK &= checkNonEmployeeTravelTabState(dvDocument);
063
064 preRulesOK &= checkWireTransferTabState(dvDocument);
065
066 preRulesOK &= checkForeignDraftTabState(dvDocument);
067
068 preRulesOK &= checkBankCodeActive(dvDocument);
069
070 return preRulesOK;
071 }
072
073 /**
074 * If the special handling name and address 1 fields have value, this will mark the special handling indicator for the user.
075 *
076 * @param dvDocument submitted disbursement voucher document
077 */
078 protected void checkSpecialHandlingIndicator(DisbursementVoucherDocument dvDocument) {
079 if (StringUtils.isNotBlank(dvDocument.getDvPayeeDetail().getDisbVchrSpecialHandlingPersonName()) && StringUtils.isNotBlank(dvDocument.getDvPayeeDetail().getDisbVchrSpecialHandlingLine1Addr()) && allowTurningOnOfSpecialHandling(dvDocument)) {
080 dvDocument.setDisbVchrSpecialHandlingCode(true);
081 }
082 }
083
084 /**
085 * Allows the automatic turning on of special handling indicator - which will not be allowed at the Campus route level
086 * @param dvDocument the document to allow turning on of special handling for
087 * @return true if special handling can be automatically turned on, false otherwise
088 */
089 protected boolean allowTurningOnOfSpecialHandling(DisbursementVoucherDocument dvDocument) {
090 try {
091 List<String> currentNodes = Arrays.asList(dvDocument.getDocumentHeader().getWorkflowDocument().getNodeNames());
092 return !(currentNodes.contains(DisbursementVoucherConstants.RouteLevelNames.CAMPUS));
093 }
094 catch (WorkflowException we) {
095 throw new RuntimeException("Workflow Exception while attempting to check route levels", we);
096 }
097 }
098
099 /**
100 * This method checks non-employee travel tab state is valid
101 *
102 * @param dvDocument submitted disbursement voucher document
103 * @return true if the state of all the tabs is valid, false otherwise.
104 */
105 protected boolean checkNonEmployeeTravelTabState(DisbursementVoucherDocument dvDocument) {
106 boolean tabStatesOK = true;
107
108 DisbursementVoucherNonEmployeeTravel dvNonEmplTrav = dvDocument.getDvNonEmployeeTravel();
109 if (!hasNonEmployeeTravelValues(dvNonEmplTrav)) {
110 return true;
111 }
112
113 String paymentReasonCode = dvDocument.getDvPayeeDetail().getDisbVchrPaymentReasonCode();
114 List<String> nonEmpltravelPaymentReasonCodes = SpringContext.getBean(ParameterService.class).getParameterValues(DisbursementVoucherDocument.class, NONEMPLOYEE_TRAVEL_PAY_REASONS_PARM_NM);
115
116 if (nonEmpltravelPaymentReasonCodes == null || !nonEmpltravelPaymentReasonCodes.contains(paymentReasonCode) || dvDocument.getDvPayeeDetail().isEmployee()) {
117 String nonEmplTravReasonStr = getValidPaymentReasonsAsString(nonEmpltravelPaymentReasonCodes);
118
119 String paymentReasonName = dvDocument.getDvPayeeDetail().getDisbVchrPaymentReasonName();
120 Object[] args = { "payment reason", "'" + paymentReasonName + "'", "Non-Employee Travel", nonEmplTravReasonStr };
121
122 String questionText = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSKeyConstants.QUESTION_CLEAR_UNNEEDED_TAB);
123 questionText = MessageFormat.format(questionText, args);
124
125 boolean clearTab = super.askOrAnalyzeYesNoQuestion(KFSConstants.DisbursementVoucherDocumentConstants.CLEAR_NON_EMPLOYEE_TAB_QUESTION_ID, questionText);
126 if (clearTab) {
127 DisbursementVoucherNonEmployeeTravel blankDvNonEmplTrav = new DisbursementVoucherNonEmployeeTravel();
128 blankDvNonEmplTrav.setDocumentNumber(dvNonEmplTrav.getDocumentNumber());
129 blankDvNonEmplTrav.setVersionNumber(dvNonEmplTrav.getVersionNumber());
130 dvDocument.setDvNonEmployeeTravel(blankDvNonEmplTrav);
131 }
132 else {
133 // return to document if the user doesn't want to clear the Non Employee Travel tab
134 super.event.setActionForwardName(KFSConstants.MAPPING_BASIC);
135 tabStatesOK = false;
136 }
137 }
138
139 return tabStatesOK;
140 }
141
142 /**
143 * Returns true if non-employee travel tab contains any data in any of its fields
144 *
145 * @param dvNonEmplTrav disbursement voucher non employee travel object
146 * @return True if non employee travel tab contains any data in any fields.
147 */
148 protected boolean hasNonEmployeeTravelValues(DisbursementVoucherNonEmployeeTravel dvNonEmplTrav) {
149 boolean hasValues = false;
150
151 // Checks each explicit field in the tab for user entered values
152 hasValues = hasNonEmployeeTravelGeneralValues(dvNonEmplTrav);
153
154 // Checks per diem section for values
155 if (!hasValues) {
156 hasValues = hasNonEmployeeTravelPerDiemValues(dvNonEmplTrav);
157 }
158
159 if (!hasValues) {
160 hasValues = hasNonEmployeeTravelPersonalVehicleValues(dvNonEmplTrav);
161 }
162
163 if (!hasValues) {
164 hasValues = dvNonEmplTrav.getDvNonEmployeeExpenses().size() > 0;
165 }
166
167 if (!hasValues) {
168 hasValues = dvNonEmplTrav.getDvPrePaidEmployeeExpenses().size() > 0;
169 }
170
171 return hasValues;
172 }
173
174 /**
175 * Returns true if any values are not blank on non employee travel tab
176 *
177 * @param dvNonEmplTrav disbursement voucher non employee travel object
178 * @return True if any values are found in the non employee travel tab
179 */
180 protected boolean hasNonEmployeeTravelGeneralValues(DisbursementVoucherNonEmployeeTravel dvNonEmplTrav) {
181 return StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrNonEmpTravelerName()) || StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrServicePerformedDesc()) || StringUtils.isNotBlank(dvNonEmplTrav.getDvServicePerformedLocName()) || StringUtils.isNotBlank(dvNonEmplTrav.getDvServiceRegularEmprName()) || StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrTravelFromCityName()) || StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrTravelFromStateCode()) || StringUtils.isNotBlank(dvNonEmplTrav.getDvTravelFromCountryCode()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDvPerdiemStartDttmStamp()) || StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrTravelToCityName()) || StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrTravelToStateCode()) || StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrTravelToCountryCode()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDvPerdiemEndDttmStamp());
182 }
183
184 /**
185 * Returns true if non employee travel tab contains data in any of the fields in the per diem section
186 *
187 * @param dvNonEmplTrav disbursement voucher non employee travel object
188 * @return True if non employee travel tab contains data in any of the fields in the per diem section
189 */
190 protected boolean hasNonEmployeeTravelPerDiemValues(DisbursementVoucherNonEmployeeTravel dvNonEmplTrav) {
191 return StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrPerdiemCategoryName()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDisbVchrPerdiemRate()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDisbVchrPerdiemCalculatedAmt()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDisbVchrPerdiemActualAmount()) || StringUtils.isNotBlank(dvNonEmplTrav.getDvPerdiemChangeReasonText());
192 }
193
194 /**
195 * Returns true if non employee travel tab contains data in any of the fields in the personal vehicle section
196 *
197 * @param dvNonEmplTrav disbursement voucher non employee travel object
198 * @return True if non employee travel tab contains data in any of the fields in the personal vehicle section
199 */
200 protected boolean hasNonEmployeeTravelPersonalVehicleValues(DisbursementVoucherNonEmployeeTravel dvNonEmplTrav) {
201 return StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrAutoFromCityName()) || StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrAutoFromStateCode()) || StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrAutoToCityName()) || StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrAutoToStateCode()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDisbVchrMileageCalculatedAmt()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDisbVchrPersonalCarAmount());
202 }
203
204 /**
205 * Returns true if the state of all the tabs is valid, false otherwise.
206 *
207 * @param dvDocument submitted disbursemtn voucher document
208 * @return true if the state of all the tabs is valid, false otherwise.
209 */
210 protected boolean checkForeignDraftTabState(DisbursementVoucherDocument dvDocument) {
211 boolean tabStatesOK = true;
212
213 DisbursementVoucherWireTransfer dvForeignDraft = dvDocument.getDvWireTransfer();
214
215 // if payment method is CHECK and wire tab contains data, ask user to clear tab
216 if ((StringUtils.equals(DisbursementVoucherConstants.PAYMENT_METHOD_CHECK, dvDocument.getDisbVchrPaymentMethodCode()) || StringUtils.equals(DisbursementVoucherConstants.PAYMENT_METHOD_WIRE, dvDocument.getDisbVchrPaymentMethodCode())) && hasForeignDraftValues(dvForeignDraft)) {
217 String questionText = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSKeyConstants.QUESTION_CLEAR_UNNEEDED_TAB);
218
219 Object[] args = { "payment method", dvDocument.getDisbVchrPaymentMethodCode(), "Foreign Draft", DisbursementVoucherConstants.PAYMENT_METHOD_DRAFT };
220 questionText = MessageFormat.format(questionText, args);
221
222 boolean clearTab = super.askOrAnalyzeYesNoQuestion(KFSConstants.DisbursementVoucherDocumentConstants.CLEAR_FOREIGN_DRAFT_TAB_QUESTION_ID, questionText);
223 if (clearTab) {
224 // NOTE: Can't replace with new instance because Wire Transfer uses same object
225 clearForeignDraftValues(dvForeignDraft);
226 }
227 else {
228 // return to document if the user doesn't want to clear the Wire Transfer tab
229 super.event.setActionForwardName(KFSConstants.MAPPING_BASIC);
230 tabStatesOK = false;
231 }
232 }
233
234 return tabStatesOK;
235 }
236
237 /**
238 * Returns true if foreign draft tab contains any data in any fields. NOTE: Currently does not validate based on only required
239 * fields. Checks all fields within tab for data.
240 *
241 * @param dvForeignDraft disbursement foreign draft object
242 * @return True if foreign draft tab contains any data in any fields.
243 */
244 protected boolean hasForeignDraftValues(DisbursementVoucherWireTransfer dvForeignDraft) {
245 boolean hasValues = false;
246
247 // Checks each explicit field in the tab for user entered values
248 hasValues |= StringUtils.isNotBlank(dvForeignDraft.getDisbursementVoucherForeignCurrencyTypeCode());
249 hasValues |= StringUtils.isNotBlank(dvForeignDraft.getDisbursementVoucherForeignCurrencyTypeName());
250
251 return hasValues;
252 }
253
254 /**
255 * This method sets foreign currency type code and name to null for passed in disbursement foreign draft object
256 *
257 * @param dvForeignDraft disbursement foreign draft object
258 */
259 protected void clearForeignDraftValues(DisbursementVoucherWireTransfer dvForeignDraft) {
260 dvForeignDraft.setDisbursementVoucherForeignCurrencyTypeCode(null);
261 dvForeignDraft.setDisbursementVoucherForeignCurrencyTypeName(null);
262 }
263
264 /**
265 * This method returns true if the state of all the tabs is valid, false otherwise.
266 *
267 * @param dvDocument submitted disbursement voucher document
268 * @return Returns true if the state of all the tabs is valid, false otherwise.
269 */
270 protected boolean checkWireTransferTabState(DisbursementVoucherDocument dvDocument) {
271 boolean tabStatesOK = true;
272
273 DisbursementVoucherWireTransfer dvWireTransfer = dvDocument.getDvWireTransfer();
274
275 // if payment method is CHECK and wire tab contains data, ask user to clear tab
276 if ((StringUtils.equals(DisbursementVoucherConstants.PAYMENT_METHOD_CHECK, dvDocument.getDisbVchrPaymentMethodCode()) || StringUtils.equals(DisbursementVoucherConstants.PAYMENT_METHOD_DRAFT, dvDocument.getDisbVchrPaymentMethodCode())) && hasWireTransferValues(dvWireTransfer)) {
277 String questionText = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSKeyConstants.QUESTION_CLEAR_UNNEEDED_TAB);
278
279 Object[] args = { "payment method", dvDocument.getDisbVchrPaymentMethodCode(), "Wire Transfer", DisbursementVoucherConstants.PAYMENT_METHOD_WIRE };
280 questionText = MessageFormat.format(questionText, args);
281
282 boolean clearTab = super.askOrAnalyzeYesNoQuestion(KFSConstants.DisbursementVoucherDocumentConstants.CLEAR_WIRE_TRANSFER_TAB_QUESTION_ID, questionText);
283 if (clearTab) {
284 // NOTE: Can't replace with new instance because Foreign Draft uses same object
285 clearWireTransferValues(dvWireTransfer);
286 }
287 else {
288 // return to document if the user doesn't want to clear the Wire Transfer tab
289 super.event.setActionForwardName(KFSConstants.MAPPING_BASIC);
290 tabStatesOK = false;
291 }
292 }
293
294 return tabStatesOK;
295 }
296
297 /**
298 * If bank specification is enabled, prompts user to use the continuation bank code when the given bank code is inactive
299 *
300 * @param dvDocument document containing bank code
301 * @return true
302 */
303 protected boolean checkBankCodeActive(DisbursementVoucherDocument dvDocument) {
304 boolean continueRules = true;
305
306 // if bank specification is not enabled, no need to validate bank
307 if (!SpringContext.getBean(BankService.class).isBankSpecificationEnabled()) {
308 return continueRules;
309 }
310
311 // refresh bank reference so continuation bank can be checked for active status
312 dvDocument.refreshReferenceObject(KFSPropertyConstants.BANK);
313 Bank bank = dvDocument.getBank();
314
315 // if bank is inactive and continuation is active, prompt user to use continuation bank
316 if (bank != null && !bank.isActive() && bank.getContinuationBank().isActive()) {
317 String questionText = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSKeyConstants.QUESTION_BANK_INACTIVE);
318 questionText = MessageFormat.format(questionText, dvDocument.getDisbVchrBankCode(), bank.getContinuationBankCode());
319
320 boolean useContinuation = super.askOrAnalyzeYesNoQuestion(KFSConstants.USE_CONTINUATION_BANK_QUESTION, questionText);
321 if (useContinuation) {
322 dvDocument.setDisbVchrBankCode(bank.getContinuationBankCode());
323 }
324 }
325
326 return continueRules;
327 }
328
329 /**
330 * Returns true if wire transfer tab contains any data in any fields.
331 *
332 * @param dvWireTransfer disbursement voucher wire transfer
333 * @return true if wire transfer tab contains any data in any fields.
334 */
335 protected boolean hasWireTransferValues(DisbursementVoucherWireTransfer dvWireTransfer) {
336 boolean hasValues = false;
337
338 // Checks each explicit field in the tab for user entered values
339 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbursementVoucherAutomatedClearingHouseProfileNumber());
340 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbursementVoucherBankName());
341 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrBankRoutingNumber());
342 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrBankCityName());
343 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrBankStateCode());
344 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrBankCountryCode());
345 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrPayeeAccountNumber());
346 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrAttentionLineText());
347 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrCurrencyTypeName());
348 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrAdditionalWireText());
349 hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbursementVoucherPayeeAccountName());
350
351 return hasValues;
352 }
353
354 /**
355 * This method sets all values in the passed in disbursement wire transfer object to null
356 *
357 * @param dvWireTransfer
358 */
359 protected void clearWireTransferValues(DisbursementVoucherWireTransfer dvWireTransfer) {
360 dvWireTransfer.setDisbursementVoucherAutomatedClearingHouseProfileNumber(null);
361 dvWireTransfer.setDisbursementVoucherBankName(null);
362 dvWireTransfer.setDisbVchrBankRoutingNumber(null);
363 dvWireTransfer.setDisbVchrBankCityName(null);
364 dvWireTransfer.setDisbVchrBankStateCode(null);
365 dvWireTransfer.setDisbVchrBankCountryCode(null);
366 dvWireTransfer.setDisbVchrPayeeAccountNumber(null);
367 dvWireTransfer.setDisbVchrAttentionLineText(null);
368 dvWireTransfer.setDisbVchrCurrencyTypeName(null);
369 dvWireTransfer.setDisbVchrAdditionalWireText(null);
370 dvWireTransfer.setDisbursementVoucherPayeeAccountName(null);
371 }
372
373 /**
374 * put the valid payment reason codes along with their description together
375 *
376 * @param validPaymentReasonCodes the given valid payment reason codes
377 * @return the valid payment reason codes along with their description as a string
378 */
379 protected String getValidPaymentReasonsAsString(List<String> validPaymentReasonCodes) {
380 List<String> payementReasonString = new ArrayList<String>();
381
382 if (validPaymentReasonCodes == null || validPaymentReasonCodes.isEmpty()) {
383 return StringUtils.EMPTY;
384 }
385
386 List<KeyLabelPair> reasons = new PaymentReasonValuesFinder().getKeyValues();
387 for (KeyLabelPair reason : reasons) {
388 if (validPaymentReasonCodes.contains(reason.getKey())) {
389 payementReasonString.add(reason.getLabel());
390 }
391 }
392
393 return payementReasonString.toString();
394 }
395 }