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 }