Understanding the Main Class Functions
--------------------------------------

The following document identifies the main functions that are called (and in which order that they are called
in) for objects that are derived from the standard "class_base" base class (i.e. all Meta "Class" objects).

It should be noted that the "validate" function actually gets called twice (the first call occurs immediately
after the "to_store" call and the second call occurs immediately after the "for_store" call). The second call
is not explicitly documented below as this secondary validation is only intended as a failsafe mechanism (for
unexpected bad code in "for_store").

-------------------------------------------------------------------------------------------------------------
1) Creating a new record:

Protocol: perform_create <uid> <dtm> <module> <mclass> <key> field1=value1,field2=value2...
(where <key> is " " for an automatically created key and for clone "[<new_key>] <clone_key>")

Equivalent C++ Code for Rules:
object.op_create( <key> );
object.<field1>( <value1> );
object.<field2>( <value2> );
...
object.op_apply( );

Function       Lock Before   Lock After   Description
-------------------------------------------------------------------------------------------------------------
                                          (op_create - <key> [<clone_key>])
at_create      Create        Create       Record is in an "empty" or cloned state. This function is available
                                          to provide different default field values, although it is generally
                                          only used to erase unwanted cloned values for clones. Assuming that
                                          no error occurs (the most likely being that an attempt to create an
                                          already existing record occurred) then the record will be ready for
                                          the setting of field values prior to the call to "op_apply".

                                          (op_apply)
post_init      Create        Create       This function is available to provide values for fields that depend
                                          upon other field values (unlike "to_store" this function will later
                                          be called again in case "to_store" causes these values to change).

to_store       Create        Create       This function is available to provide values for fields that depend
                                          upon other field values (that were "set" between the commencment of
                                          the "op_create" and the call to "op_apply"). This is the main place
                                          to put things like "calculated" field values or to set/clear values
                                          due to other values. After the function returns it is expected that
                                          the record should be valid for storing.

post_init      Create        Create       This function is available to provide values for fields that depend
                                          upon other field values (second call for "to_store" side-effects).

validate       Create        Create       Prior to any attempt to store the record in the DB this function is
                                          called to perform all record validation and to provide feedback for
                                          end users of any validation issues.

for_store      Create        Update       If the "validate" call found no issues then this function is called
[starts a tx if not already in one]       and then "validate" again prior to record storage. This function is
[secondary *validate* before store]       mostly intended for making necessary changes to other records (when
                                          such other records do not require this record to have actually been
                                          created in the DB), and to provide calculated values that rely upon
                                          other DB records. Provided no exception occurs in this function the
                                          record will be stored in the DB with the lock being tranformed into
                                          an "update" lock (necessary for the next function).

after_store    Update        <none>       Assuming no error has occurred previously this function is the last
[commits the tx if one was started]       step in the create process which allows further operations that are
                                          expecting the record to exist in the DB to be performed. Often such
                                          operations will be the creating or updating of some related "child"
                                          records, but also could include things like dealing with "external"
                                          files.
-------------------------------------------------------------------------------------------------------------

Passing " " as the key (to automatically create a key) should never be performed in rules code as a generated
key will contain the current time stamp (for the protocol command " " is replaced prior to being logged). For
record creation in rules keys should constructed from a known key with a suffix (e.g. <key>_X or <key>_000001
where <key> already exists and with the latter pattern applicable when needing to create multiple records).

2) Updating an existing record:

Protocol: perform_update <uid> <dtm> <module> <mclass> <key> [=<ver_info>] field1=value1,field2=value2...

Equivalent C++ Code for Rules:
object.op_update( <key> );
object.<field1>( <value1> );
object.<field2>( <value2> );
...
object.op_apply( );

Function       Lock Before   Lock After   Description
-------------------------------------------------------------------------------------------------------------
                                          (op_update - <key>)
post_init      Update        Update       This function is available to provide values for fields that depend
                                          upon other field values.

after_fetch    Update        Update       The lock is acquired before the record is fetched. If both the lock
                                          is acquired and the record is found then this function is generally
                                          used in order to determine the value of transient fields which need
                                          to use other field (or other record field) values.

                                          (op_apply)
post_init      Update        Update       This function is available to provide values for fields that depend
                                          upon other field values (unlike "to_store" this function will later
                                          be called again in case "to_store" causes these values to change).

to_store       Update        Update       This function is available to provide values for fields that depend
                                          upon other field values (that were "set" between the commencment of
                                          the "op_update" and the call to "op_apply"). This is the main place
                                          to put things like "calculated" field values or to set/clear values
                                          due to other values. After the function returns it is expected that
                                          the record should be valid for storing.

post_init      Update        Update       This function is available to provide values for fields that depend
                                          upon other field values (second call for "to_store" side-effects).

validate       Update        Update       Prior to any attempt to store the record in the DB this function is
                                          called to perform all record validation and to provide feedback for
                                          end users of any validation issues.

