001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.chart.rules;
017:
018: import org.apache.commons.lang.StringUtils;
019: import org.kuali.core.bo.PersistableBusinessObject;
020: import org.kuali.core.bo.user.UniversalUser;
021: import org.kuali.core.document.MaintenanceDocument;
022: import org.kuali.core.exceptions.UserNotFoundException;
023: import org.kuali.core.maintenance.rules.MaintenanceDocumentRuleBase;
024: import org.kuali.core.service.UniversalUserService;
025: import org.kuali.core.util.GlobalVariables;
026: import org.kuali.core.util.KualiDecimal;
027: import org.kuali.core.util.ObjectUtils;
028: import org.kuali.kfs.KFSConstants;
029: import org.kuali.kfs.KFSKeyConstants;
030: import org.kuali.kfs.context.SpringContext;
031: import org.kuali.kfs.service.ParameterService;
032: import org.kuali.module.chart.bo.Delegate;
033: import org.kuali.module.chart.bo.OrganizationRoutingModel;
034: import org.kuali.module.chart.bo.OrganizationRoutingModelName;
035:
036: /**
037: * This class implements the business rules specific to the {@link OrganizationRoutingModelName} Maintenance Document.
038: */
039: public class OrganizationRoutingModelRule extends
040: MaintenanceDocumentRuleBase {
041:
042: private OrganizationRoutingModelName model;
043:
044: private static final KualiDecimal ZERO = new KualiDecimal(0);
045: private static final String ORG_ROUTING_MODEL_PREFIX = "organizationRoutingModel";
046:
047: /**
048: * Constructs a OrganizationRoutingModelRule
049: */
050: public OrganizationRoutingModelRule() {
051: }
052:
053: /**
054: * This method sets the convenience objects like model, so you have short and easy handles to the new and
055: * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load
056: * all sub-objects from the DB by their primary keys, if available.
057: * @see org.kuali.core.maintenance.rules.MaintenanceDocumentRuleBase#setupConvenienceObjects()
058: */
059: @Override
060: public void setupConvenienceObjects() {
061: model = (OrganizationRoutingModelName) super .getNewBo();
062: for (OrganizationRoutingModel delegateModel : model
063: .getOrganizationRoutingModel()) {
064: delegateModel.refreshNonUpdateableReferences();
065: }
066: }
067:
068: /**
069: * This performs rules checks on document approve
070: * <ul>
071: * <li>{@link OrganizationRoutingModelRule#checkSimpleRules(OrganizationRoutingModelName)}</li>
072: * </ul>
073: * This rule fails on business rule failures
074: * @see org.kuali.core.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.core.document.MaintenanceDocument)
075: */
076: @Override
077: protected boolean processCustomApproveDocumentBusinessRules(
078: MaintenanceDocument document) {
079: setupConvenienceObjects();
080: return checkSimpleRules(this .model);
081: }
082:
083: /**
084: * This performs rules checks on document route
085: * <ul>
086: * <li>{@link OrganizationRoutingModelRule#checkSimpleRules(OrganizationRoutingModelName)}</li>
087: * </ul>
088: * This rule fails on business rule failures
089: * @see org.kuali.core.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.core.document.MaintenanceDocument)
090: */
091: @Override
092: protected boolean processCustomRouteDocumentBusinessRules(
093: MaintenanceDocument document) {
094: setupConvenienceObjects();
095: return checkSimpleRules(this .model);
096: }
097:
098: /**
099: * This performs rules checks on document save
100: * <ul>
101: * <li>{@link OrganizationRoutingModelRule#checkSimpleRules(OrganizationRoutingModelName)}</li>
102: * </ul>
103: * This rule does not fail on business rule failures
104: * @see org.kuali.core.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.core.document.MaintenanceDocument)
105: */
106: @Override
107: protected boolean processCustomSaveDocumentBusinessRules(
108: MaintenanceDocument document) {
109: setupConvenienceObjects();
110: checkSimpleRules(this .model);
111: return true;
112: }
113:
114: /**
115: * This method calls
116: * <ul>
117: * <li>{@link OrganizationRoutingModelRule#checkSimpleRulesForOrganizationRoutingModel(OrganizationRoutingModelName, OrganizationRoutingModel)}</li>
118: * </ul>
119: * @see org.kuali.core.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.core.document.MaintenanceDocument,
120: * java.lang.String, org.kuali.core.bo.PersistableBusinessObject)
121: */
122: @Override
123: public boolean processCustomAddCollectionLineBusinessRules(
124: MaintenanceDocument document, String collectionName,
125: PersistableBusinessObject line) {
126: setupConvenienceObjects();
127: return checkSimpleRulesForOrganizationRoutingModel(this .model,
128: (OrganizationRoutingModel) line);
129: }
130:
131: /**
132: * Checks the given rules against the entire Organization Routing Model parent.
133: *
134: * @param globalDelegateTemplate the Organization Routing Model parent to check
135: * @return true if document passes all rules, false if otherwise
136: */
137: protected boolean checkSimpleRules(
138: OrganizationRoutingModelName globalDelegateTemplate) {
139: boolean success = true;
140:
141: success &= checkModelNameHasAtLeastOneModel(globalDelegateTemplate);
142:
143: int line = 0;
144: for (OrganizationRoutingModel delegateModel : globalDelegateTemplate
145: .getOrganizationRoutingModel()) {
146: GlobalVariables.getErrorMap().addToErrorPath(
147: MAINTAINABLE_ERROR_PATH
148: + ".organizationRoutingModel[" + line
149: + "].");
150: success &= checkSimpleRulesForOrganizationRoutingModel(
151: globalDelegateTemplate, delegateModel);
152: GlobalVariables.getErrorMap().addToErrorPath(
153: MAINTAINABLE_ERROR_PATH
154: + ".organizationRoutingModel[" + line
155: + "].");
156: line++;
157: }
158: return success;
159: }
160:
161: /**
162: * This method checks a series of basic rules for a single org routing model.
163: *
164: * @return true if model passes all the checks, false if otherwise
165: */
166: protected boolean checkSimpleRulesForOrganizationRoutingModel(
167: OrganizationRoutingModelName globalDelegateTemplate,
168: OrganizationRoutingModel delegateModel) {
169: boolean success = true;
170:
171: if (delegateModel.isActive()) {
172: success &= checkDelegateFromAmountPositive(delegateModel);
173: success &= checkDelegateToAmountNotNull(delegateModel);
174: success &= checkDelegateToAmountGreaterThanFromAmount(delegateModel);
175: success &= checkDelegateUserRules(delegateModel);
176: success &= checkPrimaryRoutePerDocType(
177: globalDelegateTemplate, delegateModel);
178: }
179:
180: return success;
181: }
182:
183: /**
184: * This method makes certain that the collection of account delegates in the "mo itdel" has at least one account delegate
185: * template in it.
186: *
187: * @param globalDelegateTemplate the account delegate model to check
188: * @return true if account delegate model has at least one account delegate template in it
189: */
190: protected boolean checkModelNameHasAtLeastOneModel(
191: OrganizationRoutingModelName globalDelegateTemplate) {
192: boolean success = true;
193: if (globalDelegateTemplate.getOrganizationRoutingModel().size() == 0) {
194: success = false;
195: GlobalVariables
196: .getErrorMap()
197: .putError(
198: KFSConstants.MAINTENANCE_NEW_MAINTAINABLE
199: + "add.organizationRoutingModel.financialDocumentTypeCode",
200: KFSKeyConstants.ERROR_DOCUMENT_DELEGATE_CHANGE_NO_DELEGATE,
201: new String[0]);
202: }
203: return success;
204: }
205:
206: /**
207: * This method checks that the account delegate model has at least one active "model" within it.
208: *
209: * @param globalDelegateTemplate the account delegate model to check
210: * @return true if account delegate model has at least one active model in it.
211: */
212: // method not currently in use, as per Bill's comments in KULRNE-4805
213: protected boolean checkModelNameHasAtLeastOneActiveModel(
214: OrganizationRoutingModelName globalDelegateTemplate) {
215: boolean success = true;
216: int activeModelCount = 0;
217:
218: for (OrganizationRoutingModel mdl : globalDelegateTemplate
219: .getOrganizationRoutingModel()) {
220: if (mdl.isActive()) {
221: activeModelCount++;
222: }
223: }
224:
225: if (activeModelCount == 0) {
226: success = false;
227: if (globalDelegateTemplate.getOrganizationRoutingModel()
228: .size() == 0) {
229: GlobalVariables
230: .getErrorMap()
231: .putError(
232: KFSConstants.MAINTENANCE_NEW_MAINTAINABLE
233: + "add.organizationRoutingModel.active",
234: KFSKeyConstants.ERROR_DOCUMENT_DELEGATE_CHANGE_NO_ACTIVE_DELEGATE,
235: new String[0]);
236: } else {
237: GlobalVariables
238: .getErrorMap()
239: .putError(
240: KFSConstants.MAINTENANCE_NEW_MAINTAINABLE
241: + "organizationRoutingModel[0].active",
242: KFSKeyConstants.ERROR_DOCUMENT_DELEGATE_CHANGE_NO_ACTIVE_DELEGATE,
243: new String[0]);
244: }
245: }
246: return success;
247: }
248:
249: /**
250: * Checks that if approval from amount is not null, then it is positive
251: *
252: * @param delegateModel Organization Routing Model to check
253: * @return true if Organization Routing Model passes the checks, false if otherwise
254: */
255: protected boolean checkDelegateFromAmountPositive(
256: OrganizationRoutingModel delegateModel) {
257: boolean result = true;
258: if (!ObjectUtils.isNull(delegateModel
259: .getApprovalFromThisAmount())) {
260: if (delegateModel.getApprovalFromThisAmount().isLessThan(
261: ZERO)) {
262: GlobalVariables
263: .getErrorMap()
264: .putError(
265: "approvalFromThisAmount",
266: KFSKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_FROM_AMOUNT_NONNEGATIVE,
267: new String[0]);
268: result = false;
269: }
270: }
271: return result;
272: }
273:
274: /**
275: * Checks that if approval from amount is not null then approval to amount is also not null
276: *
277: * @param delegateModel Organization Routing Model to check
278: * @return true if Organization Routing Model passes all checks, false if otherwise
279: */
280: protected boolean checkDelegateToAmountNotNull(
281: OrganizationRoutingModel delegateModel) {
282: boolean result = true;
283: if (!ObjectUtils.isNull(delegateModel
284: .getApprovalFromThisAmount())
285: && ObjectUtils.isNull(delegateModel
286: .getApprovalToThisAmount())) {
287: GlobalVariables
288: .getErrorMap()
289: .putError(
290: "approvalToThisAmount",
291: KFSKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_TO_AMOUNT_MORE_THAN_FROM_OR_ZERO,
292: new String[0]);
293: result = false;
294: }
295: return result;
296: }
297:
298: /**
299: * Checks that if approval from amount is null, that approval to this amount is null or zero; and then checks that approval to
300: * amount is greater than or equal to approval from amount.
301: *
302: * @param delegateModel Organization Routing Model to check
303: * @return true if the Organization Routing Model passes the checks, false if otherwise
304: */
305: protected boolean checkDelegateToAmountGreaterThanFromAmount(
306: OrganizationRoutingModel delegateModel) {
307: boolean result = true;
308: if (ObjectUtils.isNull(delegateModel
309: .getApprovalFromThisAmount())) {
310: if (!ObjectUtils.isNull(delegateModel
311: .getApprovalToThisAmount())
312: && !delegateModel.getApprovalToThisAmount().equals(
313: ZERO)) {
314: GlobalVariables
315: .getErrorMap()
316: .putError(
317: "approvalToThisAmount",
318: KFSKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_TO_AMOUNT_MORE_THAN_FROM_OR_ZERO,
319: new String[0]);
320: result = false;
321: }
322: } else if (!ObjectUtils.isNull(delegateModel
323: .getApprovalToThisAmount())) {
324: if (delegateModel.getApprovalToThisAmount().isLessThan(
325: delegateModel.getApprovalFromThisAmount())) {
326: GlobalVariables
327: .getErrorMap()
328: .putError(
329: "approvalToThisAmount",
330: KFSKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_TO_AMOUNT_MORE_THAN_FROM_OR_ZERO,
331: new String[0]);
332: result = false;
333: }
334: }
335: return result;
336: }
337:
338: /**
339: * Checks that the account delegate listed exists in the system, and that user has an active status and is a professional type
340: *
341: * @param delegateModel the Organization Routing Model to check
342: * @return true if delegate user passes the rules described above; false if they fail
343: */
344: protected boolean checkDelegateUserRules(
345: OrganizationRoutingModel delegateModel) {
346: boolean success = true;
347:
348: // refresh account delegate
349: try {
350: delegateModel.setAccountDelegate(SpringContext.getBean(
351: UniversalUserService.class).getUniversalUser(
352: delegateModel.getAccountDelegateUniversalId()));
353: } catch (UserNotFoundException e) {
354: if (LOG.isDebugEnabled()) {
355: LOG.debug("User Not Found Exception: " + e);
356: }
357: }
358:
359: // user must exist
360: if (delegateModel.getAccountDelegate() == null) {
361: GlobalVariables
362: .getErrorMap()
363: .putError(
364: "accountDelegate.personUserIdentifier",
365: KFSKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_USER_DOESNT_EXIST,
366: new String[0]);
367: success = false;
368: }
369:
370: if (success) {
371: UniversalUser user = delegateModel.getAccountDelegate();
372:
373: // user must be of the allowable statuses (A - Active)
374: if (!SpringContext
375: .getBean(ParameterService.class)
376: .getParameterEvaluator(
377: Delegate.class,
378: KFSConstants.ChartApcParms.DELEGATE_USER_EMP_STATUSES,
379: user.getEmployeeStatusCode())
380: .evaluationSucceeds()) {
381: GlobalVariables
382: .getErrorMap()
383: .putError(
384: "accountDelegate.personUserIdentifier",
385: KFSKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_USER_NOT_ACTIVE,
386: new String[0]);
387: success = false;
388: }
389:
390: // user must be of the allowable types (P - Professional)
391: if (!SpringContext
392: .getBean(ParameterService.class)
393: .getParameterEvaluator(
394: Delegate.class,
395: KFSConstants.ChartApcParms.DELEGATE_USER_EMP_TYPES,
396: user.getEmployeeTypeCode())
397: .evaluationSucceeds()) {
398: GlobalVariables
399: .getErrorMap()
400: .putError(
401: "accountDelegate.personUserIdentifier",
402: KFSKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_USER_NOT_PROFESSIONAL,
403: new String[0]);
404: success = false;
405: }
406: }
407:
408: return success;
409: }
410:
411: /**
412: * This method validates the rule that says there can be only one PrimaryRoute delegate for each given docType. It checks the
413: * delegateGlobalToTest against the list, to determine whether adding this new delegateGlobalToTest would violate any
414: * PrimaryRoute business rule violations. If any of the incoming variables is null or empty, the method will do nothing, and
415: * return Null. It will only process the business rules if there is sufficient data to do so.
416: *
417: * @param delegateGlobalToTest A delegateGlobal line that you want to test against the list.
418: * @param delegateGlobals A List of delegateGlobal items that is being tested against.
419: * @return true if model, delegate template or org routing model is null, or if the primary routing indicator is set to false or the doc type code is empty
420: * otherwise it checks to make sure that there is indeed one model marked as the primary route
421: */
422: protected boolean checkPrimaryRoutePerDocType(
423: OrganizationRoutingModelName globalDelegateTemplate,
424: OrganizationRoutingModel delegateModel) {
425: boolean success = true;
426:
427: // exit immediately if the adding line isnt a Primary routing
428: if (delegateModel == null
429: || globalDelegateTemplate == null
430: || globalDelegateTemplate.getOrganizationRoutingModel()
431: .isEmpty()) {
432: return success;
433: }
434: if (!delegateModel.getAccountDelegatePrimaryRoutingIndicator()) {
435: return success;
436: }
437: if (StringUtils.isBlank(delegateModel
438: .getFinancialDocumentTypeCode())) {
439: return success;
440: }
441:
442: // at this point, the delegateGlobal being added is a Primary for ALL docTypes, so we need to
443: // test whether any in the existing list are also Primary, regardless of docType
444: String docType = delegateModel.getFinancialDocumentTypeCode();
445: for (OrganizationRoutingModel currDelegateModel : globalDelegateTemplate
446: .getOrganizationRoutingModel()) {
447: if (currDelegateModel.isActive()
448: && !delegateModel.equals(currDelegateModel)
449: && currDelegateModel
450: .getAccountDelegatePrimaryRoutingIndicator()
451: && delegateModel
452: .getFinancialDocumentTypeCode()
453: .equals(
454: currDelegateModel
455: .getFinancialDocumentTypeCode())) {
456: success = false;
457: GlobalVariables
458: .getErrorMap()
459: .putError(
460: "accountDelegatePrimaryRoutingIndicator",
461: KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_DELEGATEMAINT_PRIMARY_ROUTE_ALREADY_EXISTS_FOR_DOCTYPE,
462: new String[0]);
463: }
464: }
465:
466: return success;
467: }
468:
469: }
|