@sparkerp/plugin-sdk 0.1.0 → 1.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/bundle/blocks.json +169 -0
- package/bundle/catalog.json +614 -5
- package/bundle/docs/applications/hcm/admin-access-policies.md +38 -0
- package/bundle/docs/applications/hcm/admin-approval-hierarchies.md +33 -0
- package/bundle/docs/applications/hcm/admin-data-transfer.md +40 -0
- package/bundle/docs/applications/hcm/admin-hcm-users.md +35 -0
- package/bundle/docs/applications/hcm/admin-logs.md +57 -0
- package/bundle/docs/applications/hcm/admin-permissions-catalog.md +64 -0
- package/bundle/docs/applications/hcm/ai-intelligence-overview.md +37 -0
- package/bundle/docs/applications/hcm/ai-intelligence-people-risk.md +33 -0
- package/bundle/docs/applications/hcm/ai-intelligence-recruitment.md +37 -0
- package/bundle/docs/applications/hcm/ai-intelligence-tools.md +39 -0
- package/bundle/docs/applications/hcm/analytics-operations.md +31 -0
- package/bundle/docs/applications/hcm/analytics-overview.md +31 -0
- package/bundle/docs/applications/hcm/analytics-people.md +36 -0
- package/bundle/docs/applications/hcm/analytics-tools.md +35 -0
- package/bundle/docs/applications/hcm/assets-audits.md +30 -0
- package/bundle/docs/applications/hcm/assets-custody.md +36 -0
- package/bundle/docs/applications/hcm/assets-inventory.md +34 -0
- package/bundle/docs/applications/hcm/assets-maintenance.md +25 -0
- package/bundle/docs/applications/hcm/assets-software-licenses.md +27 -0
- package/bundle/docs/applications/hcm/attendance-core.md +46 -0
- package/bundle/docs/applications/hcm/attendance-exceptions.md +33 -0
- package/bundle/docs/applications/hcm/attendance-location.md +25 -0
- package/bundle/docs/applications/hcm/attendance-policies.md +23 -0
- package/bundle/docs/applications/hcm/attendance-reports.md +20 -0
- package/bundle/docs/applications/hcm/benefits-allowances.md +39 -0
- package/bundle/docs/applications/hcm/benefits-analytics.md +25 -0
- package/bundle/docs/applications/hcm/benefits-employee-processes.md +44 -0
- package/bundle/docs/applications/hcm/benefits-plans.md +60 -0
- package/bundle/docs/applications/hcm/communications-announcements.md +31 -0
- package/bundle/docs/applications/hcm/communications-history.md +23 -0
- package/bundle/docs/applications/hcm/communications-notifications.md +31 -0
- package/bundle/docs/applications/hcm/communications-templates.md +35 -0
- package/bundle/docs/applications/hcm/compensation-allowances-benefits.md +44 -0
- package/bundle/docs/applications/hcm/compensation-analytics.md +30 -0
- package/bundle/docs/applications/hcm/compensation-bonus-incentive.md +35 -0
- package/bundle/docs/applications/hcm/compensation-cycles.md +49 -0
- package/bundle/docs/applications/hcm/compensation-equity.md +36 -0
- package/bundle/docs/applications/hcm/compensation-salary.md +47 -0
- package/bundle/docs/applications/hcm/compliance-analytics.md +25 -0
- package/bundle/docs/applications/hcm/compliance-audits-calendar.md +52 -0
- package/bundle/docs/applications/hcm/compliance-employee-tracking.md +41 -0
- package/bundle/docs/applications/hcm/compliance-regulatory.md +31 -0
- package/bundle/docs/applications/hcm/country-packs-admin.md +33 -0
- package/bundle/docs/applications/hcm/country-packs-compliance.md +31 -0
- package/bundle/docs/applications/hcm/country-packs-localization.md +37 -0
- package/bundle/docs/applications/hcm/country-packs.md +102 -0
- package/bundle/docs/applications/hcm/dashboard.md +38 -0
- package/bundle/docs/applications/hcm/designations.md +32 -0
- package/bundle/docs/applications/hcm/emp-documents-analytics.md +27 -0
- package/bundle/docs/applications/hcm/emp-documents-core.md +33 -0
- package/bundle/docs/applications/hcm/emp-documents-letters.md +40 -0
- package/bundle/docs/applications/hcm/emp-documents-signatures.md +24 -0
- package/bundle/docs/applications/hcm/emp-documents-workflow.md +32 -0
- package/bundle/docs/applications/hcm/employee-directory.md +55 -0
- package/bundle/docs/applications/hcm/employee-documents.md +51 -0
- package/bundle/docs/applications/hcm/employee-info-background.md +41 -0
- package/bundle/docs/applications/hcm/employee-info-core.md +32 -0
- package/bundle/docs/applications/hcm/employee-info-documents.md +31 -0
- package/bundle/docs/applications/hcm/employee-info-other.md +21 -0
- package/bundle/docs/applications/hcm/employee-movements.md +46 -0
- package/bundle/docs/applications/hcm/employee-reference-data.md +40 -0
- package/bundle/docs/applications/hcm/employee-relations-analytics.md +23 -0
- package/bundle/docs/applications/hcm/employee-relations-cases.md +26 -0
- package/bundle/docs/applications/hcm/employee-relations-conflicts-feedback.md +25 -0
- package/bundle/docs/applications/hcm/employee-relations-processes.md +34 -0
- package/bundle/docs/applications/hcm/employee-services-daily.md +32 -0
- package/bundle/docs/applications/hcm/employee-services-manager.md +21 -0
- package/bundle/docs/applications/hcm/employee-services-more.md +41 -0
- package/bundle/docs/applications/hcm/employee-services-overview.md +43 -0
- package/bundle/docs/applications/hcm/expenses-analytics.md +26 -0
- package/bundle/docs/applications/hcm/expenses-claims.md +47 -0
- package/bundle/docs/applications/hcm/expenses-self-service.md +30 -0
- package/bundle/docs/applications/hcm/expenses-setup.md +43 -0
- package/bundle/docs/applications/hcm/health-safety-analytics.md +26 -0
- package/bundle/docs/applications/hcm/health-safety-incidents.md +44 -0
- package/bundle/docs/applications/hcm/health-safety-medical.md +25 -0
- package/bundle/docs/applications/hcm/health-safety-training.md +29 -0
- package/bundle/docs/applications/hcm/helpdesk.md +28 -0
- package/bundle/docs/applications/hcm/holiday-calendar.md +69 -0
- package/bundle/docs/applications/hcm/hr-policies.md +64 -0
- package/bundle/docs/applications/hcm/hr-settings.md +98 -0
- package/bundle/docs/applications/hcm/index.md +272 -0
- package/bundle/docs/applications/hcm/industry-it-software.md +52 -0
- package/bundle/docs/applications/hcm/job-classifications.md +48 -0
- package/bundle/docs/applications/hcm/learning-analytics.md +21 -0
- package/bundle/docs/applications/hcm/learning-assessments.md +22 -0
- package/bundle/docs/applications/hcm/learning-catalog.md +25 -0
- package/bundle/docs/applications/hcm/learning-certifications.md +27 -0
- package/bundle/docs/applications/hcm/learning-enrollment.md +26 -0
- package/bundle/docs/applications/hcm/learning-instructors-providers.md +21 -0
- package/bundle/docs/applications/hcm/leave-accrual-and-carryforward.md +30 -0
- package/bundle/docs/applications/hcm/leave-balance.md +26 -0
- package/bundle/docs/applications/hcm/leave-dashboard-and-reports.md +32 -0
- package/bundle/docs/applications/hcm/leave-encashment.md +27 -0
- package/bundle/docs/applications/hcm/leave-requests.md +26 -0
- package/bundle/docs/applications/hcm/leave-setup.md +30 -0
- package/bundle/docs/applications/hcm/leave-team-and-calendar.md +25 -0
- package/bundle/docs/applications/hcm/navigation-and-approvals.md +61 -0
- package/bundle/docs/applications/hcm/offboarding-and-exit.md +71 -0
- package/bundle/docs/applications/hcm/onboarding-documents-verification.md +43 -0
- package/bundle/docs/applications/hcm/onboarding-orientation-probation.md +65 -0
- package/bundle/docs/applications/hcm/onboarding-overview.md +47 -0
- package/bundle/docs/applications/hcm/onboarding-provisioning-assets.md +48 -0
- package/bundle/docs/applications/hcm/onboarding-reports.md +38 -0
- package/bundle/docs/applications/hcm/onboarding-templates-checklists.md +38 -0
- package/bundle/docs/applications/hcm/onboarding-to-confirmation.md +45 -0
- package/bundle/docs/applications/hcm/org-structure.md +53 -0
- package/bundle/docs/applications/hcm/org-units.md +86 -0
- package/bundle/docs/applications/hcm/organizations.md +81 -0
- package/bundle/docs/applications/hcm/payroll-analytics.md +31 -0
- package/bundle/docs/applications/hcm/payroll-post-run.md +40 -0
- package/bundle/docs/applications/hcm/payroll-runs.md +30 -0
- package/bundle/docs/applications/hcm/payroll-setup.md +38 -0
- package/bundle/docs/applications/hcm/payroll-tax.md +21 -0
- package/bundle/docs/applications/hcm/payroll-transactions.md +43 -0
- package/bundle/docs/applications/hcm/performance-analytics.md +23 -0
- package/bundle/docs/applications/hcm/performance-appraisals.md +50 -0
- package/bundle/docs/applications/hcm/performance-continuous-feedback.md +23 -0
- package/bundle/docs/applications/hcm/performance-cycles-and-goals.md +54 -0
- package/bundle/docs/applications/hcm/performance-okrs.md +33 -0
- package/bundle/docs/applications/hcm/performance-pips.md +29 -0
- package/bundle/docs/applications/hcm/performance-ratings.md +25 -0
- package/bundle/docs/applications/hcm/positions.md +67 -0
- package/bundle/docs/applications/hcm/recruitment-agencies-and-sources.md +26 -0
- package/bundle/docs/applications/hcm/recruitment-analytics.md +25 -0
- package/bundle/docs/applications/hcm/recruitment-candidates.md +33 -0
- package/bundle/docs/applications/hcm/recruitment-offers.md +32 -0
- package/bundle/docs/applications/hcm/recruitment-pipeline.md +46 -0
- package/bundle/docs/applications/hcm/recruitment-requisitions-and-openings.md +33 -0
- package/bundle/docs/applications/hcm/roles-permissions.md +129 -0
- package/bundle/docs/applications/hcm/separation-clearance.md +33 -0
- package/bundle/docs/applications/hcm/separation-documents.md +28 -0
- package/bundle/docs/applications/hcm/separation-final-settlement.md +24 -0
- package/bundle/docs/applications/hcm/separation-reports.md +23 -0
- package/bundle/docs/applications/hcm/separation-resignation.md +43 -0
- package/bundle/docs/applications/hcm/settings-extensibility.md +31 -0
- package/bundle/docs/applications/hcm/settings-general.md +29 -0
- package/bundle/docs/applications/hcm/settings-integrations.md +14 -0
- package/bundle/docs/applications/hcm/settings-module-defaults.md +41 -0
- package/bundle/docs/applications/hcm/settings-process.md +28 -0
- package/bundle/docs/applications/hcm/shift-scheduling.md +33 -0
- package/bundle/docs/applications/hcm/talent-analytics.md +26 -0
- package/bundle/docs/applications/hcm/talent-career.md +31 -0
- package/bundle/docs/applications/hcm/talent-competencies-skills.md +30 -0
- package/bundle/docs/applications/hcm/talent-profiles.md +40 -0
- package/bundle/docs/applications/hcm/talent-succession.md +42 -0
- package/bundle/docs/applications/hcm/teams-and-tags.md +46 -0
- package/bundle/docs/applications/hcm/timesheets.md +33 -0
- package/bundle/docs/applications/hcm/travel-advances-expenses.md +34 -0
- package/bundle/docs/applications/hcm/travel-analytics.md +28 -0
- package/bundle/docs/applications/hcm/travel-bookings.md +35 -0
- package/bundle/docs/applications/hcm/travel-requests.md +38 -0
- package/bundle/docs/applications/hcm/workforce-org-design.md +22 -0
- package/bundle/docs/applications/hcm/workforce-planning-analytics.md +26 -0
- package/bundle/docs/applications/hcm/workforce-planning-core.md +36 -0
- package/bundle/docs/applications/hcm/workforce-planning-scenarios.md +29 -0
- package/bundle/docs/docs.json +1 -0
- package/bundle/docs/guides/add-app-owned-roles-and-permissions.md +144 -0
- package/bundle/docs/guides/checkout-an-installed-plugin.md +151 -0
- package/bundle/docs/guides/extend-a-shipped-application.md +132 -0
- package/bundle/docs/guides/index.md +3 -0
- package/bundle/docs/reference/entity-aggregation-config.md +1 -1
- package/bundle/docs/reference/entity-document-generator-config.md +1 -1
- package/bundle/docs/tutorial/01-create-the-plugin.md +7 -1
- package/bundle/docs/tutorial/07-return-due-reminder-job.md +28 -5
- package/bundle/docs/tutorial/08-menus-i18n-publish.md +5 -5
- package/bundle/manifest.json +4 -4
- package/bundle/schemas/page.schema.json +13 -0
- package/bundle/schemas/plugin-manifest.schema.json +13 -0
- package/bundle/validators/block-engine.mjs +167 -7
- package/bundle/validators/page-engine.mjs +226 -21
- package/erp-cli/authoring-root.mjs +12 -1
- package/erp-cli/erp.mjs +679 -15
- package/package.json +1 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Check out an installed plugin and work on it
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Check out an installed plugin and work on it
|
|
7
|
+
|
|
8
|
+
## What you're doing
|
|
9
|
+
|
|
10
|
+
You already have a plugin **installed** on an environment — maybe you built
|
|
11
|
+
it yourself weeks ago and lost the local source, maybe you inherited it from
|
|
12
|
+
someone else, maybe you're fixing a real, confirmed bug in your own
|
|
13
|
+
tenant-owned plugin. Either way you want a real local copy: something you
|
|
14
|
+
can `git diff`, edit, and ship back — not a live-database patch, and not a
|
|
15
|
+
raw dump of API responses.
|
|
16
|
+
|
|
17
|
+
**Before you reach for this:** if what you actually want is to *extend* a
|
|
18
|
+
shipped, vendor-owned application (HCM, CRM, ...) rather than edit a plugin
|
|
19
|
+
you own, this is the wrong page — see
|
|
20
|
+
[Extend a shipped application](./extend-a-shipped-application.md) instead.
|
|
21
|
+
Editing another module's own source is never the right move for a feature
|
|
22
|
+
request, only for a confirmed bug in a plugin that's genuinely yours.
|
|
23
|
+
|
|
24
|
+
## The command set
|
|
25
|
+
|
|
26
|
+
Modeled on git, on purpose — the mental model is the same one you already
|
|
27
|
+
have:
|
|
28
|
+
|
|
29
|
+
| git | erp | what it does |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `git branch` / `git remote -v` | `erp plugin list` | Every plugin installed on the current env, as a readable table (`pluginId`, `version`, `state`, `name`) — pick one before cloning. `--json` for the raw response. |
|
|
32
|
+
| `git clone` | `erp plugin clone <pluginId> [--out <dir>]` | A **real, full local checkout** — every page, menu, data service, data view, mobile nav, provider, application/module membership, and this plugin's own i18n keys it owns, written to `<out>/<pluginId>/spk-assembly/metadata/...` in the same on-disk shape a shipped module has. Always gets the latest version of every artifact. |
|
|
33
|
+
| `git checkout <ref>` | `erp plugin checkout <pluginId> --version <n> [--out <dir>]` | Same full checkout as `clone`, but for each artifact independently, prefers version `n` from *that artifact's own* history if it has one that old, else falls back to latest. See the caveat below — this is not a single point-in-time snapshot. |
|
|
34
|
+
| `git add` / `git commit` | plain `git`, inside the checked-out directory | It's a real folder now. `cd <pluginId> && git init && git add . && git commit -m "..."` gives you real, diffable history — no special erp command needed. |
|
|
35
|
+
| `git push` | `erp plugin build <dir> -o out.spk` then `erp plugin publish out.spk --env <name>` (or `erp plugin push`, an alias) | Package your edited `spk-assembly/` into a `.spk` and ship it to an environment. See [Publish and upgrade a plugin](./publish-and-upgrade.md). |
|
|
36
|
+
|
|
37
|
+
`erp plugin pull <pluginId>` (no `--full`) still exists separately — it's
|
|
38
|
+
the *thin* form: just `plugin.json` + install config + install state, no
|
|
39
|
+
artifact bodies. Useful for a quick "what version is installed, what's its
|
|
40
|
+
manifest" check without the full fan-out `clone`/`checkout` do.
|
|
41
|
+
|
|
42
|
+
## The complete example
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# 1. See what's there
|
|
46
|
+
erp plugin list
|
|
47
|
+
|
|
48
|
+
# PLUGIN ID VERSION STATE NAME
|
|
49
|
+
# hcm-foundation 1.0.299 installed HCM Foundation
|
|
50
|
+
# office-equipment 1.0.0 installed Office Equipment
|
|
51
|
+
# ...
|
|
52
|
+
|
|
53
|
+
# 2. Clone the one you own
|
|
54
|
+
erp plugin clone office-equipment
|
|
55
|
+
|
|
56
|
+
# == Checking out plugin "office-equipment" ==
|
|
57
|
+
# -- spk-assembly/plugin.json (version 1.0.0) --
|
|
58
|
+
# -- pages: 3 owned by office-equipment --
|
|
59
|
+
# wrote page/office-equipment-list.json (id 4021, v2)
|
|
60
|
+
# ...
|
|
61
|
+
# -- i18n --
|
|
62
|
+
# wrote i18n/en.json (41 keys under "office-equipment.*")
|
|
63
|
+
#
|
|
64
|
+
# == Full checkout done: 9 artifacts + plugin.json + i18n written to office-equipment/spk-assembly ==
|
|
65
|
+
#
|
|
66
|
+
# Now a real local directory — e.g.:
|
|
67
|
+
# cd office-equipment && git init && git add . && git commit -m "Clone of office-equipment@1.0.0"
|
|
68
|
+
|
|
69
|
+
# 3. Real git, from here on
|
|
70
|
+
cd office-equipment
|
|
71
|
+
git init && git add . && git commit -m "Clone of office-equipment@1.0.0"
|
|
72
|
+
|
|
73
|
+
# 4. Edit under spk-assembly/metadata/, commit as you go
|
|
74
|
+
# (e.g. spk-assembly/metadata/page/office-equipment-list.json)
|
|
75
|
+
git add -A && git commit -m "Add a status filter to the equipment list"
|
|
76
|
+
|
|
77
|
+
# 5. Ship it — dev first, then prod
|
|
78
|
+
erp plugin build . -o office-equipment-1.0.1.spk
|
|
79
|
+
erp plugin publish office-equipment-1.0.1.spk --env dev
|
|
80
|
+
# ...verify it looks right...
|
|
81
|
+
erp plugin publish office-equipment-1.0.1.spk --env prod
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## What actually gets written
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
office-equipment/
|
|
88
|
+
├── plugin.json ← THIN pull output (manifest only, kept for compat)
|
|
89
|
+
├── config.json
|
|
90
|
+
├── installation-state.json
|
|
91
|
+
└── spk-assembly/ ← the real, editable, buildable tree
|
|
92
|
+
├── plugin.json
|
|
93
|
+
└── metadata/
|
|
94
|
+
├── page/*.json
|
|
95
|
+
├── menu/*.json
|
|
96
|
+
├── mobile_nav/*.json
|
|
97
|
+
├── provider/*.json
|
|
98
|
+
├── data_service/*.json
|
|
99
|
+
├── data_view/*.json
|
|
100
|
+
├── application/*.json ← membership rows, if this plugin owns any
|
|
101
|
+
├── module/*.json
|
|
102
|
+
├── entity/*.json ← best-effort, see caveat below
|
|
103
|
+
└── i18n/en.json ← only this plugin's own `<pluginId>.*` keys
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Every artifact file is `{ name, description, metadata, definition }` — the
|
|
107
|
+
same shape `erp plugin build` reads when packaging a `.spk`.
|
|
108
|
+
|
|
109
|
+
## Known limits (disclosed, not hidden)
|
|
110
|
+
|
|
111
|
+
- **`--version` is per-artifact, not a plugin-wide snapshot.** `plugin.json`'s
|
|
112
|
+
own `version` (e.g. `1.0.299`) is bumped once per `.spk` release, but each
|
|
113
|
+
individual artifact versions independently, on its own publish cadence.
|
|
114
|
+
There's no server-side record of "which version of every one of this
|
|
115
|
+
plugin's 28 artifacts was live when the plugin itself was at 1.0.298" — so
|
|
116
|
+
`checkout --version 4` takes artifact-level `v4` wherever that artifact
|
|
117
|
+
has one, and its latest otherwise. Good enough to inspect an older cut of
|
|
118
|
+
one screen; not a substitute for real git tags on your own commits going
|
|
119
|
+
forward.
|
|
120
|
+
- **Entities are best-effort.** Unlike pages/menus/data-services/etc.,
|
|
121
|
+
entities have no `ownerPlugin` field at all — the closest available signal
|
|
122
|
+
is a free-text `category` field, matched against the plugin id. Confirmed
|
|
123
|
+
live to hold real plugin ids for entity-heavy modules, but it's not an
|
|
124
|
+
enforced foreign key.
|
|
125
|
+
- **`erp plugin validate`** isn't available in packaged SDK mode (needs
|
|
126
|
+
platform build tooling not shipped in the authoring bundle) — validate
|
|
127
|
+
what you can with `erp schema validate <file> --schema <name>` per file
|
|
128
|
+
instead.
|
|
129
|
+
|
|
130
|
+
## Common mistakes
|
|
131
|
+
|
|
132
|
+
- **Cloning a vendor-owned plugin to "fix" a feature gap.** If it's not a
|
|
133
|
+
confirmed bug in code you own, use
|
|
134
|
+
[Extend a shipped application](./extend-a-shipped-application.md) instead
|
|
135
|
+
— a companion plugin, not an edit to someone else's source.
|
|
136
|
+
- **Editing the THIN `plugin.json`/`config.json`/`installation-state.json`
|
|
137
|
+
files at the top level.** Those are install-state snapshots, not build
|
|
138
|
+
input — edit under `spk-assembly/metadata/` instead; that's what
|
|
139
|
+
`erp plugin build` actually reads.
|
|
140
|
+
- **Forgetting `--env`.** `clone`/`checkout` read from whatever `erp env
|
|
141
|
+
use`'s current environment is (or `--env <name>` for one call) — cloning
|
|
142
|
+
from `prod` when you meant `dev` gets you prod's live content, not a
|
|
143
|
+
sandbox to break.
|
|
144
|
+
|
|
145
|
+
## See also
|
|
146
|
+
|
|
147
|
+
- [Publish and upgrade a plugin](./publish-and-upgrade.md) — the `build`/
|
|
148
|
+
`publish` half of this flow, in more depth.
|
|
149
|
+
- [Extend a shipped application](./extend-a-shipped-application.md) — the
|
|
150
|
+
right tool when you don't own the plugin's source.
|
|
151
|
+
- [Validate and test a plugin](./validate-and-test.md)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Extend a shipped application (e.g. HCM)
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Extend a shipped application
|
|
7
|
+
|
|
8
|
+
This page answers a question the other guides don't: *"HCM (or CRM, or any
|
|
9
|
+
other shipped application) already has the screen I want — how do I add to
|
|
10
|
+
it without forking the product?"* It uses HCM as the worked example because
|
|
11
|
+
it's the largest shipped application, but the pattern is identical for every
|
|
12
|
+
other one.
|
|
13
|
+
|
|
14
|
+
## What you're doing
|
|
15
|
+
|
|
16
|
+
There are exactly three ways to extend a shipped application. Picking the
|
|
17
|
+
wrong one is the most common mistake — start here.
|
|
18
|
+
|
|
19
|
+
| You want to... | Use | Why |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| Add a field to an existing screen (e.g. a new column on the Employee form) | **Custom Fields** (no code) — see [Document Settings, Custom Fields, Custom Forms & Numbering Sequences](../applications/hcm/settings-extensibility.md) | It's a tenant admin setting, not a development task. No plugin needed. |
|
|
22
|
+
| Add a new screen, report, KPI, or workflow that *reads* existing data (e.g. a dashboard of employees whose certifications expire soon) | **A companion plugin** that reads the shipped module's entities through a **Data Service / Data View** (read-only) | This is what the rest of this page walks through. |
|
|
23
|
+
| Add a new approval step or business rule triggered by an existing entity changing | **A workflow or business rule** attached via metadata to the existing entity | See [Add an approval workflow](./add-an-approval-workflow.md) and [Add business rules and expressions](./add-business-rules.md) — no Java, and no edit to the shipped plugin. |
|
|
24
|
+
|
|
25
|
+
What you must **never** do: edit `hcm-employee`'s (or any shipped plugin's)
|
|
26
|
+
own Java source or its metadata files. Those are the platform's, upgraded
|
|
27
|
+
independently of your tenant, and per
|
|
28
|
+
[[feedback-avoid-frequent-core-code-changes]] only a real, confirmed bug
|
|
29
|
+
justifies touching another module's code — a feature request never does.
|
|
30
|
+
Every legitimate extension in the table above is additive: a new plugin, a
|
|
31
|
+
new metadata file, a new rule. Nothing you write ever modifies a file that
|
|
32
|
+
ships with `hcm-employee`, `hcm-leave`, or any other application module.
|
|
33
|
+
|
|
34
|
+
## The complete example
|
|
35
|
+
|
|
36
|
+
A companion plugin, `hcm-cert-tracker`, that adds one new read-only page to
|
|
37
|
+
HCM: **Employees by Department** — a KPI built entirely from data that
|
|
38
|
+
already lives in the shipped `hcm-employee` module's own database table,
|
|
39
|
+
without touching that module at all.
|
|
40
|
+
|
|
41
|
+
`spk-assembly/metadata/data_view/employees-by-department-view.json`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"name": "hcm-cert-tracker-employees-by-department-view",
|
|
46
|
+
"description": "Read-only view over hcm-employee's own `employee` table — active headcount grouped by department. This plugin never writes to this table.",
|
|
47
|
+
"definition": {
|
|
48
|
+
"source": { "table": "employee", "alias": "e", "excludeDeleted": false, "schema": "hcm" },
|
|
49
|
+
"joins": [
|
|
50
|
+
{ "table": "org_unit", "alias": "dept", "type": "INNER", "on": [{ "leftRef": "e.department_id", "rightRef": "dept.id" }], "excludeDeleted": false, "schema": "hcm" }
|
|
51
|
+
],
|
|
52
|
+
"fields": [{ "ref": "dept.name", "outputName": "label" }],
|
|
53
|
+
"calculatedFields": [],
|
|
54
|
+
"filter": "{\"and\":[{\"field\":\"e.employment_status\",\"operator\":\"eq\",\"value\":\"active\"}]}",
|
|
55
|
+
"groupBy": ["dept.name"],
|
|
56
|
+
"aggregations": [{ "ref": "e.id", "fn": "COUNT", "outputName": "value" }],
|
|
57
|
+
"sort": [{ "ref": "dept.name", "descending": false }],
|
|
58
|
+
"pagination": { "defaultPageSize": 50, "maxPageSize": 100 },
|
|
59
|
+
"permissionKey": null
|
|
60
|
+
},
|
|
61
|
+
"metadata": {},
|
|
62
|
+
"modules": ["hcm-cert-tracker-dashboard"]
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`spk-assembly/metadata/data_service/employees-by-department.json`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"name": "hcm-cert-tracker-employees-by-department",
|
|
71
|
+
"operation": "search",
|
|
72
|
+
"source": { "kind": "dataView", "ref": "hcm-cert-tracker-employees-by-department-view" }
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The page then binds a `core.donut-chart` (or `core.list`) block to
|
|
77
|
+
`POST /api/v1/data-services/hcm-cert-tracker-employees-by-department/execute`
|
|
78
|
+
— the exact same `callApi` → `setValue` → binding pattern in
|
|
79
|
+
[Wire a page's data](./wire-a-pages-data.md).
|
|
80
|
+
|
|
81
|
+
## Line by line
|
|
82
|
+
|
|
83
|
+
- **`source.schema: "hcm"`** — every HCM module's tables live in the shared
|
|
84
|
+
`hcm` Postgres schema, not a per-plugin schema. Get this from the shipped
|
|
85
|
+
module's own real, already-installed `data_view` files (as done here,
|
|
86
|
+
copied from `hcm-employee`'s own
|
|
87
|
+
`department-headcount-distribution-view.json`) — never guess it, and never
|
|
88
|
+
trust a plugin's `plugin.json` `schemaName` field, which can be stale (see
|
|
89
|
+
[[feedback-entity-engine-tables-must-route-to-app-schema]]).
|
|
90
|
+
- **`source.table: "employee"`** — the real table name. Reverse-engineer real
|
|
91
|
+
table/column names the same way: read an existing, shipped `data_view`
|
|
92
|
+
JSON from the module you're extending. Every shipped HCM module's
|
|
93
|
+
`metadata/data_view/` directory is real, readable reference material for
|
|
94
|
+
exactly this purpose.
|
|
95
|
+
- **This view is read-only** — a `dataView`/`dataService` pair can only
|
|
96
|
+
`search`/`get`/`count`; there is no write path through this mechanism.
|
|
97
|
+
Writing to another module's table is not supported and not safe — if you
|
|
98
|
+
need to change HCM data, do it through HCM's own real forms/APIs, not by
|
|
99
|
+
reaching into its schema.
|
|
100
|
+
- **`modules: ["hcm-cert-tracker-dashboard"]`** — scopes this data service to
|
|
101
|
+
your own plugin's page, not to `hcm-employee`'s.
|
|
102
|
+
|
|
103
|
+
## How to verify it worked
|
|
104
|
+
|
|
105
|
+
1. `erp plugin build && erp plugin publish --env dev`
|
|
106
|
+
2. `curl -X POST $BASE/api/v1/data-services/hcm-cert-tracker-employees-by-department/execute -H "Authorization: Bearer $TOKEN"` and confirm it returns real `{label, value}` rows matching your tenant's actual employee/department data — spot-check one row against the HCM Employee Directory screen itself.
|
|
107
|
+
3. Confirm `hcm-employee`'s own files are untouched: `git status` inside `backend/modules/hcm-employee` should show nothing.
|
|
108
|
+
|
|
109
|
+
## Common mistakes
|
|
110
|
+
|
|
111
|
+
- **Editing the shipped module instead of reading it.** If you find yourself
|
|
112
|
+
opening `hcm-employee`'s source to add a field or endpoint, stop — that's
|
|
113
|
+
Custom Fields or a companion plugin, not a source edit.
|
|
114
|
+
- **Guessing the schema name.** Always confirm it from a real, already-shipped
|
|
115
|
+
`data_view` file in the module you're reading from.
|
|
116
|
+
- **Building a bespoke REST controller instead of a Data Service.** See
|
|
117
|
+
[[feedback-prefer-dataview-over-bespoke-rest-for-reads]] — if you're
|
|
118
|
+
reading rows, a `dataView`/`dataService` pair is almost always the right
|
|
119
|
+
tool, not a hand-written `@RestController`.
|
|
120
|
+
- **Forgetting `TenantContext`.** If any part of your companion plugin adds a
|
|
121
|
+
`NO_AUTH` endpoint that touches this data outside the normal
|
|
122
|
+
tenant-request path, it must wrap the call in
|
|
123
|
+
`TenantContext.set()`/`finally clear()` — see
|
|
124
|
+
[[feedback-no-auth-endpoints-need-manual-tenant-context]].
|
|
125
|
+
|
|
126
|
+
## What to read next
|
|
127
|
+
|
|
128
|
+
- [Add a data provider, data view, or data service](./add-a-data-provider.md) — the full reference for what this guide's worked example used.
|
|
129
|
+
- [Build a page](./build-a-page.md) and [Wire a page's data](./wire-a-pages-data.md) — to add the screen this data feeds.
|
|
130
|
+
- [Add a KPI or aggregation](./add-a-kpi.md) — for the KPI-card version of this same pattern.
|
|
131
|
+
- [Add an approval workflow](./add-an-approval-workflow.md) — for the "add a workflow step to an existing entity" extension path.
|
|
132
|
+
- [Add your own app-owned roles & permissions](./add-app-owned-roles-and-permissions.md) — the correct pattern if your companion plugin needs its own admin role, instead of widening a shipped module's access checks.
|
|
@@ -39,6 +39,8 @@ Every code sample is a real file under
|
|
|
39
39
|
|
|
40
40
|
- [Add business rules and expressions](./add-business-rules.md)
|
|
41
41
|
- [Add an approval workflow](./add-an-approval-workflow.md)
|
|
42
|
+
- [Add your own app-owned roles & permissions](./add-app-owned-roles-and-permissions.md)
|
|
43
|
+
- [Extend a shipped application (e.g. HCM)](./extend-a-shipped-application.md)
|
|
42
44
|
|
|
43
45
|
## Scheduled jobs (no Java)
|
|
44
46
|
|
|
@@ -49,5 +51,6 @@ Every code sample is a real file under
|
|
|
49
51
|
|
|
50
52
|
## Ship it
|
|
51
53
|
|
|
54
|
+
- [Check out an installed plugin and work on it](./checkout-an-installed-plugin.md)
|
|
52
55
|
- [Validate and test a plugin](./validate-and-test.md)
|
|
53
56
|
- [Publish and upgrade a plugin](./publish-and-upgrade.md)
|
|
@@ -27,7 +27,7 @@ Pull the full JSON Schema: `erp schema pull entity-aggregation-config` ·&
|
|
|
27
27
|
| `agg_field` | string | | Required for sum/avg/min/max; the numeric column to fold. |
|
|
28
28
|
| `group_by_field` | string | | Optional; one result bucket per distinct value. |
|
|
29
29
|
| `target_entity` | string | yes | |
|
|
30
|
-
| `target_key_field` | string | yes | Column on target_entity that holds the bucket key (a declared field). Special value "id": the bucket key IS a row's own primary key — the fold is written straight back onto target_entity row
|
|
30
|
+
| `target_key_field` | string | yes | Column on target_entity that holds the bucket key (a declared field). Special value "id": the bucket key IS a row's own primary key — the fold is written straight back onto target_entity row #<key> (never a create). Use with group_by_field yielding the parent id (e.g. maintenance_id), target_entity = that parent entity, and a blank target_key_prefix — this is the true 'roll child lines up onto the parent record' form (MaintenanceCostSyncJob). |
|
|
31
31
|
| `target_key_prefix` | string | | Prepended to the bucket key when writing (so multiple configs can share one summary entity without collisions). |
|
|
32
32
|
| `result_key` | string | | Used as the bucket key when group_by_field is blank; defaults to sweep_code. |
|
|
33
33
|
| `target_value_field` | string | yes | |
|
|
@@ -24,7 +24,7 @@ Pull the full JSON Schema: `erp schema pull entity-document-generator-config` &n
|
|
|
24
24
|
| `format` | string | | one of: `json`, `csv` |
|
|
25
25
|
| `cabinet_id` | integer | | DMS cabinet id (engine-file) the artifact is stored in. Give this OR cabinet_name. |
|
|
26
26
|
| `cabinet_name` | string | | Portable alternative to cabinet_id: the job resolves a cabinet by this name for the tenant, creating it if absent. Preferred for seed-data rows (no hardcoded id). |
|
|
27
|
-
| `file_name_template` | string | | Generated file name. Supports ${id} ${date} ${ts} ${uuid} and ${field
|
|
27
|
+
| `file_name_template` | string | | Generated file name. Supports ${id} ${date} ${ts} ${uuid} and ${field:<name>}. Default: <document_code>-${id}-${date}.<ext>. |
|
|
28
28
|
| `include_fields` | string | | Comma-separated source fields to include in the artifact. Blank = all fields. |
|
|
29
29
|
| `child_entity` | string | | Optional child entity whose rows (matched by child_match_field == source id) are embedded (json) alongside the record. |
|
|
30
30
|
| `child_match_field` | string | | FK column on child_entity pointing at the source row's id. |
|
|
@@ -8,12 +8,18 @@ audience: tenant
|
|
|
8
8
|
## Scaffold
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
erp plugin create example-plugin --name "Office Equipment" --type business-
|
|
11
|
+
erp plugin create example-plugin --name "Office Equipment" --type business-application
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
> The folder is `example-plugin/` to match this docs tree. The plugin **id** we
|
|
15
15
|
> use is `office-equipment` — set below. (In real life pick one name and use it
|
|
16
16
|
> for both.)
|
|
17
|
+
>
|
|
18
|
+
> The `--type` value is written into `plugin.json` verbatim, with no
|
|
19
|
+
> validation against what the rest of the platform actually uses — pass
|
|
20
|
+
> `business-application` exactly (not `business-app` or anything else), since
|
|
21
|
+
> that's the real convention every shipped plugin (`hcm-foundation`,
|
|
22
|
+
> `crm-foundation`, ...) uses.
|
|
17
23
|
|
|
18
24
|
You get `example-plugin/spk-assembly/` with empty `metadata/*` folders and a
|
|
19
25
|
`plugin.json` with `"mainClass": null`.
|
|
@@ -78,12 +78,35 @@ numbers) before the write, so a config targeting a `boolean`/`integer`/`numeric`
|
|
|
78
78
|
`set_field` is fully supported. An earlier draft of this tutorial hit a SQL type
|
|
79
79
|
error doing exactly that — that platform bug is fixed.
|
|
80
80
|
|
|
81
|
-
## 3.
|
|
81
|
+
## 3. Ship the rule that registers the job
|
|
82
|
+
|
|
83
|
+
Unlike this doc's own first draft claimed, **the platform does not ship this
|
|
84
|
+
rule for you** — every plugin using a shared sweep-style entity
|
|
85
|
+
(`entity_status_date_sweep_config`, `entity_aggregation_config`,
|
|
86
|
+
`entity_compliance_config`, ...) ships its own copy of the one small
|
|
87
|
+
`AFTER_CREATE` rule that registers the corresponding job, the same way
|
|
88
|
+
`hcm-assets` ships its own `entity_aggregation_config_register.json` /
|
|
89
|
+
`entity_compliance_config_register.json`. Skip this file and your config rows
|
|
90
|
+
sit in the table forever with no job ever scheduled to read them — confirmed
|
|
91
|
+
live (2026-09-22): a tenant with pre-existing `entity_status_date_sweep_config`
|
|
92
|
+
rows from another plugin still had no `engine-entity.status-date-sweep` job at
|
|
93
|
+
all until this rule shipped.
|
|
94
|
+
|
|
95
|
+
`spk-assembly/metadata/rules/entity_status_date_sweep_config_register.json`
|
|
96
|
+
([real file](./example-plugin/spk-assembly/metadata/rules/entity_status_date_sweep_config_register.json)):
|
|
82
97
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"entityType": "entity_status_date_sweep_config",
|
|
101
|
+
"name": "ensure_status_date_sweep_job_registered",
|
|
102
|
+
"description": "Register the generic EntityStatusDateSweepJob for this tenant on first config row.",
|
|
103
|
+
"triggerEvent": "AFTER_CREATE",
|
|
104
|
+
"conditions": null,
|
|
105
|
+
"actions": "[{\"type\": \"EXECUTE_SERVICE\", \"service\": \"ensureEntityStatusDateSweepJobRegistered\"}]",
|
|
106
|
+
"priority": 10,
|
|
107
|
+
"active": true
|
|
108
|
+
}
|
|
109
|
+
```
|
|
87
110
|
|
|
88
111
|
## Verify
|
|
89
112
|
|
|
@@ -44,7 +44,7 @@ erp plugin test example-plugin/spk-assembly
|
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
```
|
|
47
|
-
|
|
47
|
+
OK — 3 page(s) valid
|
|
48
48
|
|
|
49
49
|
Plugin Tests
|
|
50
50
|
────────────────────────────────────────
|
|
@@ -65,24 +65,24 @@ node developer-docs/examples/test-examples.mjs
|
|
|
65
65
|
## Build
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
erp plugin build example-plugin/spk-assembly -o example-plugin/office-equipment-1.0.
|
|
68
|
+
erp plugin build example-plugin/spk-assembly -o example-plugin/office-equipment-1.0.4.spk
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
```
|
|
72
72
|
validated 3 page(s) — clean
|
|
73
|
-
packaged
|
|
73
|
+
packaged 32 files -> example-plugin/office-equipment-1.0.4.spk (230832 bytes)
|
|
74
74
|
sha256: ...
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
## Publish
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
|
-
erp plugin publish example-plugin/office-equipment-1.0.
|
|
80
|
+
erp plugin publish example-plugin/office-equipment-1.0.4.spk --tenant 2
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
```
|
|
84
84
|
installed:
|
|
85
|
-
{"pluginId":"office-equipment","version":"1.0.
|
|
85
|
+
{"pluginId":"office-equipment","version":"1.0.4","state":"installed","pf4jState":"STARTED", ...}
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
## Confirm it's all live
|
package/bundle/manifest.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"bundleVersion": "2026-09-
|
|
2
|
+
"bundleVersion": "2026-09-23.1",
|
|
3
3
|
"platformVersion": "0.0.0",
|
|
4
|
-
"generatedAt": "2026-09-
|
|
4
|
+
"generatedAt": "2026-09-23T08:53:09.048Z",
|
|
5
5
|
"generatedBy": "erp bundle build (tools/erp-cli/erp.mjs bundleBuildCommand)",
|
|
6
6
|
"schemaCount": 27,
|
|
7
|
-
"docCount":
|
|
7
|
+
"docCount": 249,
|
|
8
8
|
"exampleFileCount": 14,
|
|
9
9
|
"blockCount": 124,
|
|
10
|
-
"catalogGeneratedAt": "2026-09-
|
|
10
|
+
"catalogGeneratedAt": "2026-09-23T08:53:08.093Z",
|
|
11
11
|
"catalogEngineCount": 43,
|
|
12
12
|
"catalogContractUnitCount": 74,
|
|
13
13
|
"sdkMode": "packaged"
|
|
@@ -285,6 +285,19 @@
|
|
|
285
285
|
"isSessionExpiredPage": { "type": "boolean" },
|
|
286
286
|
"isAccessDeniedPage": { "type": "boolean" },
|
|
287
287
|
"devicePersistence": { "type": "array", "items": { "$ref": "#/$defs/devicePersistenceRule" } },
|
|
288
|
+
"events": {
|
|
289
|
+
"type": "array",
|
|
290
|
+
"items": {
|
|
291
|
+
"type": "object",
|
|
292
|
+
"properties": {
|
|
293
|
+
"hook": { "const": "onLoad" },
|
|
294
|
+
"actions": { "type": "array", "items": { "type": "object" }, "description": "Frozen @erp/action-engine ActionDefinition[] - validated against the live ActionRegistry at mount time, same as a block item's own events." }
|
|
295
|
+
},
|
|
296
|
+
"required": ["hook", "actions"],
|
|
297
|
+
"additionalProperties": false
|
|
298
|
+
},
|
|
299
|
+
"description": "Page-level lifecycle hooks - today only \"onLoad\", fired once after the page mounts."
|
|
300
|
+
},
|
|
288
301
|
"modules": { "type": "array", "items": { "type": "string" }, "description": "App-module ids this page belongs to (e.g. \"hcm-foundation-home\") - TenantPageHost's per-module page listing only surfaces a page if it's present here. Load-bearing in practice for any page reached via the app sidenav/module shell, even on pages authored before this was documented." }
|
|
289
302
|
},
|
|
290
303
|
"required": ["contractVersion", "id", "version", "publisher", "title", "rows", "route", "designer"],
|
|
@@ -42,6 +42,19 @@
|
|
|
42
42
|
"entrypointExport": { "type": "string" }
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
|
+
"publicApis": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"description": "Added 2026-09-16 (direct user instruction: \"ideally during plugin installation - plugin should tell what are the apis are what is their behaviour\"). Gateway-public API endpoints this plugin declares — installed into the real, admin-editable gateway_public_read_path table at install/upgrade time (PluginPublicApiInstaller in engine-plugin) instead of a human hand-authoring a platform migration or a gateway Java-source change. Absent/empty (every manifest that predates this field, and the overwhelming majority of plugins going forward) means this plugin declares no public APIs — its whole surface stays tenant/session-gated, same as before this field existed.",
|
|
48
|
+
"items": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": ["pathPattern"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"pathPattern": { "type": "string", "description": "Ant-style path, e.g. \"/api/v1/my-plugin/webhook\"." },
|
|
53
|
+
"category": { "type": "string", "enum": ["PUBLIC_READ", "NO_AUTH"], "default": "PUBLIC_READ", "description": "PUBLIC_READ (GET/HEAD only, gateway injects the platform tenant's own id, never trusted from the client) or NO_AUTH (any verb, no tenant context injected at all — genuinely unauthenticated). Defaults to PUBLIC_READ, the narrower/safer of the two, when absent." },
|
|
54
|
+
"description": { "type": "string", "description": "Why this endpoint must be reachable before a tenant/session exists — shown to a platform admin reviewing what a plugin is asking to expose." }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
45
58
|
"roles": {
|
|
46
59
|
"type": "array",
|
|
47
60
|
"items": {
|