Skip to main content
Version: 2022

Logic concepts

Introduction to Business logic

Business logic of the application that cannot be modeled can be added using code. This business logic not only comprises traditional business rules but also the logic to control the user interfaces, process flows and messages.

Business functionality can be integrated into various business logic concepts.

Schematic overview of the various business logic concepts

An overview of the available logic concepts

The following concepts are available:

ConceptFunctionActivation
DefaultChanges the value of fields during import or modification on the basis of values that are entered into other fields.
Moves the position of the cursor on the basis of the entered fields.
Is called automatically:
- as soon as the user clicks on Add or executes a Task or Report
- in edit mode, as soon as a field that has been modified by the user is exited
LayoutDetermines the visibility of fields and whether fields are modifiable and mandatory during import or modification on the basis of values that are entered into other fields.
Determines the availability of the data manipulation buttons on the basis of the values in the fields of the current record.
Is called automatically:
- if another record is selected
- as soon as the user clicks on Add or Modify, or executes a Task or Report
- in edit mode, as soon as a field that has been modified by the user is exited
ContextDetermines which tasks, reports and detail tabs are available, given the values in the fields of the current record.
Determines the active detail tab on the basis of the values in the fields of the current record.
Is called automatically:
- if another record is selected
- in edit mode, as soon as a field that has been modified by the user is exited
ProcessFollowing a process action, determines which following process actions must be carried out and in which sequence.
Can make use and exercise influence on process flow variables.
Is called automatically after an action is executed that is part of a process flow.
Trigger/eventPerforms controls to possibly reverse transactions, or updates other fields or rows in other tables.Is called automatically after data in a table is added, modified or deleted.
Available for insert, update and delete events, that fire before, after or instead of the event
TaskStandalone (business) functionality.Is explicitly started by the user or by means of scheduling (batch). A task may also be called from any other concept.
BadgeDetermining the number that is shown on the badge of a subject, task or report.Automatically on the predefined interval and after data modification of the subject in question.
Change detectionInforms the user interfaces during certain events whether or not a subject has been changed and needs to be refreshed.Activated just before the user interface performs an auto-refresh.
HandlerOverrides the default insert, update, and delete SQL commands with your own business logic.Handlers are activated if the action is initiated by the GUI or the API (Indicium).
OtherDetermines the operation of a subroutine.Depends on the type of subroutine. The user interface never makes explicit use of subroutines. Other logic concepts can make use of this. There are no application specifications written for this. They cannot be found in the user interface and do not contribute to specs but support other logic.

Default

The following options can be set within a Default:

  • The value of each field
  • The name of the field where the cursor will be placed

The following context is provided when executing the Default logic:

Parameter
default_modeIndicates whether it concerns an insert (0) or an update (1).
import_modeIndicates what action is performed:
- 0 = regular, GUI action
- 1 = import row, executed once for the entire row (cursor_from_col_id is empty)
- 3 = import, executed for each column (cursor_from_col_id for each column)
Import_mode 1 is used when importing with the GUI and import_mode 3 is used for directly calling PUT, POST or PATCH on an entity via Indicium.
cursor_from_col_idThe field that triggered the default to be executed. When performing the add or modify action the value is null.
A cursor_from_col_id value is only entered if a user manually changed the field value. If the field value is changed another way, for example, by using input parameters in a process flow, the cursor_from_col_id value is null.
cursor_to_col_idThe field where the cursor must move to after leaving the default procedure.
- Windows GUI Only when the default procedure is triggered by Enter, the focus can move to another field.
- Universal GUI Universal GUI uses Tab instead of Enter.
[col_id]All columns in the table, both input and output. Every value can therefore be modified.

This Default template code ensures that the current date is set for the activated_date field when the activated field has just received the value 1.

if (@cursor_from_col_id = 'activated' and @activated = 1)
begin
set @activated_date = getdate();
end

The default logic is not limited by settings in terms of visibility and validity, both at a model level and through authorization.

