001/*
002 * The Kuali Financial System, a comprehensive financial management system for higher education.
003 * 
004 * Copyright 2005-2014 The Kuali Foundation
005 * 
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU Affero General Public License as
008 * published by the Free Software Foundation, either version 3 of the
009 * License, or (at your option) any later version.
010 * 
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU Affero General Public License for more details.
015 * 
016 * You should have received a copy of the GNU Affero General Public License
017 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package org.kuali.kfs.pdp.document.validation.impl;
020
021import java.util.Collection;
022import java.util.HashMap;
023import java.util.Map;
024
025import org.apache.commons.lang.StringUtils;
026import org.apache.log4j.Logger;
027import org.kuali.kfs.pdp.PdpConstants;
028import org.kuali.kfs.pdp.PdpKeyConstants;
029import org.kuali.kfs.pdp.PdpPropertyConstants;
030import org.kuali.kfs.pdp.businessobject.CustomerBank;
031import org.kuali.kfs.pdp.businessobject.CustomerProfile;
032import org.kuali.kfs.sys.businessobject.Bank;
033import org.kuali.kfs.sys.context.SpringContext;
034import org.kuali.rice.kns.document.MaintenanceDocument;
035import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
036import org.kuali.rice.krad.bo.PersistableBusinessObject;
037import org.kuali.rice.krad.service.BusinessObjectService;
038import org.kuali.rice.krad.util.GlobalVariables;
039import org.kuali.rice.krad.util.KRADConstants;
040import org.kuali.rice.krad.util.MessageMap;
041import org.kuali.rice.krad.util.ObjectUtils;
042import org.kuali.rice.location.api.postalcode.PostalCode;
043import org.kuali.rice.location.api.postalcode.PostalCodeService;
044
045public class CustomerProfileRule extends MaintenanceDocumentRuleBase {
046    protected static Logger LOG = org.apache.log4j.Logger.getLogger(CustomerProfileRule.class);
047
048    /**
049     * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument, java.lang.String, org.kuali.rice.krad.bo.PersistableBusinessObject)
050     */
051    @Override
052    public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject line) {
053        boolean isValid = true;
054        isValid &= super.processCustomAddCollectionLineBusinessRules(document, collectionName, line);
055        MessageMap errorMap = GlobalVariables.getMessageMap();
056        isValid &= !errorMap.hasErrors();
057
058        if (isValid) {
059            if (collectionName.equals(PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_BANKS)) {
060                CustomerBank newCustomerBank = (CustomerBank) line;
061                if(newCustomerBank.getDisbursementTypeCode()!=null){
062                    // does the same disbursement type code exist?
063                    CustomerProfile customerProfile = (CustomerProfile) document.getNewMaintainableObject().getBusinessObject();
064                    for (CustomerBank bank : customerProfile.getCustomerBanks()) {
065                        if (bank.getDisbursementTypeCode().equalsIgnoreCase(newCustomerBank.getDisbursementTypeCode())) {
066                            errorMap.putError(PdpPropertyConstants.DISBURSEMENT_TYPE_CODE, PdpKeyConstants.ERROR_ONE_BANK_PER_DISBURSEMENT_TYPE_CODE);
067                            isValid = false;
068                        }
069                    }
070                }
071                if(newCustomerBank.getBank()!=null){
072                    // check if the bank code type is allowed
073                    newCustomerBank.refreshReferenceObject(PdpPropertyConstants.CUSTOMER_BANK);
074                    Bank newBank = newCustomerBank.getBank();
075                    if (newCustomerBank.getDisbursementTypeCode().equalsIgnoreCase(PdpConstants.DisbursementTypeCodes.ACH) && !newBank.isBankAchIndicator()) {
076                        errorMap.putError(PdpPropertyConstants.DISBURSEMENT_TYPE_CODE, PdpKeyConstants.ERROR_PDP_ACH_BANK_NOT_ALLOWED, newBank.getBankCode() + " (" + newBank.getBankName() + ")");
077                        isValid = false;
078                    }
079                    if (newCustomerBank.getDisbursementTypeCode().equalsIgnoreCase(PdpConstants.DisbursementTypeCodes.CHECK) && !newBank.isBankCheckIndicator()) {
080                        errorMap.putError(PdpPropertyConstants.DISBURSEMENT_TYPE_CODE, PdpKeyConstants.ERROR_PDP_CHECK_BANK_NOT_ALLOWED, newBank.getBankCode() + " (" + newBank.getBankName() + ")");
081                        isValid = false;
082                    }
083                }
084            }
085        }
086        return isValid;
087    }
088
089    /**
090     * KFSMI-3771
091     * This method checks if the Disbursement Bank information is provided in the Customer profile.
092     * If Bank information is provided; It's required that a Check bank is present.
093     * If the Customer profile has ACH disbursement type information provided, it should have an ACH bank Information as well.
094     * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
095     * @param MaintenanceDocument  - CustomerProfileMaintenanceDocument
096     * @return boolean - true if business rules are satisfied; false otherwise.
097     */
098    @Override
099    protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
100
101        boolean isValid = true;
102        isValid &= super.processCustomRouteDocumentBusinessRules(document);
103
104        CustomerProfile customerProfile = (CustomerProfile) document.getNewMaintainableObject().getBusinessObject();
105
106        if (customerProfile != null) {
107            customerProfile.refreshNonUpdateableReferences();
108            setStateFromZip(customerProfile);
109        }
110
111        boolean checkBankPresent = false;
112        boolean ACHBankPresent = false;
113
114        //Check if the customer profile has ACH transaction type information present.
115        boolean customerHasACHType = customerProfile.getAchTransactionType() != null ? true : false;
116
117        //Check if customer profile has bank information
118        if (customerProfile.getCustomerBanks().isEmpty()){
119            putFieldError(KRADConstants.MAINTENANCE_ADD_PREFIX+"customerBanks."+PdpPropertyConstants.DISBURSEMENT_TYPE_CODE, PdpKeyConstants.Format.ErrorMessages.ERROR_FORMAT_BANK_MISSING, customerProfile.getId().toString());
120
121            isValid = false;
122        }else{
123            for (CustomerBank customerBank : customerProfile.getCustomerBanks()){
124
125                // check if the bank code type is allowed
126                Bank bank = customerBank.getBank();
127                if (customerBank.getDisbursementTypeCode().equalsIgnoreCase(PdpConstants.DisbursementTypeCodes.ACH) && !bank.isBankAchIndicator()) {
128                    putFieldError(PdpPropertyConstants.DISBURSEMENT_TYPE_CODE, PdpKeyConstants.ERROR_PDP_ACH_BANK_NOT_ALLOWED, bank.getBankCode() + " (" + bank.getBankName() + ")");
129                    isValid = false;
130                }
131                if (customerBank.getDisbursementTypeCode().equalsIgnoreCase(PdpConstants.DisbursementTypeCodes.CHECK) && !bank.isBankCheckIndicator()) {
132                    putFieldError(PdpPropertyConstants.DISBURSEMENT_TYPE_CODE, PdpKeyConstants.ERROR_PDP_CHECK_BANK_NOT_ALLOWED, bank.getBankCode() + " (" + bank.getBankName() + ")");
133                    isValid = false;
134                }
135
136                //Check if customer profile has Check type Bank information
137                if(customerBank.getDisbursementTypeCode().equalsIgnoreCase(PdpConstants.DisbursementTypeCodes.CHECK)){
138                    checkBankPresent = true;
139                }
140                //Check if customer profile has ACH type Bank information
141                if(customerBank.getDisbursementTypeCode().equalsIgnoreCase(PdpConstants.DisbursementTypeCodes.ACH)){
142                        ACHBankPresent = true;
143                 }
144                if(checkBankPresent && ACHBankPresent){
145                    break;
146                }
147            }
148
149            //Generate error message if check bank is not present.
150            if(!checkBankPresent){
151                isValid = false;
152                putFieldError(KRADConstants.MAINTENANCE_ADD_PREFIX+"customerBanks."+PdpPropertyConstants.DISBURSEMENT_TYPE_CODE, PdpKeyConstants.ERROR_PDP_CHECK_BANK_REQUIRED);
153            }
154
155            //Generate error message if ACH bank is not present for profile with ACH disbursement type.
156            if(customerHasACHType && !ACHBankPresent){
157                isValid = false;
158                putFieldError(KRADConstants.MAINTENANCE_ADD_PREFIX+"customerBanks."+PdpPropertyConstants.DISBURSEMENT_TYPE_CODE, PdpKeyConstants.ERROR_PDP_ACH_BANK_REQUIRED);
159            }
160
161        }
162
163        // KFSMI-5158 Uniqueness check only for new Customer Profiles
164        if (document.isNew()) {
165            isValid &= verifyChartUnitSubUnitIsUnique(customerProfile);
166        }
167
168        return isValid;
169    }
170
171    //KFSMI-8330
172    /**
173     * sets city name and state code on the form
174     *
175     * @param customerProfile
176     */
177    protected void setStateFromZip(CustomerProfile customerProfile) {
178        if (StringUtils.isNotBlank(customerProfile.getZipCode()) && StringUtils.isNotBlank(customerProfile.getCountryCode()) ) {
179            PostalCode zip = SpringContext.getBean(PostalCodeService.class).getPostalCode( customerProfile.getCountryCode(), customerProfile.getZipCode() );
180
181            // If user enters a valid zip code, override city name and state code entered by user
182            if (ObjectUtils.isNotNull(zip)) { // override old user inputs
183                customerProfile.setCity(zip.getCityName());
184                customerProfile.setStateCode(zip.getStateCode());
185            }
186        }
187    }
188
189    /**
190     * Verifies that the chart/unit/sub-unit combination on this customer profile is unique
191     * @param customerProfile the customer profile to check
192     * @return true if the chart/unit/sub-unit is unique, false otherwise
193     */
194    protected boolean verifyChartUnitSubUnitIsUnique(CustomerProfile customerProfile) {
195        boolean result = true;
196
197        if (!StringUtils.isBlank(customerProfile.getChartCode()) && !StringUtils.isBlank(customerProfile.getUnitCode()) && !StringUtils.isBlank(customerProfile.getSubUnitCode())) {
198            final BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
199            Map<String, Object> searchKeys = new HashMap<String, Object>();
200            searchKeys.put(PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_CHART_CODE, customerProfile.getChartCode());
201            searchKeys.put(PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_UNIT_CODE, customerProfile.getUnitCode());
202            searchKeys.put(PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_SUB_UNIT_CODE, customerProfile.getSubUnitCode());
203
204            final Collection foundCustomerProfiles = businessObjectService.findMatching(CustomerProfile.class, searchKeys);
205            if (foundCustomerProfiles != null && foundCustomerProfiles.size() > 0) {
206                result = false;
207                putFieldError(PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_UNIT_CODE, PdpKeyConstants.ERROR_CUSTOMER_PROFILE_CHART_UNIT_SUB_UNIT_NOT_UNIQUE, new String[] { customerProfile.getChartCode(), customerProfile.getUnitCode(), customerProfile.getSubUnitCode()});
208            }
209        }
210
211        return result;
212    }
213
214}