A special case of a transaction is that where only one record is allowed to exist. This kind of transaction is often used for System Parameters implementations.
The way to make sure that there aren't any more records is by defining a Surrogate Key and setting its value in the rules:
Transaction: Parameters
Structure
ParameterId* // this is the surrogate key
DefaultCurrency
ChartServer
Rules
// housekeeping to ensure only one record exists
noaccept(ParameterId);
ParameterId = 1;
error('Cannot delete parameters') if delete;
// default values
default(DefaultCurrency, DefaultCurrencyConstant.Currency); // DefaultCurrencyConstant enum domain
default(ChartServer, GeneralConstants.ChartServer); // GeneralConstants enum domain
This transaction can be used for setting the attributes, but there are several ways to access them in code:
For Each
Where ParameterId = 1 // not strictly needed, just for defensive programming
&ChartServer = ChartServer
...
When None
&ChartServer = GeneralConstants.ChartServer // the record wasn't created yet
...
Endfor
In order to define the condition in only one place, Data Selectors can be used:
For Each Using ParameterDataSelector() // where ParameterDataSelector has the ParameterId = 1 condition
&ChartServer = ChartServer
...
When None
&ChartServer = GeneralConstants.ChartServer // the record wasn't created yet
...
Endfor
The problem with this solution is that the When None logic has to be duplicated everywhere (unless there is a way to make sure that a record is always inserted). A better solution can be the one below.
Procedure: GetChartServer
parm(out:&ChartServer)
for each
where ParameterId = 1
&ChartServer = ChartServer
when none
&ChartServer = GeneralConstants.ChartServer // defensive programming, just in case the parameter record wasn't inserted
endfor
This solves the problem of always having values for every attribute even if the record wasn't inserted yet (in this case default values are returned).
ChartInfo Where ParameterId = 1
{
Server = ChartServer
Parms = FixedParms
}
ChartInfo Default //equivalent to when none in procedural
{
Server = GeneralConstants.ChartServer
Parms = GeneralConstants.FixedParms
}
Note: it's very easy to add another layer with this approach, for example, to check first for User Parameters, later for System Parameters and at last for System Constants:
ChartInfo Where UserParameterId = UserId()
{
Server = UserChartServer
Parms = UserFixedParms
}
ChartInfo Default Where ParameterId = 1
{
Server = ChartServer
Parms = FixedParms
}
ChartInfo Default //equivalent to when none in procedural
{
Server = GeneralConstants.ChartServer
Parms = GeneralConstants.FixedParms
}
In gxwiki itself all system parameters were implemented using a single One Record Transaction: