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
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:
Parameter | Use |
---|---|
PROPERTY | Code of the property in which the error occurs. If empty, the error is general and not linked to a property. |
MESSAGE | String value containing the error message (in the language of the user). |
ERROR_TYPE | Status (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).
|
CATEGORY | An 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:
Parameter | Description |
---|---|
Value Integer SEARCH_TYPE | Contains one of the following valuesCST_ACURRENT : if only the errors from the current instance are extractedCST_ALL :if the nested sub-instance and the copied instance are extracted |
Value Char PROPERTY | Property concerned only (if not empty and if ERR_LOCAL is CST_ACURRENT ) |
Value Integer ERROR_TYPE | Can have the following values:CST_ALL : returns the highest error in the instance and nested sub-instancesCST_ACURRENT : returns the highest error in the current instance only. |
Variable Char PROPERTY_LABEL | Label associated to the property if PROPERTY returned |
Variable Char MESSAGE | Error message |
Variable Integer CATEGORY | Category (given by the developer when throwing the error) |
Variable Char ENVIRONMENT | Environment 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:
Parameter | Definition |
---|---|
Value Integer SEARCH_TYPE | Contains one of the following valuesCST_ACURRENT : if only the errors from the current instance are extractedCST_ALL :if the nested sub-instance and the copied instance are extracted |
Value Char PROPERTY | Property concerned only (if not empty and if ERR_LOCAL is CST_ACURRENT ) |
Value Integer ERROR_TYPE | Filter on status(CST_ASUCCESS , CST_AINFO , CST_AWARNING , CST_AERROR , CST_AFATAL )
|
Value Integer SEL_TYPE | Comparizon 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:
Parameter | Definition |
---|---|
Variable Integer ERROR_TYPE | status error (CST_ASUCCESS , CST_AINFO , CST_AWARNING , CST_AERROR or CST_AFATAL ) |
Variable Char PROPERTY | Corresponding property if applicable |
Variable Char PROPERTY_LABEL | Property description if applicable |
Variable Char MESSAGE | Error message |
Variable Integer CATEGORY | Category (given by the developer when throwing the error) |
Variable Char ENVIRONMENT | Environment 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()
Parameter | Definition |
---|---|
Value Char PROPERTY | Property 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 copiedENVIRONMENT
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 event | Event Codes | Behavior |
---|---|---|
CRUD operation events | AINSERT, AREAD, AUPDATE, ADELETE | The database update should not have been performed. |
Properties events | INIT, GET, CONTROL, PROPAGATE | The 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 events | AINSERT_CONTROL_BEFORE, AINSERT_CONTROL_AFTER, ADELETE_CONTROL_BEFORE, ADELETE_CONTROL_AFTER, AUPDATE_CONTROL_BEFORE, AUPDATE_CONTROL_AFTER | The control failed. The operation 'Update operation' will not be performed (the control is not fulfilled). |
CRUD update events | AINSERT_BEFORE, AINSERT_AFTER, ADELETE_BEFORE, ADELETE_AFTER, AUPDATE_BEFORE, AUPDATE_AFTER | An error occurred in the transaction. The corresponding rollback operation will be executed. |
CRUD error handling events | AINSERT_ROLLBACK, ADELETE_ROLLBACK, AUPDATE_ROLLBACK | An 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.
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