Error handling

When code is written to be executed through an event, errors and warnings may need to be handled, for example, an erroneous value entered in a field.

This document describes how to trigger an error or a warning message to send to the user. Additional errors methods have been added in update 9.

1. Errors status values 6. Errors transferring methods
2. Errors throwing methods 7. ASTATUS variable
3. Max error retrieving methods 8. How to handle the errors
4. Error list retrieving methods 9. Example of controls
5. Errors deleting methods

1. Errors status values

The management of error gravity is based on a numeric value called status. Available statuses are defined by constant values (in increasing order, which mean that you can test the fact that the status is greater or equal than [V]CST_ERROR, for example):

Constant Meaning
[V]CST_ASUCCESS The requested operation has been successfully performed.
[V]CST_AINFO Information message: no error occurs.
[V]CST_AWARNING Warning message: an unusual set of values has been entered. The system sends a warning and allows you to modify the data entered.
[V]CST_AERROR An error occurred. The data entered must be modified to execute the operation requested.
[V]CST_AFATAL A fatal error occurred. A restart of the complete process is necessary.

2. Errors throwing methods

Two standard methods available on the classes allow to throw errors. One allows to give an additional category, the other remains for historical reasons. These methods can be invoked several times when more than one error occurs.

INTEGER_VAL=fmet this.ASETERRORCAT (PROPERTY,MESSAGE, ERROR_TYPE, CATEGORY)
INTEGER_VAL=fmet this.ASETERROR(PROPERTY, MESSAGE, ERROR_TYPE)

The parameters of these methods are:
ParameterUse
PROPERTYCode of the property in which the error occurs. If empty, the error is general and not linked to a property.
MESSAGEString value containing the error message (in the language of the user).
ERROR_TYPEStatus (see chapter 1 above).
[V]CST_AOK must not be used with ASETERROR error throwing method (if the user interface is implied, it returns an error without a message).
CATEGORYAn integer value that contains an error category. This can freely be defined by application developers.

3. Max error retrieving methods

On every instance, we will provide a set of methods:

INTEGER_VAL=fmet this.AGETMAXERRORALL()
INTEGER_VAL=fmet this.AGETMAXERROR(PROPERTY) 
INTEGER_VAL=fmet this.AGETMAXERRORTXT(SEARCH_TYPE, PROPERTY, ERROR_TYPE, PROPERTY_LABEL, MESSAGE,CATEGORY,ENVIRONMENT)

The parameters of the last method are described in the following table:
ParameterDescription
Value Integer SEARCH_TYPEContains one of the following values
CST_ACURRENT: if only the errors from the current instance are extracted
CST_ALL:if the nested sub-instance and the copied instance are extracted
Value Char PROPERTYProperty concerned only (if not empty and if ERR_LOCAL is CST_ACURRENT)
Value Integer ERROR_TYPECan have the following values:
CST_ALL: returns the highest error in the instance and nested sub-instances
CST_ACURRENT: returns the highest error in the current instance only.
Variable Char PROPERTY_LABELLabel associated to the property if PROPERTY returned
Variable Char MESSAGEError message
Variable Integer CATEGORYCategory (given by the developer when throwing the error)
Variable Char ENVIRONMENTEnvironment code given by developer when the error is transferred (empty if the error was not copied from another instance)

4. Error list retrieving methods

Accessing to the complete error stack is performed with the following methods:

Initial call

INTEGER_VAL=fmet this.ASEARCHINFOS(SEARCH_TYPE, PROPERTY, ERROR_TYPE, SEL_TYPE)
This first method initializes a temporary error storage structure. It returns an integer value that is the first error index (this value is 0 if no errors have been found). The parameters are the following:
ParameterDefinition
Value Integer SEARCH_TYPEContains one of the following values
CST_ACURRENT: if only the errors from the current instance are extracted
CST_ALL:if the nested sub-instance and the copied instance are extracted
Value Char PROPERTYProperty concerned only (if not empty and if ERR_LOCAL is CST_ACURRENT)
Value Integer ERROR_TYPEFilter on status(CST_ASUCCESS, CST_AINFO, CST_AWARNING, CST_AERROR, CST_AFATAL)
Value Integer SEL_TYPEComparizon operator on status ([V]CST_ALOWER=less or equal to ASTATUS, [V]CST_AEQUAL=equal to ASTATUS, [V]CST_AGREATER=greater than ASTATUS)

Calls to get the next errors

INTEGER_VAL=fmet this.AGETNEXTINFOS(ERROR_TYPE, PROPERTY, PROPERTY_LABEL, MESSAGE, CATEGORY, ENVIRONMENT)
This second method returns the errors in the chronological order and fills the corresponding parameters. When the last error has been sent back, 0 is returned and the temporary error structure is cleaned up.

The parameters are the following:

ParameterDefinition
Variable Integer ERROR_TYPEstatus error (CST_ASUCCESS, CST_AINFO, CST_AWARNING, CST_AERROR or CST_AFATAL)
Variable Char PROPERTYCorresponding property if applicable
Variable Char PROPERTY_LABELProperty description if applicable
Variable Char MESSAGEError message
Variable Integer CATEGORYCategory (given by the developer when throwing the error)
Variable Char ENVIRONMENTEnvironment code given by developer when the error is transferred (empty if the error was not copied from another instance)

Example of error handling

This example uses a log file to manage the errors:
$ERROR_LOG_WRITE
# Instantiate a class to generate the log
Local Instance MYLOG Using C_ALOG
  MYLOG=NewInstance C_ALOG AllocGroup Null
# Open the log file
Local Integer OK
  OK=fmet MYLOG.ABEGINLOG("Error dump")
# Fill the error structure
Local Integer IF_ERROR
Local Integer ERR_STAT, ERR_CAT
Local Char ERR_PROP(250), ERR_LAB(250), ERR_MESS(250), ERR_ENV(250)
  IF_ERROR= fmet this.ASEARCHINFOS([V]CST_ALL,"",[V]CST_AINFO,[V]CST_AGREATER)
  # Display an error header log
  If IF_ERROR<>[V]CST_AOK
    OK=fmet MYLOG.APUTLINE("Error stack generated by"-this.ACTX.USER-"at"-num$(timestamp$))
    OK=fmet MYLOG.APUTLINE(string$(80,"-"))
  Endif
  # Loop as long as errors are encountered
  While IF_ERROR=[V]CST_AOK
    OK=fmet MYLOG.APUTLINE("Index"-num$(IF_ERROR)+":")
    IF_ERROR=fmet this.AGETNEXTINFOS(ERR_STAT, ERR_PROP, ERR_LAB, ERR_MESS, ERR_CAT, ERR_ENV)
    OK=fmet MYLOG.APUTLINE("  Status="+string$(ERR_STAT=[V]CST_AERROR,"Error")
&                                     +string$(ERR_STAT=[V]CST_AWARNING,"Warning")
&                                     +string$(ERR_STAT=[V]CST_AFATAL,"Fatal error"))
    OK=fmet MYLOG.APUTLINE("  Message="+ERR_MES)
    IF ERR_PROP<>""
      OK=fmet MYLOG.APUTLINE("  on property="+ERR_PROP+string$(ERR_LAB<>"","("+ERR_LAB+")"))
    Endif
    IF ERR_CAT<>0
      OK=fmet MYLOG.APUTLINE("  application category="+num$(ERR_CAT))
    Endif
    IF ERR_ENV<>""
      OK=fmet MYLOG.APUTLINE("  origin environment="+ERR_ENV)
    Endif
   Wend
  OK=fmet MYLOG.AENDLOG()
  FreeGroup MYLOG
Return

5. Errors deleting methods

These methods delete the errors. The first one deletes errors only on the property name given as parameter. It can be called with a dedicated value of PROPERTY (ie. `CST_ALLERRORS`) to delete all errors, but this should be considered as deprecated, as the second method has been implemented.
INTEGER_VAL=fmet this.ADELETERROR(PROPERTY)
INTEGER_VAL=fmet this.ADELETERRORALL()
ParameterDefinition
Value Char PROPERTYProperty on which the error must be deleted

6. Errors transferring methods

This method copies the error structures from the instance given as parameters to the error structure associated to this.
The environnement string allows to give a context:

INTEGER_VALUE=fmet this.AGETERRORSFROM(INSTANCE,ENVIRONMENT)

  • INSTANCE is the instance from which the errors are copied
  • ENVIRONMENT is a character value given by the developer to indicate the origin of a nested error.

