AppMill Documentation : Audit History

Access

Click Features > Audit History to access the history of changes that were done in the database tables.

Table of Contents

Overview

The Audit History functionality provides the possibility to automatically collect and display the changes that were done in the database tables (including the adding a new table and deleting an existing one).

The page provides the history of changes for those tables that were selected for tracking on the Advanced Audit (Full Audit List) page (Features > Advanced Audit). For more details about the tables selected for tracking, refer to the Advanced Audit page of the User Manual.

Note that the data will be loaded after filling in the search fields.

To display the data on the Audit History page, you can enter the following data in the search filters fields:

  • Table Name - enter the table for which the history of changes should be displayed.

  • Account Name - enter the user account name who supposed to be the author of the changes.

  • PK Before Changes - define the primary key before the changes were done.

  • PK After Changes - define the primary key value after the changes.

  • Date and Time - enter the assumed date and time when the changes were done.

Navigate

Use the navigation bar at the bottom of the Overview page to navigate through the pages. Click the appropriate page number to open it. Use the Arrow icons to go to the next page or the previous page of the search results list accordingly.

View Audit History

Click Advanced Search button to open the Audit History page.

The page containing the Advanced Search form will be opened. The search results will be displayed below the search form.

The page consists of two main sections:

  • Advanced Search - to display the audit history details, you need to fill in the search filters in the Advanced Search section first.

Initiate the search by clicking the Find button and the search result section will be displayed below the search form.

  • Audit History Table - this section contains the results of the search based on the entered search filters.

Let’s see the details below:

Advanced Search

Here you can use the search filters to get the particularized information:

  • Table Name - enter the table for which the history of changes should be displayed.

  • Account Name - enter the user account name who supposed to be the author of the changes.

  • Exact Match - select the check box to indicate that exactly search phrase a search filter value.

  • Date From - Date To - specify the date range when the changes were done.

  • PK Before - enter the previous value of primary key in case it was changed.

The data selected on the Overview page will be predefined in the fields.

Audit History Table

In the search result table below the search form, all the changes are displayed.

For each row, you can view the changes data that you are interested in. In the operation column, click the following icons to display the corresponding information:

  • Data Before - shows the data before the changes.

  • Only Data Changes - shows only the changes done on the data.

  • Previous Changes - shows the changes on the previous change action.

The pop-up windows with the corresponding information will be opened.

Manual Audit History Addition

The Manual Audit History Addition functionality allows programmatic addition of audit history records to the c_audit_history table on demand. This is useful for:

  • creating audit trails for operations not automatically tracked,

  • integrating with external systems that require custom audit logging.

The functionality is implemented through the RID_ADD_AUDIT_HISTORY_ON_DEMAND database request, which accepts JSON-formatted parameters and can be called from various programming languages, including PHP, JavaScript, Python, C#, and C++.

Database Request Parameters

The RID_ADD_AUDIT_HISTORY_ON_DEMAND request requires the following JSON-formatted parameters:

  • request_id (string) – set this value to "RID_ADD_AUDIT_HISTORY_ON_DEMAND".

  • table_name (string) – enter the name of the table for which audit history records are added.

  • operation (string) – specify the performed operation. Accepted values are "insert" or "update".

  • pk_before (string, semicolon-delimited) – enter the primary key values before the operation. Required only for "update" operations and should be omitted for "insert" operations.

  • pk_after (string, semicolon-delimited) – enter the primary key values after the operation. Required for "insert" operations and also required for "update" operations if primary key columns have changed.

  • session_handle (string) – provide the current session handle, required to capture the active user. If omitted, the user in the audit history record will be set to -1.

JSON Structure Example

{
  "request_id": "RID_ADD_AUDIT_HISTORY_ON_DEMAND",
  "table_name": "Demo_Agent",
  "operation": "update",
  "pk_before": "000B000012",
  "pk_after": "000N000012",
  "session_handle": "your_session_handle_value"
}

Follow the correct operation sequence when implementing manual audit history addition:

  • For "insert" operations, add the record into the target table first, then execute the RID_ADD_AUDIT_HISTORY_ON_DEMAND request.

  • For "update" operations, execute the RID_ADD_AUDIT_HISTORY_ON_DEMAND request first, then update the record in the target table.

Programming Language Examples

The following examples show implementations of the RID_ADD_AUDIT_HISTORY_ON_DEMAND request in different programming languages.

PHP Plugin Function Example

