View Javadoc
1   /*
2    * Copyright 2007 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.coa.document.validation.impl;
17  
18  import java.util.List;
19  
20  import org.apache.commons.lang.StringUtils;
21  import org.kuali.ole.coa.businessobject.AccountGlobalDetail;
22  import org.kuali.ole.coa.businessobject.SubObjectCodeGlobal;
23  import org.kuali.ole.coa.businessobject.SubObjectCodeGlobalDetail;
24  import org.kuali.ole.sys.OLEConstants;
25  import org.kuali.ole.sys.OLEKeyConstants;
26  import org.kuali.ole.sys.OLEPropertyConstants;
27  import org.kuali.rice.kns.document.MaintenanceDocument;
28  import org.kuali.rice.krad.bo.PersistableBusinessObject;
29  import org.kuali.rice.krad.util.GlobalVariables;
30  import org.kuali.rice.krad.util.ObjectUtils;
31  
32  /**
33   * 
34   * This class implements the business rules specific to the {@link SubObjCdGlobal} Maintenance Document.
35   */
36  public class SubObjCdGlobalRule extends GlobalDocumentRuleBase {
37      protected SubObjectCodeGlobal subObjCdGlobal;
38  
39      /**
40       * This method sets the convenience objects like subObjCdGlobal and all the detail objects, so you have short and easy handles to the new and
41       * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load
42       * all sub-objects from the DB by their primary keys, if available. This also loops through each detail item (SubObjCdGlobalDetail and AccountGlobalDetail)
43       * are refreshed
44       * 
45       * @param document - the maintenanceDocument being evaluated
46       */
47      @Override
48      public void setupConvenienceObjects() {
49  
50          // setup subObjCdGlobal convenience objects,
51          // make sure all possible sub-objects are populated
52          subObjCdGlobal = (SubObjectCodeGlobal) super.getNewBo();
53  
54          // forces refreshes on all the sub-objects in the lists
55          for (SubObjectCodeGlobalDetail objectCodeGlobalDetail : subObjCdGlobal.getSubObjCdGlobalDetails()) {
56              objectCodeGlobalDetail.refreshNonUpdateableReferences();
57          }
58          for (AccountGlobalDetail accountGlobalDetail : subObjCdGlobal.getAccountGlobalDetails()) {
59              accountGlobalDetail.refreshNonUpdateableReferences();
60          }
61      }
62  
63      /**
64       * This performs rules checks on document approve
65       * <ul>
66       * <li>{@link SubObjCdGlobalRule#checkSimpleRulesAllLines()}</li>
67       * </ul>
68       * This rule fails on business rule failures
69       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
70       */
71      @Override
72      protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) {
73          boolean success = true;
74          setupConvenienceObjects();
75          // check simple rules
76          success &= checkSimpleRulesAllLines();
77  
78          success &= checkOnlyOneChartErrorWrapper(subObjCdGlobal.getAccountGlobalDetails());
79  
80          return success;
81      }
82  
83      /**
84       * This performs rules checks on document route
85       * <ul>
86       * <li>{@link SubObjCdGlobalRule#checkSimpleRulesAllLines()}</li>
87       * </ul>
88       * This rule fails on business rule failures
89       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
90       */
91      @Override
92      protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
93          boolean success = true;
94          setupConvenienceObjects();
95          // check simple rules
96          success &= checkSimpleRulesAllLines();
97  
98          success &= checkAccountDetails(subObjCdGlobal.getAccountGlobalDetails());
99  
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.krad.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", OLEKeyConstants.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", OLEKeyConstants.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", OLEKeyConstants.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", OLEKeyConstants.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", OLEKeyConstants.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(OLEConstants.MAINTENANCE_ADD_PREFIX + "accountGlobalDetails.accountNumber", OLEKeyConstants.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", OLEKeyConstants.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", OLEKeyConstants.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(OLEConstants.MAINTENANCE_ADD_PREFIX + OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + OLEPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE, OLEKeyConstants.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(OLEConstants.MAINTENANCE_ADD_PREFIX + OLEPropertyConstants.ACCOUNT_CHANGE_DETAILS + "." + OLEPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE, OLEKeyConstants.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 = OLEConstants.EMPTY_STRING;
358         // first must have an actual fiscal year
359         if (ObjectUtils.isNull(socChangeDetail.getUniversityFiscal())) {
360             if (add) {
361                 errorPath = OLEConstants.MAINTENANCE_ADD_PREFIX + OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "universityFiscalYear";
362                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_FISCAL_YEAR_MUST_EXIST);
363             }
364             else {
365                 errorPath = OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "universityFiscalYear";
366                 putFieldError(errorPath, OLEKeyConstants.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 = OLEConstants.MAINTENANCE_ADD_PREFIX + OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "universityFiscalYear";
376                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_FISCAL_YEAR_MUST_BE_SAME);
377             }
378             else {
379                 errorPath = OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "universityFiscalYear";
380                 putFieldError(errorPath, OLEKeyConstants.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 = OLEConstants.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 = OLEConstants.MAINTENANCE_ADD_PREFIX + OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "chartOfAccountsCode";
411                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_EXIST);
412             }
413             else {
414                 errorPath = OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
415                 putFieldError(errorPath, OLEKeyConstants.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 = OLEConstants.MAINTENANCE_ADD_PREFIX + OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "chartOfAccountsCode";
425                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_BE_SAME);
426             }
427             else {
428                 errorPath = OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
429                 putFieldError(errorPath, OLEKeyConstants.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 = OLEConstants.EMPTY_STRING;
451         // first must have an actual fiscal year
452         if (acctDetail.getChartOfAccounts() == null) {
453             if (add) {
454                 errorPath = OLEConstants.MAINTENANCE_ADD_PREFIX + OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "." + "chartOfAccountsCode";
455                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_EXIST);
456             }
457             else {
458                 errorPath = OLEPropertyConstants.SUB_OBJ_CODE_CHANGE_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
459                 putFieldError(errorPath, OLEKeyConstants.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 = OLEConstants.MAINTENANCE_ADD_PREFIX + OLEPropertyConstants.ACCOUNT_CHANGE_DETAILS + "." + "chartOfAccountsCode";
469                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_BE_SAME);
470             }
471             else {
472                 errorPath = OLEPropertyConstants.ACCOUNT_CHANGE_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode";
473                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_SUBOBJECTMAINT_CHART_MUST_BE_SAME);
474             }
475             success &= false;
476             return success;
477         }
478 
479         return success;
480     }
481 
482 
483 }