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.sys.document.validation.impl;
017
018 import java.util.Iterator;
019 import java.util.Map;
020 import java.util.Set;
021
022 import org.apache.commons.lang.StringUtils;
023 import org.kuali.kfs.sys.KFSConstants;
024 import org.kuali.kfs.sys.KFSKeyConstants;
025 import org.kuali.kfs.sys.KFSPropertyConstants;
026 import org.kuali.kfs.sys.businessobject.AccountingLine;
027 import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
028 import org.kuali.kfs.sys.document.AccountingDocument;
029 import org.kuali.kfs.sys.document.Correctable;
030 import org.kuali.kfs.sys.document.authorization.AccountingLineAuthorizer;
031 import org.kuali.kfs.sys.document.authorization.AccountingLineAuthorizerBase;
032 import org.kuali.kfs.sys.document.datadictionary.AccountingLineGroupDefinition;
033 import org.kuali.kfs.sys.document.datadictionary.FinancialSystemTransactionalDocumentEntry;
034 import org.kuali.kfs.sys.document.validation.GenericValidation;
035 import org.kuali.kfs.sys.document.validation.event.AddAccountingLineEvent;
036 import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
037 import org.kuali.kfs.sys.document.validation.event.DeleteAccountingLineEvent;
038 import org.kuali.kfs.sys.document.validation.event.UpdateAccountingLineEvent;
039 import org.kuali.rice.kim.bo.Person;
040 import org.kuali.rice.kns.rule.event.KualiDocumentEvent;
041 import org.kuali.rice.kns.service.DataDictionaryService;
042 import org.kuali.rice.kns.util.GlobalVariables;
043
044 /**
045 * A validation that checks whether the given accounting line is accessible to the given user or not
046 */
047 public 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(KFSKeyConstants.ERROR_ACCOUNTINGLINE_INACCESSIBLE_ADD), DELETE(KFSKeyConstants.ERROR_ACCOUNTINGLINE_INACCESSIBLE_DELETE), UPDATE(KFSKeyConstants.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.kfs.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(), KFSPropertyConstants.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(), KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE), accountingLineForValidation.getChartOfAccountsCode(), principalName};
088 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, convertEventToMessage(event), chartErrorParams);
089
090 final String[] accountErrorParams = new String[] { getDataDictionaryService().getAttributeLabel(accountingLineForValidation.getClass(), KFSPropertyConstants.ACCOUNT_NUMBER), accountingLineForValidation.getAccountNumber(), principalName };
091 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.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() ? KFSConstants.SOURCE_ACCOUNTING_LINES_GROUP_NAME : KFSConstants.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() ? KFSConstants.PermissionAttributeValue.SOURCE_ACCOUNTING_LINES.value : KFSConstants.PermissionAttributeValue.TARGET_ACCOUNTING_LINES.value;
131 }
132 if (propertyName.equals("newSourceLine")) return KFSConstants.PermissionAttributeValue.SOURCE_ACCOUNTING_LINES.value;
133 if (propertyName.equals("newTargetLine")) return KFSConstants.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