for_store      Update        Update       If the "validate" call found no issues then this function is called
[starts a tx if not already in one]       and then "validate" again prior to record storage. This function is
[secondary *validate* before store]       mostly intended for making necessary changes to other records (when
                                          such other records do not require this record to have actually been
                                          created in the DB) and to provid calculated values that depend upon
                                          other DB records.

after_store    Update        <none>       Assuming no error has occurred previously this function is the last
[commits the tx if one was started]       step in the update process which allows further operations that are
                                          expecting the record to exist in the DB to be performed. Often such
                                          operations will be the creating or updating of some related "child"
                                          records, but also could include things like dealing with "external"
                                          files.
-------------------------------------------------------------------------------------------------------------

3) Deleting an existing record:

Protocol: perform_destroy <uid> <dtm> <module> <mclass> <key> [=<ver_info>]

Equivalent C++ Code for Rules:
object.op_destroy( <key> );
object.op_apply( );

Function       Lock Before   Lock After   Description
-------------------------------------------------------------------------------------------------------------
                                          (op_destroy - <key>)
post_init      Destroy       Destroy      This function is available to provide values for fields that depend
                                          upon other field values.

after_fetch    Destroy       Destroy      The lock is acquired before the record is fetched and if the record
                                          has any related child records then either cascade locks to all such
                                          records will also have been acquired or an error will have occurred
                                          either due to a cascade restriction or record locking. It should be
                                          noted that the existence of the record will be checked prior to any
                                          cascade checks. If no error has occurred this function is generally
                                          used in order to determine the value of transient fields which need
                                          to use other field (or other record field) values.

can_destroy    Destroy       Destroy      This function is available to provide a means of protecting certain
                                          specific records from being deleted (if they are not protected from
                                          deletion by their state). If a specific error message is wanted for
                                          display then an exception should be thrown in this function.

                                          (op_apply)
for_destroy    Destroy       Destroy      This function is intended for making necessary changes (normally as
[starts a tx if not already in one]       updates) to other records that are not simple cascades (such as the
                                          increment or decrement of parent totals). At the point of this call
                                          the record still exists in the DB.

after_destroy  Destroy       <none>       Assuming no error has occurred previously this function is the last
[commits the tx if one was started]       to step in the delete process which permits further operations that
                                          expect the record to have been deleted in the DB to occur. Normally
                                          this function is used to clean up "external" files or for some type
                                          of special conditional cascading.
-------------------------------------------------------------------------------------------------------------

4) Fetching an existing record:

Protocol: perform_fetch <module> <mclass> <key> #1 field1,field2...

Equivalent C++ Code for Rules:
object.perform_fetch( <key> ); // NOTE: Via rules a single record fetch will always retrieve all fields.

Function       Lock Before   Lock After   Description
-------------------------------------------------------------------------------------------------------------
                                          (perform_fetch - <key>)
post_init      <none>        <none>       This function is available to provide values for fields that depend
                                          upon other field values.

after_fetch    <none>        <none>       It should be noted that fetching a record using this method doesn't
                                          involve locks at all (for maximum performance). Generally this hook
                                          is used to determine the value of transient fields that depend upon
                                          other field (or another record's field) values.
-------------------------------------------------------------------------------------------------------------

Equivalent C++ Code for Rules:
object.begin_review( <key> ); // NOTE: Via rules a single record fetch will always retrieve all fields.
...
object.finish_review( );

Function       Lock Before   Lock After   Description
-------------------------------------------------------------------------------------------------------------
                                          (begin_review - <key>)
post_init      Review        Review       This function is available to provide values for fields that depend
                                          upon other field values.

after_fetch    Review        Review       This method of fetching a record is available via protocol but only
                                          using the "object" protocol commands (which are only intended to be
                                          used for regression test purposes) therefore it is intended to only
                                          really be used via rules code to ensure that a record will not have
                                          any changes made to it whilst the lock is held. The hook is used as
                                          already mentioned.

                                          (finish_review)
n/a            Review        <none>       This will release the lock (with no other side-effects).
-------------------------------------------------------------------------------------------------------------

5) Fetching multiple existing records:

Protocol: perform_fetch <module> <mclass> "" [#<limit>] field1,field2...

Equivalent C++ Code for Rules:
if( object.iterate_forwards( "", "field1,field2..." ) )
{
   do
   {
...
   } while( object.iterate_next( ) );
}

Function       Lock Before   Lock After   Description
-------------------------------------------------------------------------------------------------------------
                                          (perform_fetch)
post_init      <none>        <none>       This function is available to provide values for fields that depend
                                          upon other field values.

after_fetch    <none>        <none>       Locking is not involved (for maximum performance) and the SQL query
                                          will be limited according to the field list (optional for C++ code)
                                          and fields that are required to determine state and for inter-field
                                          dependencies. The hook is used as already mentioned.
-------------------------------------------------------------------------------------------------------------

It should be noted that when fetching multiple records via the protocol if the record "limit" is omitted then
if is generating PDF a default limit will be used otherwise the number of records returned is unlimited. When
wanting to discontinue iteration in rules code by breaking out of an iteration loop the method "iterate_stop"
needs to be called otherwise object state errors can occur.