001/*
002 * Copyright 2005 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 */
016package org.kuali.ole.fp.document.validation.impl;
017
018import java.text.MessageFormat;
019import java.util.ArrayList;
020import java.util.List;
021import java.util.Set;
022
023import org.apache.commons.lang.StringUtils;
024import org.kuali.ole.fp.businessobject.DisbursementVoucherNonEmployeeTravel;
025import org.kuali.ole.fp.businessobject.DisbursementVoucherWireTransfer;
026import org.kuali.ole.fp.businessobject.options.PaymentReasonValuesFinder;
027import org.kuali.ole.fp.document.DisbursementVoucherConstants;
028import org.kuali.ole.fp.document.DisbursementVoucherDocument;
029import org.kuali.ole.sys.OLEConstants;
030import org.kuali.ole.sys.OLEKeyConstants;
031import org.kuali.ole.sys.OLEPropertyConstants;
032import org.kuali.ole.sys.businessobject.Bank;
033import org.kuali.ole.sys.context.SpringContext;
034import org.kuali.ole.sys.service.BankService;
035import org.kuali.rice.core.api.config.property.ConfigurationService;
036import org.kuali.rice.core.api.util.KeyValue;
037import org.kuali.rice.kns.rules.PromptBeforeValidationBase;
038import org.kuali.rice.krad.document.Document;
039import org.kuali.rice.krad.util.ObjectUtils;
040
041/**
042 * Checks warnings and prompt conditions for dv document.
043 */
044public class DisbursementVoucherDocumentPreRules extends PromptBeforeValidationBase implements DisbursementVoucherConstants {
045
046    /**
047     * Executes pre-rules for Disbursement Voucher Document
048     *
049     * @param document submitted document
050     * @return true if pre-rules execute successfully
051     * @see org.kuali.rice.kns.rules.PromptBeforeValidationBase#doRules(org.kuali.rice.kns.document.MaintenanceDocument)
052     */
053    @Override
054    public boolean doPrompts(Document document) {
055        boolean preRulesOK = true;
056
057        DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) document;
058        checkSpecialHandlingIndicator(dvDocument);
059
060        preRulesOK &= checkNonEmployeeTravelTabState(dvDocument);
061
062        preRulesOK &= checkWireTransferTabState(dvDocument);
063
064        preRulesOK &= checkForeignDraftTabState(dvDocument);
065
066        preRulesOK &= checkBankCodeActive(dvDocument);
067
068        return preRulesOK;
069    }
070
071    /**
072     * If the special handling name and address 1 fields have value, this will mark the special handling indicator for the user.
073     *
074     * @param dvDocument submitted disbursement voucher document
075     */
076    protected void checkSpecialHandlingIndicator(DisbursementVoucherDocument dvDocument) {
077        if (StringUtils.isNotBlank(dvDocument.getDvPayeeDetail().getDisbVchrSpecialHandlingPersonName()) && StringUtils.isNotBlank(dvDocument.getDvPayeeDetail().getDisbVchrSpecialHandlingLine1Addr()) && allowTurningOnOfSpecialHandling(dvDocument)) {
078            dvDocument.setDisbVchrSpecialHandlingCode(true);
079        }
080    }
081
082    /**
083     * Allows the automatic turning on of special handling indicator - which will not be allowed at the Campus route level
084     * @param dvDocument the document to allow turning on of special handling for
085     * @return true if special handling can be automatically turned on, false otherwise
086     */
087    protected boolean allowTurningOnOfSpecialHandling(DisbursementVoucherDocument dvDocument) {
088        Set<String> currentNodes = dvDocument.getDocumentHeader().getWorkflowDocument().getCurrentNodeNames();
089        return !(currentNodes.contains(DisbursementVoucherConstants.RouteLevelNames.CAMPUS));
090    }
091
092    /**
093     * This method checks non-employee travel tab state is valid
094     *
095     * @param dvDocument submitted disbursement voucher document
096     * @return true if the state of all the tabs is valid, false otherwise.
097     */
098    protected boolean checkNonEmployeeTravelTabState(DisbursementVoucherDocument dvDocument) {
099        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}