Property Calculator Advanced Examples
Overview
Eight Calculate Expression examples covering multi-step variable chains, cross-object aggregation, dynamic numbering, lookup set operations, regex transformation, complex business rules, FOREACH, and combined functions.
Each example includes the scenario, the expression, a step-by-step explanation, and the expected result. All examples use Calculate Expression mode unless noted otherwise.
13. Multi-Step Calculation with Variables — Full Invoice Computation
Scenario: Calculate subtotal, discount, tax, and total for an invoice — all in a single expression that saves the total.
| Mode: Calculate Expression | Evaluate as Expression: ✅ |
store('subtotal', Sum(%PROPERTY_{PD.InvoiceLines}.PROPERTY_{PD.Amount}%)) *0+
store('discountRate', iif(%PROPERTY_{PD.IsVIP}%, 0.10, 0.00)) *0+
store('discountedSubtotal', get('subtotal') * (1 - get('discountRate'))) *0+
store('vatRate', switch(lookupName(%PROPERTY_{PD.Country}%),
'Finland', 0.255,
'Sweden', 0.25,
'Germany', 0.19,
0.00)) *0+
store('vat', Round(get('discountedSubtotal') * get('vatRate'), 2)) *0+
Round(get('discountedSubtotal') + get('vat'), 2)
Walkthrough:
store('subtotal', ...)— sum all line item amounts → 5000store('discountRate', ...)— 10% discount for VIP customers, 0% otherwise → 0.10store('discountedSubtotal', ...)— 5000 × 0.90 → 4500store('vatRate', ...)— look up country-specific VAT → 0.255store('vat', ...)— 4500 × 0.255 → 1147.50- Final result: 4500 + 1147.50 → 5647.50
*0+ chains store() calls without letting their return values leak into the result. See Variables in the NCalc reference for how the pattern works.
14. Cross-Object Aggregation — Project Budget Utilization
Scenario: A project has an MSLU linking to tasks. Calculate budget utilization percentage from the project’s budget and task costs.
| Mode: Calculate Expression | Evaluate as Expression: ✅ |
let('totalCost', Sum(%PROPERTY_{PD.Tasks}.PROPERTY_{PD.TaskCost}%),
let('budget', %PROPERTY_{PD.ProjectBudget}%,
iif(get('budget') > 0,
concat(Round(get('totalCost') / get('budget') * 100, 1), '%'),
'No budget set')))
| Budget | Task Costs | Result |
|---|---|---|
| 100000 | 25000 + 35000 + 15000 = 75000 | 75.0% |
| 100000 | 95000 + 12000 = 107000 | 107.0% |
| 0 | 5000 | No budget set |
15. Dynamic Document Numbering — Year-Based Sequence
Scenario: Generate a document number in the format DOC-YYYY-NNNN using the current year and a sequence number property.
| Mode: Calculate Expression | Evaluate as Expression: ✅ |
concat('DOC-',
formatDate(now(), 'yyyy'),
'-',
padLeft(tostring(%PROPERTY_{PD.SequenceNumber}%), 4, '0'))
| SequenceNumber | Result |
|---|---|
| 1 | DOC-2026-0001 |
| 42 | DOC-2026-0042 |
| 1337 | DOC-2026-1337 |
16. Lookup Set Operations — Active Participants Only
Scenario: A meeting object has “All Invited” (MSLU) and “Declined” (MSLU) properties. Calculate “Confirmed Attendees” by removing declined people.
| Mode: Calculate Expression | Evaluate as Expression: ✅ |
lookupExcept(%PROPERTY_{PD.AllInvited}%, %PROPERTY_{PD.Declined}%)
| All Invited | Declined | Result |
|---|---|---|
| Alice (1), Bob (2), Carol (3), Dave (4) | Bob (2), Dave (4) | Alice (1), Carol (3) |
With attendee count:
let('confirmed', lookupExcept(%PROPERTY_{PD.AllInvited}%, %PROPERTY_{PD.Declined}%),
concat(lookupCount(get('confirmed')), ' confirmed: ', lookupNames(get('confirmed'))))
Result: 2 confirmed: Alice, Carol
17. Regex-Based Data Transformation — Phone Number Formatting
Scenario: Normalize phone numbers to international format.
| Mode: Calculate Expression | Evaluate as Expression: ✅ |
let('phone', trim(%PROPERTY_{PD.PhoneNumber}%),
iif(startsWith(get('phone'), '+'), get('phone'),
iif(startsWith(get('phone'), '0'),
concat('+358', substring(get('phone'), 1)),
concat('+358', get('phone')))))
| Input | Result |
|---|---|
| +358 40 1234567 | +358 40 1234567 |
| 040 1234567 | +35840 1234567 |
| 0401234567 | +358401234567 |
18. Complex Business Rule — SLA Compliance Check
Scenario: Determine SLA status based on response time, priority, and whether it’s a business day. The expression checks if the issue was responded to within the SLA timeframe.
| Mode: Calculate Expression | Evaluate as Expression: ✅ |
store('responseHours', dateDiff(%PROPERTY_{PD.Created}%,
ifNull(%PROPERTY_{PD.FirstResponse}%, now()), 'hours')) *0+
store('slaLimit', switch(lookupName(%PROPERTY_{PD.Priority}%),
'Critical', 4,
'High', 8,
'Medium', 24,
'Low', 72,
48)) *0+
store('isResolved', not isNull(%PROPERTY_{PD.FirstResponse}%)) *0+
iif(not get('isResolved'),
iif(get('responseHours') > get('slaLimit'),
concat('🔴 SLA BREACHED (', Round(get('responseHours'), 0), 'h / ', get('slaLimit'), 'h limit)'),
concat('🟡 Pending (', Round(get('responseHours'), 0), 'h / ', get('slaLimit'), 'h limit)')),
iif(get('responseHours') <= get('slaLimit'),
concat('🟢 Met SLA (', Round(get('responseHours'), 0), 'h / ', get('slaLimit'), 'h limit)'),
concat('🔴 SLA Missed (', Round(get('responseHours'), 0), 'h / ', get('slaLimit'), 'h limit)')))
| Priority | Created | First Response | Response Hours | SLA Limit | Result |
|---|---|---|---|---|---|
| Critical | May 5, 09:00 | May 5, 11:30 | 2.5h | 4h | 🟢 Met SLA (3h / 4h limit) |
| High | May 3, 14:00 | May 4, 10:00 | 20h | 8h | 🔴 SLA Missed (20h / 8h limit) |
| Medium | May 6, 08:00 | (not yet) | 30h | 24h | 🔴 SLA BREACHED (30h / 24h limit) |
19. FOREACH — Generate Line Item Summary
Scenario: Create a text summary listing all invoice line items with their amounts.
| Mode: Calculate Expression | Evaluate as Expression: ✅ |
%PROPERTY_{PD.InvoiceLines}.FOREACH%• %PROPERTY_{PD.Description}%: %PROPERTY_{PD.Amount}% EUR
%
Result (3 line items):
• Software License: 2500 EUR
• Consulting Services: 1200 EUR
• Training: 800 EUR
20. Combined Functions — Smart Contract Summary
Scenario: Generate a comprehensive one-line summary for a contract, combining multiple data sources and calculations.
| Mode: Calculate Expression | Evaluate as Expression: ✅ |
store('value', %PROPERTY_{PD.ContractValue}%) *0+
store('daysLeft', dateDiff(today(), %PROPERTY_{PD.EndDate}%, 'days')) *0+
store('status', lookupName(%PROPERTY_{PD.Status}%)) *0+
concat(
get('status'), ' | ',
lookupName(%PROPERTY_{PD.Customer}%), ' | ',
iif(get('value') >= 100000, '💰 ', ''),
Round(get('value'), 0), ' EUR | ',
iif(get('daysLeft') < 0,
concat('Expired ', Abs(get('daysLeft')), 'd ago'),
iif(get('daysLeft') < 30,
concat('⚠️ ', get('daysLeft'), 'd left'),
concat(Round(get('daysLeft') / 30, 0), ' months left'))),
' | ',
lookupCount(%PROPERTY_{PD.Attachments}%), ' files')
Result: Active | Acme Corp | 💰 250000 EUR | ⚠️ 18d left | 5 files