@dforge-core/dforge-mcp 0.2.33 → 0.2.34

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.
@@ -10,7 +10,6 @@
10
10
  "$defs": {
11
11
  "setting": {
12
12
  "type": "object",
13
- "required": ["fieldTypeCd"],
14
13
  "properties": {
15
14
  "fieldTypeCd": {
16
15
  "type": "string",
@@ -47,8 +46,32 @@
47
46
  "params": {
48
47
  "type": "object",
49
48
  "description": "Field-type-specific parameters. Examples: dropdown → { options: [...] }; number → { min, max }; lookup → { link: { entity, otherKey } }."
49
+ },
50
+ "domain": {
51
+ "type": "string",
52
+ "description": "Reference to a reusable column domain. A bare 'domain_cd' means this module's own domain, then a tenant-created one, and never another module's, even one with the same code. 'module_cd.domain_cd' names a domain of any installed module; declare that module as a dependency. The domain supplies fieldTypeCd, baseDatatypeCd and params; declaring any of those alongside 'domain' is rejected at install. The setting keeps its own label, defaultValue, formula and required flag."
50
53
  }
51
54
  },
55
+ "oneOf": [
56
+ {
57
+ "title": "Domain-backed setting",
58
+ "description": "Setting whose control and option list come from a reusable column domain. Requires 'domain' and must NOT restate what the domain owns.",
59
+ "required": ["domain"],
60
+ "properties": {
61
+ "fieldTypeCd": false,
62
+ "baseDatatypeCd": false,
63
+ "params": false
64
+ }
65
+ },
66
+ {
67
+ "title": "Inline setting",
68
+ "description": "Setting that declares its own control and parameters. Requires 'fieldTypeCd'.",
69
+ "required": ["fieldTypeCd"],
70
+ "properties": {
71
+ "domain": false
72
+ }
73
+ }
74
+ ],
52
75
  "additionalProperties": false
53
76
  }
54
77
  }
@@ -79,9 +79,34 @@
79
79
  "serverDefault": { "$ref": "#/$defs/serverDefault" }
80
80
  },
81
81
  "additionalProperties": true
82
+ },
83
+ "domain": {
84
+ "type": "string",
85
+ "description": "Reference to a reusable column domain. A bare 'domain_cd' resolves in the module of the entity taking the trait, then tenant-created domains, so one generic trait gives each module its own list; 'module_cd.domain_cd' pins one module's domain for every entity. A module's own traits.json must qualify a domain the module itself defines: written bare, it would bind to a same-named domain of whichever module takes the trait, and pack rejects it. The domain supplies the type, control, sizing and options."
82
86
  }
83
87
  },
84
- "additionalProperties": false
88
+ "additionalProperties": false,
89
+ "oneOf": [
90
+ {
91
+ "title": "Domain-backed trait field",
92
+ "description": "Defers to a column domain. Must not restate what the domain owns, and takes no 'link' — it is a plain scalar column.",
93
+ "required": ["domain"],
94
+ "properties": {
95
+ "link": false,
96
+ "dbDatatype": false,
97
+ "fieldTypeCd": false,
98
+ "maxLen": false,
99
+ "params": false
100
+ }
101
+ },
102
+ {
103
+ "title": "Inline trait field",
104
+ "description": "Declares its own type and control, as trait fields always have.",
105
+ "properties": {
106
+ "domain": false
107
+ }
108
+ }
109
+ ]
85
110
  },
