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.coa.document.validation.impl;
017    
018    import java.util.List;
019    
020    import org.apache.commons.lang.StringUtils;
021    import org.kuali.kfs.coa.businessobject.AccountGlobalDetail;
022    import org.kuali.kfs.coa.businessobject.SubObjectCodeGlobal;
023    import org.kuali.kfs.coa.businessobject.SubObjectCodeGlobalDetail;
024    import org.kuali.kfs.sys.KFSConstants;
025    import org.kuali.kfs.sys.KFSKeyConstants;
026    import org.kuali.kfs.sys.KFSPropertyConstants;
027    import org.kuali.rice.kns.bo.PersistableBusinessObject;
028    import org.kuali.rice.kns.document.MaintenanceDocument;
029    import org.kuali.rice.kns.util.GlobalVariables;
030    import org.kuali.rice.kns.util.ObjectUtils;
031    
032    /**
033     * 
034     * This class implements the business rules specific to the {@link SubObjCdGlobal} Maintenance Document.
035     */
036    public class SubObjCdGlobalRule extends GlobalDocumentRuleBase {
037        protected SubObjectCodeGlobal subObjCdGlobal;
038    
039        /**
040         * This method sets the convenience objects like subObjCdGlobal and all the detail objects, so you have short and easy handles to the new and
041         * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load
042         * all sub-objects from the DB by their primary keys, if available. This also loops through each detail item (SubObjCdGlobalDetail and AccountGlobalDetail)
043         * are refreshed
044         * 
045         * @param document - the maintenanceDocument being evaluated
046         */
047        @Override
048        public void setupConvenienceObjects() {
049    
050            // setup subObjCdGlobal convenience objects,
051            // make sure all possible sub-objects are populated
052            subObjCdGlobal = (SubObjectCodeGlobal) super.getNewBo();
053    
054            // forces refreshes on all the sub-objects in the lists
055            for (SubObjectCodeGlobalDetail objectCodeGlobalDetail : subObjCdGlobal.getSubObjCdGlobalDetails()) {
056                objectCodeGlobalDetail.refreshNonUpdateableReferences();
057            }
058            for (AccountGlobalDetail accountGlobalDetail : subObjCdGlobal.getAccountGlobalDetails()) {
059                accountGlobalDetail.refreshNonUpdateableReferences();
060            }
061        }
062    
063        /**
064         * This performs rules checks on document approve
065         * <ul>
066         * <li>{@link SubObjCdGlobalRule#checkSimpleRulesAllLines()}</li>
067         * </ul>
068         * This rule fails on business rule failures
069         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
070         */
071        @Override
072        protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) {
073            boolean success = true;
074            setupConvenienceObjects();
075            // check simple rules
076            success &= checkSimpleRulesAllLines();
077    
078            success &= checkOnlyOneChartErrorWrapper(subObjCdGlobal.getAccountGlobalDetails());
079    
080            return success;
081        }
082    
083        /**
084         * This performs rules checks on document route
085         * <ul>
086         * <li>{@link SubObjCdGlobalRule#checkSimpleRulesAllLines()}</li>
087         * </ul>
088         * This rule fails on business rule failures
089         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
090         */
091        @Override
092        protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
093            boolean success = true;
094            setupConvenienceObjects();
095            // check simple rules
096            success &= checkSimpleRulesAllLines();
097    
098            success &= checkAccountDetails(subObjCdGlobal.getAccountGlobalDetails());
099    
100            return success;
101        }
102    
103        /**
104         * This performs rules checks on document save
105         * <ul>
106         * <li>{@link SubObjCdGlobalRule#checkSimpleRulesAllLines()}</li>
107         * </ul>
108         * This rule does not fail on business rule failures
109         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
110         */
111        @Override
112        protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
113            setupConvenienceObjects();
114            // check simple rules
115            checkSimpleRulesAllLines();
116    
117            return true;
118        }
119    
120        /**
121         * Before adding either a {@link AccountGlobalDetail} or {@link SubObjCdGlobalDetail} this checks to make sure
122         * that the account and chart are filled in, in the case of SubObjCdGlobalDetail it also checks
123         * that the object code and fiscal year are filled in
124         * If any of these fail, it fails to add the new line to the collection
125         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument, java.lang.String, org.kuali.rice.kns.bo.PersistableBusinessObject)
126         */
127        public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject bo) {
128            boolean success = true;
129            if (bo instanceof AccountGlobalDetail) {
130                AccountGlobalDetail detail = (AccountGlobalDetail) bo;
131                // make sure that both primary keys are available for this object
132                if (!checkEmptyValue(detail.getAccountNumber())) {
133                    // put an error about accountnumber
134                    GlobalVariables.getMessageMap().putError("accountNumber", KFSKeyConstants.ERROR_REQUIRED, "Account Number");
135                    success &= false;
136                }
137                if (!checkEmptyValue(detail.getChartOfAccountsCode())) {
138                    // put an error about chart code
139                    GlobalVariables.getMessageMap().putError("chartOfAccountsCode", KFSKeyConstants.ERROR_REQUIRED, "Chart of Accounts Code");
140                    success &= false;
141                }
142                success &= checkAccountDetails(detail);
143            }
144            else if (bo instanceof SubObjectCodeGlobalDetail) {
145                SubObjectCodeGlobalDetail detail = (SubObjectCodeGlobalDetail) bo;
146                if (!checkEmptyValue(detail.getChartOfAccountsCode())) {
147                    // put an error about accountnumber
148                    GlobalVariables.getMessageMap().putError("chartOfAccountsCode", KFSKeyConstants.ERROR_REQUIRED, "Chart of Accounts Code");
149                    success &= false;
150                }
151                if (!checkEmptyValue(detail.getFinancialObjectCode())) {
152                    // put an error about financial object code
153                    GlobalVariables.getMessageMap().putError("financialObjectCode", KFSKeyConstants.ERROR_REQUIRED, "Financial Object Code");
154                    success &= false;
155                }
156                if (!checkEmptyValue(detail.getUniversityFiscalYear())) {
157                    // put an error about financial object code
158                    GlobalVariables.getMessageMap().putError("universityFiscalYear", KFSKeyConstants.ERROR_REQUIRED, "University Fiscal Year");
159                    success &= false;
160                }
161                success &= checkSubObjectCodeDetails(detail);
162            }
163            return success;
164        }
165    
166        /**
167         * 
168         * This calls the {@link SubObjCdGlobalRule#checkAccountDetails(AccountGlobalDetail)} on each AccountGlobalDetail as well as calling
169         * {@link SubObjCdGlobalRule#checkOnlyOneChartErrorWrapper(List)} to ensure there is just one chart
170         * @param details
171         * @return false if any of the detail objects fail they're sub-rule
172         */
173        public boolean checkAccountDetails(List<AccountGlobalDetail> details) {
174            boolean success = true;
175    
176            // check if there are any accounts
177            if (details.size() == 0) {
178                putFieldError(KFSConstants.MAINTENANCE_ADD_PREFIX + "accountGlobalDetails.accountNumber", KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCOUNT_NO_ACCOUNTS);
179                success = false;
180            }
181            else {
182                // check each account
183                int index = 0;
184                for (AccountGlobalDetail dtl : details) {
185                    String errorPath = MAINTAINABLE_ERROR_PREFIX + "accountGlobalDetails[" + index + "]";
186                    GlobalVariables.getMessageMap().addToErrorPath(errorPath);
187                    success &= checkAccountDetails(dtl);
188                    GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
189                    index++;
190                }
191                success &= checkOnlyOneChartErrorWrapper(details);
192            }
193    
194            return success;
195        }
196    
197        /**
198         * 
199         * This checks that if the account and chart are entered that the  account associated with the AccountGlobalDetail is valid
200         * @param dtl - the AccountGlobalDetail we are dealing with
201         * @return false if any of the fields are found to be invalid
202         */
203        public boolean checkAccountDetails(AccountGlobalDetail dtl) {
204            boolean success = true;
205            int originalErrorCount = GlobalVariables.getMessageMap().getErrorCount();
206            getDictionaryValidationService().validateBusinessObject(dtl);
207            if (StringUtils.isNotBlank(dtl.getAccountNumber()) && StringUtils.isNotBlank(dtl.getChartOfAccountsCode())) {
208                dtl.refreshReferenceObject("account");
209                if (ObjectUtils.isNull(dtl.getAccount())) {
210                    GlobalVariables.getMessageMap().putError("accountNumber", KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCOUNT_INVALID_ACCOUNT, new String[] { dtl.getChartOfAccountsCode(), dtl.getAccountNumber() });
211                }
212            }
213            success &= GlobalVariables.getMessageMap().getErrorCount() == originalErrorCount;
214    
215            return success;
216        }
217    
218        /**
219         * 
220         * This checks that if the object code, chart code, and fiscal year are entered it is a valid Object Code, chart, and Fiscal Year
221         * associated with this SubObjectCode
222         * @param dtl - the SubObjCdGlobalDetail we are checking
223         * @return false if any of the fields are found to be invalid
224         */
225        public boolean checkSubObjectCodeDetails(SubObjectCodeGlobalDetail dtl) {
226            boolean success = true;
227            int originalErrorCount = GlobalVariables.getMessageMap().getErrorCount();
228            getDictionaryValidationService().validateBusinessObject(dtl);
229            if (StringUtils.isNotBlank(dtl.getFinancialObjectCode()) && StringUtils.isNotBlank(dtl.getChartOfAccountsCode()) && dtl.getUniversityFiscalYear() > 0) {
230                dtl.refreshReferenceObject("financialObject");
231                dtl.refreshReferenceObject("universityFiscal");
232                dtl.refreshReferenceObject("chartOfAccounts");
233                if (ObjectUtils.isNull(dtl.getChartOfAccounts()) || ObjectUtils.isNull(dtl.getUniversityFiscal()) || ObjectUtils.isNull(dtl.getFinancialObject())) {
234                    GlobalVariables.getMessageMap().putError("financialObjectCode", KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_INVALID_OBJECT_CODE, new String[] { dtl.getFinancialObjectCode(), dtl.getChartOfAccountsCode(), dtl.getUniversityFiscalYear().toString() });
235                }
236            }
237            success &= GlobalVariables.getMessageMap().getErrorCount() == originalErrorCount;
238    
239            return success;
240        }
241    
242        /**
243         * This method checks the simple rules for all lines at once and gets called on save, submit, etc. but not on add
244         * <ul>
245         * <li>{@link SubObjCdGlobalRule#checkForSubObjCdGlobalDetails(List)}</li>
246         * <li>{@link SubObjCdGlobalRule#checkForAccountGlobalDetails(List)}</li>
247         * <li>{@link SubObjCdGlobalRule#checkFiscalYearAllLines(SubObjCdGlobal)}</li>
248         * <li>{@link SubObjCdGlobalRule#checkChartAllLines(SubObjCdGlobal)}</li>
249         * </ul>
250         * @return
251         */
252        protected boolean checkSimpleRulesAllLines() {
253            boolean success = true;
254            // check if there are any object codes and accounts, if either fails this should fail
255            if (!checkForSubObjCdGlobalDetails(subObjCdGlobal.getSubObjCdGlobalDetails()) && !checkForAccountGlobalDetails(subObjCdGlobal.getAccountGlobalDetails())) {
256                success = false;
257            }
258            else {
259                // check object codes
260                success &= checkFiscalYearAllLines(subObjCdGlobal);
261    
262                // check chart code
263                success &= checkChartAllLines(subObjCdGlobal);
264    
265            }
266            return success;
267        }
268    
269        /**
270         * 
271         * This checks that the SubObjCdGlobalDetail list isn't empty or null
272         * @param subObjCdGlobalDetails
273         * @return false if the list is null or empty
274         */
275        protected boolean checkForSubObjCdGlobalDetails(List<SubObjectCodeGlobalDetail> subObjCdGlobalDetails) {
276            if (subObjCdGlobalDetails == null || subObjCdGlobalDetails.size() == 0) {
277                putFieldError(KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_NO_OBJECT_CODE);
278                return false;
279            }
280            return true;
281        }
282    
283        /**
284         * 
285         * This checks that the AccountGlobalDetail list isn't empty or null
286         * @param acctChangeDetails
287         * @return false if the list is null or empty
288         */
289        protected boolean checkForAccountGlobalDetails(List<AccountGlobalDetail> acctChangeDetails) {
290            if (acctChangeDetails == null || acctChangeDetails.size() == 0) {
291                putFieldError(KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.ACCOUNT_CHANGE_DETAILS + "." + KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_NO_ACCOUNT);
292                return false;
293            }
294            return true;
295        }
296    
297        /**
298         * 
299         * This checks that the fiscal year is the same on the doc and all SubObjCdGlobalDetails
300         * @param socChangeDocument
301         * @return false if the fiscal year is not the same on the doc and any of the SubObjCdGlobalDetails
302         */
303        protected boolean checkFiscalYearAllLines(SubObjectCodeGlobal socChangeDocument) {
304            boolean success = true;
305            int i = 0;
306            for (SubObjectCodeGlobalDetail subObjCdGlobal : socChangeDocument.getSubObjCdGlobalDetails()) {
307    
308                // check fiscal year first
309                success &= checkFiscalYear(socChangeDocument, subObjCdGlobal, i, false);
310    
311                // increment counter for sub object changes list
312                i++;
313            }
314    
315            return success;
316        }
317    
318        /**
319         * 
320         * This checks that the chart is the same on the document, SubObjCdGlobalDetails and AccountGlobalDetails
321         * @param socChangeDocument
322         * @return false if the chart is missing or not the same on the doc, or the detail lists
323         */
324        protected boolean checkChartAllLines(SubObjectCodeGlobal socChangeDocument) {
325            boolean success = true;
326            int i = 0;
327            for (SubObjectCodeGlobalDetail subObjCdGlobal : socChangeDocument.getSubObjCdGlobalDetails()) {
328    
329                // check chart
330                success &= checkChartOnSubObjCodeDetails(socChangeDocument, subObjCdGlobal, i, false);
331                // increment counter for sub object changes list
332                i++;
333            }
334    
335            // check each account change
336            i = 0;
337            for (AccountGlobalDetail acctChangeDetail : socChangeDocument.getAccountGlobalDetails()) {
338    
339                // check chart
340                success &= checkChartOnAccountDetails(socChangeDocument, acctChangeDetail, i, false);
341                // increment counter for account changes list
342                i++;
343            }
344    
345            return success;
346        }
347    
348        /**
349         * This checks to make sure that the fiscal year on the {@link SubObjCdGlobalDetail} is not empty and
350         * the document's fiscal year matches the detail's fiscal year
351         * 
352         * @param socChangeDocument
353         * @return false if the fiscal year is missing or is not the same between the doc and the detail
354         */
355        protected boolean checkFiscalYear(SubObjectCodeGlobal socChangeDocument, SubObjectCodeGlobalDetail socChangeDetail, int lineNum, boolean add) {
356            boolean success = true;
357            String errorPath = KFSConstants.EMPTY_STRING;
358            // first must have an actual fiscal year
359            if (ObjectUtils.isNull(socChangeDetail.getUniversityFiscal())) {
360                if (add) {
361                    errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "universityFiscalYear";
362                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_FISCAL_YEAR_MUST_EXIST);
363                }
364                else {
365                    errorPath = KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "universityFiscalYear";
366                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_FISCAL_YEAR_MUST_EXIST);
367                }
368                success &= false;
369                return success;
370            }
371    
372            // the two fiscal years from the document and detail must match
373            if (!socChangeDocument.getUniversityFiscal().equals(socChangeDetail.getUniversityFiscal())) {
374                if (add) {
375                    errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "universityFiscalYear";
376                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_FISCAL_YEAR_MUST_BE_SAME);
377                }
378                else {
379                    errorPath = KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "universityFiscalYear";
380                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_FISCAL_YEAR_MUST_BE_SAME);
381                }
382                success &= false;
383                return success;
384            }
385    
386            return success;
387        }
388    
389        /**
390         * 
391         * This checks to make sure that the chart of accounts on the {@link SubObjCdGlobalDetail} is not empty and 
392         * the document's chart matches the detail's chart
393         * @param socChangeDocument
394         * @param socChangeDetail
395         * @param lineNum
396         * @param add
397         * @return false if the chart is missing or is not the same between the doc and the detail
398         */
399        protected boolean checkChartOnSubObjCodeDetails(SubObjectCodeGlobal socChangeDocument, SubObjectCodeGlobalDetail socChangeDetail, int lineNum, boolean add) {
400            boolean success = true;
401            String errorPath = KFSConstants.EMPTY_STRING;
402            
403            if (StringUtils.isBlank(socChangeDetail.getChartOfAccountsCode())) {
404                return success; // just return, the existence check will balk at empty details
405            }
406            
407            // first must have an actual fiscal year
408            if (socChangeDetail.getChartOfAccounts() == null) {
409                if (add) {
410                    errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "chartOfAccountsCode";
411                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_EXIST);
412                }
413                else {
414                    errorPath = KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
415                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_EXIST);
416                }
417                success &= false;
418                return success;
419            }
420    
421            // the two fiscal years from the document and detail must match
422            if (!socChangeDocument.getChartOfAccounts().equals(socChangeDetail.getChartOfAccounts())) {
423                if (add) {
424                    errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "chartOfAccountsCode";
425                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_BE_SAME);
426                }
427                else {
428                    errorPath = KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
429                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_BE_SAME);
430                }
431                success &= false;
432                return success;
433            }
434    
435            return success;
436        }
437    
438        /**
439         * 
440         * This checks that the chart of accounts on the {@link AccountGlobalDetail} is not empty and matches
441         * the document's chart matches the detail's chart
442         * @param socChangeDocument
443         * @param acctDetail
444         * @param lineNum
445         * @param add
446         * @return false if the chart is missing or is not the same between the doc and the detail
447         */
448        protected boolean checkChartOnAccountDetails(SubObjectCodeGlobal socChangeDocument, AccountGlobalDetail acctDetail, int lineNum, boolean add) {
449            boolean success = true;
450            String errorPath = KFSConstants.EMPTY_STRING;
451            // first must have an actual fiscal year
452            if (acctDetail.getChartOfAccounts() == null) {
453                if (add) {
454                    errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "chartOfAccountsCode";
455                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_EXIST);
456                }
457                else {
458                    errorPath = KFSPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
459                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_EXIST);
460                }
461                success &= false;
462                return success;
463            }
464    
465            // the two fiscal years from the document and detail must match
466            if (!socChangeDocument.getChartOfAccounts().equals(acctDetail.getChartOfAccounts())) {
467                if (add) {
468                    errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.ACCOUNT_CHANGE_DETAILS + "." + "chartOfAccountsCode";
469                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_BE_SAME);
470                }
471                else {
472                    errorPath = KFSPropertyConstants.ACCOUNT_CHANGE_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
473                    putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_BE_SAME);
474                }
475                success &= false;
476                return success;
477            }
478    
479            return success;
480        }
481    
482    
483    }