Best Practices - Handling Collections
The purpose of this document is to provide best practices to handle collections.
Collections of child instances are managed as arrays, with additional technical properties and methods that simplify their management. For persistent classes, the supervisor handles all the code, and no additional line needs to be written.
Insertion and deletion
- Use the ADDLINE method to add a line.
- Use the ADELLINE method to delete a line.
- Use the ADDLINE_BEFORE, ADDLINE_AFTER, ADELLINE_BEFORE, and ADELLINE_AFTER events to add or delete lines in a specific position.
Control rules
- A consistency control on a single line must be written at the line level.
- A consistency control on several lines must be written at the parent class level.
Propagation rules
- Propagation within a line must be written at the line level.
- Propagation from the header to the line must be written at the header level.
- Propagation between lines is prohibited. Refer to Best Practices - Propagation Rules.
Line count
- The line count property is updated after a new line has been entered.
- If you need to know the number of lines during a control, count them using the ASTALIN property value (which cannot equal [V]CST_ANEWDEL or [V]CST_ADEL).
Example
The following example of code summarizes the different concepts mentioned above. Every section of this program can be considered autonomously.
- A document called MYDOCUMENT, with a key called NUMDOC, includes a collection called HISTORY that displays the list of users having modified this document. It contains the following properties:
- The key of the document updated (DOCUMENT_UPD).
- The user code (USER_UPD).
- The last update date/time for the user (CRETIM_UPD).
- The number of updates done by the user (NBTIMES_UPD).
- A percentage of updates per user, computed and displayed when the document is read (PERC_UPD).
- The user update history is done by calling a dedicated method, either during the control in CRUD operations (such as the AINSERT_CONTROL_BEFORE and AUPDATE_CONTROL_BEFORE events), or through a dedicated link called in an edit facet. This method, called UPDATE_HISTORY, performs the following actions:
- A user line history is added for the current user.
- It keeps up to 10 lines in the history. If an additional line is entered, the oldest line is deleted.
- The lines that record updates older than five years are purged, except for the current user.
- The first line in the history corresponds to the latest update.
To handle this, the code is created at the document class level.
#
# The history update is done after all the other controls, and before launching the update or the creation actions.
# The percentage of update number is computed after read (if creation, no history exists)
$METHODS
Case CURPTH
When "HISTORY" : # Events on a collection
Case ACTION
When "ADDLINE_AFTER"
Gosub INIT_HISTORY_LINE
Endcase
When "" : # Global events
Case ACTION
When "UPDATE_HISTORY"
Gosub UPDATE_HISTORY
When "READ_AFTER"
Gosub COMPUTE_HISTORY
Endcase
Endcase
Return
$INIT_HISTORY_LINE
this.DOCUMENT_UPD = this.APARENT.MYDOC
this.USER_UPD=this.ACTX.USER
this.CRETIM_UPD=timestamp$
this.NBTIMES_UPD=1
Return
# A propagation rule is triggered on the history if document number is changed
$PROPERTIES
Case CURPRO
When "NUMDOC"
Case ACTION
When "PROPAGATE"
Gosub PROPAGATE_NUMDOC
Endcase
Endcase
Return
# Dedicated method usually called before the creation and the update
# Updates the history lines
# "this" is the current document instance
$UPDATE_HISTORY
Local Integer NUML, TWO_YEARS, OLDLINE, NEWLINE, NBLINES, OK, FIVE_YEARS
Local Decimal TIMESTP
# Inititialize values: current timestamp value in milliseconds, number of days for the last 5 years...
TIMESTP=val(timestamp$)
FIVE_YEARS=(date$-gdat$(day(date$),month(date$),year(date$)-5))
OK=[V]CST_AOK
OLDLINE=0
# Delete the line that is too old (if it is still present and not associated with the user)
# Compute also in this loop OLDNUM: If not null, the user is already present
OK=0
For NUML=1 To maxtab(this.HISTORY)
If this.HISTORY(NUML)<>null : # The pointer can be null if a first validation has been done
If find(this.HISTORY(NUML).ASTALIN,[V]CST_ADEL,[V]CST_ANEWDEL)=0
If this.HISTORY(NUML).USER_UPD=this.ACTX.USER
OLDLINE=NUML
Elsif val(this.CRETIME_UPD)+FIVE_YEARS*24*3600*1000<TIMESTP : # Timestamps are in milliseconds
OK=fmet this.ADELLINE("HISTORY",NUML)
Break (OK<>[V]CST_AOK)
Endif
Endif
Endif
Next NUML
# Handle the error
If OK<>[V]CST_AOK
ASTATUS=fmet this.ASETERROR("","Cannot delete line from history"-num$(NUML),[V]CST_AERROR)
Return
Endif
# Insert a line at the first position of the grid to store the most recent user updating the document
NEWLINE=fmet this.ADDLINE("HISTORY",[V]CST_AFIRSTPOS)
If NEWLINE=[V]CST_ANOTDEFINED
ASTATUS=fmet this.ASETERROR("","Cannot add line in history",[V]CST_AERROR)
Return
Endif
# If a line already existed for the customer, update the last line and delete the previous one
If OLDLINE<>0
this.HISTORY(NEWLINE).NBTIMES_UPD=this.HISTORY(OLDLINE).NBTIMES_UPD+1
OK=fmet this.ADELLINE("HISTORY",OLDLINE)
Endif
# Handle the error
If OK<>[V]CST_AOK
ASTATUS=fmet this.ASETERROR("","Cannot delete line from history"-num$(NUML),[V]CST_AERROR)
Return
Endif
# Count the number of lines in the collection
NBLINES=0
For NUML=1 To maxtab(this.HISTORY)
If this.HISTORY(NUML)<>null & find(this.HISTORY(NUML).ASTALIN,[V]CST_ADEL,[V]CST_ANEWDEL)=0
NBLINES+=1
Endif
Next NUML
# Delete the line having an index over 10
# Here the technical property AORDER is used
# But the AGETINDBYLINE method could also have been used
If NBLINES>10
OK=[V]CST_AOK
For NUML=1 To maxtab(this.HISTORY)
If this.HISTORY(NUML)<>null & find(this.HISTORY(NUML).ASTALIN,[V]CST_ADEL,[V]CST_ANEWDEL)=0
If this.HISTORY(NUML).AORDER>10
OK=fmet this.ADELLINE("HISTORY",NUML)
Break (OK<>[V]CST_AOK)
Endif
Endif
Next NUML
Endif
# Handle the error
If OK<>[V]CST_AOK
ASTATUS=fmet this.ASETERROR("","Cannot delete line from history"-num$(NUML),[V]CST_AERROR)
Return
Endif
Return
$PROPAGATE_NUMDOC
For NUML=1 To maxtab(this.HISTORY)
If this.HISTORY(NUML)<>null & find(this.HISTORY(NUML).ASTALIN,[V]CST_ADEL,[V]CST_ANEWDEL)=0
this.HISTORY(NUML).DOCUMENT_UPD = this.MYDOC
Endif
Next NUML
Return
# Computes the percentage of updates
$COMPUTE_HISTORY
Local Integer NBLINE, TOTLINE
# Count the number of lines in the collection and the total
NBLINES=0
For NUML=1 To maxtab(this.HISTORY)
If this.HISTORY(NUML)<>null & find(this.HISTORY(NUML).ASTALIN,[V]CST_ADEL,[V]CST_ANEWDEL)=0
NBLINES+=1
TOTLINE+=this.HISTORY(NUML).NBTIMES_UPD
Endif
Next NUML
# Assign the percentages
For NUML=1 To maxtab(this.HISTORY)
If this.HISTORY(NUML)<>null & find(this.HISTORY(NUML).ASTALIN,[V]CST_ADEL,[V]CST_ANEWDEL)=0
this.HISTORY(NUML).PERC_UPD=ar2(100*this.HISTORY(NUML).NBTIMES_UPD/TOTLINE)
Endif
Next NUML
Return
Refer to the Classes Standard Methods Developer Guide for more information on collections.