7. ASTATUS variable and supervisor behaviour

This variable is available on all the events, whether this is available or not. The value range is the same as the status values used by the ASETERROR method.

Setting this variable to [V]CST_AERROR or [V]CST_AFATAL is important when an error occurs because these values are tested by the supervisor layer on a return from an event code.

If an error is reported, the supervisor behaves as follows:

Type of eventEvent CodesBehavior
CRUD operation eventsAINSERT, AREAD, AUPDATE, ADELETEThe database update should not have been performed.
Properties eventsINIT, GET, CONTROL, PROPAGATEThe event failed. If this happens during an input, the error will be displayed. If it happens on control at the final stage before the database updates, the updates will not be triggered, but the controls will continue on every Property. A complete list of the errors will be generated.
CRUD control eventsAINSERT_CONTROL_BEFORE, AINSERT_CONTROL_AFTER, ADELETE_CONTROL_BEFORE, ADELETE_CONTROL_AFTER, AUPDATE_CONTROL_BEFORE, AUPDATE_CONTROL_AFTERThe control failed. The operation 'Update operation' will not be performed (the control is not fulfilled).
CRUD update eventsAINSERT_BEFORE, AINSERT_AFTER, ADELETE_BEFORE, ADELETE_AFTER, AUPDATE_BEFORE, AUPDATE_AFTERAn error occurred in the transaction. The corresponding rollback operation will be executed.
CRUD error handling eventsAINSERT_ROLLBACK, ADELETE_ROLLBACK, AUPDATE_ROLLBACKAn error occurred during the rollback operation.

When errors or warnings are triggered while using these methods, the result is the following:

  • If ASTATUS code is [V]CST_AOK, nothing occurs.
  • If ASTATUS code is [V]CST_ASUCCESS, a message can be transmitted to the user interface.
  • If ASTATUS code is [V]CST_AINFO or [V]CST_AWARNING, the message is considered an informational or a warning message.
  • If ASTATUS code is [V]CST_AERROR or [V]CST_AFATAL, the message is considered an error message.

The supervisor layer considers that an error occurred during a class operation or method if ASTATUS returns a value of [V]CST_AERROR or [V]CST_AFATAL.

The warning or error messages updated by these methods will be displayed if a user's interface is involved. For example, if an error occurs during data entry, the error is displayed on the top of the screen with a link that brings the cursor to the Property field where the error was found.

If no user interface is involved, the development partner calling a method that returns errors can manage them in their own code or write them in a log file.

8. How to handle the errors

The following principles must be followed:

In an event or a rule

The following principles must be followed:

  • the this.ASETERROR() method must be used to set the message error. The property code must be filled if the error is linked to a Property.
  • The ASTATUS variable must also be set to prompt the supervisor to abort the operation whenever an error occurs.

In a method or an operation

Every method and operation returns a value. This can be used either for returning a result or for returning an error code.

The this.ASETERROR() method must be used to return an error. The value returned by the operation or method (ARET_VALUE) must be assigned with the right value chosen when an error is encountered.

An example of a call of a supervisor method is provided below:
Local Integer RETURN_VALUE
RETURN_VALUE=this.AREAD(KEY)
If RETURN_VALUE > [V]CST_AWARNING
   # An error occurred
    End RETURN_VALUE
Else 
    # The program continues
    ...
Endif

9. Example of controls

This code is called in a control event on a property that cannot be increased (a warning is sent if the amount is decreased by more than 10%).

# Note : no need to assign [L]ASTATUS=[V]CST_AOK if no error (done by default)
Local Decimal DECR_PERCENT
If this.AMOUNT>this.snapshot.AMOUNT
  [L]ASTATUS=fmet this.ASETERROR("AMOUNT","Amount cannot be > "-num$(this.snapshot.AMOUNT),[V]CST_AERROR)
Else
  DECR_PERCENT=100*(this.snapshot.AMOUNT-this.AMOUNT)/(this.snapshot.AMOUNT+(this.snapshot.AMOUNT=0))
  If DECR_PERCENT>=10
    [L]ASTATUS=fmet this.ASETERROR("AMOUNT","Amount decreased by"-num$(int(DECR_PERCENT))-"%",[V]CST_AWARNING)
  Endif
Endif
Return