Layout

A layout can be used to disable fields and operations, depending on the context. The following settings can be made:

  • Make fields read only, invisible and hidden
  • Make fields mandatory
  • Disable or hide add, update, delete, save and cancel button

The following context is provided when executing the layout logic:

Parameter
layout_modeIndicates whether it concerns an insert (0) or an update (1).
import_modeIndicates what action is performed:
- 0 = regular, GUI action
- 1 = import row, executed once for the entire row (cursor_from_col_id is empty)
- 2 = export
- 3 = import, executed for each column (cursor_from_col_id for each column)
Import_mode 1 is used when importing with the GUI and import_mode 3 is used for directly calling PUT, POST or PATCH on an entity via Indicium.
add_button_type
update_button_type
delete_button_type
confirm_button_type
cancel_button_type
Indicates how the corresponding button should be displayed:
- 0 = enabled
- 1 = disabled
- 2 = hidden
[col_id]All columns of a table. These values can be used to make decisions about the behavior.
[col_id]_typeThe type can be modified for each field:
- 0 = normal
- 1 = read only
- 2 = hidden within the form (space remains reserved)
- 3 = hidden outside the form
[col_id]_mandWhether fields are mandatory can be set for each field:
- 0 = optional
- 1 = mandatory

The variables for type and mandatory only need to be set if the value differs from the default value of the meta-level. It is therefore not necessary to reset the value.

In the following layout template code, the activated_date field is made mandatory when the user is navigating around the screen and the activated field has a value of 1. The activated_date field is hidden outside the form when the activated field has a value of 0.

if (@layout_mode = 1 /* update */ and @activated = 1)
begin
set @activated_date_mand = 1; /* mandatory */
end

if @activated = 0
begin
set @activated_data_type = 3; /* hidden outside form */
end

The layout logic should always be written in such a way that the fields are stateless. For example, when a field is made mandatory by the layout logic, this change only applies until the next call of the layout logic. If the field in the layout logic is subsequently not explicitly set to mandatory again, the status of the field will revert back to the default setting.

note

The layout logic does not have the possibility to provide more access than the model and authorization settings allow. For example, when a column in the model is set to hidden, the field cannot be set to read only by means of the layout procedure. A layout can only restrict.

Context

A context procedure offers the following options:

  • Disabling and hiding detail tabs
  • Disabling and hiding tasks and reports
  • Changing the active tab

The following information is given when executing the context logic:

Parameter
active _ref_idDisplays the name of the active reference tab
[col_id]All columns of a table. These values can be used to make decisions about the behavior
[ref_id]_typeIndicates whether a reference tab should be displayed
- 0 = enabled
- 1 = disabled
- 2 = hidden
[task_id]_typeIndicates whether a task must be displayed:
- 0 = enabled
- 1 = disabled
- 2 = hidden
[report_id]_typeIndicates whether a report must be displayed:
- 0 = enabled
- 1 = disabled
- 2 = hidden

The following template disables the detail tab inactive_property when the activated field has a value of 1.

if (@activated = 1)
begin
set @ref_item_inactive_property_type = 1; /* hidden */
end

The Context logic should always be written in such a way that the fields are stateless. For example, when a field is hidden by the Context logic, this change applies until the next call of the Context logic. If the field in the Context logic is subsequently not explicitly set to hidden again, the availability of the field will revert back to the default setting.

note

The Context logic does not have the possibility to provide more access than the model and authorization settings allow. For example, when a detail tab in the model is set to hidden, the detail cannot be set to visible by means of the Context logic. The Context logic can only restrict in this instance.

Badge

Badges can be used to indicate to the user that there are still open tasks or something similar. These are numbers for a table, view, task or report. They can also be made visible in the menu, on detail tiles and on detail tabs.

The following information is available when executing the badge logic:

