Portal Community
PropertyValue
Directive namemath
Required isolation levelSafe
ProjectFunctions.Services
Syntax form$math.functionName(arg1, arg2, ...) or arithmetic path
PrecisionDecimal arithmetic — no floating-point drift
When to use $math vs $js Use $math for straightforward arithmetic and named functions on already-resolved values. Use $js when you need conditionals, loops, or string manipulation alongside the math. $math runs at Safe isolation and is faster — no engine startup cost.

Built-in Functions Reference

FunctionSignatureReturnsExample
roundround(value, decimals)Decimal$math.round(12.456, 2)12.46
floorfloor(value)Decimal$math.floor(12.9)12
ceilceil(value)Decimal$math.ceil(12.1)13
absabs(value)Decimal$math.abs(-45.5)45.5
minmin(a, b)Decimal$math.min(10, 20)10
maxmax(a, b)Decimal$math.max(10, 20)20
addadd(a, b)Decimal$math.add(100, 25)125
subsub(a, b)Decimal$math.sub(100, 25)75
mulmul(a, b)Decimal$math.mul(100, 0.2)20
divdiv(a, b)Decimal$math.div(100, 4)25
modmod(a, b)Decimal$math.mod(17, 5)2
pctpct(value, percent)Decimal$math.pct(500, 20)100
pctOfpctOf(part, total)Decimal (0–100)$math.pctOf(25, 100)25
clampclamp(value, min, max)Decimal$math.clamp(150, 0, 100)100
powpow(base, exponent)Decimal$math.pow(2, 8)256
sqrtsqrt(value)Decimal$math.sqrt(144)12

Using Resolved Values as Arguments

Arguments to $math functions can themselves be BizFirst expressions. The orchestrator resolves inner expressions first (via multi-pass), then passes results to the math function:

VAT on input amount — resolves input first, then calculates
{@ $math.pct({@ $input.current.netAmount }, 20) }
Equivalent to: netAmount × 0.20

Complex Scenarios

Scenario 1 — Gross Pay Calculation from Rate and Hours

Gross pay
{@ $math.round(mul({@ $input.current.hourlyRate }, {@ $input.current.hoursWorked }), 2) }
hourlyRate × hoursWorked, rounded to 2dp

Scenario 2 — Invoice Total with VAT

// Subtotal field
{@ $math.round(mul({@ $input.current.qty }, {@ $input.current.unitPrice }), 2) }

// VAT amount (20%)
{@ $math.pct({@ $var.subtotal }, 20) }

// Grand total
{@ $math.add({@ $var.subtotal }, {@ $var.vatAmount }) }

Scenario 3 — Prorated Salary for Partial Month

// Working days in month from input, standard working days in month = 22
// Prorated monthly salary:
{@ $math.round(
  mul(
    div({@ $input.current.monthlySalary }, 22),
    {@ $input.current.daysWorked }
  ), 2
) }
// (monthlySalary / 22) × daysWorked, rounded to 2dp

Scenario 4 — Overtime Premium Calculation

// Standard hours = 40/week; overtime rate = 1.5×
// Regular pay
{@ $math.mul({@ $input.current.hourlyRate }, {@ $math.min({@ $input.current.hoursWorked }, 40) }) }

// Overtime hours (if any)
{@ $math.max(sub({@ $input.current.hoursWorked }, 40), 0) }

// Overtime pay (at 1.5×)
{@ $math.mul({@ $var.overtimeHours }, mul({@ $input.current.hourlyRate }, 1.5)) }

Scenario 5 — Contribution Rate Clamped to Legal Maximum

// Pension contribution: employee elects a rate, but legislation caps it at 40%
{@ $math.clamp({@ $input.current.electedRate }, 0, 40) }
// e.g. input 55% → clamped to 40%

// Contribution amount from the clamped rate:
{@ $math.round(pct({@ $input.current.pensionablePay }, {@ $var.clampedRate }), 2) }

Scenario 6 — Percentage Completion Reporting

// After batch: report what percentage of records processed successfully
{@ $math.round(pctOf({@ $var.successCount }, {@ $var.totalCount }), 1) }
// "94.7" — percentage of records that succeeded

// With result embedded in a message:
Processed {@ $var.successCount } / {@ $var.totalCount } records
({@ $math.round(pctOf({@ $var.successCount }, {@ $var.totalCount }), 1) }% success rate)

Common Errors

ErrorCauseFix
MathError: division by zerodiv(x, 0) — denominator resolved to zeroGuard with $js or ensure denominator is non-zero; use @default:0
MathError: not a numberAn argument resolved to a non-numeric stringEnsure upstream data is numeric; parse with $js if needed
PathNotFound: unknownFnUsing a function name that doesn't existCheck the built-in functions table above; use $js for custom logic