A practical composite function example for ExtendedTenant, demonstrating how to extend the engine's built-in scripting functions with custom C# classes.
This is the reference implementation for the Extended Functions wiki tutorial.
One employee, one wage type. The Salary value is not read directly via a
No-Code expression — it is delegated to a composite function class, demonstrating
the full three-step pattern for function extension.
| Employee | Salary | Period |
|---|---|---|
| Mario Nuñez | 5 000 | January 2023 |
Function extension involves three steps:
A plain C# class that receives WageTypeValueFunction as a constructor argument
and exposes domain methods:
public class CompositeWageTypeValueFunction(WageTypeValueFunction function)
{
private WageTypeValueFunction Function { get; } = function
?? throw new ArgumentNullException(nameof(function));
public decimal GetSalary()
{
return Function.CaseValue["Salary"];
}
}Extend the engine's WageTypeValueFunction using partial to expose the
composite class as a named property:
public partial class WageTypeValueFunction
{
private CompositeWageTypeValueFunction function;
public CompositeWageTypeValueFunction MyRegulation => function ??= new(this);
}The property MyRegulation is now available in every valueExpression of this
regulation — lazy-initialized on first access.
The composite method is called directly from the wage type valueExpression:
{
"wageTypeNumber": 100,
"name": "Salary",
"valueExpression": "MyRegulation.GetSalary()"
}| Case | Type | Field | ValueType | TimeType |
|---|---|---|---|---|
Salary |
Employee | Salary | Money | CalendarPeriod |
| # | Name | ValueExpression | Collector |
|---|---|---|---|
| 100 | Salary | MyRegulation.GetSalary() |
— |
| Script | Function type | File |
|---|---|---|
CompositeWageTypeValueFunction |
WageTypeValue | Scripts/CompositeWageTypeValueFunction.cs |
WageTypeValueFunction |
WageTypeValue | Scripts/WageTypeValueFunction.cs |
| Employee | WT 100 Salary |
|---|---|
| Mario Nuñez | 5 000 |
The composite pattern wraps WageTypeValueFunction in a separate class rather
than adding extension methods directly. This allows grouping multiple related
methods under a named namespace (MyRegulation) and enables full IDE support —
IntelliSense, refactoring, and debugger step-through — when developing regulation
logic in Visual Studio.
MyRegulation => function ??= new(this) creates the composite instance on first
access and reuses it for subsequent calls within the same wage type evaluation.
This avoids repeated construction without requiring manual lifecycle management.
Because the composite class is a plain C# type injected via constructor, higher-level regulations can subclass or wrap it — keeping shared calculation logic in the base regulation while country- or client-specific logic lives in derived layers.
| File | Purpose |
|---|---|
Payroll.json |
Tenant, regulation, case, wage type, script registrations |
Scripts/CompositeWageTypeValueFunction.cs |
Business logic class (GetSalary) |
Scripts/WageTypeValueFunction.cs |
Partial extension — registers MyRegulation property |
Test.et.json |
Employee payroll test — January 2023, WT 100 = 5 000 |
Setup.pecmd |
Full setup: delete + import + set password |
Delete.pecmd |
Remove the ExtendedTenant |
Test.pecmd |
Run employee payroll test |
# Full setup
Setup.pecmd
# Run test
Test.pecmd
# Teardown
Delete.pecmd
| Field | Value |
|---|---|
| User | lucy.smith@foo.com |
| Password | @ayroll3nginE |
- Extended Functions — wiki tutorial this example accompanies