Parameter
variant_idThe variant the badge is executed for. The badge concept is the only variant specific concept. The reason for this is that the badge value is often dependent of the default prefilters, which can deviate per variant.
badge_valueThe value that is to be displayed by the GUI. Currently, only an integer can be used with a value between 0 and 99.
col_idAll column values, linked task parameter values, or linked report parameter values that are part of a detail foreign key.

The following template fills the badge value with the number of validation messages or empty when 0.

select @badge_value = nullif(count(1), 0)
from validation
where project_id = @project_id
and project_vrs_id = @project_vrs_id

This is presented as a number in square brackets in your end application:

Badge in Windows GUI Example: badge in tab Validation (Windows GUI)

Process

A process procedure offers the possibility to influence the further course of a process flow by means of the following options:

  • Changing the sequence of the next immediate steps to be taken.
  • Enabling and disabling subsequent steps.

The initial status of the subsequent steps is available in all Process logic. This status is determined by the way in which the executed process action has been completed. When unsuccessful, the subsequent steps for a successful execution are disabled. When successfully completed, the subsequent steps for an unsuccessful execution are disabled. The sequence of the remaining subsequent steps is determined by the settings in the model.

The following information is available when executing the process logic:

Parameter
[col_id]All column values of the table, task or report.
[follow_up_process_action_id]All follow up process actions. A value of null, 0 or a negative value means the process action will not be executed.

Whether or not a process action has been successfully completed is shown by the status of the subsequent steps, which represent both successful and unsuccessful completion.

All process flow variables marked either Process input or Process output can be respectively used or modified in the Process logic.

No further options are present in the context for types other than those specified in this overview. When an action has not been completed successfully, it may be that certain field values have not been filled.

The following template ensures that the next step to print_report is not executed when the order_status is 1 in the task process action.

if (@order_status = 1)
begin
set @print_report = 0
end

Trigger/event

Trigger or Event based logic is performed around the data manipulation. This logic concept is highly platform dependent. This logic concept offers the following possibilities:

  • Perform actions as a result of (attempted) changes in data

  • Prevent or undo changes in data (control)

In general, the concept can be divided into three types, with three moments per type. The available context is different for each type.

  • Instead of / before / after performing a create action - The field values of the record(s) to be added or that have been added.

  • Instead of / before / after performing an update action - The old and new field values of the record(s) to be changed or that have been changed.

  • Instead of / before / after performing a delete action - The field values of the record(s) to be deleted or that have been deleted.

The following trigger ensures that when deleting an order, a record is written to the log:

insert into deleted_order_log
select
d.order_id as order_id,
getdate() as deleted_date
from deleted d

Change detection

The logic concept Change detection allows you to inform the user interfaces during certain events whether or not a subject has been changed and needs to be refreshed. This logic concept is only available for subjects, such as tables and views. It is not available for tasks and reports as they have no data.

You can enable it at a subject's configuration (menu User interface > Subjects > tab Settings > tab Performance > checkbox Change detection). Once enabled, the Change detection logic can also be called directly via the API.

caution

Enabling this logic without assigning any code will effectively disable auto-refresh.

The Change detection mechanism is only used in conjunction with the auto-refresh functionality. Every time an auto-refresh is performed, the user interface can call this logic concept to conditionally prevent it.

You can use the CHANGE_DETECTION code group for a control procedure to assign logic for change detection.

Control procedure change detection A control procedure with code group CHANGE_DETECTION

The logic has the following input parameters:

Input parameter
variant_idOptional. The variant for which the change detection logic is called.
last_refresh_utcOptional. The date and time used for the change detection. The user interface fills this parameter with the last time the data was refreshed.
For the Universal GUI, this is the date and time of the last auto-refresh (null if the data has not been refreshed).
For the Windows GUI, this is the date and time of the last manual or auto-refresh.
col_idOnly columns which are part of a detail which is mapped to the subject are available as parameters. Subjects that are never available as a detail have no parameters for columns.
To illustrate this, a sales_order_line that is a detail of sales_order will have the sales_order_id column available as a context column. The column will only have a value when the logic is called if sales_order_line is shown as a detail of a sales_order.

