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.