View Javadoc
1   /*
2    * Copyright 2005 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.fp.document.validation.impl;
17  
18  import java.text.MessageFormat;
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Set;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.kuali.ole.fp.businessobject.DisbursementVoucherNonEmployeeTravel;
25  import org.kuali.ole.fp.businessobject.DisbursementVoucherWireTransfer;
26  import org.kuali.ole.fp.businessobject.options.PaymentReasonValuesFinder;
27  import org.kuali.ole.fp.document.DisbursementVoucherConstants;
28  import org.kuali.ole.fp.document.DisbursementVoucherDocument;
29  import org.kuali.ole.sys.OLEConstants;
30  import org.kuali.ole.sys.OLEKeyConstants;
31  import org.kuali.ole.sys.OLEPropertyConstants;
32  import org.kuali.ole.sys.businessobject.Bank;
33  import org.kuali.ole.sys.context.SpringContext;
34  import org.kuali.ole.sys.service.BankService;
35  import org.kuali.rice.core.api.config.property.ConfigurationService;
36  import org.kuali.rice.core.api.util.KeyValue;
37  import org.kuali.rice.kns.rules.PromptBeforeValidationBase;
38  import org.kuali.rice.krad.document.Document;
39  import org.kuali.rice.krad.util.ObjectUtils;
40  
41  /**
42   * Checks warnings and prompt conditions for dv document.
43   */
44  public class DisbursementVoucherDocumentPreRules extends PromptBeforeValidationBase implements DisbursementVoucherConstants {
45  
46      /**
47       * Executes pre-rules for Disbursement Voucher Document
48       *
49       * @param document submitted document
50       * @return true if pre-rules execute successfully
51       * @see org.kuali.rice.kns.rules.PromptBeforeValidationBase#doRules(org.kuali.rice.kns.document.MaintenanceDocument)
52       */
53      @Override
54      public boolean doPrompts(Document document) {
55          boolean preRulesOK = true;
56  
57          DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) document;
58          checkSpecialHandlingIndicator(dvDocument);
59  
60          preRulesOK &= checkNonEmployeeTravelTabState(dvDocument);
61  
62          preRulesOK &= checkWireTransferTabState(dvDocument);
63  
64          preRulesOK &= checkForeignDraftTabState(dvDocument);
65  
66          preRulesOK &= checkBankCodeActive(dvDocument);
67  
68          return preRulesOK;
69      }
70  
71      /**
72       * If the special handling name and address 1 fields have value, this will mark the special handling indicator for the user.
73       *
74       * @param dvDocument submitted disbursement voucher document
75       */
76      protected void checkSpecialHandlingIndicator(DisbursementVoucherDocument dvDocument) {
77          if (StringUtils.isNotBlank(dvDocument.getDvPayeeDetail().getDisbVchrSpecialHandlingPersonName()) && StringUtils.isNotBlank(dvDocument.getDvPayeeDetail().getDisbVchrSpecialHandlingLine1Addr()) && allowTurningOnOfSpecialHandling(dvDocument)) {
78              dvDocument.setDisbVchrSpecialHandlingCode(true);
79          }
80      }
81  
82      /**
83       * Allows the automatic turning on of special handling indicator - which will not be allowed at the Campus route level
84       * @param dvDocument the document to allow turning on of special handling for
85       * @return true if special handling can be automatically turned on, false otherwise
86       */
87      protected boolean allowTurningOnOfSpecialHandling(DisbursementVoucherDocument dvDocument) {
88          Set<String> currentNodes = dvDocument.getDocumentHeader().getWorkflowDocument().getCurrentNodeNames();
89          return !(currentNodes.contains(DisbursementVoucherConstants.RouteLevelNames.CAMPUS));
90      }
91  
92      /**
93       * This method checks non-employee travel tab state is valid
94       *
95       * @param dvDocument submitted disbursement voucher document
96       * @return true if the state of all the tabs is valid, false otherwise.
97       */
98      protected boolean checkNonEmployeeTravelTabState(DisbursementVoucherDocument dvDocument) {
99          boolean tabStatesOK = true;
100 
101         DisbursementVoucherNonEmployeeTravel dvNonEmplTrav = dvDocument.getDvNonEmployeeTravel();
102         if (!hasNonEmployeeTravelValues(dvNonEmplTrav)) {
103             return true;
104         }
105 
106         String paymentReasonCode = dvDocument.getDvPayeeDetail().getDisbVchrPaymentReasonCode();
107 //        List<String> nonEmpltravelPaymentReasonCodes = new ArrayList<String>( SpringContext.getBean(ParameterService.class).getParameterValuesAsString(DisbursementVoucherDocument.class, NONEMPLOYEE_TRAVEL_PAY_REASONS_PARM_NM) );
108       //Modified for the jira issue OLE-3415
109         List<String> nonEmpltravelPaymentReasonCodes = new ArrayList<String>();
110 
111         if (nonEmpltravelPaymentReasonCodes == null || !nonEmpltravelPaymentReasonCodes.contains(paymentReasonCode) || dvDocument.getDvPayeeDetail().isEmployee()) {
112             String nonEmplTravReasonStr = getValidPaymentReasonsAsString(nonEmpltravelPaymentReasonCodes);
113 
114             String paymentReasonName = dvDocument.getDvPayeeDetail().getDisbVchrPaymentReasonName();
115             Object[] args = { "payment reason", "'" + paymentReasonName + "'", "Non-Employee Travel", nonEmplTravReasonStr };
116 
117             String questionText = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(OLEKeyConstants.QUESTION_CLEAR_UNNEEDED_TAB);
118             questionText = MessageFormat.format(questionText, args);
119 
120             boolean clearTab = super.askOrAnalyzeYesNoQuestion(OLEConstants.DisbursementVoucherDocumentConstants.CLEAR_NON_EMPLOYEE_TAB_QUESTION_ID, questionText);
121             if (clearTab) {
122                 DisbursementVoucherNonEmployeeTravel blankDvNonEmplTrav = new DisbursementVoucherNonEmployeeTravel();
123                 blankDvNonEmplTrav.setDocumentNumber(dvNonEmplTrav.getDocumentNumber());
124                 blankDvNonEmplTrav.setVersionNumber(dvNonEmplTrav.getVersionNumber());
125                 dvDocument.setDvNonEmployeeTravel(blankDvNonEmplTrav);
126             }
127             else {
128                 // return to document if the user doesn't want to clear the Non Employee Travel tab
129                 super.event.setActionForwardName(OLEConstants.MAPPING_BASIC);
130                 tabStatesOK = false;
131             }
132         }
133 
134         return tabStatesOK;
135     }
136 
137     /**
138      * Returns true if non-employee travel tab contains any data in any of its fields
139      *
140      * @param dvNonEmplTrav disbursement voucher non employee travel object
141      * @return True if non employee travel tab contains any data in any fields.
142      */
143     protected boolean hasNonEmployeeTravelValues(DisbursementVoucherNonEmployeeTravel dvNonEmplTrav) {
144         boolean hasValues = false;
145 
146         // Checks each explicit field in the tab for user entered values
147         hasValues = hasNonEmployeeTravelGeneralValues(dvNonEmplTrav);
148 
149         // Checks per diem section for values
150         if (!hasValues) {
151             hasValues = hasNonEmployeeTravelPerDiemValues(dvNonEmplTrav);
152         }
153 
154         if (!hasValues) {
155             hasValues = hasNonEmployeeTravelPersonalVehicleValues(dvNonEmplTrav);
156         }
157 
158         if (!hasValues) {
159             hasValues = dvNonEmplTrav.getDvNonEmployeeExpenses().size() > 0;
160         }
161 
162         if (!hasValues) {
163             hasValues = dvNonEmplTrav.getDvPrePaidEmployeeExpenses().size() > 0;
164         }
165 
166         return hasValues;
167     }
168 
169     /**
170      * Returns true if any values are not blank on non employee travel tab
171      *
172      * @param dvNonEmplTrav disbursement voucher non employee travel object
173      * @return True if any values are found in the non employee travel tab
174      */
175     protected boolean hasNonEmployeeTravelGeneralValues(DisbursementVoucherNonEmployeeTravel dvNonEmplTrav) {
176         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());
177     }
178 
179     /**
180      * Returns true if non employee travel tab contains data in any of the fields in the per diem section
181      *
182      * @param dvNonEmplTrav disbursement voucher non employee travel object
183      * @return True if non employee travel tab contains data in any of the fields in the per diem section
184      */
185     protected boolean hasNonEmployeeTravelPerDiemValues(DisbursementVoucherNonEmployeeTravel dvNonEmplTrav) {
186         return StringUtils.isNotBlank(dvNonEmplTrav.getDisbVchrPerdiemCategoryName()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDisbVchrPerdiemRate()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDisbVchrPerdiemCalculatedAmt()) || ObjectUtils.isNotNull(dvNonEmplTrav.getDisbVchrPerdiemActualAmount()) || StringUtils.isNotBlank(dvNonEmplTrav.getDvPerdiemChangeReasonText());
187     }
188 
189     /**
190      * Returns true if non employee travel tab contains data in any of the fields in the personal vehicle section
191      *
192      * @param dvNonEmplTrav disbursement voucher non employee travel object
193      * @return True if non employee travel tab contains data in any of the fields in the personal vehicle section
194      */
195     protected boolean hasNonEmployeeTravelPersonalVehicleValues(DisbursementVoucherNonEmployeeTravel dvNonEmplTrav) {
196         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());
197     }
198 
199     /**
200      * Returns true if the state of all the tabs is valid, false otherwise.
201      *
202      * @param dvDocument submitted disbursemtn voucher document
203      * @return true if the state of all the tabs is valid, false otherwise.
204      */
205     protected boolean checkForeignDraftTabState(DisbursementVoucherDocument dvDocument) {
206         boolean tabStatesOK = true;
207 
208         DisbursementVoucherWireTransfer dvForeignDraft = dvDocument.getDvWireTransfer();
209 
210         // if payment method is CHECK and wire tab contains data, ask user to clear tab
211         if ((StringUtils.equals(DisbursementVoucherConstants.PAYMENT_METHOD_CHECK, dvDocument.getDisbVchrPaymentMethodCode()) || StringUtils.equals(DisbursementVoucherConstants.PAYMENT_METHOD_WIRE, dvDocument.getDisbVchrPaymentMethodCode())) && hasForeignDraftValues(dvForeignDraft)) {
212             String questionText = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(OLEKeyConstants.QUESTION_CLEAR_UNNEEDED_TAB);
213 
214             Object[] args = { "payment method", dvDocument.getDisbVchrPaymentMethodCode(), "Foreign Draft", DisbursementVoucherConstants.PAYMENT_METHOD_DRAFT };
215             questionText = MessageFormat.format(questionText, args);
216 
217             boolean clearTab = super.askOrAnalyzeYesNoQuestion(OLEConstants.DisbursementVoucherDocumentConstants.CLEAR_FOREIGN_DRAFT_TAB_QUESTION_ID, questionText);
218             if (clearTab) {
219                 // NOTE: Can't replace with new instance because Wire Transfer uses same object
220                 clearForeignDraftValues(dvForeignDraft);
221             }
222             else {
223                 // return to document if the user doesn't want to clear the Wire Transfer tab
224                 super.event.setActionForwardName(OLEConstants.MAPPING_BASIC);
225                 tabStatesOK = false;
226             }
227         }
228 
229         return tabStatesOK;
230     }
231 
232     /**
233      * Returns true if foreign draft tab contains any data in any fields. NOTE: Currently does not validate based on only required
234      * fields. Checks all fields within tab for data.
235      *
236      * @param dvForeignDraft disbursement foreign draft object
237      * @return True if foreign draft tab contains any data in any fields.
238      */
239     protected boolean hasForeignDraftValues(DisbursementVoucherWireTransfer dvForeignDraft) {
240         boolean hasValues = false;
241 
242         // Checks each explicit field in the tab for user entered values
243         hasValues |= StringUtils.isNotBlank(dvForeignDraft.getDisbursementVoucherForeignCurrencyTypeCode());
244         hasValues |= StringUtils.isNotBlank(dvForeignDraft.getDisbursementVoucherForeignCurrencyTypeName());
245 
246         return hasValues;
247     }
248 
249     /**
250      * This method sets foreign currency type code and name to null for passed in disbursement foreign draft object
251      *
252      * @param dvForeignDraft disbursement foreign draft object
253      */
254     protected void clearForeignDraftValues(DisbursementVoucherWireTransfer dvForeignDraft) {
255         dvForeignDraft.setDisbursementVoucherForeignCurrencyTypeCode(null);
256         dvForeignDraft.setDisbursementVoucherForeignCurrencyTypeName(null);
257     }
258 
259     /**
260      * This method returns true if the state of all the tabs is valid, false otherwise.
261      *
262      * @param dvDocument submitted disbursement voucher document
263      * @return Returns true if the state of all the tabs is valid, false otherwise.
264      */
265     protected boolean checkWireTransferTabState(DisbursementVoucherDocument dvDocument) {
266         boolean tabStatesOK = true;
267 
268         DisbursementVoucherWireTransfer dvWireTransfer = dvDocument.getDvWireTransfer();
269 
270         // if payment method is CHECK and wire tab contains data, ask user to clear tab
271         if ((StringUtils.equals(DisbursementVoucherConstants.PAYMENT_METHOD_CHECK, dvDocument.getDisbVchrPaymentMethodCode()) || StringUtils.equals(DisbursementVoucherConstants.PAYMENT_METHOD_DRAFT, dvDocument.getDisbVchrPaymentMethodCode())) && hasWireTransferValues(dvWireTransfer)) {
272             String questionText = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(OLEKeyConstants.QUESTION_CLEAR_UNNEEDED_TAB);
273 
274             Object[] args = { "payment method", dvDocument.getDisbVchrPaymentMethodCode(), "Wire Transfer", DisbursementVoucherConstants.PAYMENT_METHOD_WIRE };
275             questionText = MessageFormat.format(questionText, args);
276 
277             boolean clearTab = super.askOrAnalyzeYesNoQuestion(OLEConstants.DisbursementVoucherDocumentConstants.CLEAR_WIRE_TRANSFER_TAB_QUESTION_ID, questionText);
278             if (clearTab) {
279                 // NOTE: Can't replace with new instance because Foreign Draft uses same object
280                 clearWireTransferValues(dvWireTransfer);
281             }
282             else {
283                 // return to document if the user doesn't want to clear the Wire Transfer tab
284                 super.event.setActionForwardName(OLEConstants.MAPPING_BASIC);
285                 tabStatesOK = false;
286             }
287         }
288 
289         return tabStatesOK;
290     }
291 
292     /**
293      * If bank specification is enabled, prompts user to use the continuation bank code when the given bank code is inactive
294      *
295      * @param dvDocument document containing bank code
296      * @return true
297      */
298     protected boolean checkBankCodeActive(DisbursementVoucherDocument dvDocument) {
299         boolean continueRules = true;
300 
301         // if bank specification is not enabled, no need to validate bank
302         if (!SpringContext.getBean(BankService.class).isBankSpecificationEnabled()) {
303             return continueRules;
304         }
305 
306         // refresh bank reference so continuation bank can be checked for active status
307         dvDocument.refreshReferenceObject(OLEPropertyConstants.BANK);
308         Bank bank = dvDocument.getBank();
309 
310         // if bank is inactive and continuation is active, prompt user to use continuation bank
311         if (bank != null && !bank.isActive() && bank.getContinuationBank().isActive()) {
312             String questionText = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(OLEKeyConstants.QUESTION_BANK_INACTIVE);
313             questionText = MessageFormat.format(questionText, dvDocument.getDisbVchrBankCode(), bank.getContinuationBankCode());
314 
315             boolean useContinuation = super.askOrAnalyzeYesNoQuestion(OLEConstants.USE_CONTINUATION_BANK_QUESTION, questionText);
316             if (useContinuation) {
317                 dvDocument.setDisbVchrBankCode(bank.getContinuationBankCode());
318             }
319         }
320 
321         return continueRules;
322     }
323 
324     /**
325      * Returns true if wire transfer tab contains any data in any fields.
326      *
327      * @param dvWireTransfer disbursement voucher wire transfer
328      * @return true if wire transfer tab contains any data in any fields.
329      */
330     protected boolean hasWireTransferValues(DisbursementVoucherWireTransfer dvWireTransfer) {
331         boolean hasValues = false;
332 
333         // Checks each explicit field in the tab for user entered values
334         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbursementVoucherAutomatedClearingHouseProfileNumber());
335         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbursementVoucherBankName());
336         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrBankRoutingNumber());
337         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrBankCityName());
338         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrBankStateCode());
339         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrBankCountryCode());
340         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrPayeeAccountNumber());
341         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrAttentionLineText());
342         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrCurrencyTypeName());
343         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbVchrAdditionalWireText());
344         hasValues |= StringUtils.isNotBlank(dvWireTransfer.getDisbursementVoucherPayeeAccountName());
345 
346         return hasValues;
347     }
348 
349     /**
350      * This method sets all values in the passed in disbursement wire transfer object to null
351      *
352      * @param dvWireTransfer
353      */
354     protected void clearWireTransferValues(DisbursementVoucherWireTransfer dvWireTransfer) {
355         dvWireTransfer.setDisbursementVoucherAutomatedClearingHouseProfileNumber(null);
356         dvWireTransfer.setDisbursementVoucherBankName(null);
357         dvWireTransfer.setDisbVchrBankRoutingNumber(null);
358         dvWireTransfer.setDisbVchrBankCityName(null);
359         dvWireTransfer.setDisbVchrBankStateCode(null);
360         dvWireTransfer.setDisbVchrBankCountryCode(null);
361         dvWireTransfer.setDisbVchrPayeeAccountNumber(null);
362         dvWireTransfer.setDisbVchrAttentionLineText(null);
363         dvWireTransfer.setDisbVchrCurrencyTypeName(null);
364         dvWireTransfer.setDisbVchrAdditionalWireText(null);
365         dvWireTransfer.setDisbursementVoucherPayeeAccountName(null);
366     }
367 
368     /**
369      * put the valid payment reason codes along with their description together
370      *
371      * @param validPaymentReasonCodes the given valid payment reason codes
372      * @return the valid payment reason codes along with their description as a string
373      */
374     protected String getValidPaymentReasonsAsString(List<String> validPaymentReasonCodes) {
375         List<String> payementReasonString = new ArrayList<String>();
376 
377         if (validPaymentReasonCodes == null || validPaymentReasonCodes.isEmpty()) {
378             return StringUtils.EMPTY;
379         }
380 
381         List<KeyValue> reasons = new PaymentReasonValuesFinder().getKeyValues();
382         for (KeyValue reason : reasons) {
383             if (validPaymentReasonCodes.contains(reason.getKey())) {
384                 payementReasonString.add(reason.getValue());
385             }
386         }
387 
388         return payementReasonString.toString();
389     }
390 }