Skip to content

Best Practices: Scripting

Use the Scripting API for cross-period lookbacks, not the backend runtime

Reading results from previous periods is a common requirement — cumulative YTD totals, carry-forward corrections, progressive coefficient recalculations. The backend exposes this capability through PayrunRuntimeBase, but those methods are not available in the scripting context of valueExpression or resultExpression. The scripting layer provides a separate, type-safe API that wraps the same operations.

What does not work (backend-internal signatures):

// CS0103 – these names do not exist in the scripting context
var evalPeriod = GetEvaluationPeriod();
var prevPeriod = GetPeriod(evalPeriod.Item1, -1);
var results = GetWageTypeResults(new List<decimal> { 990m },
    prevPeriod.Item1, prevPeriod.Item2, null, 0);
decimal prev = results.Any() ? results[0].Item4 : 0m;

Correct equivalents in the scripting API:

Period navigation uses pre-built properties — no method call required:

Need Property / method
Current period Period, PeriodStart, PeriodEnd
Previous period PreviousPeriod
Arbitrary offset GetPeriod(-2), Periods[-3]
Cycle start/end Cycle, CycleStart, CycleEnd
All past cycle periods GetPastCyclePeriods()

Result queries return IList<WageTypeResult> with a .Value property, not tuples:

// Previous period — carry-forward or negative net recovery
var results = GetWageTypeResults(
    new List<decimal> { 990m },
    PreviousPeriod.Start, PreviousPeriod.End,
    null, PayrunJobStatus.Complete);
decimal prevNet = results.FirstOrDefault()?.Value ?? 0m;
// YTD accumulation — all completed periods of the current cycle up to now
var ytdResults = GetConsolidatedWageTypeResults(
    new WageTypeConsolidatedResultQuery([801m])
    {
        PeriodMoment = PeriodStart,
        JobStatus = PayrunJobStatus.Complete,
        NoRetro = true
    });
decimal ytdTotal = ytdResults.Sum(r => r.Value);

GetWageTypeResults vs. GetConsolidatedWageTypeResults:

Method Use when
GetWageTypeResults You need results for a specific date range (e.g. one previous period, a fixed window)
GetConsolidatedWageTypeResults You need the deduplicated YTD view for all periods of the current cycle up to a moment — handles retro overlaps correctly

Common pitfalls:

  • PayrunJobStatus.Complete is the enum member for finished statutory runs. Passing the integer 0 selects Draft jobs — the opposite of what is intended. Always use the enum.
  • GetConsolidatedWageTypeResults with NoRetro = false includes retro correction results. Set NoRetro = true for a clean cycle-to-date sum.
  • Scripting is C# syntax embedded in JSON/YAML — no .NET project or build step is needed. The engine compiles expressions at import time and reports CS0103 errors immediately if a name is not in scope.

Separate case input validation from payrun calculation logic

C# in regulations is a scripting syntax — expressions are embedded directly in JSON or YAML and evaluated by the engine at runtime. No .NET project or build pipeline is required. .NET is only needed for Extended Functions and the Automator Client Services SDK.

Custom Actions and expression scripts serve different execution contexts. Custom Actions run during case input in the UI and are designed for immediate user feedback — issues, localized messages, input guards. Expression scripts with extension methods run during the payrun on the server and have access to calculation APIs that are not available in the case context.

In practice: use functionTypes: ["CaseBuild"] or ["CaseValidate"] for Custom Actions that guard data entry — input ranges, format checks, period restrictions. Use valueExpression with extension methods for payrun logic that queries results, triggers retro runs, or performs multi-step calculations. Both layers can coexist for the same business rule: a CaseBuild action prevents invalid input upfront; the WageTypeValue expression handles the calculation consequence at payrun time.


See Also