function AuditHistoryExample($json_params) {
    $json_body = array(
        'request_id' => 'RID_ADD_AUDIT_HISTORY_ON_DEMAND',
        'table_name' => 'Demo_Agent',
        'pk_before' => '000B000012',
        'pk_after' => '000N000012',
        'operation' => 'update',
        'session_handle' => $json_params['session_handle']
    );
    
    $response = $this->poolLoader->executeFunctionProcessJson(json_encode($json_body));
    var_dump('RESPONSE: ' . $response);
    
    return array(
        'resultbody' => array(
            'requestresult' => 'successfully', 
            'extendedinfo' => 'OK'
        )
    );
}

JavaScript Plugin Function Example

AuditHistoryExample(json_params) {
    var json_body = {
        'request_id': 'RID_ADD_AUDIT_HISTORY_ON_DEMAND',
        'table_name': 'Demo_Agent',
        'pk_before': '000B000012',
        'pk_after': '000N000012',
        'operation': 'update',
        'session_handle': json_params['session_handle']
    }
    
    var response = this.poolLoader.executeFunctionProcessJson(JSON.stringify(json_body));
    cmodule.print('RESPONSE: ' + response);
    
    return system.prepareResponse(true, 'OK');
}

Python Plugin Function Example

def AuditHistoryExample(self, json_params):
    json_body = {
        'request_id': 'RID_ADD_AUDIT_HISTORY_ON_DEMAND',
        'table_name': 'Demo_Agent',
        'pk_before': '000B000012',
        'pk_after': '000N000012',
        'operation': 'update',
        'session_handle': json_params['session_handle']
    }
    
    response = self.pool_loader.execute_function_process_json(json.dumps(json_body))
    print('RESPONSE: ' + str(response))
    
    return json.dumps(BasePlugin.get_success_response('OK'))

C# Plugin Function Example

public JObject AuditHistoryExample(JObject jsonParam)
{
    var jsonBody = new JObject
    {
        ["request_id"] = "RID_ADD_AUDIT_HISTORY_ON_DEMAND",
        ["table_name"] = "Demo_Agent",
        ["pk_before"] = "000B000012",
        ["pk_after"] = "000N000012",
        ["operation"] = "update",
        ["session_handle"] = jsonParam["session_handle"]?.ToString()
    };
    
    string response = DataLoader.ExecuteProcessJson(jsonBody).ToString();
    Log($"RESPONSE: {response}");
    
    return DataLoaderHelpers.PrepareResponse(status: true, message: "OK", data: null);
}

C++ Plugin Function Example

cp_char_t AuditHistoryExample(cp_char_t pJsonParams)
{
    web::json::value jsonParams = web::json::value::parse(pJsonParams);
    
    web::json::value jsonBody;
    jsonBody[TEXT("request_id")] = web::json::value(TEXT("RID_ADD_AUDIT_HISTORY_ON_DEMAND"));
    jsonBody[TEXT("table_name")] = web::json::value(TEXT("Demo_Agent"));
    jsonBody[TEXT("pk_before")] = web::json::value(TEXT("000B000012"));
    jsonBody[TEXT("pk_after")] = web::json::value(TEXT("000N000012"));
    jsonBody[TEXT("operation")] = web::json::value(TEXT("update"));
    jsonBody[TEXT("session_handle")] = jsonParams.at(TEXT("session_handle"));
    
    web::json::value response = this->poolLoader->ExecuteFunctionProcessJson(jsonBody.serialize().c_str());
    std::wcout << TEXT("RESPONSE: ") << response.serialize() << std::endl;
    
    return PrepareResponse(TEXT("OK"), true);
}

Implementation Notes

Insert Operations

For insert operations, the JSON body must be structured as follows:

  • Set "operation": "insert"

  • Include the "pk_after" parameter with the newly created record's primary key

  • Omit the "pk_before" parameter entirely

Example:

{
  "request_id": "RID_ADD_AUDIT_HISTORY_ON_DEMAND",
  "table_name": "Demo_Agent",
  "operation": "insert",
  "pk_after": "000N000013",
  "session_handle": "your_session_handle_value"
}

Update Operations with Unchanged Primary Keys

For update operations where the primary key remains unchanged:

  • Set "operation": "update"

  • Include both "pk_before" and "pk_after" parameters, using the same value for each

Composite Primary Keys

For tables with composite primary keys, specify the values as semicolon-delimited strings:

{
  "pk_before": "key1;key2;key3",
  "pk_after": "newKey1;newKey2;newKey3"
}

Post-Implementation Verification

After implementation, execute the corresponding plugin function and confirm that:

  1. A new record has been added to the c_audit_history table.

  2. All fields are populated correctly, including user information derived from the session_handle.

  3. The manually inserted records are displayed in the user interface alongside automatically generated audit entries.