The following output parameter is available:

Output parameter
refresh_data- A bit indicating whether or not a refresh should be initiated by the user interface. Possible values are 0 (no) and 1 (yes).
- If no value is set by the logic, the value will be interpreted as 0, indicating no refresh is needed.

System-versioned example

Change detection works extra well with system versioned tables. An example of a template for this logic for SQL Server, for a system-versioned temporal invoice table:

if @last_refresh_utc is null
begin
-- No refresh was done yet
set @refresh_data = 1
end
else if exists(
-- New or modified records
select * from invoice
except
select * from invoice for system_time as of @last_refresh_utc)
or exists(
-- Modified or deleted records
select * from invoice for system_time as of @last_refresh_utc
except
select * from invoice)
begin
set @refresh_data = 1
end

Some warnings when writing this logic:

  • If the invoice table (or a variant) has locked prefilters or unauthorized columns, this example might yield false positives for changes in the unauthorized rows or columns, causing unnecessary refreshes.

    • It is not possible to avoid false positives entirely. There is no way to see the currently applied filters or prefilters. Pagination information is also not available.
  • Changes to expression columns or lookup translations of the invoice table are not evaluated by this sample logic and may produce false-negative results, preventing timely refresh.

  • Changes to the data in the detail tabs of the invoice table (normally included in an auto-refresh) play no role in the decision-making with this example logic. If the parent subject is not refreshed, the details will not be refreshed, either.

Non system-versioned examples

This topic provides two more Change detection template examples for SQL Server that do not use system-versioned temporal tables.

To detect an updated sales order in order to refresh a corresponding main sales order subject:

if @last_refresh_utc is null
begin
-- No refresh was done yet
set @refresh_data = 1
end
else if exists(
-- Updated sales order
select 1 from sales_order
where last_updated > @last_refresh_utc
)
begin
set @refresh_data = 1
end

To detect a new sales order line in order to refresh a main sales order subject, for example, to see a newly calculated total amount:

if @last_refresh_utc is null
begin
-- No refresh was done yet
set @refresh_data = 1
end
else if exists(
-- New sales order line
select 1 from sales_order_line
where last_inserted > @last_refresh_utc
)
begin
set @refresh_data = 1
end

Handler

Handler procedures allow you to override the default insert, update, and delete SQL commands initiated by the user interface and Indicium with your own business logic.

A handler can be used best for actions that need to be performed row-by-row, for instance, to delete child records before deleting a parent record. For set-based actions, it is best to use triggers.

  • Handlers only fire when the action is directly initiated by the GUI or API. Therefore, using handlers can be a good way to avoid a cascade of trigger activation.
  • The handlers' single-row approach is well suited for calling other procedures, such as tasks. When using set-based triggers, a cursor may be required to achieve this. Often, handlers are a better alternative to instead-of triggers on views modified solely by the UI or API.

The handler procedures are atomic by default, which means all code is run inside a transaction. This ensures that all mutations will either run entirely successfully or fail and roll back to their original state.

The stored procedures for handlers can also be unit tested.

Activate the handler logic

You can activate the handler logic per table and action (menu User interface > Subjects > tab Default > tab Settings > tab Performance).

note

If you create an Insert handler without an insert in the procedure, nothing will be inserted. In that case, only the GUI will be refreshed. The same applies to Update and Delete handlers: the user or procedure is responsible for updating or deleting the row.

The following input parameters are available:

HandlerParameters
InsertAll table columns and possibly identity column as an output parameter.
When using an insert handler on a table with an identity column, you must add the following line to the handler:

set @id = SCOPE_IDENTITY() where @id is the column id of the identity column.

For more information about the SCOPE_IDENTITY() T-SQL function, see here.

