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.module.purap.document.validation.impl;
017    
018    import org.kuali.kfs.coa.document.validation.impl.MaintenancePreRulesBase;
019    import org.kuali.kfs.module.purap.PurapConstants;
020    import org.kuali.kfs.module.purap.PurapKeyConstants;
021    import org.kuali.kfs.module.purap.PurapPropertyConstants;
022    import org.kuali.kfs.module.purap.businessobject.ReceivingAddress;
023    import org.kuali.kfs.module.purap.document.service.ReceivingAddressService;
024    import org.kuali.kfs.sys.context.SpringContext;
025    import org.kuali.rice.kns.document.MaintenanceDocument;
026    import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
027    import org.kuali.rice.kns.util.GlobalVariables;
028    
029    /**
030     * Business Prerules applicable to ReceivingAddressMaintenanceDocument. 
031     * These prerules check whether the maintenance action to a Receiving Address business objects abide certain constraint rules.
032     */
033    public class ReceivingAddressPreRules extends MaintenancePreRulesBase {
034    
035        /**
036         * Checks whether the maintenance action to a Receiving Address business objects abide to the contraint that, 
037         * there is one and only one active default receiving address for each chart or chart/org at any given time, 
038         * if there exists at least one active receiving address for this chart or chart/org. 
039         * If the contraint would be broken as a result of the update, the method will present an error or warning 
040         * to the user; and if proceed, enforce the rule by updating the related receiving address as well.
041         * Note: this method relies on the condition that the current status of the DB satisfies the constraints.
042         * 
043         * @see org.kuali.kfs.coa.document.validation.impl.MaintenancePreRulesBase#doCustomPreRules(org.kuali.rice.kns.document.MaintenanceDocument)
044         */
045        @Override
046        protected boolean doCustomPreRules( MaintenanceDocument document ) {                
047            ReceivingAddress raOld = ( ReceivingAddress )document.getOldMaintainableObject().getBusinessObject();
048            ReceivingAddress raNew = ( ReceivingAddress )document.getNewMaintainableObject().getBusinessObject();
049    
050            /* The fields that affect the rule are the default and active indicators. 
051             * According to the create/copy/edit action, and various combinations of updates to these two fields, 
052             * including unchanged (No->NO or Yes->Yes), set (No->Yes), unset (Yes->No), the rule checking will 
053             * proceed respectively. The following boolean variables indicates the various updates and combinations.
054             */
055            boolean isNew = document.isNew();
056            boolean isEdit = document.isEdit();
057            boolean wasActive = isEdit && raOld.isActive();        
058            boolean wasDefault = isEdit && raOld.isDefaultIndicator();
059            boolean isActive = raNew.isActive();        
060            boolean isDefault = raNew.isDefaultIndicator();
061            boolean stayActive = wasActive && isActive;
062            boolean stayDefault = wasDefault && isDefault; 
063            boolean setActive = (isNew || !wasActive) && isActive;
064            boolean unsetActive = wasActive && !isActive;
065            boolean setDefault = (isNew || !wasDefault) && isDefault;
066            boolean unsetDefault = wasDefault && !isDefault;        
067            
068            /* Check whether there're other active RA exist.
069             * We only need to search within the same chart/org group, since we don't allow editting on chart/org.
070             * However, we need to exclude the current address being edited if it is not a new one and was active.
071             */ 
072            /*
073            Map criteria = new HashMap();
074            criteria.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, raNew.getChartOfAccountsCode());
075            criteria.put(KFSPropertyConstants.ORGANIZATION_CODE, raNew.getOrganizationCode());
076            //criteria.put(PurapPropertyConstants.RCVNG_ADDR_DFLT_IND, true);        
077            criteria.put(PurapPropertyConstants.RCVNG_ADDR_ACTIVE, true);        
078            int count = SpringContext.getBean(BusinessObjectService.class).countMatching(ReceivingAddress.class, criteria);
079            */
080            int count = SpringContext.getBean(ReceivingAddressService.class).countActiveByChartOrg(raNew.getChartOfAccountsCode(),raNew.getOrganizationCode());      
081            boolean existOther = wasActive ? (count>1) : (count>0);
082                
083            /* Case 1 - adding the first active address: 
084             * Force it to be the default one.
085             */ 
086            if ( setActive && !isDefault && !existOther ) {
087                raNew.setDefaultIndicator( true );
088            }
089            /* Case 2 - switching the default address from another one to this one: 
090             * Give warning; if proceed, will unset the other default address in post-processing.
091             */
092            else if ( (stayActive && setDefault) || (setActive && isDefault && existOther) ) {
093                if (!super.askOrAnalyzeYesNoQuestion(PurapConstants.CONFIRM_CHANGE_DFLT_RVNG_ADDR, PurapConstants.CONFIRM_CHANGE_DFLT_RVNG_ADDR_TXT) ) {
094                    abortRulesCheck();
095                }
096            }
097            /* Case 3 - unsetting the default address that's still active:
098             * Give error: Can't unset the default address; you must set another default address to replace this one. 
099             */
100            else if ( stayActive && unsetDefault ) {
101                putFieldError(PurapPropertyConstants.RECEIVING_ADDRESS_DEFAULT_INDICATOR, PurapKeyConstants.ERROR_RCVNG_ADDR_UNSET_DFLT);
102                abortRulesCheck();
103                return false;
104            }
105            /* Case 4 - deactivating the default address while there're still other active ones:
106             * Give error: Can't deactivate the default address when there're still other active ones;
107             * you must set another default address first.
108             */
109            else if ( unsetActive && wasDefault && existOther ) {            
110                putFieldError(PurapPropertyConstants.BO_ACTIVE, PurapKeyConstants.ERROR_RCVNG_ADDR_DEACTIVATE_DFLT);
111                abortRulesCheck();
112                return false;
113            }         
114            /* Other cases are harmless, i.e. won't break the constraint, so we can proceed without doing anything extra.
115             */
116            
117            return true;
118        }
119     
120        /**
121         * Convenience method to add a property-specific error to the global errors list, with the correct prefix
122         * added to the property name so that it will display correctly on maintenance documents.
123         * 
124         * @param propertyName Property name of the element that is associated with the error, to mark the field as errored in the UI.
125         * @param errorConstant - Error Constant that can be mapped to a resource for the actual text message.
126         * 
127         */
128        protected void putFieldError(String propertyName, String errorConstant) {
129            GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(MaintenanceDocumentRuleBase.MAINTAINABLE_ERROR_PREFIX+propertyName, errorConstant);
130        }
131        
132    }