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.external.kc.service.impl;
017
018 import java.util.ArrayList;
019 import java.util.Collection;
020 import java.util.HashMap;
021 import java.util.List;
022 import java.util.Map;
023
024 import org.apache.commons.lang.StringUtils;
025 import org.kuali.kfs.coa.businessobject.Account;
026 import org.kuali.kfs.coa.businessobject.AccountGuideline;
027 import org.kuali.kfs.coa.businessobject.Chart;
028 import org.kuali.kfs.coa.service.AccountService;
029 import org.kuali.kfs.coa.service.ChartService;
030 import org.kuali.kfs.integration.cg.ContractsAndGrantsConstants;
031 import org.kuali.kfs.integration.cg.ContractsAndGrantsModuleService;
032 import org.kuali.kfs.integration.cg.dto.AccountCreationStatusDTO;
033 import org.kuali.kfs.integration.cg.dto.AccountParametersDTO;
034 import org.kuali.kfs.integration.cg.service.AccountCreationService;
035 import org.kuali.kfs.module.external.kc.businessobject.AccountAutoCreateDefaults;
036 import org.kuali.kfs.module.external.kc.util.GlobalVariablesExtractHelper;
037 import org.kuali.kfs.module.external.kc.util.KcUtils;
038 import org.kuali.kfs.sys.KFSConstants;
039 import org.kuali.kfs.sys.KFSKeyConstants;
040 import org.kuali.kfs.sys.KFSPropertyConstants;
041 import org.kuali.kfs.sys.context.SpringContext;
042 import org.kuali.rice.kew.exception.WorkflowException;
043 import org.kuali.rice.kim.bo.Person;
044 import org.kuali.rice.kim.service.KIMServiceLocator;
045 import org.kuali.rice.kim.service.PersonService;
046 import org.kuali.rice.kns.UserSession;
047 import org.kuali.rice.kns.datadictionary.AttributeDefinition;
048 import org.kuali.rice.kns.datadictionary.BusinessObjectEntry;
049 import org.kuali.rice.kns.datadictionary.validation.ValidationPattern;
050 import org.kuali.rice.kns.datadictionary.validation.charlevel.AlphaNumericValidationPattern;
051 import org.kuali.rice.kns.document.Document;
052 import org.kuali.rice.kns.document.MaintenanceDocument;
053 import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
054 import org.kuali.rice.kns.document.authorization.MaintenanceDocumentAuthorizerBase;
055 import org.kuali.rice.kns.exception.ValidationException;
056 import org.kuali.rice.kns.rule.event.BlanketApproveDocumentEvent;
057 import org.kuali.rice.kns.rule.event.RouteDocumentEvent;
058 import org.kuali.rice.kns.rule.event.SaveDocumentEvent;
059 import org.kuali.rice.kns.service.BusinessObjectService;
060 import org.kuali.rice.kns.service.DataDictionaryService;
061 import org.kuali.rice.kns.service.DocumentService;
062 import org.kuali.rice.kns.service.KualiRuleService;
063 import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
064 import org.kuali.rice.kns.service.ParameterService;
065 import org.kuali.rice.kns.util.GlobalVariables;
066 import org.kuali.rice.kns.util.KNSConstants;
067 import org.kuali.rice.kns.util.ObjectUtils;
068
069 public class AccountCreationServiceImpl implements AccountCreationService {
070
071 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountCreationServiceImpl.class);
072
073 protected static final String ACCT_PREFIX_RESTRICTION = "PREFIXES";
074
075 private DocumentService documentService;
076 private ParameterService parameterService;
077 private DataDictionaryService dataDictionaryService;
078 private BusinessObjectService businessObjectService;
079
080 /**
081 * This is the web service method that creates a new account 1. Creates an account object using the parameters from KC and the
082 * default Account table 2. Creates an account automatic maintenance document and puts the account object into it 3. Returns the
083 * status object
084 *
085 * @param AccountAutoCreateDefaults
086 * @return AccountCreationStatusDTO
087 */
088 public AccountCreationStatusDTO createAccount(AccountParametersDTO accountParameters) {
089
090 AccountCreationStatusDTO accountCreationStatus = new AccountCreationStatusDTO();
091 accountCreationStatus.setErrorMessages(new ArrayList<String>());
092 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_SUCCESS);
093
094 // check to see if the user has the permission to create account
095 String principalId = accountParameters.getPrincipalId();
096 if (!isValidUser(principalId)) {
097 this.setFailStatus(accountCreationStatus, KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_INVALID_USER, new String[]{principalId}));
098 return accountCreationStatus;
099 }
100
101
102 // get the defaults table
103 String unitNumber = accountParameters.getUnit();
104 AccountAutoCreateDefaults defaults = getAccountDefaults(unitNumber);
105
106 if (defaults == null) {
107 this.setFailStatus(accountCreationStatus, ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_ACCOUNT_PARAMS_UNIT_NOT_DEFINED);
108 return accountCreationStatus;
109 }
110
111 try {
112 // create an account object
113 Account account = createAccountObject(accountParameters, defaults);
114
115 //if invalid chart/account number, failure status and return to KC
116 if (! isValidAccount(account, accountCreationStatus)) {
117 return accountCreationStatus;
118 }
119 // create an account automatic maintenance document
120 createAutomaticCGAccountMaintenanceDocument(account, accountCreationStatus);
121
122 } catch (Exception ex ) {
123 this.setFailStatus(accountCreationStatus, ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_ACCOUNT_GENERATION_PROBLEM);
124 return accountCreationStatus;
125 }
126
127 // set required values to AccountCreationStatus
128 if (accountCreationStatus.getStatus().equals(ContractsAndGrantsConstants.KcWebService.STATUS_KC_SUCCESS)) {
129 accountCreationStatus.setAccountNumber(accountParameters.getAccountNumber());
130 accountCreationStatus.setChartOfAccountsCode(defaults.getChartOfAccountsCode());
131 }
132
133 return accountCreationStatus;
134 }
135
136 /**
137 * This method creates an account to be used for automatic maintenance document
138 *
139 * @param AccountParametersDTO
140 * @return Account
141 */
142 public Account createAccountObject(AccountParametersDTO parameters, AccountAutoCreateDefaults defaults) {
143
144 Account account = new Account();
145
146 // * Account: required but off campus indicator, closed, fringe benefit indicator, fringe benefit COA, endowment
147 account.setChartOfAccountsCode(defaults.getChartOfAccountsCode());
148 account.setOrganizationCode(defaults.getOrganizationCode());
149 account.setAccountNumber(parameters.getAccountNumber()); // what if account number is null?
150 account.setAccountName(parameters.getAccountName());
151 account.setAccountPhysicalCampusCode(defaults.getAccountPhysicalCampusCode());
152 if (parameters.getExpirationDate() != null) account.setAccountExpirationDate(new java.sql.Date(parameters.getExpirationDate().getTime()));
153 if (parameters.getEffectiveDate() != null) account.setAccountEffectiveDate(new java.sql.Date(parameters.getEffectiveDate().getTime()));
154 boolean isKCOverrideKFS = parameterService.getIndicatorParameter(Account.class, ContractsAndGrantsConstants.AccountCreationService.PARAMETER_KC_OVERRIDES_KFS_DEFAULT_ACCOUNT_IND);
155 if (isKCOverrideKFS) {
156 // set the right address based on the system parameter ACCOUNT_ADDRESS_TYPE
157 List<String> addressTypes = parameterService.getParameterValues(Account.class, ContractsAndGrantsConstants.AccountCreationService.PARAMETER_KC_ACCOUNT_ADDRESS_TYPE);
158 for (String addressType : addressTypes) {
159 if (addressType.equals(ContractsAndGrantsConstants.AccountCreationService.PI_ADDRESS_TYPE) && (!StringUtils.isBlank(parameters.getDefaultAddressStreetAddress()))) {
160 account.setAccountStreetAddress(parameters.getDefaultAddressStreetAddress());
161 account.setAccountCityName(parameters.getDefaultAddressCityName());
162 account.setAccountStateCode(parameters.getDefaultAddressStateCode());
163 account.setAccountZipCode(parameters.getDefaultAddressZipCode());
164 break;
165 }
166 else if (addressType.equals(ContractsAndGrantsConstants.AccountCreationService.ADMIN_ADDRESS_TYPE) && (!StringUtils.isBlank(parameters.getAdminContactAddressStreetAddress()))) {
167 account.setAccountStreetAddress(parameters.getAdminContactAddressStreetAddress());
168 account.setAccountCityName(parameters.getAdminContactAddressCityName());
169 account.setAccountStateCode(parameters.getAdminContactAddressStateCode());
170 account.setAccountZipCode(parameters.getAdminContactAddressZipCode());
171 break;
172 }
173 }
174
175 } else {
176 // use default address
177 account.setAccountStreetAddress(defaults.getAccountStreetAddress());
178 account.setAccountCityName(defaults.getAccountCityName());
179 account.setAccountStateCode(defaults.getAccountStateCode());
180 account.setAccountZipCode(defaults.getAccountZipCode());
181 }
182
183 //set the following from parameters
184 account.setAccountOffCampusIndicator(parameters.isOffCampusIndicator());
185 account.setFinancialHigherEdFunctionCd(parameters.getHigherEdFunctionCode());
186 account.setAcctIndirectCostRcvyTypeCd(parameters.getIndirectCostTypeCode());
187 account.setFinancialIcrSeriesIdentifier(parameters.getIndirectCostRate());
188 account.setAccountGuideline(new AccountGuideline());
189 account.getAccountGuideline().setAccountExpenseGuidelineText(parameters.getExpenseGuidelineText());
190 account.getAccountGuideline().setAccountIncomeGuidelineText(parameters.getIncomeGuidelineText());
191 account.getAccountGuideline().setAccountPurposeText(parameters.getPurposeText());
192
193 account.setClosed(false);
194 account.setAccountTypeCode(defaults.getAccountTypeCode());
195 account.setSubFundGroupCode(defaults.getSubFundGroupCode());
196
197 account.setAccountsFringesBnftIndicator(defaults.isAccountsFringesBnftIndicator());
198
199 account.setReportsToAccountNumber(defaults.getReportsToAccountNumber());
200 account.setReportsToChartOfAccountsCode(defaults.getReportsToChartOfAccountsCode());
201
202 account.setAccountRestrictedStatusCode("R");
203 account.setAccountRestrictedStatusDate(null);
204
205 account.setEndowmentIncomeChartOfAccounts(null);
206 account.setEndowmentIncomeAccountNumber(null);
207
208 // * Accounts Responsibility: required - fiscal officer principal name, account supervisor principal name, account manager
209 // principal name, budget record level, account sufficient funds
210 account.setAccountFiscalOfficerSystemIdentifier(defaults.getAccountFiscalOfficerSystemIdentifier());
211
212 account.setAccountsSupervisorySystemsIdentifier(defaults.getAccountsSupervisorySystemsIdentifier());
213 account.setAccountManagerSystemIdentifier(defaults.getAccountManagerSystemIdentifier());
214
215 account.setContinuationFinChrtOfAcctCd(defaults.getContinuationFinChrtOfAcctCd());
216 account.setContinuationAccountNumber(defaults.getContinuationAccountNumber());
217
218 account.setIncomeStreamAccountNumber(defaults.getIncomeStreamAccountNumber());
219 account.setIncomeStreamChartOfAccounts(defaults.getIncomeStreamChartOfAccounts());
220 account.setIncomeStreamFinancialCoaCode(defaults.getIncomeStreamFinancialCoaCode());
221
222 account.setBudgetRecordingLevelCode(defaults.getBudgetRecordingLevelCode());
223 account.setAccountSufficientFundsCode(defaults.getAccountSufficientFundsCode());
224
225 account.setPendingAcctSufficientFundsIndicator(defaults.isPendingAcctSufficientFundsIndicator());
226 account.setExtrnlFinEncumSufficntFndIndicator(defaults.isExtrnlFinEncumSufficntFndIndicator());
227 account.setIntrnlFinEncumSufficntFndIndicator(defaults.isIntrnlFinEncumSufficntFndIndicator());
228 account.setFinPreencumSufficientFundIndicator(defaults.isFinPreencumSufficientFundIndicator());
229 account.setFinancialObjectivePrsctrlIndicator(defaults.isFinancialObjectivePrsctrlIndicator());
230
231 // * Contract and Grants: not required
232 account.setContractControlFinCoaCode(null);
233 account.setContractControlAccountNumber(null);
234 account.setIndirectCostRcvyFinCoaCode(defaults.getIndirectCostRcvyFinCoaCode());
235 account.setIndirectCostRecoveryAcctNbr(defaults.getIndirectCostRecoveryAcctNbr());
236 account.setContractsAndGrantsAccountResponsibilityId(defaults.getContractsAndGrantsAccountResponsibilityId());
237 account.setAccountCfdaNumber(parameters.getCfdaNumber());
238
239 return account;
240 }
241
242 protected void setFailStatus(AccountCreationStatusDTO accountCreationStatus, String message) {
243 accountCreationStatus.getErrorMessages().add(message);
244 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_FAILURE);
245 }
246
247 /**
248 * This method will create a maintenance document for CG account create, set its description and then sets the account business
249 * object in it. The document will then be tried to route, save or blanket approve automatically based on the system parameter.
250 * If successful, the method returns the newly created document number to the caller.
251 *
252 * @return documentNumber returns the documentNumber
253 * @see org.kuali.kfs.coa.document.service.CreateAccountService#createAutomaticCGAccountMaintenanceDocument()
254 */
255 protected void createAutomaticCGAccountMaintenanceDocument(Account account, AccountCreationStatusDTO accountCreationStatus) {
256
257 // create a new maintenance document
258 MaintenanceDocument maintenanceAccountDocument = (MaintenanceDocument) createCGAccountMaintenanceDocument(accountCreationStatus);
259
260 if (ObjectUtils.isNotNull(maintenanceAccountDocument)) {
261 // set document header description...
262 maintenanceAccountDocument.getDocumentHeader().setDocumentDescription(ContractsAndGrantsConstants.AccountCreationService.AUTOMATCICG_ACCOUNT_MAINTENANCE_DOCUMENT_DESCRIPTION);
263
264 // set the account object in the maintenance document.
265 maintenanceAccountDocument.getNewMaintainableObject().setBusinessObject(account);
266 maintenanceAccountDocument.getNewMaintainableObject().setMaintenanceAction(KNSConstants.MAINTENANCE_NEW_ACTION);
267 // the maintenance document will now be routed based on the system parameter value for routing.
268 createRouteAutomaticCGAccountDocument(maintenanceAccountDocument, accountCreationStatus);
269 }
270 }
271
272 /**
273 * This method processes the workflow document actions like save, route and blanket approve depending on the
274 * ACCOUNT_AUTO_CREATE_ROUTE system parameter value. If the system parameter value is not of save or submit or blanketapprove,
275 * put an error message and quit. Throws an document WorkflowException if the specific document action fails to perform.
276 *
277 * @param maintenanceAccountDocument, errorMessages
278 * @return
279 */
280 protected void createRouteAutomaticCGAccountDocument(MaintenanceDocument maintenanceAccountDocument, AccountCreationStatusDTO accountCreationStatus) {
281
282 try {
283 String accountAutoCreateRouteValue = getParameterService().getParameterValue(Account.class, ContractsAndGrantsConstants.AccountCreationService.PARAMETER_KC_ACCOUNT_ADMIN_AUTO_CREATE_ACCOUNT_WORKFLOW_ACTION);
284
285 // if the accountAutoCreateRouteValue is not save or submit or blanketApprove then put an error message and quit.
286 if (!accountAutoCreateRouteValue.equalsIgnoreCase(KFSConstants.WORKFLOW_DOCUMENT_SAVE) && !accountAutoCreateRouteValue.equalsIgnoreCase("submit") && !accountAutoCreateRouteValue.equalsIgnoreCase(KFSConstants.WORKFLOW_DOCUMENT_BLANKET_APPROVE)) {
287 this.setFailStatus( accountCreationStatus, ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_SYSTEM_PARAMETER_INCORRECT_DOCUMENT_ACTION_VALUE);
288 return;
289 }
290
291 if (accountAutoCreateRouteValue.equalsIgnoreCase(KFSConstants.WORKFLOW_DOCUMENT_SAVE)) {
292
293 //attempt to save if apply rules were successful and there are no errors
294 boolean rulesPassed = SpringContext.getBean(KualiRuleService.class).applyRules(new SaveDocumentEvent(maintenanceAccountDocument));
295
296 if( rulesPassed && GlobalVariables.getMessageMap().hasNoErrors()){
297 getDocumentService().saveDocument(maintenanceAccountDocument);
298 }else{
299 //get errors from apply rules invocation, also clears global variables
300 accountCreationStatus.setErrorMessages(GlobalVariablesExtractHelper.extractGlobalVariableErrors());
301 try{
302 //save document, and catch VE's as we want to do this silently
303 getDocumentService().saveDocument(maintenanceAccountDocument);
304 }catch(ValidationException ve){}
305
306 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_SUCCESS);
307 LOG.error( KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_ACCOUNT_RULES_EXCEPTION, new String[]{maintenanceAccountDocument.getDocumentNumber()}));
308 }
309
310 }
311 else if (accountAutoCreateRouteValue.equalsIgnoreCase(KFSConstants.WORKFLOW_DOCUMENT_BLANKET_APPROVE)) {
312
313 //attempt to blanket approve if apply rules were successful and there are no errors
314 boolean rulesPassed = SpringContext.getBean(KualiRuleService.class).applyRules(new BlanketApproveDocumentEvent(maintenanceAccountDocument));
315
316 if( rulesPassed && GlobalVariables.getMessageMap().hasNoErrors()){
317 getDocumentService().blanketApproveDocument(maintenanceAccountDocument, "", null);
318 }else{
319 //get errors from apply rules invocation, also clears global variables
320 accountCreationStatus.setErrorMessages(GlobalVariablesExtractHelper.extractGlobalVariableErrors());
321 try{
322 //save document, and catch VE's as we want to do this silently
323 getDocumentService().saveDocument(maintenanceAccountDocument);
324 }catch(ValidationException ve){}
325
326 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_SUCCESS);
327 LOG.error( KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_ACCOUNT_RULES_EXCEPTION, new String[]{maintenanceAccountDocument.getDocumentNumber()}));
328 }
329
330 }
331 else if (accountAutoCreateRouteValue.equalsIgnoreCase("submit")) {
332
333 //attempt to route if apply rules were successful and there are no errors
334 boolean rulesPassed = SpringContext.getBean(KualiRuleService.class).applyRules(new RouteDocumentEvent(maintenanceAccountDocument));
335
336 if( rulesPassed && GlobalVariables.getMessageMap().hasNoErrors()){
337 getDocumentService().routeDocument(maintenanceAccountDocument, "", null);
338 }else{
339 //get errors from apply rules invocation, also clears global variables
340 accountCreationStatus.setErrorMessages(GlobalVariablesExtractHelper.extractGlobalVariableErrors());
341 try{
342 //save document, and catch VE's as we want to do this silently
343 getDocumentService().saveDocument(maintenanceAccountDocument);
344 }catch(ValidationException ve){}
345
346 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_SUCCESS);
347 LOG.error( KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_ACCOUNT_RULES_EXCEPTION, new String[]{maintenanceAccountDocument.getDocumentNumber()}));
348 }
349
350 }
351
352 // set the document number
353 accountCreationStatus.setDocumentNumber(maintenanceAccountDocument.getDocumentNumber());
354
355 }
356 catch (WorkflowException wfe) {
357
358 LOG.error( KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_WORKFLOW_EXCEPTION_DOCUMENT_ACTIONS, null) + ": " + wfe.getMessage());
359 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_FAILURE);
360 accountCreationStatus.getErrorMessages().add( KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.WARNING_KC_DOCUMENT_WORKFLOW_EXCEPTION_DOCUMENT_ACTIONS, null) + ": " + wfe.getMessage());
361
362 try {
363 // save it even though it fails to route or blanket approve the document
364 try{
365 getDocumentService().saveDocument(maintenanceAccountDocument);
366 }catch(ValidationException ve){
367 //ok to catch validation exceptions at this point
368 }
369 accountCreationStatus.setDocumentNumber(maintenanceAccountDocument.getDocumentNumber());
370 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_SUCCESS);
371 }
372 catch (WorkflowException e) {
373 LOG.error( KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.WARNING_KC_DOCUMENT_WORKFLOW_EXCEPTION_DOCUMENT_ACTIONS, null) + ": " + e.getMessage());
374 accountCreationStatus.setErrorMessages(GlobalVariablesExtractHelper.extractGlobalVariableErrors());
375 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_FAILURE);
376 }
377
378 }
379 catch (Exception ex) {
380
381 LOG.error("Unknown exception occurred: " + ex.getMessage());
382 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_FAILURE);
383 accountCreationStatus.setErrorMessages(GlobalVariablesExtractHelper.extractGlobalVariableErrors());
384 accountCreationStatus.getErrorMessages().add(KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.WARNING_KC_DOCUMENT_WORKFLOW_EXCEPTION_DOCUMENT_ACTIONS, null) + ": " + ex.getMessage());
385
386 try {
387 // save it even though it fails to route or blanket approve the document
388 try{
389 getDocumentService().saveDocument(maintenanceAccountDocument);
390 }catch(ValidationException ve){
391 //ok to catch validation exceptions at this point
392 }
393 accountCreationStatus.setDocumentNumber(maintenanceAccountDocument.getDocumentNumber());
394 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_SUCCESS);
395 }
396 catch (WorkflowException e) {
397 LOG.error( KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.WARNING_KC_DOCUMENT_WORKFLOW_EXCEPTION_DOCUMENT_ACTIONS, null) + ": " + e.getMessage());
398 accountCreationStatus.setErrorMessages(GlobalVariablesExtractHelper.extractGlobalVariableErrors());
399 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_FAILURE);
400 }
401 }
402 }
403
404 /**
405 * This method will use the DocumentService to create a new document. The documentTypeName is gathered by using
406 * MaintenanceDocumentDictionaryService which uses Account class to get the document type name.
407 *
408 * @param AccountCreationStatusDTO
409 * @return document returns a new document for the account document type or null if there is an exception thrown.
410 */
411 public Document createCGAccountMaintenanceDocument(AccountCreationStatusDTO accountCreationStatus) {
412
413 boolean internalUserSession = false;
414 try {
415 if (GlobalVariables.getUserSession() == null) {
416 internalUserSession = true;
417 GlobalVariables.setUserSession(new UserSession(KNSConstants.SYSTEM_USER));
418 GlobalVariables.clear();
419 }
420 Document document = getDocumentService().getNewDocument(SpringContext.getBean(MaintenanceDocumentDictionaryService.class).getDocumentTypeName(Account.class));
421 return document;
422
423 }
424 catch (Exception e) {
425 accountCreationStatus.setErrorMessages(GlobalVariablesExtractHelper.extractGlobalVariableErrors());
426 accountCreationStatus.setStatus(ContractsAndGrantsConstants.KcWebService.STATUS_KC_FAILURE);
427 return null;
428 }
429 finally {
430 // if a user session was established for this call, clear it our
431 if (internalUserSession) {
432 GlobalVariables.clear();
433 GlobalVariables.setUserSession(null);
434 }
435 }
436 }
437
438 /**
439 * This method looks up the default table
440 *
441 * @param String unitNumber
442 * @return AccountAutoCreateDefaults
443 */
444 protected AccountAutoCreateDefaults getAccountDefaults(String unitNumber) {
445
446 AccountAutoCreateDefaults defaults = null;
447
448 if (unitNumber == null || unitNumber.isEmpty()) {
449 return null;
450 }
451
452 Map<String, String> criteria = new HashMap<String, String>();
453 criteria.put("kcUnit", unitNumber);
454 defaults = (AccountAutoCreateDefaults) businessObjectService.findByPrimaryKey(AccountAutoCreateDefaults.class, criteria);
455
456 // if the matching defaults is null, try the parents in the hierarchy
457 if (defaults == null) {
458
459 List<String> parentUnits = null;
460 try {
461 parentUnits = SpringContext.getBean(ContractsAndGrantsModuleService.class).getParentUnits(unitNumber);
462 }
463 catch (Exception ex) {
464 LOG.error( KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_ACCOUNT_PARAMS_UNIT_NOTFOUND, null) + ": " + ex.getMessage());
465
466 GlobalVariables.getMessageMap().putError(ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_ACCOUNT_PARAMS_UNIT_NOTFOUND, "kcUnit", ex.getMessage());
467
468 }
469
470 if (parentUnits != null) {
471 for (String unit : parentUnits) {
472 criteria.put("kcUnit", unit);
473 defaults = (AccountAutoCreateDefaults) businessObjectService.findByPrimaryKey(AccountAutoCreateDefaults.class, criteria);
474 if (defaults != null)
475 break;
476 }
477 }
478
479 }
480
481 return defaults;
482 }
483
484 /**
485 * Check to see if the main link between KFS and KC is valid, namely the chart and account number.
486 * If these two values have some kind of error, then we don't want to generate an Account document
487 * and we'll want to return a failure to KC.
488 *
489 *
490 * @param account
491 * @param accountCreationStatus
492 * @return
493 */
494 protected boolean isValidAccount(Account account, AccountCreationStatusDTO accountCreationStatus) {
495 boolean isValid = true;
496 String errorMessage = "";
497 String strSize = "";
498
499 if (account == null) {
500 //account was not created
501 setFailStatus(accountCreationStatus, ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_ACCOUNT_GENERATION_PROBLEM);
502 return false;
503 }
504
505 if (StringUtils.isBlank(account.getChartOfAccountsCode()) || StringUtils.isBlank(account.getAccountNumber())){
506 //chart of accounts or account number blank
507 setFailStatus(accountCreationStatus, ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_ACCOUNT_MISSING_CHART_OR_ACCT_NBR);
508 return false;
509 }
510
511 if (!isValidChartCode(account.getChartOfAccountsCode())) {
512 //the chart of accounts code is not valid
513 setFailStatus( accountCreationStatus, ContractsAndGrantsConstants.AccountCreationService.AUTOMATCICG_ACCOUNT_MAINTENANCE_CHART_NOT_DEFINED);
514 return false;
515 }
516
517 if (!isValidAccountNumberLength(account.getAccountNumber(), accountCreationStatus)){
518 //the account number is an inappropriate length
519 //error set in method
520 return false;
521 }
522
523 if (!checkUniqueAccountNumber(account.getAccountNumber())){
524 //account is not unique
525 setFailStatus( accountCreationStatus, KcUtils.getErrorMessage(KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_ACCT_NMBR_NOT_UNIQUE, new String[]{account.getAccountNumber()}));
526 return false;
527 }
528
529 if (isValidChartAccount(account.getChartOfAccountsCode(), account.getAccountNumber())) {
530 //the chart and account already exist
531 setFailStatus( accountCreationStatus, ContractsAndGrantsConstants.AccountCreationService.AUTOMATCICG_ACCOUNT_MAINTENANCE_ACCT_ALREADY_DEFINED);
532 return false;
533 }
534
535 if (!checkAccountNumberPrefix(account.getAccountNumber(), accountCreationStatus)){
536 //account begins with invalid prefix
537 //error set in method
538 return false;
539 }
540
541 return isValid;
542 }
543
544 public boolean accountsCanCrossCharts() {
545 return SpringContext.getBean(AccountService.class).accountsCanCrossCharts();
546 }
547
548 public boolean isValidAccount(String accountNumber) {
549 Collection<Account> accounts = SpringContext.getBean(AccountService.class).getAccountsForAccountNumber(accountNumber);
550 return (accounts != null && !accounts.isEmpty());
551 }
552
553 public boolean isValidChartCode(String chartOfAccountsCode) {
554 Chart chart = SpringContext.getBean(ChartService.class).getByPrimaryId(chartOfAccountsCode);
555 return (chart != null);
556 }
557
558
559 public boolean isValidChartAccount(String chartOfAccountsCode, String accountNumber) {
560 AccountService accountService = SpringContext.getBean(AccountService.class);
561 Account account = accountService.getByPrimaryId(chartOfAccountsCode, accountNumber);
562 return (account != null);
563 }
564
565 /**
566 * Checks an account numbers exact length
567 *
568 * @param accountNumber
569 * @param size to be returned
570 * @return
571 */
572 protected boolean isValidAccountNumberLength(String accountNumber, AccountCreationStatusDTO accountCreationStatus){
573
574 boolean isValid = false;
575 int fieldSize = -1;
576
577 //grab account number length from DD and set size
578 final BusinessObjectEntry entry = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(Account.class.getName());
579 AttributeDefinition attributeDefinition = entry.getAttributeDefinition(KFSPropertyConstants.ACCOUNT_NUMBER);
580
581 if(ObjectUtils.isNotNull(attributeDefinition)){
582 final ValidationPattern validationPattern = attributeDefinition.getValidationPattern();
583
584 if(ObjectUtils.isNotNull(validationPattern) && validationPattern instanceof AlphaNumericValidationPattern){
585 AlphaNumericValidationPattern alphaPattern = (AlphaNumericValidationPattern)validationPattern;
586 fieldSize = alphaPattern.getExactLength();
587 }
588 }
589
590 //skip if account number null
591 if(ObjectUtils.isNotNull(accountNumber)){
592
593 //data dictionary defined size must equal length of incoming value
594 if(fieldSize == accountNumber.length()){
595 isValid = true;
596 }
597 }
598
599 if(isValid == false){
600 setFailStatus( accountCreationStatus, KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.ERROR_KR_ALPHANUMERIC_VALIDATION_EXACT_LENGTH, new String[]{"account number", String.valueOf(fieldSize)}));
601 }
602
603 return isValid;
604 }
605
606 /**
607 * This method tests whether the accountNumber passed in is prefixed with an allowed prefix, or an illegal one. The illegal
608 * prefixes are passed in as an array of strings.
609 *
610 * @param accountNumber - The Account Number to be tested.
611 * @return false if the accountNumber starts with any of the illegalPrefixes, true otherwise
612 */
613 protected boolean checkAccountNumberPrefix(String accountNumber, AccountCreationStatusDTO accountCreationStatus){
614
615 boolean success = true;
616
617 // Enforce institutionally specified restrictions on account number prefixes
618 // (e.g. the account number cannot begin with a 3 or with 00.)
619 // Only bother trying if there is an account string to test
620 if (!StringUtils.isBlank(accountNumber)) {
621
622 List<String> illegalValues = getParameterService().getParameterValues(Account.class, ACCT_PREFIX_RESTRICTION);
623
624 for (String illegalValue : illegalValues) {
625 if (accountNumber.startsWith(illegalValue)) {
626 success = false;
627 setFailStatus( accountCreationStatus, KcUtils.getErrorMessage(KFSKeyConstants.ERROR_DOCUMENT_ACCMAINT_ACCT_NMBR_NOT_ALLOWED, new String[] { accountNumber, illegalValue }));
628 }
629 }
630 }
631
632 return success;
633 }
634
635 /**
636 * If accounts can't cross charts, then we need to make sure the account number is unique.
637 *
638 * @param accountNumber
639 * @return
640 */
641 protected boolean checkUniqueAccountNumber(String accountNumber) {
642 boolean success = true;
643
644 // while account is not allowed to cross chart
645 //and with an account number that already exists
646 if (!SpringContext.getBean(AccountService.class).accountsCanCrossCharts() &&
647 !SpringContext.getBean(AccountService.class).getAccountsForAccountNumber(accountNumber).isEmpty()) {
648 success = false;
649 }
650
651 return success;
652 }
653
654 /**
655 * This method check to see if the user can create the account maintenance document and set the user session
656 *
657 * @param String principalId
658 * @return boolean
659 */
660 protected boolean isValidUser(String principalId) {
661
662 PersonService<Person> personService = KIMServiceLocator.getPersonService();
663 if (principalId == null) return false;
664 Person user = personService.getPerson(principalId);
665 if (user == null) return false;
666 DocumentAuthorizer documentAuthorizer = new MaintenanceDocumentAuthorizerBase();
667 if (documentAuthorizer.canInitiate(SpringContext.getBean(MaintenanceDocumentDictionaryService.class).getDocumentTypeName(Account.class), user)) {
668 // set the user session so that the user name can be displayed in the saved document
669 GlobalVariables.setUserSession(new UserSession(user.getPrincipalName()));
670 return true;
671 }
672
673 LOG.error(KcUtils.getErrorMessage(ContractsAndGrantsConstants.AccountCreationService.ERROR_KC_DOCUMENT_INVALID_USER, new String[]{principalId}));
674
675 return false;
676 }
677
678 /**
679 * Gets the documentService attribute.
680 *
681 * @return Current value of documentService.
682 */
683 protected DocumentService getDocumentService() {
684 return documentService;
685 }
686
687 /**
688 * Sets the documentService attribute value.
689 *
690 * @param documentService
691 */
692 public void setDocumentService(DocumentService documentService) {
693 this.documentService = documentService;
694 }
695
696 /**
697 * Gets the parameterService attribute.
698 *
699 * @return Returns the parameterService.
700 */
701 protected ParameterService getParameterService() {
702 return parameterService;
703 }
704
705 /**
706 * Sets the parameterService attribute value.
707 *
708 * @param parameterService The parameterService to set.
709 */
710 public void setParameterService(ParameterService parameterService) {
711 this.parameterService = parameterService;
712 }
713
714 protected DataDictionaryService getDataDictionaryService() {
715 return dataDictionaryService;
716 }
717
718 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
719 this.dataDictionaryService = dataDictionaryService;
720 }
721
722 /**
723 * Sets the businessObjectService attribute value.
724 *
725 * @param businessObjectService The businessObjectService to set.
726 */
727 public void setBusinessObjectService(BusinessObjectService businessObjectService) {
728 this.businessObjectService = businessObjectService;
729 }
730
731 /**
732 * Gets the businessObjectService attribute.
733 *
734 * @return Returns the businessObjectService.
735 */
736 protected BusinessObjectService getBusinessObjectService() {
737 return businessObjectService;
738 }
739
740 }