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    }