Skip to content

Commit df0fd1d

Browse files
author
Jani Giannoudis
committed
- Orchestrator: McpServer integration (v32) + wiki cleanup job (v33)
- Examples: refactored ReportPayroll, added TemporalPayroll - Tests: added CaseScope, ConsolidatedEdge, Timeless, VersionEdge, MultiDivision, PayrunTag - DevOps: MySQL db scripts, removed legacy export scripts - Images: added case temporal perspectives, report screenshots
1 parent 5ee04d8 commit df0fd1d

170 files changed

Lines changed: 8284 additions & 1924 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/orchestrate-release.yml

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ on:
1818
description: 'WebApp version (leave empty to skip)'
1919
required: false
2020
default: ''
21+
mcpserver_version:
22+
description: 'McpServer version (leave empty to skip)'
23+
required: false
24+
default: ''
2125
update_wiki:
2226
description: 'Update Wiki release notes (deprecated)'
2327
type: boolean
@@ -45,9 +49,11 @@ jobs:
4549
build_backend: ${{ steps.resolve.outputs.build_backend }}
4650
build_console: ${{ steps.resolve.outputs.build_console }}
4751
build_webapp: ${{ steps.resolve.outputs.build_webapp }}
52+
build_mcpserver: ${{ steps.resolve.outputs.build_mcpserver }}
4853
backend_prerelease: ${{ steps.resolve.outputs.backend_prerelease }}
4954
console_prerelease: ${{ steps.resolve.outputs.console_prerelease }}
5055
webapp_prerelease: ${{ steps.resolve.outputs.webapp_prerelease }}
56+
mcpserver_prerelease: ${{ steps.resolve.outputs.mcpserver_prerelease }}
5157
any_prerelease: ${{ steps.resolve.outputs.any_prerelease }}
5258
dry_run: ${{ steps.resolve.outputs.dry_run }}
5359
steps:
@@ -73,11 +79,12 @@ jobs:
7379
7480
ANY_PRE="false"
7581
76-
for APP in backend console webapp; do
82+
for APP in backend console webapp mcpserver; do
7783
case $APP in
78-
backend) RAW_VERSION="${{ inputs.backend_version }}" ;;
79-
console) RAW_VERSION="${{ inputs.console_version }}" ;;
80-
webapp) RAW_VERSION="${{ inputs.webapp_version }}" ;;
84+
backend) RAW_VERSION="${{ inputs.backend_version }}" ;;
85+
console) RAW_VERSION="${{ inputs.console_version }}" ;;
86+
webapp) RAW_VERSION="${{ inputs.webapp_version }}" ;;
87+
mcpserver) RAW_VERSION="${{ inputs.mcpserver_version }}" ;;
8188
esac
8289
8390
if [ "${DRY_RUN}" = "true" ]; then
@@ -113,6 +120,7 @@ jobs:
113120
echo " Backend: ${{ inputs.backend_version || '(skip)' }} (prerelease: ${{ steps.resolve.outputs.backend_prerelease }})"
114121
echo " Console: ${{ inputs.console_version || '(skip)' }} (prerelease: ${{ steps.resolve.outputs.console_prerelease }})"
115122
echo " WebApp: ${{ inputs.webapp_version || '(skip)' }} (prerelease: ${{ steps.resolve.outputs.webapp_prerelease }})"
123+
echo " McpServer: ${{ inputs.mcpserver_version || '(skip)' }} (prerelease: ${{ steps.resolve.outputs.mcpserver_prerelease }})"
116124
echo " Wiki update: ${{ inputs.update_wiki }}"
117125
echo "═══════════════════════════════════════════"
118126
@@ -163,7 +171,8 @@ jobs:
163171
const apps = [
164172
{ repo: 'PayrollEngine.Backend', version: '${{ inputs.backend_version }}' },
165173
{ repo: 'PayrollEngine.PayrollConsole', version: '${{ inputs.console_version }}' },
166-
{ repo: 'PayrollEngine.WebApp', version: '${{ inputs.webapp_version }}' }
174+
{ repo: 'PayrollEngine.WebApp', version: '${{ inputs.webapp_version }}' },
175+
{ repo: 'PayrollEngine.McpServer', version: '${{ inputs.mcpserver_version }}' }
167176
].filter(a => a.version);
168177
169178
if (apps.length > 0) console.log(`\n🐳 Checking app versions\n`);
@@ -663,13 +672,39 @@ jobs:
663672
"dry_run": "${{ needs.prepare.outputs.dry_run }}"
664673
}
665674
675+
wave-5-mcpserver:
676+
needs: [prepare, wait-wave-4]
677+
if: always() && needs.wait-wave-4.result == 'success' && needs.prepare.outputs.build_mcpserver == 'true'
678+
runs-on: ubuntu-latest
679+
steps:
680+
- name: Resolve app version
681+
id: ver
682+
run: |
683+
if [ "${{ needs.prepare.outputs.dry_run }}" = "true" ]; then
684+
echo "version=0.0.0-dryrun.${{ github.run_number }}" >> $GITHUB_OUTPUT
685+
else
686+
echo "version=${{ inputs.mcpserver_version }}" >> $GITHUB_OUTPUT
687+
fi
688+
- uses: peter-evans/repository-dispatch@v3
689+
with:
690+
token: ${{ secrets.PAT_DISPATCH }}
691+
repository: ${{ env.ORG }}/PayrollEngine.McpServer
692+
event-type: release
693+
client-payload: >-
694+
{
695+
"version": "${{ steps.ver.outputs.version }}",
696+
"is_prerelease": "${{ needs.prepare.outputs.mcpserver_prerelease }}",
697+
"dry_run": "${{ needs.prepare.outputs.dry_run }}"
698+
}
699+
666700
wait-wave-5:
667-
needs: [prepare, wave-5-backend, wave-5-console, wave-5-webapp]
701+
needs: [prepare, wave-5-backend, wave-5-console, wave-5-webapp, wave-5-mcpserver]
668702
if: |
669703
always() && (
670704
needs.wave-5-backend.result == 'success' ||
671705
needs.wave-5-console.result == 'success' ||
672-
needs.wave-5-webapp.result == 'success'
706+
needs.wave-5-webapp.result == 'success' ||
707+
needs.wave-5-mcpserver.result == 'success'
673708
)
674709
runs-on: ubuntu-latest
675710
timeout-minutes: 45
@@ -693,6 +728,8 @@ jobs:
693728
apps.push({ repo: 'PayrollEngine.PayrollConsole', tag: `v${{ inputs.console_version }}` });
694729
if ('${{ needs.wave-5-webapp.result }}' === 'success')
695730
apps.push({ repo: 'PayrollEngine.WebApp', tag: `v${{ inputs.webapp_version }}` });
731+
if ('${{ needs.wave-5-mcpserver.result }}' === 'success')
732+
apps.push({ repo: 'PayrollEngine.McpServer', tag: `v${{ inputs.mcpserver_version }}` });
696733
697734
const maxAttempts = 90;
698735
const delay = 15000;
@@ -772,9 +809,10 @@ jobs:
772809
body += ` |\n`;
773810
}
774811
const apps = [
775-
{ name: 'Backend', version: '${{ inputs.backend_version }}', pre: '${{ needs.prepare.outputs.backend_prerelease }}' },
776-
{ name: 'PayrollConsole', version: '${{ inputs.console_version }}', pre: '${{ needs.prepare.outputs.console_prerelease }}' },
777-
{ name: 'WebApp', version: '${{ inputs.webapp_version }}', pre: '${{ needs.prepare.outputs.webapp_prerelease }}' }
812+
{ name: 'Backend', version: '${{ inputs.backend_version }}', pre: '${{ needs.prepare.outputs.backend_prerelease }}' },
813+
{ name: 'PayrollConsole', version: '${{ inputs.console_version }}', pre: '${{ needs.prepare.outputs.console_prerelease }}' },
814+
{ name: 'WebApp', version: '${{ inputs.webapp_version }}', pre: '${{ needs.prepare.outputs.webapp_prerelease }}' },
815+
{ name: 'McpServer', version: '${{ inputs.mcpserver_version }}', pre: '${{ needs.prepare.outputs.mcpserver_prerelease }}' }
778816
].filter(a => a.version);
779817
if (apps.length > 0) {
780818
body += `\n## 🐳 Docker Images (Linux)\n\n| App | Version | Pull Command |\n|-----|---------|-------------|\n`;

Examples/ActionPayroll/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,14 @@ Delete.pecmd
124124
|:--|:--|
125125
| User | `lucy.smith@foo.com` |
126126
| Password | `@ayroll3nginE` |
127+
128+
---
129+
130+
## Features Demonstrated
131+
132+
- **Custom `CaseValidate` action**`[CaseValidateAction]` registers a named, reusable validation method callable from No-Code `validateActions`
133+
- **ISO 7064 check digit**`CheckDigit(modulus, radix, ...)` API validates structured registration numbers
134+
- **Localized action issues** — issue messages stored in a lookup with `valueLocalizations`; the engine resolves the active culture at runtime
135+
- **Input mask**`input.valueMask: "REG-000.000.000"` enforces the structured input format
136+
- **No-Code action call**`? CheckRegistrationNumber('RegistrationNumber', ^:RegistrationNumber)` invokes the custom action without any expression scripting at the call site
137+
- **Case test**`Test.ct.json` covers both valid and invalid registration numbers

Examples/CaseDefPayroll/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,18 @@ Delete.pecmd
219219
|:--|:--|
220220
| User | `lucy.smith@foo.com` |
221221
| Password | `@ayroll3nginE` |
222+
223+
---
224+
225+
## Features Demonstrated
226+
227+
- **All value types** — every engine `ValueType` (String, Money, Percent, Boolean, Date, Lookup, None …) demonstrated with practical input attributes
228+
- **All time types** — Moment, Period, ClosedPeriod, CalendarPeriod, ClosedCalendarPeriod, Timeless side-by-side
229+
- **Inline lists and alias mapping**`StringAliasList`, `IntegerAliasList` etc. map display labels to different stored values
230+
- **Lookup binding** — object lookup (`Location`) and range lookup (`Commission`) with object field access
231+
- **Case slot hierarchy** — three-level parent/child/grandchild slots driven by a `ChildCount` field
232+
- **Case relations with conditions**`buildExpression` on relations activates or suppresses child cases dynamically
233+
- **No-Code build and validate actions**`Range`, `SetFieldValue`, `Input.DisableValue`, `ValueGreaterThan`, `Email` and custom conditional chains
234+
- **Available actions**`CaseAvailableAction` hides a case from the UI when a toggle field is set
235+
- **Override and cancel patterns**`overrideType: Inactive`, `cancellationType: Case`
236+
- **Culture-specific formatting**`culture: de-DE` on a Money field renders German decimal separators

Examples/Delete.All.pecmd

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
ActionPayroll/Delete.pecmd
2-
GlobalPayroll/Delete.pecmd
32
CaseDefPayroll/Delete.pecmd
4-
DerivedPayroll/Delete.pecmd
53
ExtendedPayroll/Delete.pecmd
4+
ForecastPayroll/Delete.pecmd
5+
GlobalPayroll/Delete.pecmd
6+
MarchClausePayroll/Delete.pecmd
7+
MinWagePayroll/Delete.pecmd
8+
MultiCountryPayroll/Delete.pecmd
9+
PartTimePayroll/Delete.pecmd
610
ReportPayroll/Delete.pecmd
7-
SimplePayroll/Delete.pecmd
8-
StartPayroll/Delete.pecmd
9-
WeekSimplePayroll/Delete.pecmd
10-
TaxTablePayroll/Delete.pecmd
11+
RetroPayroll/Delete.pecmd
12+
StartPayroll.Json/Delete.pecmd
13+
StartPayroll.Yaml/Delete.pecmd
14+
TaxTablePayroll/Delete.pecmd
15+
TemporalPayroll/Delete.pecmd
16+
TimesheetPayroll/Delete.pecmd

Examples/ExtendedPayroll/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,14 @@ Delete.pecmd
160160
## See Also
161161

162162
- [Extended Functions](https://github.com/Payroll-Engine/PayrollEngine/wiki/Extended-Functions) — wiki tutorial this example accompanies
163+
164+
---
165+
166+
## Features Demonstrated
167+
168+
- **Composite function pattern** — business logic encapsulated in a plain C# class injected via constructor into the engine's function
169+
- **`partial` class extension**`WageTypeValueFunction` extended with a named property (`MyRegulation`) that exposes the composite class
170+
- **Lazy initialization**`??=` creates the composite instance on first access; no manual lifecycle management
171+
- **No-Code call site**`valueExpression: "MyRegulation.GetSalary()"` reads the composite method directly without boilerplate
172+
- **Regulation-level reuse** — the composite class is available in every wage type expression of the regulation; derived regulations can subclass or wrap it
173+

Examples/ExtendedPayroll/Test.et.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{
1+
{
22
"$schema": "../../Schemas/PayrollEngine.Exchange.schema.json",
33
"tenants": [
44
{
@@ -37,7 +37,7 @@
3737
"mario.nuñez@foo.com"
3838
],
3939
"reason": "Test Payrun Jan 23",
40-
"jobStatus": "Complete",
40+
"completedJobStatus": "Complete",
4141
"periodStart": "2023-01-01T00:00:00.0Z",
4242
"evaluationDate": "2023-02-01T00:00:00.0Z"
4343
}

Examples/ForecastPayroll/Payroll.fp.yaml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# yaml-language-server: $schema=../../Schemas/PayrollEngine.Exchange.schema.json
1+
# yaml-language-server: $schema=../../Schemas/PayrollEngine.Exchange.schema.json
22
# ForecastPayroll — PlanSoft GmbH
33
# Demonstrates: forecast payrun isolation, two parallel what-if scenarios,
44
# No-Code condition guard on planned bonus wage type.
@@ -172,7 +172,7 @@ tenants:
172172
payrunName: PlanSoftPayrun
173173
payrollName: PlanSoftPayroll
174174
userIdentifier: hr@plansoft.com
175-
jobStatus: Complete
175+
completedJobStatus: Complete
176176
periodStart: 2026-01-01T00:00:00Z
177177
evaluationDate: 2026-01-31T00:00:00Z
178178
reason: January 2026 — production baseline
@@ -183,7 +183,6 @@ tenants:
183183
payrunName: PlanSoftPayrun
184184
payrollName: PlanSoftPayroll
185185
userIdentifier: hr@plansoft.com
186-
jobStatus: Forecast
187186
forecast: SalaryIncrease2026
188187
periodStart: 2026-01-01T00:00:00Z
189188
evaluationDate: 2026-01-31T00:00:00Z
@@ -194,7 +193,6 @@ tenants:
194193
payrunName: PlanSoftPayrun
195194
payrollName: PlanSoftPayroll
196195
userIdentifier: hr@plansoft.com
197-
jobStatus: Forecast
198196
forecast: SalaryIncrease2026
199197
periodStart: 2026-02-01T00:00:00Z
200198
evaluationDate: 2026-02-28T00:00:00Z
@@ -205,7 +203,6 @@ tenants:
205203
payrunName: PlanSoftPayrun
206204
payrollName: PlanSoftPayroll
207205
userIdentifier: hr@plansoft.com
208-
jobStatus: Forecast
209206
forecast: SalaryIncrease2026
210207
periodStart: 2026-03-01T00:00:00Z
211208
evaluationDate: 2026-03-31T00:00:00Z
@@ -217,7 +214,6 @@ tenants:
217214
payrunName: PlanSoftPayrun
218215
payrollName: PlanSoftPayroll
219216
userIdentifier: hr@plansoft.com
220-
jobStatus: Forecast
221217
forecast: BonusScenario2026
222218
periodStart: 2026-01-01T00:00:00Z
223219
evaluationDate: 2026-01-31T00:00:00Z
@@ -228,7 +224,6 @@ tenants:
228224
payrunName: PlanSoftPayrun
229225
payrollName: PlanSoftPayroll
230226
userIdentifier: hr@plansoft.com
231-
jobStatus: Forecast
232227
forecast: BonusScenario2026
233228
periodStart: 2026-02-01T00:00:00Z
234229
evaluationDate: 2026-02-28T00:00:00Z
@@ -239,7 +234,6 @@ tenants:
239234
payrunName: PlanSoftPayrun
240235
payrollName: PlanSoftPayroll
241236
userIdentifier: hr@plansoft.com
242-
jobStatus: Forecast
243237
forecast: BonusScenario2026
244238
periodStart: 2026-03-01T00:00:00Z
245239
evaluationDate: 2026-03-31T00:00:00Z

Examples/ForecastPayroll/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,15 @@ Test.pecmd
160160
# Teardown
161161
Delete.pecmd
162162
```
163+
164+
---
165+
166+
## Features Demonstrated
167+
168+
- **Forecast data isolation** — case values tagged with a scenario name are invisible to production payruns; the `forecast` field on the payrun job activates the matching scenario
169+
- **Parallel what-if scenarios** — multiple forecast scenarios share one regulation and payroll; no duplication required
170+
- **`^^` operator in forecast context** — reads scenario-scoped case data; falls back to production value when no forecast override exists
171+
- **No-Code condition guard**`? ^^PlannedBonus > 0` stops the wage type action chain when no bonus is planned
172+
- **Dedicated net output wage type** — WT 300 exposes the bottom-line figure directly without a collector aggregation step
173+
- **No-Code regulation** — entire regulation implemented with action expressions; no C# scripting required
174+

Examples/GlobalPayroll/Payroll.Jobs.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{
1+
{
22
"$schema": "../../Schemas/PayrollEngine.Exchange.schema.json",
33
"tenants": [
44
{
@@ -10,7 +10,7 @@
1010
"payrunName": "GlobalTechPayrun",
1111
"payrollName": "GlobalTechPayroll",
1212
"userIdentifier": "admin@globaltech.com",
13-
"jobStatus": "Complete",
13+
"completedJobStatus": "Complete",
1414
"periodStart": "2024-01-01T00:00:00.0Z",
1515
"evaluationDate": "2024-02-01T00:00:00.0Z",
1616
"reason": "January 2024 Payroll"
@@ -20,7 +20,7 @@
2020
"payrunName": "GlobalTechPayrun",
2121
"payrollName": "GlobalTechPayroll",
2222
"userIdentifier": "admin@globaltech.com",
23-
"jobStatus": "Complete",
23+
"completedJobStatus": "Complete",
2424
"periodStart": "2024-02-01T00:00:00.0Z",
2525
"evaluationDate": "2024-03-01T00:00:00.0Z",
2626
"reason": "February 2024 Payroll"
@@ -30,7 +30,7 @@
3030
"payrunName": "GlobalTechPayrun",
3131
"payrollName": "GlobalTechPayroll",
3232
"userIdentifier": "admin@globaltech.com",
33-
"jobStatus": "Complete",
33+
"completedJobStatus": "Complete",
3434
"periodStart": "2024-03-01T00:00:00.0Z",
3535
"evaluationDate": "2024-04-01T00:00:00.0Z",
3636
"reason": "March 2024 Payroll"

Examples/GlobalPayroll/Test.et.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{
1+
{
22
"$schema": "../../Schemas/PayrollEngine.Exchange.schema.json",
33
"tenants": [
44
{
@@ -127,7 +127,7 @@
127127
"name": "GlobalTechPayrun.Test.Jan24",
128128
"payrunName": "GlobalTechPayrun",
129129
"userIdentifier": "admin@globaltech.com",
130-
"jobStatus": "Complete",
130+
"completedJobStatus": "Complete",
131131
"periodStart": "2024-01-01T00:00:00.0Z",
132132
"evaluationDate": "2024-02-01T00:00:00.0Z",
133133
"reason": "January 2024 Payroll Test",
@@ -140,7 +140,7 @@
140140
"name": "GlobalTechPayrun.Test.Feb24",
141141
"payrunName": "GlobalTechPayrun",
142142
"userIdentifier": "admin@globaltech.com",
143-
"jobStatus": "Complete",
143+
"completedJobStatus": "Complete",
144144
"periodStart": "2024-02-01T00:00:00.0Z",
145145
"evaluationDate": "2024-03-01T00:00:00.0Z",
146146
"reason": "February 2024 Payroll Test — verifies Moment bonus is not carried into subsequent periods",

0 commit comments

Comments
 (0)