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

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.