When using an insert handler on a view to insert a record on an underlying table with an identity column, the GUI will refresh and attempt to jump to the newly inserted record.
To let the GUI know which record to select, you need to set the identity column of the table as an identity column on the view as well. This will turn the parameter in the insert handler into an output parameter that can be set using a scope_identity, and thus used by the GUI to select the appropriate row.
UpdateAll table columns, plus a set of primary key columns to determine which record is updated.
DeleteOnly primary key columns.

Handler example

In this example, an order is not deleted but instead archived when the user uses the delete-action. A locked prefilter is used to omit archived items for the users.

Using an instead-of trigger would make it difficult to actually delete orders via logic, for instance, when archived orders have their retention period expire.

update order
set archived = 1,
retention_date = dateadd(year, 1, sysutcdatetime())
where order_id = @order_id

Other

In addition to the logic concepts above, there are platform-specific concepts that are often defined on the basis of a subroutine. These logic concepts can be used in other logic components. Consider, for instance, database functions: the context of this logic is generally consistent with the parameters defined for a subroutine.

View/Snapshot

View and Snapshot logic determines the contents of a view or snapshot respectively. This logic can only be applied in template-based views and snapshots. In the template, the complete query is defined for the data that will be displayed by means of the view or snapshot.

When modeling a view the developer provides the columns which must be available in the view. These columns become available when writing the functionality as parameters. All these parameters must also be used in the code.

Template code for a view could then look as follows:

select
h.hour_id,
h.project_id,
h.description,
e.name as employee_name
from hour h
join employee e
on e.employee_id = h.employee_id

Generated session variables

When using SQL Server as your RDBMS platform, the following session variables are available to use in your business logic. These session variables can be read from the SQL Server's session context using the SESSION_CONTEXT function.

  • tsf_ipv4 - contains the IPv4 address of the client that made the request (null if the connection was established with IPv6). When using proxies, it is necessary to apply the X-Forwarded-For header to get the correct IP address. See Reverse proxy settings for more information.
  • tsf_ipv6 - contains the IPv6 address of the client that made the request (null if the connection was established with IPv4). When using proxies, it is necessary to apply the X-Forwarded-For header to get the correct IP address. See Reverse proxy settings for more information.
  • tsf_appl_lang_id - contains the language of the user who sent the request. It makes the language available to your end application instead of only to IAM. This session variable can be read by using the following statement: select SESSION_CONTEXT(N'tsf_appl_lang_id'). You could, for example, make a default stored procedure return a translation or add a translated column to a view without turning it into a lookup column.
  • tsf_use_log_session_id - contains the session-id corresponding with the session-id in this requests Session log page in IAM. This session variable can be read using the following statement: select SESSION_CONTEXT(N'tsf_use_log_session_id').
  • guid - contains a guid representation of the tsf_use_log_session_id. This session variable can be read by using the following statement: select SESSION_CONTEXT(N'guid').
  • tsf_appl_id - contains the application ID. You can read this session variable using the following statement: select SESSION_CONTEXT(N'tsf_appl_id').
  • tsf_appl_alias - contains the application alias. You can read this session variable using the following statement: select SESSION_CONTEXT(N'tsf_appl_alias').
  • tsf_original_login - Universal GUI contains the user who initially logged in. This can be useful when simulating users. You can read this session variable using the following statement: select SESSION_CONTEXT(N'tsf_original_login').

Offline logic

Mobile GUI

With offline functionality written in JavaScript, you can apply business rules when the Mobile GUI is offline. This will offer the user the same experience as if they were online.

To enable this functionality, you need to add the base project JAVASCRIPT to the project.

To update volatile data on mobile clients before it is synchronized to the service tier, extra logic concepts are available for JavaScript logic. These are: JavaScript Tasks, Before Task event, After Task event and JavaScript Before / After / Delete Triggers.

By defining subroutines of the JavaScript Function type, you can add generic functions to the offline JavaScript.

Offline logic needs to be deployed before it has an effect in the Mobile GUI. This is explained in Creation.