@dforge-core/dforge-mcp 0.2.32 → 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.
@@ -32,9 +32,9 @@
32
32
  },
33
33
  "views": {
34
34
  "type": "object",
35
- "description": "Data view code → label.",
35
+ "description": "Data view code → label and description (the subtitle under the view title).",
36
36
  "additionalProperties": {
37
- "$ref": "#/$defs/labelOnly"
37
+ "$ref": "#/$defs/labelDesc"
38
38
  }
39
39
  },
40
40
  "menus": {
@@ -60,8 +60,7 @@ correct: a data view describes the presentation, and the folder's entity view de
60
60
  what a user in that folder is allowed to see of it. The grid renders whichever
61
61
  columns survive the view — you do not maintain one data view per role.
62
62
 
63
- Entity views are also unrelated to the two other things called "view" here: a *data
64
- view* (`ui/data_views.json`) is a grid/kanban/calendar, and `isView` + `viewSql` on an
65
- entity means it is backed by a SQL view.
63
+ Entity views are also unrelated to the other thing called "view" here: a *data view*
64
+ (`ui/data_views.json`) is a grid/kanban/calendar.
66
65
 
67
66
  See `dforge://reference/security` → Column-level security for the full rules.
@@ -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
 
@@ -4,7 +4,7 @@ Data views define how users see and interact with entity data. One entity can ha
4
4
 
5
5
  Lives in: `ui/data_views.json`
6
6
 
7
- > **Not to be confused with entity views.** An *entity view* (`views` on an entity file) is column-level **security** — which columns a folder may see at all — and is what `folders.json`'s `viewName` binds. A *data view* is a presentation of an entity, referenced by a menu's `dataViewCode`. A third thing, `isView` + `viewSql` on an entity, means the entity is backed by a SQL view. See `security.md` → Column-level security.
7
+ > **Not to be confused with entity views.** An *entity view* (`views` on an entity file) is column-level **security** — which columns a folder may see at all — and is what `folders.json`'s `viewName` binds. A *data view* is a presentation of an entity, referenced by a menu's `dataViewCode`. See `security.md` → Column-level security.
8
8
 
9
9
  ## Structure
10
10
 
@@ -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 }
@@ -318,16 +318,17 @@ Each of these is an install-time error, and each is silent at runtime if it slip
318
318
 
319
319
  `"viewName": "default"` means **no view** (full column set) and declares nothing. It is what every shipped module writes, and what the runtime auto-creates for an entity reached without a folder binding — so it is exempt from the checks above. A module that really declares a view named `default` still gets it bound.
320
320
 
321
- ### Don't confuse the three "views"
321
+ ### Don't confuse the two "views"
322
322
 
323
323
  | Term | Where | What it is |
324
324
  |---|---|---|
325
325
  | **Entity view** | `entities/*.json` → `views` | Column-level security (this section) |
326
326
  | **Data view** | `ui/data_views.json` | A grid / kanban / calendar over an entity (`data-views.md`) |
327
- | **SQL view** | `entities/*.json` → `isView` + `viewSql` | An entity backed by a SQL view instead of a table |
328
327
 
329
328
  `viewName` in `folders.json` binds the first. Menus' `dataViewCode` references the second.
330
329
 
330
+ There is no SQL-view entity. `isView` / `viewSql` are rejected at validate, pack and install: a view entity would duplicate the source entity's metadata, break when that module changes a column, and read around its row and column security. For a read-only slice use a data view (nav paths, `F` columns), a `G` total, the Query Builder, or a stored-procedure report — the last is the one way to show users a slice of an entity they have no rights on, behind an explicit `sp:` grant.
331
+
331
332
  ### When to use subfolders
332
333
 
333
334
  - **Multi-location modules** (warehouses, offices, branches) — one subfolder per location, filtered by location FK
@@ -38,7 +38,7 @@ Translation files mirror the module's content structure. Each translatable objec
38
38
  "crm.sales-rep": { "label": "Sales Representative" }
39
39
  },
40
40
  "views": {
41
- "contacts": { "label": "Contacts" },
41
+ "contacts": { "label": "Contacts", "desc": "People associated with customers" },
42
42
  "contacts_list": { "label": "Contact List" },
43
43
  "contacts_gallery": { "label": "Contact Cards" }
44
44
  },
@@ -80,7 +80,7 @@ Translation files mirror the module's content structure. Each translatable objec
80
80
  | `entities` | Entity labels (`label`, `desc`) and all field labels | Entity codes → `{ label, desc, fields: { field_code: { label } } }` |
81
81
  | `folders` | Folder labels | Folder codes (root folder key from `ui/folders.json`) |
82
82
  | `roles` | Role display labels | Role codes (keys from `security/roles.json`, already module-qualified — e.g. `crm.admin`) → `{ label }` |
83
- | `views` | Data view labels | View codes (keys from `ui/data_views.json`) |
83
+ | `views` | Data view labels and descriptions (`label`, `desc`) | View codes (keys from `ui/data_views.json`) → `{ label, desc }` |
84
84
  | `menus` | Menu node labels (root + nested items) | Menu root key → `{ label, items: { item_code: { label } } }` matching `ui/menus.json` structure |
85
85
  | `actions` | Action labels, descriptions, param labels **and param dropdown option labels** | Action codes (keys from `ui/actions.json`) → `{ label, desc, params: { param_cd: { label, options: { value: … } } } }` |
86
86
  | `reports` | Report dataset captions, param labels **and param dropdown option labels** | Report codes → `{ datasets: { ds_cd: { caption } }, params: { param_cd: { label, options: { value: … } } } }` |
@@ -242,7 +242,7 @@ If you include these sections, the install succeeds (they're silently ignored)
242
242
  "crm.sales-rep": { "label": "Vertriebsmitarbeiter" }
243
243
  },
244
244
  "views": {
245
- "contacts": { "label": "Kontakte" }
245
+ "contacts": { "label": "Kontakte", "desc": "Personen, die mit Kunden verknüpft sind" }
246
246
  },
247
247
  "menus": {
248
248
  "crm_menu": {
@@ -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