@sparkerp/plugin-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/bundle/blocks.json +12229 -0
- package/bundle/catalog.json +32475 -0
- package/bundle/docs/applications/hcm/employee-profile.md +144 -0
- package/bundle/docs/concepts/artifact-types.md +80 -0
- package/bundle/docs/concepts/glossary.md +124 -0
- package/bundle/docs/concepts/overview.md +88 -0
- package/bundle/docs/concepts/page-data-flow.md +84 -0
- package/bundle/docs/concepts/plugin-lifecycle.md +81 -0
- package/bundle/docs/concepts/sdk-modes.md +71 -0
- package/bundle/docs/docs.json +109 -0
- package/bundle/docs/guides/add-a-cadence-job.md +134 -0
- package/bundle/docs/guides/add-a-compliance-job.md +139 -0
- package/bundle/docs/guides/add-a-create-edit-form.md +167 -0
- package/bundle/docs/guides/add-a-custom-block.md +225 -0
- package/bundle/docs/guides/add-a-data-provider.md +216 -0
- package/bundle/docs/guides/add-a-kpi.md +154 -0
- package/bundle/docs/guides/add-a-reminder-job.md +181 -0
- package/bundle/docs/guides/add-an-aggregation-job.md +153 -0
- package/bundle/docs/guides/add-an-approval-workflow.md +222 -0
- package/bundle/docs/guides/add-an-entity.md +170 -0
- package/bundle/docs/guides/add-business-rules.md +147 -0
- package/bundle/docs/guides/add-i18n.md +121 -0
- package/bundle/docs/guides/add-menus.md +149 -0
- package/bundle/docs/guides/build-a-code-plugin.md +141 -0
- package/bundle/docs/guides/build-a-page.md +213 -0
- package/bundle/docs/guides/build-a-polyglot-service-plugin.md +201 -0
- package/bundle/docs/guides/build-a-tenant-extension-service.md +149 -0
- package/bundle/docs/guides/create-a-plugin.md +172 -0
- package/bundle/docs/guides/expose-a-plugin-operation-as-an-ai-tool.md +114 -0
- package/bundle/docs/guides/index.md +53 -0
- package/bundle/docs/guides/publish-and-upgrade.md +152 -0
- package/bundle/docs/guides/responsive-plugin.md +117 -0
- package/bundle/docs/guides/seed-data-on-install.md +127 -0
- package/bundle/docs/guides/set-up-the-sdk.md +120 -0
- package/bundle/docs/guides/use-the-mcp-server.md +104 -0
- package/bundle/docs/guides/validate-and-test.md +119 -0
- package/bundle/docs/guides/wire-a-pages-data.md +153 -0
- package/bundle/docs/quickstart/index.md +118 -0
- package/bundle/docs/recipes/condition-based-compliance-flag.md +70 -0
- package/bundle/docs/recipes/cross-plugin-offboarding-fanout.md +68 -0
- package/bundle/docs/recipes/cross-row-aggregation.md +67 -0
- package/bundle/docs/recipes/entity-with-approval-workflow.md +72 -0
- package/bundle/docs/recipes/index.md +40 -0
- package/bundle/docs/recipes/install-lifecycle-data-seeding.md +76 -0
- package/bundle/docs/recipes/kpi-dashboard-page.md +92 -0
- package/bundle/docs/recipes/master-detail-page.md +84 -0
- package/bundle/docs/recipes/n-days-before-reminder.md +72 -0
- package/bundle/docs/recipes/per-row-document-generator.md +65 -0
- package/bundle/docs/recipes/related-record-lookup-field.md +74 -0
- package/bundle/docs/recipes/resolved-reference-grid-column.md +63 -0
- package/bundle/docs/recipes/scheduled-record-generation.md +68 -0
- package/bundle/docs/reference/action.md +18 -0
- package/bundle/docs/reference/block-definition.md +35 -0
- package/bundle/docs/reference/block-instance.md +33 -0
- package/bundle/docs/reference/composite-pack-manifest.md +33 -0
- package/bundle/docs/reference/connector-definition.md +36 -0
- package/bundle/docs/reference/data-service-definition.md +22 -0
- package/bundle/docs/reference/data-view-definition.md +26 -0
- package/bundle/docs/reference/entity-aggregation-config.md +38 -0
- package/bundle/docs/reference/entity-cadence-config.md +33 -0
- package/bundle/docs/reference/entity-compliance-config.md +37 -0
- package/bundle/docs/reference/entity-cross-plugin-action-config.md +37 -0
- package/bundle/docs/reference/entity-definition.md +18 -0
- package/bundle/docs/reference/entity-document-generator-config.md +35 -0
- package/bundle/docs/reference/entity-rule-definition.md +29 -0
- package/bundle/docs/reference/entity-status-date-sweep-config.md +27 -0
- package/bundle/docs/reference/form.md +35 -0
- package/bundle/docs/reference/index.md +37 -0
- package/bundle/docs/reference/menu.md +26 -0
- package/bundle/docs/reference/mobile-nav.md +22 -0
- package/bundle/docs/reference/page-template.md +29 -0
- package/bundle/docs/reference/page.md +44 -0
- package/bundle/docs/reference/plugin-manifest.md +54 -0
- package/bundle/docs/reference/plugin-seed-data.md +21 -0
- package/bundle/docs/reference/print-template-definition.md +22 -0
- package/bundle/docs/reference/provider-definition.md +22 -0
- package/bundle/docs/reference/report-definition.md +22 -0
- package/bundle/docs/reference/rule.md +30 -0
- package/bundle/docs/reference/workflow-definition.md +28 -0
- package/bundle/docs/troubleshooting/grid-shows-zero-rows.md +82 -0
- package/bundle/docs/troubleshooting/job-never-runs.md +89 -0
- package/bundle/docs/troubleshooting/known-gotchas.md +193 -0
- package/bundle/docs/troubleshooting/publishing-errors.md +119 -0
- package/bundle/docs/tutorial/01-create-the-plugin.md +157 -0
- package/bundle/docs/tutorial/02-category-entity-and-seed.md +94 -0
- package/bundle/docs/tutorial/03-equipment-entity.md +121 -0
- package/bundle/docs/tutorial/04-equipment-register-page.md +153 -0
- package/bundle/docs/tutorial/05-checkout-entity-and-page.md +116 -0
- package/bundle/docs/tutorial/06-checkout-approval-workflow.md +139 -0
- package/bundle/docs/tutorial/07-return-due-reminder-job.md +129 -0
- package/bundle/docs/tutorial/08-menus-i18n-publish.md +123 -0
- package/bundle/docs/tutorial/index.md +72 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-aggregation-configs.json +18 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-cadence-configs.json +9 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-compliance-configs.json +74 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-cross-plugin-configs.json +9 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-document-configs.json +9 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-sweep-configs.json +260 -0
- package/bundle/examples/backend/modules/hcm-compensation/spk-assembly/metadata/entities/hcm_salary_planning_entry.json +41 -0
- package/bundle/examples/backend/modules/hcm-compensation/spk-assembly/metadata/page/salary-planning.json +1 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/page/promotions.json +10944 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.movement.json +44 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.promotion-acknowledge.json +19 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.resignation.json +29 -0
- package/bundle/examples/example-patterns.mjs +94 -0
- package/bundle/manifest.json +14 -0
- package/bundle/schemas/action.schema.json +102 -0
- package/bundle/schemas/block-definition.schema.json +222 -0
- package/bundle/schemas/block-instance.schema.json +139 -0
- package/bundle/schemas/composite-pack-manifest.schema.json +160 -0
- package/bundle/schemas/connector-definition.schema.json +29 -0
- package/bundle/schemas/data-service-definition.schema.json +64 -0
- package/bundle/schemas/data-view-definition.schema.json +148 -0
- package/bundle/schemas/entity-aggregation-config.schema.json +32 -0
- package/bundle/schemas/entity-cadence-config.schema.json +27 -0
- package/bundle/schemas/entity-compliance-config.schema.json +31 -0
- package/bundle/schemas/entity-cross-plugin-action-config.schema.json +31 -0
- package/bundle/schemas/entity-definition.schema.json +126 -0
- package/bundle/schemas/entity-document-generator-config.schema.json +29 -0
- package/bundle/schemas/entity-rule-definition.schema.json +75 -0
- package/bundle/schemas/entity-status-date-sweep-config.schema.json +21 -0
- package/bundle/schemas/form.schema.json +281 -0
- package/bundle/schemas/menu.schema.json +106 -0
- package/bundle/schemas/mobile-nav.schema.json +53 -0
- package/bundle/schemas/page-template.schema.json +46 -0
- package/bundle/schemas/page.schema.json +292 -0
- package/bundle/schemas/plugin-manifest.schema.json +139 -0
- package/bundle/schemas/plugin-seed-data.schema.json +24 -0
- package/bundle/schemas/print-template-definition.schema.json +90 -0
- package/bundle/schemas/provider-definition.schema.json +27 -0
- package/bundle/schemas/report-definition.schema.json +96 -0
- package/bundle/schemas/rule.schema.json +82 -0
- package/bundle/schemas/workflow-definition.schema.json +42 -0
- package/bundle/validators/block-engine.mjs +23222 -0
- package/bundle/validators/page-engine.mjs +21645 -0
- package/erp-cli/authoring-root.mjs +344 -0
- package/erp-cli/erp.mjs +4065 -0
- package/erp-cli/example-patterns.mjs +94 -0
- package/erp-mcp-server/index.mjs +818 -0
- package/package.json +27 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "6. The checkout approval workflow"
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 6. The checkout approval workflow
|
|
7
|
+
|
|
8
|
+
## What we're adding
|
|
9
|
+
|
|
10
|
+
When a user clicks **Submit for approval** on a checkout, its status becomes
|
|
11
|
+
`PENDING_APPROVAL`. That should start an approval process; when the Office
|
|
12
|
+
Equipment Manager approves, the status becomes `APPROVED`; if rejected,
|
|
13
|
+
`REJECTED`. No approval code — a workflow definition, an entity rule, and the
|
|
14
|
+
platform's generic callback.
|
|
15
|
+
|
|
16
|
+
## 1. The workflow
|
|
17
|
+
|
|
18
|
+
`spk-assembly/metadata/workflow/office-equipment.checkout-approval.json`
|
|
19
|
+
([real file](./example-plugin/spk-assembly/metadata/workflow/office-equipment.checkout-approval.json)):
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"name": "office-equipment.checkout-approval",
|
|
24
|
+
"description": "Single-stage Office Equipment Manager approval for a checkout submitted for approval.",
|
|
25
|
+
"stagesJson": "[\"manager\"]",
|
|
26
|
+
"tasksJson": "[{\"taskKey\":\"manager-approve\",\"stage\":\"manager\",\"taskType\":\"approval\",\"kind\":\"human\",\"payload\":{\"approvalObject\":\"office-equipment.checkout-approval\",\"amount\":0,\"dueInSeconds\":259200}}]",
|
|
27
|
+
"transitionsJson": "[]",
|
|
28
|
+
"approversJson": "{}",
|
|
29
|
+
"slasJson": "[]",
|
|
30
|
+
"escalationsJson": "[]",
|
|
31
|
+
"notificationsJson": "[]",
|
|
32
|
+
"metadataJson": "{}",
|
|
33
|
+
"approvalPermissions": [
|
|
34
|
+
{ "roleCode": "OFFICE_EQUIPMENT_MANAGER", "approvalObject": "office-equipment.checkout-approval", "maxAmount": null }
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- `stagesJson`, `tasksJson`, `transitionsJson` are **JSON-encoded strings**, not
|
|
40
|
+
objects.
|
|
41
|
+
- One stage `["manager"]`, one human approval task, no transitions (reaching the
|
|
42
|
+
only stage's decision ends the instance).
|
|
43
|
+
- `approvalPermissions` is a real array; `roleCode` references the role by its
|
|
44
|
+
stable `code` (the one we prefixed in chapter 1).
|
|
45
|
+
|
|
46
|
+
## 2. The rule
|
|
47
|
+
|
|
48
|
+
`spk-assembly/metadata/rules/oeq_checkout_submit_workflow.json`
|
|
49
|
+
([real file](./example-plugin/spk-assembly/metadata/rules/oeq_checkout_submit_workflow.json)):
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"entityType": "oeq_checkout",
|
|
54
|
+
"name": "oeq_checkout_submit_workflow",
|
|
55
|
+
"description": "On transition INTO PENDING_APPROVAL, start office-equipment.checkout-approval; the generic callback flips status to APPROVED/REJECTED.",
|
|
56
|
+
"triggerEvent": "AFTER_UPDATE",
|
|
57
|
+
"conditions": "{\"all\":[{\"field\":\"status\",\"op\":\"eq\",\"value\":\"PENDING_APPROVAL\"},{\"field\":\"status__previous\",\"op\":\"neq\",\"value\":\"PENDING_APPROVAL\"}]}",
|
|
58
|
+
"actions": "[{\"type\":\"START_WORKFLOW\",\"workflowName\":\"office-equipment.checkout-approval\",\"callbackUrl\":\"http://localhost:8080/api/v1/entities/workflow-callback\",\"config\":{\"entityType\":\"oeq_checkout\",\"idField\":\"id\",\"fields\":[{\"name\":\"status\",\"approved\":\"APPROVED\",\"rejected\":\"REJECTED\"}]}}]",
|
|
59
|
+
"priority": 20,
|
|
60
|
+
"active": true
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- The `all` condition fires exactly on the transition *into*
|
|
65
|
+
`PENDING_APPROVAL` — `status__previous` is the value before the update.
|
|
66
|
+
Without that guard the rule would re-fire on every later save.
|
|
67
|
+
- `config.fields` maps the workflow decision back onto the row:
|
|
68
|
+
approved → `status = APPROVED`, rejected → `status = REJECTED`.
|
|
69
|
+
- `callbackUrl` — the generic entity workflow-callback endpoint. Its base
|
|
70
|
+
(`http://localhost:8080`) is environment-specific; use your ERP's base URL.
|
|
71
|
+
- `priority: 20` leaves room below for validation rules.
|
|
72
|
+
|
|
73
|
+
## 3. The Submit button
|
|
74
|
+
|
|
75
|
+
On the checkout detail dialog (chapter 5), the button `PUT`s the status:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
"events": { "clicked": { "source": "action-chain", "actions": [
|
|
79
|
+
{ "id": "a0", "order": 0, "type": "callApi",
|
|
80
|
+
"config": { "connectionRef": "self", "path": "/api/v1/entities/oeq_checkout/records/${page.detailRecord.id}", "httpMethod": "PUT", "params": { "status": "PENDING_APPROVAL" } } },
|
|
81
|
+
{ "id": "a1", "order": 1, "type": "callApi",
|
|
82
|
+
"config": { "connectionRef": "self", "path": "/api/v1/entities/oeq_checkout/records/${page.detailRecord.id}", "httpMethod": "GET", "params": {} }, "output": "refreshed" },
|
|
83
|
+
{ "id": "a2", "order": 2, "type": "setValue", "config": { "field": "page.detailRecord", "value": "${refreshed}" } },
|
|
84
|
+
{ "id": "a3", "order": 3, "type": "setValue", "config": { "field": "page.gridRefreshTick", "value": "${!page.gridRefreshTick}" } },
|
|
85
|
+
{ "id": "a4", "order": 4, "type": "showToast", "config": { "message": "Submitted for approval." } }
|
|
86
|
+
] } }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Verify
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
erp workflow validate spk-assembly/metadata/workflow/office-equipment.checkout-approval.json
|
|
93
|
+
erp schema validate spk-assembly/metadata/rules/oeq_checkout_submit_workflow.json --schema entity-rule-definition
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Both `OK`. After publish, the rule is installed:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
$ erp api get "/api/v1/entity-rules?entityType=oeq_checkout"
|
|
100
|
+
[ { "id": 388, "entityType": "oeq_checkout", "name": "oeq_checkout_submit_workflow",
|
|
101
|
+
"triggerEvent": "AFTER_UPDATE", "active": true } ]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Submit a checkout and confirm it transitions and then *stays* pending until a
|
|
105
|
+
human decides it:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
$ erp api put "/api/v1/entities/oeq_checkout/records/3" --body '{"status":"PENDING_APPROVAL"}'
|
|
109
|
+
{ "id": 3, "checkout_number": "CO-200", "status": "PENDING_APPROVAL" }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`PENDING_APPROVAL` — correct. Confirm a real workflow instance started and find
|
|
113
|
+
its pending task:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
$ erp workflow list --definition oeq_checkout_submit_workflow --record 3
|
|
117
|
+
[ { "id": 5012, "status": "RUNNING", "currentStage": "manager-approval", "correlationId": "3" } ]
|
|
118
|
+
1 instance(s) for record 3 of "oeq_checkout_submit_workflow".
|
|
119
|
+
|
|
120
|
+
$ erp workflow tasks 5012
|
|
121
|
+
[ { "id": 88, "stage": "manager-approval", "status": "PENDING", "candidateApprovers": ["MANAGER"] } ]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
(`erp workflow list/instance/tasks/history` reach the workflow engine through a
|
|
125
|
+
read-proxy on `engine-api`'s own base URL, so they work even though the workflow
|
|
126
|
+
engine runs as a separate service here.)
|
|
127
|
+
|
|
128
|
+
The Office Equipment Manager now sees the task in the ERP's **approval inbox**;
|
|
129
|
+
approving it triggers the generic callback, which sets `status = APPROVED` with
|
|
130
|
+
zero code from you.
|
|
131
|
+
|
|
132
|
+
## Common mistakes
|
|
133
|
+
|
|
134
|
+
- `stagesJson`/`tasksJson` authored as objects → install fails. They're strings.
|
|
135
|
+
- No `status__previous` guard → workflow starts on every save.
|
|
136
|
+
- `approvalPermissions` with a role *name* instead of `roleCode` → nobody can
|
|
137
|
+
approve.
|
|
138
|
+
|
|
139
|
+
**Next:** [7. The return-due reminder job](./07-return-due-reminder-job.md)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "7. The return-due reminder job"
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 7. The return-due reminder job
|
|
7
|
+
|
|
8
|
+
## What we're adding
|
|
9
|
+
|
|
10
|
+
A checkout that is `CHECKED_OUT` and:
|
|
11
|
+
|
|
12
|
+
- within **3 days** of its `due_date` → status `DUE_SOON`
|
|
13
|
+
- past its `due_date` → status `OVERDUE`
|
|
14
|
+
|
|
15
|
+
Zero Java. The platform's generic `engine-entity.status-date-sweep` job runs
|
|
16
|
+
daily and applies config rows we seed.
|
|
17
|
+
|
|
18
|
+
## 1. Ship the shared config entity
|
|
19
|
+
|
|
20
|
+
So a fresh environment that predates the shared table still gets it.
|
|
21
|
+
`spk-assembly/metadata/entities/entity_status_date_sweep_config.json`
|
|
22
|
+
([real file](./example-plugin/spk-assembly/metadata/entities/entity_status_date_sweep_config.json)) —
|
|
23
|
+
ships only the additive columns (`day_offset`, `seeded_by`); the installer is
|
|
24
|
+
idempotent-additive, so an existing table just gains anything missing.
|
|
25
|
+
|
|
26
|
+
## 2. Seed the config rows
|
|
27
|
+
|
|
28
|
+
`spk-assembly/metadata/seed-data/office-equipment-sweep-configs.json`
|
|
29
|
+
([real file](./example-plugin/spk-assembly/metadata/seed-data/office-equipment-sweep-configs.json)):
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"entity": "entity_status_date_sweep_config",
|
|
34
|
+
"keyFields": ["entity_name", "date_field", "set_status_to", "day_offset"],
|
|
35
|
+
"source": "office-equipment",
|
|
36
|
+
"rows": [
|
|
37
|
+
{
|
|
38
|
+
"entity_name": "oeq_checkout",
|
|
39
|
+
"status_field": "status",
|
|
40
|
+
"when_status_in": "CHECKED_OUT",
|
|
41
|
+
"date_field": "due_date",
|
|
42
|
+
"compare_op": "lte",
|
|
43
|
+
"day_offset": 3,
|
|
44
|
+
"set_status_to": "DUE_SOON",
|
|
45
|
+
"active": true,
|
|
46
|
+
"seeded_by": "office-equipment"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"entity_name": "oeq_checkout",
|
|
50
|
+
"status_field": "status",
|
|
51
|
+
"when_status_in": "CHECKED_OUT,DUE_SOON",
|
|
52
|
+
"date_field": "due_date",
|
|
53
|
+
"compare_op": "lt",
|
|
54
|
+
"day_offset": 0,
|
|
55
|
+
"set_status_to": "OVERDUE",
|
|
56
|
+
"active": true,
|
|
57
|
+
"seeded_by": "office-equipment"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Row 1: `day_offset: 3` + `compare_op: lte` → "fires when `due_date` is within
|
|
64
|
+
3 days" — the "N days before" reminder.
|
|
65
|
+
- Row 2: `day_offset: 0` + `compare_op: lt` → "fires once `due_date` has
|
|
66
|
+
passed". `when_status_in` includes `DUE_SOON` so a warned checkout still
|
|
67
|
+
escalates to overdue.
|
|
68
|
+
- Neither sets `set_field`, so they write `status_field` (`status`) itself.
|
|
69
|
+
- `keyFields` includes `day_offset` so the two rows for the same date column are
|
|
70
|
+
distinct on re-install.
|
|
71
|
+
|
|
72
|
+
### Status value vs. boolean flag
|
|
73
|
+
|
|
74
|
+
This module flips a `status` enum because that reads well on the checkout grid.
|
|
75
|
+
A boolean flag works too: since 2026-09-10 the generic job coerces
|
|
76
|
+
`set_status_to: "true"` / `"false"` to a real boolean (and numeric strings to
|
|
77
|
+
numbers) before the write, so a config targeting a `boolean`/`integer`/`numeric`
|
|
78
|
+
`set_field` is fully supported. An earlier draft of this tutorial hit a SQL type
|
|
79
|
+
error doing exactly that — that platform bug is fixed.
|
|
80
|
+
|
|
81
|
+
## 3. The job registers itself
|
|
82
|
+
|
|
83
|
+
The platform ships an `AFTER_CREATE` rule on `entity_status_date_sweep_config`
|
|
84
|
+
that calls `ensureEntityStatusDateSweepJobRegistered`. So the first row you seed
|
|
85
|
+
auto-registers the job for your tenant — **you ship no register rule for this
|
|
86
|
+
job** (unlike the aggregation/cadence/compliance jobs).
|
|
87
|
+
|
|
88
|
+
## Verify
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
erp schema validate spk-assembly/metadata/seed-data/office-equipment-sweep-configs.json --schema plugin-seed-data
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`OK`. After publish, the job is enabled:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
$ erp api get "/api/v1/jobs/engine-entity.status-date-sweep"
|
|
98
|
+
{ "jobCode": "engine-entity.status-date-sweep", "status": "ENABLED",
|
|
99
|
+
"cronExpression": "0 5 0 * * *", "concurrencyPolicy": "PER_TENANT" }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**This exact sequence was run against the tutorial module:**
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# a CHECKED_OUT checkout whose due_date is in the past
|
|
106
|
+
$ erp api post "/api/v1/entities/oeq_checkout/records" \
|
|
107
|
+
--body '{"checkout_number":"CO-100","equipment_id":1,"employee_id":1,"checkout_date":"2026-08-01","due_date":"2026-09-04","status":"CHECKED_OUT"}'
|
|
108
|
+
{ "id": 2, "checkout_number": "CO-100", "status": "CHECKED_OUT" }
|
|
109
|
+
|
|
110
|
+
$ erp api post "/api/v1/jobs/engine-entity.status-date-sweep/execute" --body "{}"
|
|
111
|
+
{ "executionId": 534 }
|
|
112
|
+
|
|
113
|
+
$ erp api get "/api/v1/jobs/engine-entity.status-date-sweep/executions?size=1"
|
|
114
|
+
... "resultJson": "{\"swept\": 4, \"failed\": 0, \"rowsScanned\": 102, \"configsScanned\": 30}" ...
|
|
115
|
+
|
|
116
|
+
$ erp api get "/api/v1/entities/oeq_checkout/records/2"
|
|
117
|
+
{ "checkout_number": "CO-100", "status": "OVERDUE" }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`CHECKED_OUT` → `OVERDUE`. `"failed": 0`. Verified end to end.
|
|
121
|
+
|
|
122
|
+
## Common mistakes
|
|
123
|
+
|
|
124
|
+
- `set_field` a boolean/numeric column with an *unparseable* `set_status_to`
|
|
125
|
+
(e.g. `"yes"` into a boolean) → that row counts `failed`. Use `"true"`/`"false"`.
|
|
126
|
+
- `when_status_in` casing not matching the enum → nothing sweeps.
|
|
127
|
+
- Forgot `day_offset` → the "3 days before" rule fires only on the exact day.
|
|
128
|
+
|
|
129
|
+
**Next:** [8. Menus, i18n, validate, publish](./08-menus-i18n-publish.md)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "8. Menus, i18n, validate, publish"
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 8. Menus, i18n, validate, publish
|
|
7
|
+
|
|
8
|
+
## The menu
|
|
9
|
+
|
|
10
|
+
`spk-assembly/metadata/menu/office-equipment-menu.json`
|
|
11
|
+
([real file](./example-plugin/spk-assembly/metadata/menu/office-equipment-menu.json)) —
|
|
12
|
+
one group node `Office Equipment` with three children pointing at the three
|
|
13
|
+
pages' `route.pattern` values, `modules: ["office-equipment"]`.
|
|
14
|
+
|
|
15
|
+
Because this is a **standalone** plugin (it ships its own application, chapter
|
|
16
|
+
1), there is no master menu scaffold to keep in sync — one menu file is enough.
|
|
17
|
+
|
|
18
|
+
## The translations
|
|
19
|
+
|
|
20
|
+
`spk-assembly/metadata/i18n/en.json`
|
|
21
|
+
([real file](./example-plugin/spk-assembly/metadata/i18n/en.json)) — a flat map
|
|
22
|
+
of every i18n key the three pages, the menu, the app and the module reference
|
|
23
|
+
(~70 keys):
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"office-equipment.application.title": "Office Equipment",
|
|
28
|
+
"office-equipment.module.title": "Equipment",
|
|
29
|
+
"office-equipment.equipment-register.title": "Equipment Register",
|
|
30
|
+
"office-equipment.equipment-register.newBtn": "New",
|
|
31
|
+
"office-equipment.equipment-register.column.asset_tag": "Asset Tag",
|
|
32
|
+
"office-equipment.equipment-checkout.status.OVERDUE": "Overdue",
|
|
33
|
+
"office-equipment.equipment-checkout.status.DUE_SOON": "Due Soon"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
A missing key is caught by `erp plugin test` as `I18N-001`.
|
|
38
|
+
|
|
39
|
+
## Validate everything
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
erp plugin validate example-plugin/spk-assembly
|
|
43
|
+
erp plugin test example-plugin/spk-assembly
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
validated 3 page(s) — clean
|
|
48
|
+
|
|
49
|
+
Plugin Tests
|
|
50
|
+
────────────────────────────────────────
|
|
51
|
+
✓ page:equipment-catalog.json
|
|
52
|
+
✓ page:equipment-checkout.json
|
|
53
|
+
✓ page:equipment-register.json
|
|
54
|
+
✓ plugin.json:valid-json
|
|
55
|
+
4 passed, 0 failed, 0 semantic warning(s), 3 skipped
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
And the docs example runner, which also schema-validates every artifact:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
node developer-docs/examples/test-examples.mjs
|
|
62
|
+
# ... All examples valid.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Build
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
erp plugin build example-plugin/spk-assembly -o example-plugin/office-equipment-1.0.0.spk
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
validated 3 page(s) — clean
|
|
73
|
+
packaged 33 files -> example-plugin/office-equipment-1.0.0.spk (231986 bytes)
|
|
74
|
+
sha256: ...
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Publish
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
erp plugin publish example-plugin/office-equipment-1.0.0.spk --tenant 2
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
installed:
|
|
85
|
+
{"pluginId":"office-equipment","version":"1.0.0","state":"installed","pf4jState":"STARTED", ...}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Confirm it's all live
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# pages
|
|
92
|
+
$ erp api get "/api/v1/authoring/pages?module=office-equipment"
|
|
93
|
+
[ {"name":"equipment-catalog", "route":{"pattern":"/office-equipment/equipment-catalog"}, ...},
|
|
94
|
+
{"name":"equipment-checkout", ...}, {"name":"equipment-register", ...} ]
|
|
95
|
+
|
|
96
|
+
# seeded reference data
|
|
97
|
+
$ erp api get "/api/v1/entities/oeq_category/records/query?size=10"
|
|
98
|
+
{"rows":[{"category_code":"LAPTOP", ...}, ... ],"total":4}
|
|
99
|
+
|
|
100
|
+
# the reminder job
|
|
101
|
+
$ erp api get "/api/v1/jobs/engine-entity.status-date-sweep"
|
|
102
|
+
{"status":"ENABLED", ...}
|
|
103
|
+
|
|
104
|
+
# the approval rule
|
|
105
|
+
$ erp api get "/api/v1/entity-rules?entityType=oeq_checkout"
|
|
106
|
+
[{"name":"oeq_checkout_submit_workflow","active":true, ...}]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Open `/app/office-equipment/equipment/office-equipment/equipment-register` in
|
|
110
|
+
your ERP. The module is complete: three working pages, seeded categories, an
|
|
111
|
+
approval workflow, and a nightly reminder sweep — and you wrote one line of Java.
|
|
112
|
+
|
|
113
|
+
## Iterating from here
|
|
114
|
+
|
|
115
|
+
To change anything: edit the file, **bump `plugin.json` `version`**, rebuild,
|
|
116
|
+
re-publish. Don't uninstall to iterate — see
|
|
117
|
+
[Publish and upgrade](../guides/publish-and-upgrade.md) for why.
|
|
118
|
+
|
|
119
|
+
## Where to go next
|
|
120
|
+
|
|
121
|
+
- [Guides](../guides/index.md) — every task as its own page
|
|
122
|
+
- [Recipes](../recipes/index.md) — copy-paste solutions
|
|
123
|
+
- [Reference](../reference/index.md) — every schema
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Tutorial: build the Office Equipment module"
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tutorial: build the Office Equipment module
|
|
7
|
+
|
|
8
|
+
By the end of this tutorial you will have built, from an empty folder, a small
|
|
9
|
+
real module and published it to your ERP. Every step is something you run, and
|
|
10
|
+
every result shown here was produced by actually running it against a live ERP
|
|
11
|
+
(tenant 2).
|
|
12
|
+
|
|
13
|
+
## What you're building
|
|
14
|
+
|
|
15
|
+
**Office Equipment** — a 3-page module for tracking company laptops, monitors and
|
|
16
|
+
phones, and who has them.
|
|
17
|
+
|
|
18
|
+
| Page | Backed by | Demonstrates |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| **Equipment Catalog** | `oeq_category` entity, **seeded** with 4 categories on install | an entity + install-time reference data |
|
|
21
|
+
| **Equipment Register** | `oeq_equipment` entity | a full list page: header, KPI row, search/filter, grid, create dialog, a category **lookup** field |
|
|
22
|
+
| **Equipment Checkout** | `oeq_checkout` entity | a **reference-flagged** employee field (auto `_label`), a create dialog, a detail dialog with a **"Submit for approval"** action |
|
|
23
|
+
|
|
24
|
+
Plus, with no Java:
|
|
25
|
+
|
|
26
|
+
- a **workflow** — a submitted checkout needs manager approval before it's
|
|
27
|
+
`APPROVED`;
|
|
28
|
+
- a **scheduled reminder job** — a `CHECKED_OUT` checkout past its due date is
|
|
29
|
+
swept to `OVERDUE`, and one within 3 days to `DUE_SOON`;
|
|
30
|
+
- **install seeding** — the 4 categories and the 2 sweep configs travel with the
|
|
31
|
+
`.spk`;
|
|
32
|
+
- **menus** and **i18n**.
|
|
33
|
+
|
|
34
|
+
## What it looks like at the end
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
$ erp plugin publish office-equipment-1.0.2.spk --tenant 2
|
|
38
|
+
installed:
|
|
39
|
+
{"pluginId":"office-equipment","version":"1.0.2","state":"installed","pf4jState":"STARTED", ...}
|
|
40
|
+
|
|
41
|
+
$ erp api get "/api/v1/authoring/pages?module=office-equipment"
|
|
42
|
+
[ {"name":"equipment-catalog", ...}, {"name":"equipment-checkout", ...}, {"name":"equipment-register", ...} ]
|
|
43
|
+
|
|
44
|
+
$ erp api get "/api/v1/entities/oeq_category/records/query?size=10"
|
|
45
|
+
{"rows":[{"category_code":"LAPTOP","category_name":"Laptops", ...}, ... ],"total":4}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Open `/app/office-equipment/equipment/office-equipment/equipment-register` in the
|
|
49
|
+
ERP and the module is there.
|
|
50
|
+
|
|
51
|
+
## The finished source
|
|
52
|
+
|
|
53
|
+
The whole module lives at
|
|
54
|
+
[`example-plugin/`](./example-plugin/) in this docs tree — read any file as you
|
|
55
|
+
go, or diff yours against it. It is the same module the guides reference.
|
|
56
|
+
|
|
57
|
+
## Chapters
|
|
58
|
+
|
|
59
|
+
1. [Create the plugin](./01-create-the-plugin.md) — the scaffold, the manifest, the one Java class, the application/module
|
|
60
|
+
2. [The equipment category entity + seeded data](./02-category-entity-and-seed.md)
|
|
61
|
+
3. [The equipment register entity](./03-equipment-entity.md)
|
|
62
|
+
4. [The equipment register page](./04-equipment-register-page.md) — header, KPIs, filter, grid, create dialog
|
|
63
|
+
5. [The checkout entity, page, and lookups](./05-checkout-entity-and-page.md)
|
|
64
|
+
6. [The checkout approval workflow](./06-checkout-approval-workflow.md)
|
|
65
|
+
7. [The return-due reminder job](./07-return-due-reminder-job.md)
|
|
66
|
+
8. [Menus, i18n, validate, publish](./08-menus-i18n-publish.md)
|
|
67
|
+
|
|
68
|
+
## Before you start
|
|
69
|
+
|
|
70
|
+
Work through [Set up the SDK](../guides/set-up-the-sdk.md) first — you need
|
|
71
|
+
`erp whoami` returning `"valid": true`. This tutorial assumes the `erp` command
|
|
72
|
+
is on your path (or aliased to `node .../tools/erp-cli/erp.mjs`).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity": "entity_aggregation_config",
|
|
3
|
+
"keyFields": ["sweep_code"],
|
|
4
|
+
"source": "hcm-assets",
|
|
5
|
+
"rows": [
|
|
6
|
+
{"sweep_code": "asset-inventory-reconciliation", "source_entity": "hcm_asset", "aggregation": "count", "group_by_field": "status", "target_entity": "hcm_asset_reconciliation", "target_key_field": "metric_key", "target_key_prefix": "asset_status:", "target_value_field": "metric_value", "target_timestamp_field": "last_reconciled_at", "seeded_by": "hcm-assets", "active": true},
|
|
7
|
+
{"sweep_code": "asset-allocation-reconciliation", "source_entity": "hcm_asset_allocation", "aggregation": "count", "group_by_field": "status", "target_entity": "hcm_asset_reconciliation", "target_key_field": "metric_key", "target_key_prefix": "allocation_status:", "target_value_field": "metric_value", "target_timestamp_field": "last_reconciled_at", "seeded_by": "hcm-assets", "active": true},
|
|
8
|
+
{"sweep_code": "asset-return-reconciliation", "source_entity": "hcm_asset_return", "aggregation": "count", "group_by_field": "status", "target_entity": "hcm_asset_reconciliation", "target_key_field": "metric_key", "target_key_prefix": "return_status:", "target_value_field": "metric_value", "target_timestamp_field": "last_reconciled_at", "seeded_by": "hcm-assets", "active": true},
|
|
9
|
+
{"sweep_code": "asset-audit-reconciliation", "source_entity": "hcm_asset_audit", "aggregation": "count", "group_by_field": "status", "target_entity": "hcm_asset_reconciliation", "target_key_field": "metric_key", "target_key_prefix": "audit_status:", "target_value_field": "metric_value", "target_timestamp_field": "last_reconciled_at", "seeded_by": "hcm-assets", "active": true},
|
|
10
|
+
{"sweep_code": "maintenance-history-aggregation", "source_entity": "hcm_asset_maintenance", "aggregation": "count", "group_by_field": "asset_id", "target_entity": "hcm_asset_reconciliation", "target_key_field": "metric_key", "target_key_prefix": "maint_events_asset:", "target_value_field": "metric_value", "target_timestamp_field": "last_reconciled_at", "seeded_by": "hcm-assets", "active": true},
|
|
11
|
+
{"sweep_code": "maintenance-cost-sync", "source_entity": "hcm_asset_maintenance_cost", "aggregation": "sum", "agg_field": "total_cost", "group_by_field": "maintenance_id", "target_entity": "hcm_asset_reconciliation", "target_key_field": "metric_key", "target_key_prefix": "maint_cost:", "target_value_field": "metric_value", "target_timestamp_field": "last_reconciled_at", "seeded_by": "hcm-assets", "active": true},
|
|
12
|
+
{"sweep_code": "software-license-usage-sync", "source_entity": "hcm_software_license_assignment", "aggregation": "count", "when_status_in": "ASSIGNED,ACTIVE", "status_field": "status", "group_by_field": "license_id", "target_entity": "hcm_asset_reconciliation", "target_key_field": "metric_key", "target_key_prefix": "license_seats_used:", "target_value_field": "metric_value", "target_timestamp_field": "last_reconciled_at", "seeded_by": "hcm-assets", "active": true},
|
|
13
|
+
{"sweep_code": "software-license-cost-sync", "source_entity": "hcm_software_license", "aggregation": "sum", "agg_field": "cost_amount", "group_by_field": "status", "target_entity": "hcm_asset_reconciliation", "target_key_field": "metric_key", "target_key_prefix": "license_cost_status:", "target_value_field": "metric_value", "target_timestamp_field": "last_reconciled_at", "seeded_by": "hcm-assets", "active": true},
|
|
14
|
+
{"sweep_code": "maintenance-cost-rollup", "source_entity": "hcm_asset_maintenance_cost", "aggregation": "sum", "agg_field": "total_cost", "group_by_field": "maintenance_id", "target_entity": "hcm_asset_maintenance", "target_key_field": "id", "target_value_field": "synced_cost", "target_timestamp_field": "cost_synced_at", "seeded_by": "hcm-assets", "active": true},
|
|
15
|
+
{"sweep_code": "predictive-maintenance-event-count", "source_entity": "hcm_asset_maintenance", "aggregation": "count", "date_field": "scheduled_start", "date_compare_op": "gte", "day_offset": -180, "group_by_field": "asset_id", "target_entity": "hcm_asset", "target_key_field": "id", "target_value_field": "recent_maintenance_count", "target_timestamp_field": "maintenance_risk_scored_at", "seeded_by": "hcm-assets", "active": true},
|
|
16
|
+
{"sweep_code": "predictive-open-maintenance-count", "source_entity": "hcm_asset_maintenance", "aggregation": "count", "when_status_in": "REQUESTED,PENDING_APPROVAL,APPROVED,SCHEDULED,IN_PROGRESS,WAITING_FOR_PARTS", "group_by_field": "asset_id", "target_entity": "hcm_asset", "target_key_field": "id", "target_value_field": "open_maintenance_count", "seeded_by": "hcm-assets", "active": true}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity": "entity_cadence_config",
|
|
3
|
+
"keyFields": ["cadence_code"],
|
|
4
|
+
"source": "hcm-assets",
|
|
5
|
+
"rows": [
|
|
6
|
+
{"cadence_code": "asset-audit-schedule", "target_entity": "hcm_asset_audit", "interval_days": 90, "dedupe_target_field": "cadence_marker", "template_json": {"audit_number": "AUDIT-${date}-${ts}", "audit_name": "Scheduled Quarterly Asset Audit ${date}", "audit_type": "QUARTERLY", "status": "DRAFT"}, "seeded_by": "hcm-assets", "active": true},
|
|
7
|
+
{"cadence_code": "preventive-maintenance-generation", "target_entity": "hcm_asset_maintenance", "interval_days": 1, "source_entity": "hcm_asset", "source_status_field": "status", "source_status_in": "AVAILABLE,ASSIGNED,IN_USE", "source_date_field": "next_pm_date", "due_within_days": 7, "dedupe_target_field": "pm_source_asset_id", "field_map_json": {"asset_id": "id", "asset_number": "asset_number", "pm_source_asset_id": "id"}, "template_json": {"work_order_number": "PM-${sourceId}-${ts}", "maintenance_type": "PREVENTIVE", "priority": "MEDIUM", "problem_title": "Scheduled preventive maintenance", "status": "REQUESTED"}, "seeded_by": "hcm-assets", "active": true}
|
|
8
|
+
]
|
|
9
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity": "entity_compliance_config",
|
|
3
|
+
"keyFields": ["compliance_code"],
|
|
4
|
+
"source": "hcm-assets",
|
|
5
|
+
"rows": [
|
|
6
|
+
{
|
|
7
|
+
"compliance_code": "asset-assignment-compliance",
|
|
8
|
+
"source_entity": "hcm_asset",
|
|
9
|
+
"when_status_in": "ASSIGNED,IN_USE",
|
|
10
|
+
"condition_expr": "custody_acknowledged != true",
|
|
11
|
+
"set_field": "compliance_flag",
|
|
12
|
+
"set_value": "NON_COMPLIANT",
|
|
13
|
+
"seeded_by": "hcm-assets",
|
|
14
|
+
"active": true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"compliance_code": "asset-allocation-policy-compliance",
|
|
18
|
+
"source_entity": "hcm_asset_allocation",
|
|
19
|
+
"when_status_in": "ALLOCATED,ACKNOWLEDGED,IN_USE",
|
|
20
|
+
"condition_expr": "allocation_type == \"PERMANENT\" && agreement_file_ref_id == null",
|
|
21
|
+
"set_field": "compliance_flag",
|
|
22
|
+
"set_value": "POLICY_VIOLATION",
|
|
23
|
+
"seeded_by": "hcm-assets",
|
|
24
|
+
"active": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"compliance_code": "software-license-compliance",
|
|
28
|
+
"source_entity": "hcm_software_license",
|
|
29
|
+
"when_status_in": "ACTIVE,EXPIRING",
|
|
30
|
+
"related_count_entity": "hcm_software_license_assignment",
|
|
31
|
+
"related_count_key_field": "license_id",
|
|
32
|
+
"related_count_status_field": "status",
|
|
33
|
+
"related_count_status_in": "ASSIGNED,ACTIVE",
|
|
34
|
+
"condition_expr": "related_count > entitlement_quantity",
|
|
35
|
+
"set_field": "compliance_flag",
|
|
36
|
+
"set_value": "OVER_ALLOCATED",
|
|
37
|
+
"seeded_by": "hcm-assets",
|
|
38
|
+
"active": true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"compliance_code": "software-license-unused-detection",
|
|
42
|
+
"source_entity": "hcm_software_license_assignment",
|
|
43
|
+
"when_status_in": "ASSIGNED,ACTIVE",
|
|
44
|
+
"condition_expr": "(last_used_date == null && daysBetween(start_date, now()) > 90) || (last_used_date != null && daysBetween(last_used_date, now()) > 90)",
|
|
45
|
+
"set_field": "usage_flag",
|
|
46
|
+
"set_value": "UNUSED",
|
|
47
|
+
"seeded_by": "hcm-assets",
|
|
48
|
+
"active": true
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"compliance_code": "asset-audit-exception-escalation",
|
|
52
|
+
"source_entity": "hcm_asset_return_exception",
|
|
53
|
+
"when_status_in": "OPEN,IN_PROGRESS",
|
|
54
|
+
"condition_expr": "resolution_due_date != null && daysBetween(resolution_due_date, now()) > 0",
|
|
55
|
+
"escalation_entity": "hcm_asset_compliance_escalation",
|
|
56
|
+
"escalation_dedupe_field": "source_record_id",
|
|
57
|
+
"escalation_timestamp_field": "raised_at",
|
|
58
|
+
"field_map_json": {"severity": "severity", "details": "description"},
|
|
59
|
+
"escalation_template_json": {"source_entity": "hcm_asset_return_exception", "escalation_type": "RESOLUTION_OVERDUE", "status": "OPEN"},
|
|
60
|
+
"seeded_by": "hcm-assets",
|
|
61
|
+
"active": true
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"compliance_code": "predictive-maintenance-risk",
|
|
65
|
+
"source_entity": "hcm_asset",
|
|
66
|
+
"when_status_in": "AVAILABLE,ASSIGNED,IN_USE",
|
|
67
|
+
"condition_expr": "coalesce(recent_maintenance_count, 0) >= 3 || coalesce(open_maintenance_count, 0) >= 2",
|
|
68
|
+
"set_field": "maintenance_risk",
|
|
69
|
+
"set_value": "HIGH",
|
|
70
|
+
"seeded_by": "hcm-assets",
|
|
71
|
+
"active": true
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity": "entity_cross_plugin_action_config",
|
|
3
|
+
"keyFields": ["action_code"],
|
|
4
|
+
"source": "hcm-assets",
|
|
5
|
+
"rows": [
|
|
6
|
+
{"action_code": "asset-separation-return", "trigger_entity": "employee", "trigger_status_field": "employment_status", "trigger_status_in": "TERMINATED,SEPARATED", "trigger_key_field": "id", "related_entity": "hcm_asset_allocation", "related_match_field": "employee_id", "related_status_field": "status", "related_status_in": "ALLOCATED,ACKNOWLEDGED,IN_USE", "create_entity": "hcm_asset_return", "create_dedupe_field": "allocation_id", "create_template_json": {"return_number": "ASR-${relatedId}-${ts}", "status": "REQUESTED", "reason": "EMPLOYEE_SEPARATION"}, "create_field_map_json": {"asset_id": "asset_id", "employee_id": "employee_id"}, "related_set_field": "status", "related_set_value": "RETURN_PENDING", "seeded_by": "hcm-assets", "active": true},
|
|
7
|
+
{"action_code": "software-license-reclamation", "trigger_entity": "employee", "trigger_status_field": "employment_status", "trigger_status_in": "TERMINATED,SEPARATED", "trigger_key_field": "id", "related_entity": "hcm_software_license_assignment", "related_match_field": "employee_id", "related_status_field": "status", "related_status_in": "ASSIGNED,ACTIVE", "related_set_field": "status", "related_set_value": "RECLAMATION_PENDING", "seeded_by": "hcm-assets", "active": true}
|
|
8
|
+
]
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entity": "entity_document_generator_config",
|
|
3
|
+
"keyFields": ["document_code"],
|
|
4
|
+
"source": "hcm-assets",
|
|
5
|
+
"rows": [
|
|
6
|
+
{"document_code": "asset-audit-report", "source_entity": "hcm_asset_audit", "status_field": "status", "when_status_in": "COMPLETED", "target_file_field": "report_file_ref_id", "format": "json", "cabinet_name": "HCM Assets Documents", "file_name_template": "asset-audit-report-${id}-${date}.json", "child_entity": "hcm_asset_audit_item", "child_match_field": "audit_id", "seeded_by": "hcm-assets", "active": true},
|
|
7
|
+
{"document_code": "asset-audit-certificate", "source_entity": "hcm_asset_audit", "status_field": "status", "when_status_in": "COMPLETED", "target_file_field": "certificate_file_ref_id", "format": "json", "cabinet_name": "HCM Assets Documents", "file_name_template": "asset-audit-certificate-${id}-${date}.json", "include_fields": "id,audit_number,audit_name,status,start_date,end_date,scope,location", "seeded_by": "hcm-assets", "active": true}
|
|
8
|
+
]
|
|
9
|
+
}
|