001/*
002 * Copyright 2008 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.sys.document.validation.impl;
017
018import java.util.Iterator;
019import java.util.Map;
020import java.util.Set;
021
022import org.apache.commons.lang.StringUtils;
023import org.kuali.ole.sys.OLEConstants;
024import org.kuali.ole.sys.OLEKeyConstants;
025import org.kuali.ole.sys.OLEPropertyConstants;
026import org.kuali.ole.sys.businessobject.AccountingLine;
027import org.kuali.ole.sys.businessobject.FinancialSystemDocumentHeader;
028import org.kuali.ole.sys.document.AccountingDocument;
029import org.kuali.ole.sys.document.Correctable;
030import org.kuali.ole.sys.document.authorization.AccountingLineAuthorizer;
031import org.kuali.ole.sys.document.authorization.AccountingLineAuthorizerBase;
032import org.kuali.ole.sys.document.datadictionary.AccountingLineGroupDefinition;
033import org.kuali.ole.sys.document.datadictionary.FinancialSystemTransactionalDocumentEntry;
034import org.kuali.ole.sys.document.validation.GenericValidation;
035import org.kuali.ole.sys.document.validation.event.AddAccountingLineEvent;
036import org.kuali.ole.sys.document.validation.event.AttributedDocumentEvent;
037import org.kuali.ole.sys.document.validation.event.DeleteAccountingLineEvent;
038import org.kuali.ole.sys.document.validation.event.UpdateAccountingLineEvent;
039import org.kuali.rice.kim.api.identity.Person;
040import org.kuali.rice.krad.service.DataDictionaryService;
041import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
042import org.kuali.rice.krad.util.GlobalVariables;
043
044/**
045 * A validation that checks whether the given accounting line is accessible to the given user or not
046 */
047public class AccountingLineAccessibleValidation extends GenericValidation {
048    protected DataDictionaryService dataDictionaryService;
049    protected AccountingDocument accountingDocumentForValidation;
050    protected AccountingLine accountingLineForValidation;
051    
052    /**
053     * Indicates what is being done to an accounting line. This allows the same method to be used for different actions.
054     */
055    public enum AccountingLineAction {
056        ADD(OLEKeyConstants.ERROR_ACCOUNTINGLINE_INACCESSIBLE_ADD), DELETE(OLEKeyConstants.ERROR_ACCOUNTINGLINE_INACCESSIBLE_DELETE), UPDATE(OLEKeyConstants.ERROR_ACCOUNTINGLINE_INACCESSIBLE_UPDATE);
057
058        public final String accessibilityErrorKey;
059
060        AccountingLineAction(String accessabilityErrorKey) {
061            this.accessibilityErrorKey = accessabilityErrorKey;
062        }
063    }
064
065    /**
066     * Validates that the given accounting line is accessible for editing by the current user.
067     * <strong>This method expects a document as the first parameter and an accounting line as the second</strong>
068     * @see org.kuali.ole.sys.document.validation.Validation#validate(java.lang.Object[])
069     */
070    public boolean validate(AttributedDocumentEvent event) {        
071        final Person currentUser = GlobalVariables.getUserSession().getPerson();
072        
073        if (accountingDocumentForValidation instanceof Correctable) {
074            final String errorDocumentNumber = ((FinancialSystemDocumentHeader)accountingDocumentForValidation.getDocumentHeader()).getFinancialDocumentInErrorNumber();
075            if (StringUtils.isNotBlank(errorDocumentNumber))
076                return true;
077        }
078        
079        final AccountingLineAuthorizer accountingLineAuthorizer = lookupAccountingLineAuthorizer();
080        final boolean lineIsAccessible = accountingLineAuthorizer.hasEditPermissionOnAccountingLine(accountingDocumentForValidation, accountingLineForValidation, getAccountingLineCollectionProperty(), currentUser, true);
081        final boolean isAccessible = accountingLineAuthorizer.hasEditPermissionOnField(accountingDocumentForValidation, accountingLineForValidation, getAccountingLineCollectionProperty(), OLEPropertyConstants.ACCOUNT_NUMBER, lineIsAccessible, true, currentUser);
082
083        // report errors
084        if (!isAccessible) {
085            final String principalName = currentUser.getPrincipalName();
086            
087            final String[] chartErrorParams = new String[] { getDataDictionaryService().getAttributeLabel(accountingLineForValidation.getClass(), OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE), accountingLineForValidation.getChartOfAccountsCode(),  principalName};
088            GlobalVariables.getMessageMap().putError(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, convertEventToMessage(event), chartErrorParams);
089            
090            final String[] accountErrorParams = new String[] { getDataDictionaryService().getAttributeLabel(accountingLineForValidation.getClass(), OLEPropertyConstants.ACCOUNT_NUMBER), accountingLineForValidation.getAccountNumber(), principalName };
091            GlobalVariables.getMessageMap().putError(OLEPropertyConstants.ACCOUNT_NUMBER, convertEventToMessage(event), accountErrorParams);
092        }
093
094        return isAccessible;
095    }
096    
097    /**
098     * Returns the name of the accounting line group which holds the proper authorizer to do the KIM check
099     * @return the name of the accouting line group to get the authorizer from
100     */
101    protected String getGroupName() {
102        return (accountingLineForValidation.isSourceAccountingLine() ? OLEConstants.SOURCE_ACCOUNTING_LINES_GROUP_NAME : OLEConstants.TARGET_ACCOUNTING_LINES_GROUP_NAME);
103    }
104    
105    /**
106     * @return hopefully, the best accounting line authorizer implementation to do the KIM check for to see if lines are accessible
107     */
108    protected AccountingLineAuthorizer lookupAccountingLineAuthorizer() {
109        final String groupName = getGroupName();
110        final Map<String, AccountingLineGroupDefinition> groups = ((FinancialSystemTransactionalDocumentEntry)dataDictionaryService.getDataDictionary().getDictionaryObjectEntry(accountingDocumentForValidation.getClass().getName())).getAccountingLineGroups();
111        
112        if (groups.isEmpty()) return new AccountingLineAuthorizerBase(); // no groups? just use the default...
113        if (groups.containsKey(groupName)) return groups.get(groupName).getAccountingLineAuthorizer(); // we've got the group
114
115        final Set<String> groupNames = groups.keySet(); // we've got groups, just not the proper name; try our luck and get the first group iterator
116        final Iterator<String> groupNameIterator = groupNames.iterator();
117        final String firstGroupName = groupNameIterator.next();
118        return groups.get(firstGroupName).getAccountingLineAuthorizer();
119    }
120    
121    /**
122     * Determines the property of the accounting line collection from the error prefixes
123     * @return the accounting line collection property
124     */
125    protected String getAccountingLineCollectionProperty() {
126        String propertyName = null;
127        if (GlobalVariables.getMessageMap().getErrorPath().size() > 0) {
128            propertyName = ((String)GlobalVariables.getMessageMap().getErrorPath().get(0)).replaceFirst(".*?document\\.", "");
129        } else {
130            propertyName = accountingLineForValidation.isSourceAccountingLine() ? OLEConstants.PermissionAttributeValue.SOURCE_ACCOUNTING_LINES.value : OLEConstants.PermissionAttributeValue.TARGET_ACCOUNTING_LINES.value;
131        }
132        if (propertyName.equals("newSourceLine")) return OLEConstants.PermissionAttributeValue.SOURCE_ACCOUNTING_LINES.value;
133        if (propertyName.equals("newTargetLine")) return OLEConstants.PermissionAttributeValue.TARGET_ACCOUNTING_LINES.value;
134        return propertyName;
135    }
136    
137    /**
138     * Determines what error message should be shown based on the event that required this validation
139     * @param event the event to use to determine the error message
140     * @return the key of the error message to display
141     */
142    protected String convertEventToMessage(KualiDocumentEvent event) {
143        if (event instanceof AddAccountingLineEvent) {
144            return AccountingLineAction.ADD.accessibilityErrorKey;
145        } else if (event instanceof UpdateAccountingLineEvent) {
146            return AccountingLineAction.UPDATE.accessibilityErrorKey;
147        } else if (event instanceof DeleteAccountingLineEvent) {
148            return AccountingLineAction.DELETE.accessibilityErrorKey;
149        } else {
150            return "";
151        }
152    }
153
154    /**
155     * Gets the accountingDocumentForValidation attribute. 
156     * @return Returns the accountingDocumentForValidation.
157     */
158    public AccountingDocument getAccountingDocumentForValidation() {
159        return accountingDocumentForValidation;
160    }
161
162    /**
163     * Sets the accountingDocumentForValidation attribute value.
164     * @param accountingDocumentForValidation The accountingDocumentForValidation to set.
165     */
166    public void setAccountingDocumentForValidation(AccountingDocument accountingDocumentForValidation) {
167        this.accountingDocumentForValidation = accountingDocumentForValidation;
168    }
169
170    /**
171     * Gets the accountingLineForValidation attribute. 
172     * @return Returns the accountingLineForValidation.
173     */
174    public AccountingLine getAccountingLineForValidation() {
175        return accountingLineForValidation;
176    }
177
178    /**
179     * Sets the accountingLineForValidation attribute value.
180     * @param accountingLineForValidation The accountingLineForValidation to set.
181     */
182    public void setAccountingLineForValidation(AccountingLine accountingLineForValidation) {
183        this.accountingLineForValidation = accountingLineForValidation;
184    }
185
186    /**
187     * Gets the dataDictionaryService attribute. 
188     * @return Returns the dataDictionaryService.
189     */
190    public DataDictionaryService getDataDictionaryService() {
191        return dataDictionaryService;
192    }
193
194    /**
195     * Sets the dataDictionaryService attribute value.
196     * @param dataDictionaryService The dataDictionaryService to set.
197     */
198    public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
199        this.dataDictionaryService = dataDictionaryService;
200    }
201    
202}
203