86
111
  "serverDefault": {
87
112
  "type": "object",
@@ -42,8 +42,7 @@ my_module/
42
42
  │ ├── menus.json
43
43
  │ ├── actions.json
44
44
  │ ├── folders.json
45
- │ └── reports/
46
- │ └── <report>.json
45
+ │ └── reports.json
47
46
  ├── security/
48
47
  │ └── roles.json
49
48
  ├── settings.json
@@ -54,6 +53,7 @@ my_module/
54
53
  │ └── de-DE.json # IETF tags, one file per non-English locale
55
54
  ├── print_templates/
56
55
  │ └── <template>.scriban
56
+ ├── migrations/ # upgrade data scripts, <version>.sql
57
57
  ├── files/ # static assets
58
58
  └── webhooks.json # optional
59
59
  ```
@@ -144,7 +144,8 @@ Both use **semver** (`MAJOR.MINOR.PATCH`).
144
144
  - If you added a new entity or column → bump both `version` and `dbSchemaVersion`.
145
145
  - If you changed a view or menu → bump `version` only.
146
146
  - If you changed translations only → bump `version` only.
147
- - The installer uses `dbSchemaVersion` to decide whether to run schema migrations. Wrong version = skipped migrations.
147
+ - The installer uses `dbSchemaVersion` to decide whether to apply schema changes. Wrong version = skipped DDL.
148
+ - Data a new version reshapes (backfill, move rows, drop a table it no longer declares) goes in `migrations/<version>.sql`, named by `version`, run once on an upgrade from below it. See `manifest.md` § Upgrade migrations.
148
149
  - **Never ship a package without bumping at least `version`** from the previous release. The installer may reject or silently skip a re-install with the same version.
149
150
  - For brand-new modules, start at `version: "0.1.0"` and `dbSchemaVersion: "0.0.1"`.
150
151
 
@@ -111,7 +111,7 @@ Dependencies are a map of module codes to semver ranges. The installer checks th
111
111
 
112
112
  **Declare a dependency only on a module you actually consume something from** — an entity you reference with a link column, extend, grant rights on, or read in a view, report, formula or action. Every entry you write here obliges you to write a matching `deps/<module>.json` contract (below), so a speculative dependency is not free.
113
113
 
114
- **Do not depend on `admin`.** It — like `metadata` and `workspace` — is a system module, provisioned into every tenant before any other module installs. Depending on it buys nothing. The one legitimate reason to name a system module is to require a **minimum platform version** for a feature you use, e.g. `"metadata": ">=1.5.0"` for record-report attachments; that still needs a contract naming the entity the feature introduced.
114
+ **Do not depend on `admin`.** It — like `metadata`, `workspace` and `sys-billing` — is a system module, provisioned into every tenant before any other module installs. Depending on it buys nothing. The one legitimate reason to name a system module is to require a **minimum platform version** for a feature you use, e.g. `"metadata": ">=1.5.0"` for record-report attachments. Its contract names the entity the feature introduced, or declares `"entities": {}` when the feature introduced none (`"admin": ">=1.21.0"` for upgrade migrations).
115
115
 
116
116
  **For bridge modules** (`crm-fin`, `wms-fin`, etc.), depend on both sides:
117
117
 
@@ -143,7 +143,7 @@ Every entry in `dependencies` needs a contract file at `deps/<module_code>.json`
143
143
  ```
144
144
 
145
145
  - `version` must be the same range as the manifest's dependency entry.
146
- - `entities` needs at least one entry; each needs a `pk` and a non-empty `use`.
146
+ - `entities` needs at least one entry, except against a system module, where `{}` is a pure platform-version gate. Each entry needs a `pk` and a non-empty `use`.
147
147
  - `columns` is optional — omit it when you touch no specific column of that entity. Declare only real storage columns you read, write or join on, at their **storage class** (`int8`, `varchar`, `numeric`, `bool`, `date`, `timestamptz`, …; a `cuid` PK is `int8`). Never declare a virtual column (`R`/`S`/`F`) or a column your own extension adds.
148
148
  - `use` tokens are `<kind>:<symbol>` where kind is `ref`, `extends`, `formula`, `action`, `view`, `report`, `print` or `role`. `extends` must be fully qualified: `extends:parties.party`.
149
149
 
@@ -174,4 +174,22 @@ The extension file has `"extends": "fin.invoice"` inside. See MODULE_CONVENTIONS
174
174
 
175
175
  - Bump `version` on every release (bug fix, feature add, etc.).
176
176
  - Bump `dbSchemaVersion` **only** when the DB schema changes. If you added an action but no new columns, `version` goes up but `dbSchemaVersion` stays the same.
177
- - The installer uses `dbSchemaVersion` to decide whether to run migrations.
177
+ - The installer uses `dbSchemaVersion` to decide whether to apply schema changes. Upgrade **data** scripts under `migrations/` are keyed by `version` instead — see below.
178
+
179
+ ## Upgrade migrations (`migrations/`)
180
+
181
+ A table or column change needs no script: the installer generates the DDL. A script is for the DATA a new version reshapes — filling a new column from an old one, moving rows to a new table, dropping a table the new version no longer declares (the platform never drops one on its own).
182
+
183
+ ```
184
+ migrations/
185
+ └── 0.11.0.sql # runs on an upgrade from below 0.11.0 to 0.11.0 or later
186
+ ```
187
+
188
+ - **Name** — the module `version` the file upgrades to, `MAJOR.MINOR.PATCH.sql`. Keyed by `version`, not `dbSchemaVersion`. A misnamed file, or one newer than the manifest `version`, fails the package at load.
189
+ - **When** — on an upgrade only: every file above the installed version and no higher than the package's, in ascending order. A fresh install or a same-version reinstall runs none.
190
+ - **Where** — inside the install transaction, after the new tables and columns exist and before `NOT NULL` is applied or a register's `totals` cache is rebuilt. A table the new version removed is still there to read. A failing script rolls the upgrade back and the module stays on the old version; refuse unconvertible data with `RAISE EXCEPTION '…'`, which the operator sees.
191
+ - **Ids** — `"dForge".next_id()` mints a row id in SQL. Declare `"admin": ">=1.21.0"` in `dependencies`; that is also what guarantees a platform that runs migrations.
192
+ - **Locks** — a script writing a posted document or its register journal wraps the writes in `SELECT set_config('registry.posting', 'true', true);` … `'false'`, so the post locks stand aside. Period locks do not; reopen a closed period and close it again.
193
+ - A `kind: "pack"` module cannot carry migrations.
194
+
195
+ Reference: `modules/gl/migrations/0.11.0.sql` in dForge-core.
@@ -7,11 +7,14 @@ SP files: `logic/reports/rpt_*.sql`
7
7
 
8
8
  ## Structure
9
9
 
10
- A report has `description`, a `datasets` map, a `layout` object (`{ panels: [...] }`), optional per-dataset `params`, and an optional `entities` array (record-report attachments). Panels reference datasets by code via `datasetCd`.
10
+ A report has a `label`, a `description`, a `datasets` map, a `layout` object (`{ panels: [...] }`), optional per-dataset `params`, and an optional `entities` array (record-report attachments). Panels reference datasets by code via `datasetCd`.
11
+
12
+ **Name it with `label`, describe it with `description`.** `label` is the report's name: page title, breadcrumb and parameter dialog. `description` says what it shows and appears under the label, as a data view's does. Without a `label` the description becomes the name, so a sentence ends up as the page title. `dforge_module_validate`, `module pack` and install warn about it; install does not refuse it. Translate the name as `reports.<cd>.label`.
11
13
 
12
14
  ```json
13
15
  {
14
16
  "sales_pipeline": {
17
+ "label": "Sales Pipeline",
15
18
  "description": "Open opportunities by stage",
16
19
  "datasets": {
17
20
  "pipeline": {
@@ -341,7 +344,8 @@ A report can be **attached to an entity** so it opens from a record — the way
341
344
 
342
345
  ```json
343
346
  "credit_check": {
344
- "description": "Customer Credit Check — limit against outstanding AR and open quotes",
347
+ "label": "Credit Check",
348
+ "description": "Credit limit against outstanding AR and open quotes",
345
349
  "entities": [
346
350
  { "entityCd": "parties.party", "params": { "customer_id": "party_id" }, "orderNum": 45 },
347
351
  { "entityCd": "crm.quote", "params": { "customer_id": "customer_id" }, "orderNum": 45 }
@@ -237,6 +237,10 @@ If none fire, skip specialized views for that entity.
237
237
  **3c. Reports** only when aggregation/grouping isn't covered by views — or when
238
238
  Phase 2a sent you here because the "action" only wanted to show a number.
239
239
 
240
+ Give each report a short `label` (its name: page title, breadcrumb, parameter
241
+ dialog) and a `description` of what it shows, which appears under the label.
242
+ Without a label the description becomes the title, and validate/pack warn.
243
+
240
244
  Params are **report-scoped**: declare them in the report-level `parameters`
241
245
  block, or as shorthand under `datasets.<cd>.params` when exactly one dataset uses
242
246
  the param. The installer merges both into the report's one param set, report level
@@ -101,6 +101,11 @@ role / trigger / job counts. Then confirm both version strings with the user:
101
101
  - **`version`** — always bump (semver) before packing.
102
102
  - **`dbSchemaVersion`** — bump only if entity fields were added, removed, or
103
103
  type-changed since the last install.
104
+ - **Data on upgrade** — if the new version moves or reshapes existing rows, or
105
+ removes an entity whose table must go, ship `migrations/<version>.sql` named by
106
+ the new `version` and declare `"admin": ">=1.21.0"`. The installer generates the
107
+ DDL; it never moves data or drops a table on its own. See
108
+ `dforge://docs/conventions` § Upgrade Migrations.
104
109
 
105
110
  Get explicit confirmation on both before packing. This version confirmation
106
111
  applies only to the initial pack. During the install-fix loop, re-pack without