@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,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Employee Profile
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Employee Profile (HCM)
|
|
7
|
+
|
|
8
|
+
This page documents the shipped **Employee Profile** screen in the HCM
|
|
9
|
+
Employee module (`hcm-employee`), for the people who use it day to day — HR
|
|
10
|
+
admins and anyone with access to an employee record — not for plugin
|
|
11
|
+
developers. If you're building or extending this screen yourself, it's an
|
|
12
|
+
ordinary JSON-first page (`spk-assembly/metadata/page/employee-profile.json`)
|
|
13
|
+
like any other; see [Build a page](../../guides/build-a-page.md) for that.
|
|
14
|
+
|
|
15
|
+
## Getting there
|
|
16
|
+
|
|
17
|
+
Open it from the **Employees** list (or Active Employees / Consultants /
|
|
18
|
+
Contract Employees / Interns / Probation Employees / Remote Employees / Ex
|
|
19
|
+
Employees — the other roster views in the same module): click any row. The
|
|
20
|
+
URL is `/hcm-employee/employee-profile/<employeeId>` — it always shows one
|
|
21
|
+
employee's record, never a list.
|
|
22
|
+
|
|
23
|
+
## What the header shows
|
|
24
|
+
|
|
25
|
+
- An icon avatar (initials/icon only — there's no photo field on the
|
|
26
|
+
Employee entity, so no photo ever renders here).
|
|
27
|
+
- The employee's full name, plus a status chip: **Active** (green),
|
|
28
|
+
**Inactive** (default), or **Terminated** (red) — whichever one matches
|
|
29
|
+
the record's `employmentStatus`.
|
|
30
|
+
- Their current position/designation.
|
|
31
|
+
- Employee code, email, and phone, each with an icon.
|
|
32
|
+
- Three info chips: **Department**, **Reports To** (manager name), and
|
|
33
|
+
**Employment Type**.
|
|
34
|
+
- An **Edit Profile** button, top right (see below).
|
|
35
|
+
|
|
36
|
+
## Editing the record
|
|
37
|
+
|
|
38
|
+
**Edit Profile** opens a dialog pre-filled from the current record. It
|
|
39
|
+
covers: Full Name, Email, Phone, Employment Status, Employment Type,
|
|
40
|
+
Department, Position, Reporting Manager, Date of Birth, Gender, Marital
|
|
41
|
+
Status, Nationality, Languages, and Blood Group — plus an optional
|
|
42
|
+
**Apply Change On** date, for scheduling when an edit should take effect
|
|
43
|
+
rather than applying it immediately. Full Name and Email are required;
|
|
44
|
+
Save is blocked until both are filled in.
|
|
45
|
+
|
|
46
|
+
## The tabs
|
|
47
|
+
|
|
48
|
+
The profile is organized into tabs across the top. What's real and backed by
|
|
49
|
+
live data, and what's disclosed as not yet built, differs by tab — this
|
|
50
|
+
section says which is which plainly, because the page itself does too (see
|
|
51
|
+
the on-page disclosure notes quoted below).
|
|
52
|
+
|
|
53
|
+
### Overview
|
|
54
|
+
|
|
55
|
+
The landing tab. A grid of cards:
|
|
56
|
+
|
|
57
|
+
- **About** — date of birth, gender, marital status, with a "View more" that
|
|
58
|
+
expands to also show nationality, languages, and blood group.
|
|
59
|
+
- **Job Information** — employee ID, joining date, department, designation,
|
|
60
|
+
reports-to, grade. (Work Location is deliberately not shown here — see
|
|
61
|
+
[Known gaps](#known-gaps).)
|
|
62
|
+
- **Quick Info** — tenant-defined custom fields for the employee, via the
|
|
63
|
+
platform's generic Custom Field Engine (`entityType: "employee"`). This is
|
|
64
|
+
how country- or tenant-specific identifiers (e.g. statutory IDs) show up
|
|
65
|
+
without being hardcoded into this module — an admin adds them as custom
|
|
66
|
+
field definitions, not code changes.
|
|
67
|
+
- **Reporting Structure** — the employee's direct reports, pulled live from
|
|
68
|
+
the org chart.
|
|
69
|
+
- **Recent Activities** — a real, employee-scoped feed from the platform's
|
|
70
|
+
audit trail.
|
|
71
|
+
- **Skills & Expertise** — a real list of skills, each with a proficiency
|
|
72
|
+
level (Beginner / Intermediate / Advanced / Expert). An **Add Skill**
|
|
73
|
+
control lets you add a new skill name + level to the record.
|
|
74
|
+
|
|
75
|
+
### Employment / Personal
|
|
76
|
+
|
|
77
|
+
Read-oriented detail tabs breaking the same employment and personal-info
|
|
78
|
+
fields (employment type/status, department, grade, work location, hire
|
|
79
|
+
date, reports-to, designation / date of birth, gender, marital status,
|
|
80
|
+
nationality, languages, blood group) into their own focused views, separate
|
|
81
|
+
from the Overview summary cards.
|
|
82
|
+
|
|
83
|
+
### Documents
|
|
84
|
+
|
|
85
|
+
Real: lists documents actually attached to the employee record via the
|
|
86
|
+
platform's File Management module, plus their verification status. There's
|
|
87
|
+
no upload control on this page yet — the page's own description flags that
|
|
88
|
+
as a known follow-up (needs a file-upload source wired into the page
|
|
89
|
+
runtime). A **Request Document Verification** action opens a dialog
|
|
90
|
+
(document ID + category: Identity Proof, Address Proof, Education
|
|
91
|
+
Certificate, Employment Contract, Work Authorization, or Other) that routes
|
|
92
|
+
into the platform's approval workflow.
|
|
93
|
+
|
|
94
|
+
### Compensation
|
|
95
|
+
|
|
96
|
+
Real: shows the employee's current compensation (base salary, currency, pay
|
|
97
|
+
frequency, effective date, status) and their compensation history. A
|
|
98
|
+
**Request Compensation Change** action opens a dialog (base salary,
|
|
99
|
+
currency, pay frequency, effective-from date) that also routes into an
|
|
100
|
+
approval workflow rather than writing the change directly.
|
|
101
|
+
|
|
102
|
+
### Attendance, Leave, Payroll, Performance, Assets, Notes
|
|
103
|
+
|
|
104
|
+
These six tabs are shown with representative sample content, not live data
|
|
105
|
+
— there is no Attendance, Leave, Payroll, Performance, or Assets module
|
|
106
|
+
installed for this platform yet, and Notes has no backend of its own. This
|
|
107
|
+
is a disclosed, deliberate choice the page makes explicit on-screen (small
|
|
108
|
+
disclosure text), not a silent fabrication. For example, the page's own
|
|
109
|
+
copy says: *"Attendance is not yet tracked for this tenant — no Attendance
|
|
110
|
+
module is installed. Not fabricated."* and *"Leave tracking is not yet
|
|
111
|
+
available for this tenant — no Leave module is installed. Balances shown at
|
|
112
|
+
0, not fabricated."* Treat anything on these six tabs as a preview of the
|
|
113
|
+
intended shape, not real employee data.
|
|
114
|
+
|
|
115
|
+
### Requests
|
|
116
|
+
|
|
117
|
+
A small summary tab: how many change requests are on file for this
|
|
118
|
+
employee record (i.e. pending/past edits routed through workflow, such as
|
|
119
|
+
the compensation and document-verification requests above), with a link to
|
|
120
|
+
view them.
|
|
121
|
+
|
|
122
|
+
## Known gaps
|
|
123
|
+
|
|
124
|
+
- **No photo support.** The header always shows an icon, never an uploaded
|
|
125
|
+
photo — there is no photo field on the Employee entity.
|
|
126
|
+
- **Work Location isn't shown**, even though it's a real field elsewhere in
|
|
127
|
+
HCM. Every read on this page goes through a Data Service (a platform-wide
|
|
128
|
+
rule — no page action calls a raw REST endpoint directly), and Work
|
|
129
|
+
Location would need a bespoke recursive-ancestor lookup that isn't
|
|
130
|
+
expressible as a flat Data View today. Rather than fetch it with a raw
|
|
131
|
+
call and break that rule, the field is left off this page entirely — a
|
|
132
|
+
disclosed gap, not a silent downgrade.
|
|
133
|
+
- **Document upload isn't wired into this page yet** — Documents is
|
|
134
|
+
read-only (list + verification status + a verification *request*), not a
|
|
135
|
+
place to attach a new file. That needs a `FileUploadSource` wired into the
|
|
136
|
+
page runtime.
|
|
137
|
+
- **Attendance / Leave / Payroll / Performance / Assets / Notes have no real
|
|
138
|
+
backend.** See [above](#attendance-leave-payroll-performance-assets-notes).
|
|
139
|
+
|
|
140
|
+
## Related
|
|
141
|
+
|
|
142
|
+
- [Build a page](../../guides/build-a-page.md) — if you're extending this
|
|
143
|
+
page or building something like it.
|
|
144
|
+
- [How does a page get its data?](../../concepts/page-data-flow.md)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: What are the artifact types?
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# What are the artifact types?
|
|
7
|
+
|
|
8
|
+
A plugin is a folder of JSON files. Each file is an **artifact** of one type, and
|
|
9
|
+
each type lives in its own subfolder of `spk-assembly/metadata/`. This page is
|
|
10
|
+
the map: what each one is, and which guide covers it.
|
|
11
|
+
|
|
12
|
+
## The manifest
|
|
13
|
+
|
|
14
|
+
| File | What it is |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `spk-assembly/plugin.json` | the manifest — id, version, `mainClass`, `schemaName`, roles, dependencies. [Create a plugin](../guides/create-a-plugin.md) |
|
|
17
|
+
|
|
18
|
+
## Data
|
|
19
|
+
|
|
20
|
+
| Folder | Type | One-line description | Guide |
|
|
21
|
+
| --- | --- | --- | --- |
|
|
22
|
+
| `metadata/entities/` | **entity** | a table you declare by its fields; the Entity Engine generates DDL + CRUD REST | [Add an entity](../guides/add-an-entity.md) |
|
|
23
|
+
| `metadata/provider/` | **data provider** | a name → REST base path mapping; a page's `metadata.dataSource` points here | [Add a data provider](../guides/add-a-data-provider.md) |
|
|
24
|
+
| `metadata/data_view/` | **data view** | a declared read-only SQL join over physical tables | [Add a data provider](../guides/add-a-data-provider.md) |
|
|
25
|
+
| `metadata/data_service/` | **data service** | a parameterized query: `count` / `search` / `get` / `composite` | [Add a KPI](../guides/add-a-kpi.md) |
|
|
26
|
+
| `metadata/seed-data/` | **seed data** | rows to upsert on install, idempotently | [Seed data on install](../guides/seed-data-on-install.md) |
|
|
27
|
+
|
|
28
|
+
## Screens
|
|
29
|
+
|
|
30
|
+
| Folder | Type | One-line description | Guide |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| `metadata/page/` | **page** | one screen — rows/columns of blocks, plus page-level `metadata.dataSource` | [Build a page](../guides/build-a-page.md) |
|
|
33
|
+
| `metadata/menu/` | **menu** | a tree of navigation nodes pointing at routes | [Add menus](../guides/add-menus.md) |
|
|
34
|
+
| `metadata/i18n/` | **translations** | `en.json` etc. — dotted key → text | [Add i18n](../guides/add-i18n.md) |
|
|
35
|
+
| `metadata/application/` | **application** | (standalone plugins) the top-level product surface | [Tutorial ch.1](../tutorial/01-create-the-plugin.md) |
|
|
36
|
+
| `metadata/module/` | **module** | (standalone plugins) a functional area inside an application; pages attach here | [Tutorial ch.1](../tutorial/01-create-the-plugin.md) |
|
|
37
|
+
|
|
38
|
+
## Logic
|
|
39
|
+
|
|
40
|
+
| Folder | Type | One-line description | Guide |
|
|
41
|
+
| --- | --- | --- | --- |
|
|
42
|
+
| `metadata/rules/` | **entity rule** | declarative logic at a lifecycle point — reject / set value / execute service / start workflow | [Add business rules](../guides/add-business-rules.md) |
|
|
43
|
+
| `metadata/workflow/` | **workflow** | a multi-stage approval process | [Add an approval workflow](../guides/add-an-approval-workflow.md) |
|
|
44
|
+
|
|
45
|
+
## Code
|
|
46
|
+
|
|
47
|
+
| Path | What it is |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `spk-assembly/lib/*.jar` | **optional** — a compiled `Plugin` subclass and its deps, only if the plugin ships Java extensions. A pure-JSON plugin has no `lib/` and leaves `mainClass` `null`. [Create a plugin](../guides/create-a-plugin.md); for a complete real example pairing this with a custom React component, [Build a plugin with custom React + Java code](../guides/build-a-code-plugin.md) |
|
|
50
|
+
|
|
51
|
+
A custom block — a real React component of your own, no Java required — is
|
|
52
|
+
not a `spk-assembly/metadata/` file at all; it's registered at runtime
|
|
53
|
+
through `@erp/block-engine`/`@erp/block-adapter-mui`. See
|
|
54
|
+
[Add a custom block](../guides/add-a-custom-block.md).
|
|
55
|
+
|
|
56
|
+
## Scheduled jobs are not their own artifact type
|
|
57
|
+
|
|
58
|
+
A scheduled job is **not** a file you write. It's a row you *seed* into a shared
|
|
59
|
+
platform config entity (`entity_status_date_sweep_config`,
|
|
60
|
+
`entity_aggregation_config`, `entity_cadence_config`, `entity_compliance_config`)
|
|
61
|
+
plus an `AFTER_CREATE` register rule. The generic job engine does the rest. See
|
|
62
|
+
the [job guides](../guides/index.md#scheduled-jobs-no-java).
|
|
63
|
+
|
|
64
|
+
## The two file shapes
|
|
65
|
+
|
|
66
|
+
Most artifact files use a **wrapper**:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{ "name": "...", "description": "...", "definition": { /* the real contract */ }, "metadata": {}, "modules": [] }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Entities use `{ "entity": {...}, "fields": [...] }`. Pages use the wrapper with
|
|
73
|
+
`definition` holding a `contractVersion: 1` object. `erp schema pull <name>`
|
|
74
|
+
shows you the exact shape for each.
|
|
75
|
+
|
|
76
|
+
## What to read next
|
|
77
|
+
|
|
78
|
+
- [How does a plugin get installed?](./plugin-lifecycle.md)
|
|
79
|
+
- [How does a page get its data?](./page-data-flow.md)
|
|
80
|
+
- [Glossary](./glossary.md)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Glossary
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Glossary
|
|
7
|
+
|
|
8
|
+
Every term these docs use, defined once. Terms are cross-linked from the pages
|
|
9
|
+
that introduce them.
|
|
10
|
+
|
|
11
|
+
**action chain** — an ordered list of *actions* attached to a block *event*
|
|
12
|
+
(`mounted`, `clicked`, `committed`, `rowClicked`). Actions include `callApi`,
|
|
13
|
+
`setValue`, `showToast`, `downloadFile`, `navigate`.
|
|
14
|
+
|
|
15
|
+
**application** — the top-level product surface a user opens (e.g. "HCM"). A
|
|
16
|
+
standalone plugin ships its own in `metadata/application/`.
|
|
17
|
+
|
|
18
|
+
**artifact** — one JSON file of one type in `spk-assembly/metadata/`. See
|
|
19
|
+
[artifact types](./artifact-types.md).
|
|
20
|
+
|
|
21
|
+
**authoring bundle** — the offline grounding data (schemas, capability catalog,
|
|
22
|
+
blocks, examples, docs) the SDK reads. Refreshed with `erp env sync`. See
|
|
23
|
+
[SDK modes](./sdk-modes.md).
|
|
24
|
+
|
|
25
|
+
**binding** — a block property that reads from page state:
|
|
26
|
+
`{ "source": "binding", "binding": { "scope": "page", "key": "kpiTotal" } }`.
|
|
27
|
+
The other property form is `{ "source": "static", "value": ... }`.
|
|
28
|
+
|
|
29
|
+
**block** — a UI widget (`core.grid`, `core.button`, `core.kpi-card`,
|
|
30
|
+
`core.lookup`, …). The unit of a page's layout. Never called "widget" in
|
|
31
|
+
platform terminology, but they're the same thing. `erp blocks list`.
|
|
32
|
+
|
|
33
|
+
**composite (Data Service)** — a Data Service that fans out to several other
|
|
34
|
+
Data Services in one call. Response: `{ "results": { "<step>": <envelope> } }`.
|
|
35
|
+
|
|
36
|
+
**connected mode** — running the SDK as a package against a deployed ERP with no
|
|
37
|
+
platform source. See [SDK modes](./sdk-modes.md).
|
|
38
|
+
|
|
39
|
+
**data provider** — a `metadata/provider/*.json` file mapping a `name` to a REST
|
|
40
|
+
`basePath`. A page's `metadata.dataSource` names one.
|
|
41
|
+
|
|
42
|
+
**data service** — a `metadata/data_service/*.json` file: a parameterized query
|
|
43
|
+
(`count` / `search` / `get` / `composite`). Called at
|
|
44
|
+
`/api/v1/data-services/<name>/execute`.
|
|
45
|
+
|
|
46
|
+
**data view** — a `metadata/data_view/*.json` file: a declared read-only SQL
|
|
47
|
+
join over physical tables.
|
|
48
|
+
|
|
49
|
+
**day_offset** — a field on a status-date-sweep config. `0` = compare a date
|
|
50
|
+
against *now*; `N` = compare against *now + N days* (the "N days before"
|
|
51
|
+
reminder). Negative shifts earlier.
|
|
52
|
+
|
|
53
|
+
**entity** — a table you declare by its fields (`metadata/entities/*.json`). The
|
|
54
|
+
Entity Engine generates the DDL, CRUD REST, grid query, and single-record read.
|
|
55
|
+
|
|
56
|
+
**entity rule** — a `metadata/rules/*.json` file: declarative logic
|
|
57
|
+
(`REJECT` / `SET_VALUE` / `EXECUTE_SERVICE` / `START_WORKFLOW`) at a lifecycle
|
|
58
|
+
point (`BEFORE_CREATE`, `AFTER_UPDATE`, …).
|
|
59
|
+
|
|
60
|
+
**`event.new`** — in a `committed` action chain, the value the user just entered
|
|
61
|
+
or picked. **Not** `event.value`. For a `core.lookup`, it's the picked record's
|
|
62
|
+
id.
|
|
63
|
+
|
|
64
|
+
**`.spk`** — the built plugin package. A zip of `spk-assembly/`.
|
|
65
|
+
|
|
66
|
+
**flags** — a pre-stringified JSON field on an entity field definition. Holds
|
|
67
|
+
`{ "reference": { "entityType": "Employee" } }` (turns on `_label` resolution)
|
|
68
|
+
or `{ "formula": "<expression>" }` (computed field, no column).
|
|
69
|
+
|
|
70
|
+
**i18n key** — a dotted string (`office-equipment.equipment-register.title`)
|
|
71
|
+
standing in for user-visible text. Resolved from `metadata/i18n/en.json`.
|
|
72
|
+
|
|
73
|
+
**idempotent-additive** — the install behaviour for entities and seed data:
|
|
74
|
+
re-installing adds missing columns / upserts rows by key, never drops or
|
|
75
|
+
duplicates.
|
|
76
|
+
|
|
77
|
+
**KPI card** — `core.kpi-card`, a block that shows one number bound from page
|
|
78
|
+
state.
|
|
79
|
+
|
|
80
|
+
**mainClass** — the fully-qualified name of a PF4J `Plugin` subclass in
|
|
81
|
+
`spk-assembly/lib/*.jar`. Optional: a pure-JSON plugin leaves it `null` and the
|
|
82
|
+
platform loads it through a built-in generic no-op entry point. Set it only when
|
|
83
|
+
you ship your own Java extensions.
|
|
84
|
+
|
|
85
|
+
**MCP server** — `erp-plugin-language`, the tool server your AI agent calls for
|
|
86
|
+
schemas, blocks, examples, and validation. `erp mcp install`.
|
|
87
|
+
|
|
88
|
+
**metadata.dataSource** — the page-level string that wires a `core.grid` to a
|
|
89
|
+
data provider. Page-level, never on the grid block.
|
|
90
|
+
|
|
91
|
+
**module** — a functional area inside an application (e.g. "Leave Management").
|
|
92
|
+
Owns menu entries. Pages attach to a module via their `modules` array.
|
|
93
|
+
|
|
94
|
+
**page** — one screen (`metadata/page/*.json`): rows → columns → items (blocks),
|
|
95
|
+
plus page-level `metadata`.
|
|
96
|
+
|
|
97
|
+
**page state** — the runtime key/value bag a page reads with bindings and writes
|
|
98
|
+
with `setValue`. Keys are whatever you invent (`kpiTotal`, `formDialogOpen`).
|
|
99
|
+
|
|
100
|
+
**PF4J** — the Java plugin framework the platform uses to load a plugin. A
|
|
101
|
+
pure-JSON plugin is loaded through a built-in generic entry point; you never
|
|
102
|
+
touch PF4J directly.
|
|
103
|
+
|
|
104
|
+
**register rule** — an unconditional `AFTER_CREATE` `EXECUTE_SERVICE` rule on a
|
|
105
|
+
shared config entity that auto-registers a scheduled job on the first seeded
|
|
106
|
+
config row.
|
|
107
|
+
|
|
108
|
+
**role code** — the stable identifier for a role (`OFFICE_EQUIPMENT_MANAGER`).
|
|
109
|
+
Workflows and permissions reference roles by code, never by display name.
|
|
110
|
+
|
|
111
|
+
**schemaName** — the Postgres schema an entity's table routes to, from
|
|
112
|
+
`plugin.json`. The scaffold default is `erp_core`.
|
|
113
|
+
|
|
114
|
+
**seed data** — `metadata/seed-data/*.json`: rows upserted on install by
|
|
115
|
+
`keyFields`. Used for reference data and for shared job-config rows.
|
|
116
|
+
|
|
117
|
+
**status-date-sweep** — the generic scheduled job
|
|
118
|
+
(`engine-entity.status-date-sweep`) that flips a record's status when a date
|
|
119
|
+
column crosses now (± `day_offset`). Configured by seeding an
|
|
120
|
+
`entity_status_date_sweep_config` row.
|
|
121
|
+
|
|
122
|
+
**workflow** — a `metadata/workflow/*.json` file: stages, an approval task, and
|
|
123
|
+
`approvalPermissions`. Started by a `START_WORKFLOW` rule action; the decision is
|
|
124
|
+
written back by a generic callback controller.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Platform overview
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Platform overview
|
|
7
|
+
|
|
8
|
+
A plugin is a bundle of **JSON metadata** (and optionally a little Java) that the
|
|
9
|
+
platform's generic engines execute. You rarely write CRUD code — you declare
|
|
10
|
+
what you want and an engine does it.
|
|
11
|
+
|
|
12
|
+
## App → Module → Page
|
|
13
|
+
|
|
14
|
+
- **Application** — the top-level product surface a user opens (e.g. "HCM").
|
|
15
|
+
- **Module** — a functional area inside an app (e.g. "Leave Management"). A
|
|
16
|
+
module owns menu entries.
|
|
17
|
+
- **Page** — one screen. A `metadata/page/*.json` file: rows of **blocks**
|
|
18
|
+
(widgets), plus page-level `metadata` (including `dataSource` — the single
|
|
19
|
+
field that wires a grid to its data; it is *page*-level, never grid-level).
|
|
20
|
+
|
|
21
|
+
## Entities
|
|
22
|
+
|
|
23
|
+
An **entity** (`metadata/entities/*.json`, Entity Engine) is a table you declare
|
|
24
|
+
by its fields and relationships. The platform generates the DDL, the CRUD REST
|
|
25
|
+
endpoints, grid queries, and single-record reads — zero hand-written persistence
|
|
26
|
+
code. Mark a foreign-key field with `flags.reference.entityType` and reads
|
|
27
|
+
automatically gain a resolved `<field>_label` sibling (id → display name).
|
|
28
|
+
|
|
29
|
+
## Data providers, data views, data services
|
|
30
|
+
|
|
31
|
+
- **Data Provider** (`metadata/provider/*.json`) — names a REST base path a grid
|
|
32
|
+
or lookup reads from. A page's `metadata.dataSource` must match a provider's
|
|
33
|
+
`name` exactly.
|
|
34
|
+
- **Data View** (`metadata/data_view/*.json`) — a declared multi-table SQL join
|
|
35
|
+
over physical tables, read-only.
|
|
36
|
+
- **Data Service** (`metadata/data_service/*.json`) — a parameterized query
|
|
37
|
+
(filters, `search` params, composite steps) that a `core.lookup` or a report
|
|
38
|
+
calls server-side.
|
|
39
|
+
|
|
40
|
+
Prefer a Data View / Data Service over a bespoke REST endpoint for reads.
|
|
41
|
+
|
|
42
|
+
## Workflows
|
|
43
|
+
|
|
44
|
+
A **workflow** (`metadata/workflow/*.json`, Workflow Engine) is a multi-stage
|
|
45
|
+
approval process: `stagesJson`, `tasksJson`, `transitionsJson` (each a
|
|
46
|
+
JSON-encoded string) and a top-level `approvalPermissions` array that references
|
|
47
|
+
roles by stable `role.code`. Every approval-bearing state transition should be a
|
|
48
|
+
real workflow instance, not a permission-gated direct action.
|
|
49
|
+
|
|
50
|
+
## Rules
|
|
51
|
+
|
|
52
|
+
An **entity rule** (`metadata/rules/*.json`, Rule Engine) fires declarative
|
|
53
|
+
conditions/actions at lifecycle points (`BEFORE_CREATE`, `AFTER_UPDATE`, …) —
|
|
54
|
+
computed fields, cross-field validation, "on first row, register this job".
|
|
55
|
+
|
|
56
|
+
## Themes
|
|
57
|
+
|
|
58
|
+
Theming cascades: platform → application → **user**. A plugin ships theme tokens;
|
|
59
|
+
it never hardcodes colors in page JSON.
|
|
60
|
+
|
|
61
|
+
## Scheduled jobs
|
|
62
|
+
|
|
63
|
+
You almost never write a job class. The platform ships generic, JSON-configured
|
|
64
|
+
sweep engines:
|
|
65
|
+
|
|
66
|
+
- **status-date-sweep** — flip a field when a date column crosses now (± an
|
|
67
|
+
offset for "N days before" reminders).
|
|
68
|
+
- **aggregation-sweep** — roll up count/sum/avg/min/max into a summary entity.
|
|
69
|
+
- **cadence-generator** — create records on a schedule.
|
|
70
|
+
- **compliance-sweep** — evaluate a boolean expression per row and flag/escalate.
|
|
71
|
+
|
|
72
|
+
Each is one config row. See `erp examples patterns --kind jobs`.
|
|
73
|
+
|
|
74
|
+
## Install seeding
|
|
75
|
+
|
|
76
|
+
To make config rows (job configs, reference data) travel with the `.spk`
|
|
77
|
+
instead of a manual per-tenant POST, add `metadata/seed-data/*.json`:
|
|
78
|
+
`{ entity, keyFields, source, rows }`. The installer upserts them idempotently
|
|
79
|
+
on every install/upgrade. Schema: `erp schema pull plugin-seed-data`.
|
|
80
|
+
|
|
81
|
+
## Go deeper
|
|
82
|
+
|
|
83
|
+
- [What are the artifact types?](./artifact-types.md) — the full folder map
|
|
84
|
+
- [How does a plugin get installed?](./plugin-lifecycle.md) — build → upload → pipeline
|
|
85
|
+
- [How do the SDK modes work?](./sdk-modes.md) — monorepo vs connected
|
|
86
|
+
- [How does a page get its data?](./page-data-flow.md) — the three data paths
|
|
87
|
+
- [Glossary](./glossary.md)
|
|
88
|
+
- [The tutorial](../tutorial/index.md) builds all of this into one working module.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: How does a page get its data?
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How does a page get its data?
|
|
7
|
+
|
|
8
|
+
There are exactly three ways data reaches a page. Knowing which is which stops
|
|
9
|
+
the "0 rows, no error" confusion.
|
|
10
|
+
|
|
11
|
+
## 1. The grid — via `metadata.dataSource`
|
|
12
|
+
|
|
13
|
+
A `core.grid` block has **no** data configuration on it. It renders whatever the
|
|
14
|
+
page's top-level `metadata.dataSource` resolves to:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
page.metadata.dataSource ──▶ a metadata/provider/*.json whose `name` matches
|
|
18
|
+
──▶ that provider's basePath (e.g. /api/v1/entities/oeq_equipment/records)
|
|
19
|
+
──▶ the Entity Engine serves rows at basePath + /query
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The grid's `externalFilter` binding supplies query filters; `refreshTrigger`
|
|
23
|
+
re-runs the query. This is the *only* mechanism for `core.grid`. See
|
|
24
|
+
[Wire a page's data](../guides/wire-a-pages-data.md).
|
|
25
|
+
|
|
26
|
+
## 2. Page state — via action chains
|
|
27
|
+
|
|
28
|
+
Everything that isn't the grid — KPI numbers, a detail panel, a form's current
|
|
29
|
+
values, dialog open/closed flags — lives in **page state**, a bag of key/value
|
|
30
|
+
pairs. You:
|
|
31
|
+
|
|
32
|
+
- **write** it with `setValue` actions: `setValue page.kpiTotal = ${kpi.results.total.value}`
|
|
33
|
+
- **read** it with bindings: `{ "source": "binding", "binding": { "scope": "page", "key": "kpiTotal" } }`
|
|
34
|
+
|
|
35
|
+
An **action chain** is an ordered list of actions on a block event
|
|
36
|
+
(`mounted`, `clicked`, `committed`, `rowClicked`). The typical loader chain:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
mounted:
|
|
40
|
+
callApi POST /api/v1/data-services/oeq-equipment-kpis/execute → output "kpi"
|
|
41
|
+
setValue page.kpiTotal = ${kpi.results.total.value}
|
|
42
|
+
setValue page.kpiAvailable = ${kpi.results.available.value}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`${kpi....}` references the `output` name of an earlier `callApi` in the same
|
|
46
|
+
chain. `${event.new}` (in a `committed` handler) is the value the user entered.
|
|
47
|
+
`${event.id}` (in `rowClicked`) is the clicked row's id.
|
|
48
|
+
|
|
49
|
+
## 3. A lookup field — via `optionsSourceKey`
|
|
50
|
+
|
|
51
|
+
A `core.lookup` block calls a **`search` Data Service** named by its
|
|
52
|
+
`optionsSourceKey`, passing the user's keystrokes as the `search` parameter, and
|
|
53
|
+
expects back `{ value, label }` rows. Its `committed` event fires with
|
|
54
|
+
`event.new` = the picked record's id. See
|
|
55
|
+
[the lookup recipe](../recipes/related-record-lookup-field.md).
|
|
56
|
+
|
|
57
|
+
## Data Service response shapes (the part everyone gets wrong once)
|
|
58
|
+
|
|
59
|
+
| `operation` | `/execute` returns | bind |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| `count` | `{ "value": 12 }` | `${out.value}` |
|
|
62
|
+
| `search` / `get` | `{ "items": [...] }` (or `{ "records": [...] }` for a dataView source) | `${out.items}` |
|
|
63
|
+
| `composite` | `{ "results": { "total": { "value": 12 }, ... } }` | `${out.results.total.value}` |
|
|
64
|
+
|
|
65
|
+
A parameterized Data Service is called with args nested **one level deeper**:
|
|
66
|
+
`{ "params": { "parameters": { "search": "lap" } } }`.
|
|
67
|
+
|
|
68
|
+
## Putting it together — the tutorial's Equipment Register page
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
mount loader block → callApi oeq-equipment-kpis → setValue page.kpiTotal / kpiAvailable / kpiAssigned
|
|
72
|
+
KPI cards → bind page.kpiTotal / ...
|
|
73
|
+
search + status inputs → committed → setValue page.gridExternalFilter.search / .status
|
|
74
|
+
grid → page.metadata.dataSource = "oeq-equipment-provider"; externalFilter = page.gridExternalFilter
|
|
75
|
+
"New" button → setValue page.form* = null/defaults; setValue page.formDialogOpen = true
|
|
76
|
+
dialog inputs → committed → setValue page.form<Field> = ${event.new}
|
|
77
|
+
"Save" button → callApi POST /records; setValue page.formDialogOpen=false; setValue page.gridRefreshTick=${!page.gridRefreshTick}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## What to read next
|
|
81
|
+
|
|
82
|
+
- [Wire a page's data](../guides/wire-a-pages-data.md)
|
|
83
|
+
- [Add a create / edit form](../guides/add-a-create-edit-form.md)
|
|
84
|
+
- [Add a KPI or aggregation](../guides/add-a-kpi.md)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: How does a plugin get installed?
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How does a plugin get installed?
|
|
7
|
+
|
|
8
|
+
A mental model of what happens between `erp plugin publish` and your page being
|
|
9
|
+
live. Knowing this makes install errors readable.
|
|
10
|
+
|
|
11
|
+
## Build
|
|
12
|
+
|
|
13
|
+
`erp plugin build spk-assembly -o x.spk` zips `spk-assembly/` into a `.spk`
|
|
14
|
+
file. Nothing clever — it's a zip with `plugin.json` at the root, `metadata/`,
|
|
15
|
+
and `lib/`. It runs `erp plugin validate` first and refuses to package invalid
|
|
16
|
+
pages.
|
|
17
|
+
|
|
18
|
+
## Upload
|
|
19
|
+
|
|
20
|
+
`erp plugin publish x.spk --tenant N` POSTs the file to
|
|
21
|
+
`/api/v1/authoring/plugins/upload` with your `erp login` token. The server:
|
|
22
|
+
|
|
23
|
+
1. **recomputes the checksum** and compares — it never trusts the client digest.
|
|
24
|
+
2. **stores the artifact** at `system/plugins/<id>/<version>/<id>-<version>.spk`.
|
|
25
|
+
This key is **immutable** — re-storing different bytes under the same version
|
|
26
|
+
is refused.
|
|
27
|
+
3. **reads the manifest** — `id` and `version` must be present and non-blank.
|
|
28
|
+
`mainClass` is optional: a pure-JSON plugin leaves it `null` and the platform
|
|
29
|
+
loads it via a built-in generic no-op entry point.
|
|
30
|
+
|
|
31
|
+
## Install pipeline (`PluginInstallPipeline.installNew`)
|
|
32
|
+
|
|
33
|
+
In order, all-or-nothing:
|
|
34
|
+
|
|
35
|
+
| Stage | What it does | Common failure |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| **VALIDATING** | schema + block-registry validation; artifact persistence | immutable-artifact conflict; a page block property that doesn't exist |
|
|
38
|
+
| **LOADING** | PF4J loads `lib/*.jar` (or the built-in entry point for a pure-JSON plugin), calls the plugin's `start()` | a declared `mainClass` not found in the jar; **another version of the same plugin id already loaded** |
|
|
39
|
+
| entities | create / `ALTER TABLE ADD COLUMN` (idempotent-additive — never drops) | `label` not valid JSON; `required` without `nullable` |
|
|
40
|
+
| providers / views / services / pages / menus | upsert each artifact | route/module mismatch |
|
|
41
|
+
| rules / workflows | install | `stagesJson` authored as an object not a string |
|
|
42
|
+
| i18n / roles | install | **role `name` or `code` collides with another installed plugin** |
|
|
43
|
+
| seed data | `PluginDataSeedInstaller` upserts each `metadata/seed-data/*.json` by `keyFields` | entity not shipped; non-unique key |
|
|
44
|
+
| register rules fire | `AFTER_CREATE` rules on config entities call `ensure<Job>Registered` → your scheduled jobs get an `erp_job` row | (seed no config row → job never registers) |
|
|
45
|
+
|
|
46
|
+
If any stage throws, **everything rolls back**, including the PF4J load. The
|
|
47
|
+
error names the stage:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
plugin install failed at stage LOADING: ...
|
|
51
|
+
plugin install failed after code load, rolled back PF4J load: Role "..." already exists
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Upgrade
|
|
55
|
+
|
|
56
|
+
Publishing a higher version while the plugin is installed is an **upgrade**. The
|
|
57
|
+
platform's convergence loop unloads the old PF4J code, loads the new, and re-runs
|
|
58
|
+
the artifact/seed stages (all upserts, nothing dropped).
|
|
59
|
+
|
|
60
|
+
## Uninstall (and why not to, mid-development)
|
|
61
|
+
|
|
62
|
+
`DELETE /api/v1/authoring/plugins/<id>` marks the installation removed. The
|
|
63
|
+
**code** is unloaded asynchronously by a background convergence loop, not
|
|
64
|
+
immediately. If that unload stalls (it can, on a transient DB error), the old
|
|
65
|
+
PF4J code stays loaded, and the next `publish` — which uses the *install-new*
|
|
66
|
+
path — is refused with "already loaded plugin ... same id". Recovery is a backend
|
|
67
|
+
restart or a new plugin id.
|
|
68
|
+
|
|
69
|
+
**Practical rule: to iterate, bump the version and re-publish. Don't uninstall.**
|
|
70
|
+
|
|
71
|
+
## Verify an install
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
erp plugin list # your plugin: state "installed", pf4jState "STARTED"
|
|
75
|
+
erp api get "/api/v1/authoring/pages?module=<yourModule>" # pages registered
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## What to read next
|
|
79
|
+
|
|
80
|
+
- [Publish and upgrade a plugin](../guides/publish-and-upgrade.md)
|
|
81
|
+
- [Troubleshooting: publishing and install errors](../troubleshooting/publishing-errors.md)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: How do the SDK modes work (monorepo vs connected)?
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How do the SDK modes work (monorepo vs connected)?
|
|
7
|
+
|
|
8
|
+
The `erp` CLI and the `erp-plugin-language` MCP server both need **grounding
|
|
9
|
+
data** — the JSON Schemas, the catalog of platform capabilities, the block
|
|
10
|
+
registry, curated examples, these docs. Where that data comes from depends on how
|
|
11
|
+
the SDK is installed.
|
|
12
|
+
|
|
13
|
+
## The authoring bundle
|
|
14
|
+
|
|
15
|
+
The grounding data is packaged as an **authoring bundle**: a folder of
|
|
16
|
+
`schemas/`, a frozen `catalog.json`, `blocks.json`, `validators/`, `examples/`,
|
|
17
|
+
and the tenant-public docs. It is produced by `erp bundle build`, which is the
|
|
18
|
+
**only** step that reads platform source (it scans the Java for REST mappings and
|
|
19
|
+
runs esbuild on the validators). Clients never do that scan.
|
|
20
|
+
|
|
21
|
+
## Mode 1 — monorepo (platform developers)
|
|
22
|
+
|
|
23
|
+
You're working inside the ERP source tree. `erp bundle build` runs locally
|
|
24
|
+
against the live source; `erp` and the MCP server read the freshly built bundle.
|
|
25
|
+
Everything is always current. This is how these docs were written and how the
|
|
26
|
+
tutorial module was verified.
|
|
27
|
+
|
|
28
|
+
## Mode 2 — connected (you, a third-party developer)
|
|
29
|
+
|
|
30
|
+
You have the SDK as a package, and **no platform source**. Two data sources, in
|
|
31
|
+
priority order:
|
|
32
|
+
|
|
33
|
+
1. **`erp env sync`** downloads your ERP's own authoring bundle from
|
|
34
|
+
`{baseUrl}/api/v1/dev/bundle` into `~/.erp/cache/<env>/`. This is bundle data
|
|
35
|
+
generated *by your ERP*, so it matches exactly the version you're building
|
|
36
|
+
against — including the live list of entity and theme names in your tenant.
|
|
37
|
+
Re-run it after every ERP upgrade.
|
|
38
|
+
2. **the bundle shipped inside the SDK package** — a fallback used when your ERP
|
|
39
|
+
doesn't expose `/api/v1/dev/bundle` yet. It's a snapshot; it may lag your
|
|
40
|
+
ERP's actual version.
|
|
41
|
+
|
|
42
|
+
`erp env sync --check` tells you whether your cached bundle has drifted from what
|
|
43
|
+
the server would send.
|
|
44
|
+
|
|
45
|
+
## What each mode can and can't do offline
|
|
46
|
+
|
|
47
|
+
| Capability | monorepo | connected + synced | connected, offline fallback |
|
|
48
|
+
| --- | --- | --- | --- |
|
|
49
|
+
| pull schemas, list blocks, list examples | yes | yes | yes (may lag) |
|
|
50
|
+
| validate a page / lint semantics | yes | yes | yes |
|
|
51
|
+
| `erp api ...` against the live ERP | yes | yes | no (needs the server) |
|
|
52
|
+
| `erp logs tail` for a failed call's stack trace | yes | yes | no — use the ERP admin log view |
|
|
53
|
+
| know your tenant's real entity / theme names | yes | yes (from the sync) | no |
|
|
54
|
+
| publish a plugin | yes | yes | no |
|
|
55
|
+
|
|
56
|
+
## No Java toolchain required
|
|
57
|
+
|
|
58
|
+
A pure-JSON plugin (the default shape) needs **no JDK, no pf4j jars, no compiled
|
|
59
|
+
stub**. Leave `plugin.json`'s `mainClass` `null` and the platform loads the
|
|
60
|
+
plugin through a built-in generic entry point. You only reach for a Java
|
|
61
|
+
toolchain if a plugin ships its own `Plugin` subclass for behaviour the engines
|
|
62
|
+
can't express — see [Create a plugin](../guides/create-a-plugin.md), and for a
|
|
63
|
+
complete, real Java-plus-React example, [Build a plugin with custom React +
|
|
64
|
+
Java code](../guides/build-a-code-plugin.md). A custom React component on its
|
|
65
|
+
own, reading data through the generic query engine with no Java at all, is
|
|
66
|
+
[Add a custom block](../guides/add-a-custom-block.md) instead.
|
|
67
|
+
|
|
68
|
+
## What to read next
|
|
69
|
+
|
|
70
|
+
- [Set up the SDK](../guides/set-up-the-sdk.md)
|
|
71
|
+
- [Use the MCP server with an AI agent](../guides/use-the-mcp-server.md)
|