@dforge-core/dforge-mcp 0.1.0-rc.12 → 0.1.0-rc.13
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/CHANGELOG.md +132 -0
- package/README.md +75 -21
- package/dist/server.js +2405 -272
- package/docs/creating-modules.md +7 -3
- package/package.json +11 -6
- package/resources/docs/conventions.md +12 -9
- package/resources/schemas/entity.schema.json +4 -0
- package/resources/schemas/reports.schema.json +4 -0
- package/skills/dforge-mcp-author/SKILL.md +220 -91
- package/skills/dforge-mcp-author/examples/simple-todo/README.md +38 -0
- package/skills/dforge-mcp-author/examples/simple-todo/entities/todo_item.json +83 -0
- package/skills/dforge-mcp-author/examples/simple-todo/entities/todo_list.json +43 -0
- package/skills/dforge-mcp-author/examples/simple-todo/logic/actions/mark_done.dsl +6 -0
- package/skills/dforge-mcp-author/examples/simple-todo/manifest.json +32 -0
- package/skills/dforge-mcp-author/examples/simple-todo/security/roles.json +10 -0
- package/skills/dforge-mcp-author/examples/simple-todo/seed-data/01-lists.json +17 -0
- package/skills/dforge-mcp-author/examples/simple-todo/ui/actions.json +11 -0
- package/skills/dforge-mcp-author/examples/simple-todo/ui/data_views.json +35 -0
- package/skills/dforge-mcp-author/examples/simple-todo/ui/menus.json +28 -0
- package/skills/dforge-mcp-author/references/action-dsl.md +397 -0
- package/skills/dforge-mcp-author/references/column-types.md +168 -0
- package/skills/dforge-mcp-author/references/conventions.md +177 -0
- package/skills/dforge-mcp-author/references/data-migration.md +270 -0
- package/skills/dforge-mcp-author/references/data-views.md +192 -0
- package/skills/dforge-mcp-author/references/excel-import.md +61 -0
- package/skills/dforge-mcp-author/references/field-types.md +144 -0
- package/skills/dforge-mcp-author/references/filters.md +326 -0
- package/skills/dforge-mcp-author/references/flags.md +73 -0
- package/skills/dforge-mcp-author/references/formulas.md +206 -0
- package/skills/dforge-mcp-author/references/jobs.md +149 -0
- package/skills/dforge-mcp-author/references/manifest.md +123 -0
- package/skills/dforge-mcp-author/references/menus.md +164 -0
- package/skills/dforge-mcp-author/references/number-sequences.md +117 -0
- package/skills/dforge-mcp-author/references/print-templates.md +159 -0
- package/skills/dforge-mcp-author/references/queries.md +312 -0
- package/skills/dforge-mcp-author/references/reports.md +398 -0
- package/skills/dforge-mcp-author/references/schema-import.md +331 -0
- package/skills/dforge-mcp-author/references/security.md +244 -0
- package/skills/dforge-mcp-author/references/settings.md +120 -0
- package/skills/dforge-mcp-author/references/traits.md +153 -0
- package/skills/dforge-mcp-author/references/translations.md +158 -0
- package/skills/dforge-mcp-author/references/validation-checklist.md +182 -0
- package/skills/dforge-mcp-author/scripts/xlsx_to_model.py +198 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Module Settings Reference
|
|
2
|
+
|
|
3
|
+
Module settings are **configurable values** scoped to a module. Tenant admins set them; module code reads them at runtime. Values inherit through the folder tree (child folder overrides parent, parent overrides module default).
|
|
4
|
+
|
|
5
|
+
Lives in: `settings.json` at the module root.
|
|
6
|
+
|
|
7
|
+
## Structure
|
|
8
|
+
|
|
9
|
+
Settings use the **same field type system as entity columns**. Each setting is like a single column with a default value.
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"company_name": {
|
|
14
|
+
"fieldTypeCd": "text",
|
|
15
|
+
"baseDatatypeCd": "string",
|
|
16
|
+
"description": "Company name displayed on documents",
|
|
17
|
+
"defaultValue": "Your Company Name"
|
|
18
|
+
},
|
|
19
|
+
"invoice_prefix": {
|
|
20
|
+
"fieldTypeCd": "text",
|
|
21
|
+
"baseDatatypeCd": "string",
|
|
22
|
+
"description": "Prefix for auto-generated invoice numbers",
|
|
23
|
+
"defaultValue": "INV-"
|
|
24
|
+
},
|
|
25
|
+
"default_currency": {
|
|
26
|
+
"fieldTypeCd": "dropdown",
|
|
27
|
+
"baseDatatypeCd": "string",
|
|
28
|
+
"description": "Default currency code for documents",
|
|
29
|
+
"defaultValue": "USD",
|
|
30
|
+
"params": {
|
|
31
|
+
"options": ["USD", "EUR", "GBP", "CHF"]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"allow_negative_stock": {
|
|
35
|
+
"fieldTypeCd": "checkbox",
|
|
36
|
+
"baseDatatypeCd": "bool",
|
|
37
|
+
"description": "Allow operations that result in negative stock",
|
|
38
|
+
"defaultValue": false
|
|
39
|
+
},
|
|
40
|
+
"company_logo": {
|
|
41
|
+
"fieldTypeCd": "image",
|
|
42
|
+
"description": "Shown on printed documents"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Reading settings
|
|
48
|
+
|
|
49
|
+
From formulas, reference settings with `$[setting_name]`:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
[total] * (1 + $[vat_rate] / 100)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
From DSL actions (check your dForge version for exact syntax):
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
execute:
|
|
59
|
+
rate = settings.vat_rate
|
|
60
|
+
tax = [total] * rate / 100
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
From number sequence patterns:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
"pattern": "$[invoice_prefix]-{yyyy}-{seq:4}"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Folder-scoped inheritance
|
|
70
|
+
|
|
71
|
+
Settings resolve at runtime through the folder hierarchy:
|
|
72
|
+
|
|
73
|
+
1. The folder the user is currently in → its override (if any)
|
|
74
|
+
2. Parent folder → override (if any)
|
|
75
|
+
3. ... up the tree ...
|
|
76
|
+
4. Module default (from `settings.json`)
|
|
77
|
+
|
|
78
|
+
This means different folders can have different VAT rates, different prefixes, different currencies — without any code changes.
|
|
79
|
+
|
|
80
|
+
## Field types supported in settings
|
|
81
|
+
|
|
82
|
+
Any field type that makes sense for a single scalar value:
|
|
83
|
+
|
|
84
|
+
- `text`, `textarea`, `richtext`, `code`
|
|
85
|
+
- `number`, `currency`, `percent`
|
|
86
|
+
- `checkbox`
|
|
87
|
+
- `date`, `datetime`, `time`
|
|
88
|
+
- `dropdown`, `flags`, `color`, `tags`
|
|
89
|
+
- `image`, `file`
|
|
90
|
+
- `json`
|
|
91
|
+
- `user` (if you want to store a reference to a specific user)
|
|
92
|
+
|
|
93
|
+
**Not supported** as settings:
|
|
94
|
+
|
|
95
|
+
- `lookup` (reference to an entity row) — use a `text` setting holding a code instead
|
|
96
|
+
- `grid` (set) — settings are single values
|
|
97
|
+
|
|
98
|
+
## Params
|
|
99
|
+
|
|
100
|
+
Settings support the same `params` as entity columns:
|
|
101
|
+
|
|
102
|
+
- `options` (for `dropdown` and `flags`)
|
|
103
|
+
- `min`, `max` (for `number`)
|
|
104
|
+
- `currency` (for `currency`)
|
|
105
|
+
- `maxLen` (for `text`)
|
|
106
|
+
|
|
107
|
+
## Declaring setting access in roles
|
|
108
|
+
|
|
109
|
+
By default, only the module/tenant admin can change settings. If you want specific roles to read or modify settings, grant appropriate rights on the settings objects in `security/roles.json` (check your dForge version for the exact naming).
|
|
110
|
+
|
|
111
|
+
## Common mistakes
|
|
112
|
+
|
|
113
|
+
- Forgetting to declare `defaultValue` (the key is `defaultValue`, not `default`) — missing setting values cause runtime errors when formulas reference them.
|
|
114
|
+
- Using `lookup` as a setting field type — unsupported.
|
|
115
|
+
- Hardcoding values that should be settings — makes the module non-configurable. Any string/number that might vary per deployment is a good candidate for a setting.
|
|
116
|
+
- Reading a setting that doesn't exist — returns null, which causes formula errors. Always provide a default.
|
|
117
|
+
|
|
118
|
+
## Reference
|
|
119
|
+
|
|
120
|
+
This file covers the module package format for settings. If you need details on the runtime resolution API (`settings.get`, `settings.list`, `settings.set`, `settings.clear`), ask the user to check their dForge version's settings documentation.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Entity Traits Reference
|
|
2
|
+
|
|
3
|
+
Traits are **reusable column bundles** that entities inherit. They eliminate boilerplate — instead of declaring PK columns, timestamps, and audit tracking on every entity, you declare `traits: ["identity", "audit"]` and dForge mixes them in automatically.
|
|
4
|
+
|
|
5
|
+
Declared on an entity:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"description": "Contacts",
|
|
10
|
+
"dbObject": "contact",
|
|
11
|
+
"toString": "{first_name} {last_name}",
|
|
12
|
+
"traits": ["identity", "audit"],
|
|
13
|
+
"fields": {
|
|
14
|
+
"first_name": { /* ... */ }
|
|
15
|
+
// No need to declare contact_id, created_date, last_updated, etc.
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Source of truth: `server/database/system-modules/metadata/traits.json`
|
|
21
|
+
|
|
22
|
+
## All traits
|
|
23
|
+
|
|
24
|
+
### `identity`
|
|
25
|
+
|
|
26
|
+
Adds the primary key column named `{entity}_id`.
|
|
27
|
+
|
|
28
|
+
- Column: `{dbObject}_id` (e.g. `contact_id` for `dbObject: "contact"`)
|
|
29
|
+
- `dbDatatype: "cuid"`, `flags: "I"`, `isPk: true`, `isIdentity: true`
|
|
30
|
+
- Auto-generated on insert
|
|
31
|
+
|
|
32
|
+
**FK columns pointing to an `identity`-trait entity must use `dbDatatype: "cuid"`.** Do not use `bigint`, `int`, `int8`, or `integer` — `cuid` is the platform's type name even though it is physically stored as int8. Using any other value causes an `ENTITY_COLUMN_IMPORT_ERROR` type-mismatch on install.
|
|
33
|
+
|
|
34
|
+
**Most entities should use `identity`.** Exceptions: junction tables with composite PKs, extension entities that share a PK with their parent, or entities using the `period` trait (which provides its own PK).
|
|
35
|
+
|
|
36
|
+
### `audit`
|
|
37
|
+
|
|
38
|
+
Adds **two timestamp columns** (no user tracking):
|
|
39
|
+
|
|
40
|
+
- `created_date` (`timestamptz`, formula `NOW()`, auto-set on insert)
|
|
41
|
+
- `last_updated` (`timestamptz`, formula `NOW()`, auto-set on insert and update)
|
|
42
|
+
|
|
43
|
+
Both columns have `flags: "I"` (internal — not shown in UI by default, managed by the platform).
|
|
44
|
+
|
|
45
|
+
**Important**: `audit` does NOT add `created_by` / `updated_by` user columns. For user tracking, use `audit-full`.
|
|
46
|
+
|
|
47
|
+
### `audit-full`
|
|
48
|
+
|
|
49
|
+
**Includes everything from `audit`** (timestamps) plus adds **four user-tracking columns**:
|
|
50
|
+
|
|
51
|
+
- `created_by` (cuid, FK to user)
|
|
52
|
+
- `created_by_user` (Reference column, lookup to user — for UI display)
|
|
53
|
+
- `last_updated_by` (cuid, FK to user)
|
|
54
|
+
- `last_updated_by_user` (Reference column, lookup to user — for UI display)
|
|
55
|
+
|
|
56
|
+
Also adds FK constraints: `FK_{Entity}_CreatedBy` and `FK_{Entity}_LastUpdatedBy`.
|
|
57
|
+
|
|
58
|
+
Use `audit-full` when you need to know **who** created/modified a record, not just **when**. All four user columns have `flags: "I"` (internal).
|
|
59
|
+
|
|
60
|
+
**You don't need to declare both `audit` and `audit-full`** — `audit-full` inherits from `audit` automatically.
|
|
61
|
+
|
|
62
|
+
> **⚠️ `audit-full` + seed data needs handling.** `created_by` and `last_updated_by` are
|
|
63
|
+
> **required** (NOT NULL) cuid FKs to `user`, with **no default formula**. At runtime the platform
|
|
64
|
+
> fills them from the current user, but at **seed** time there is no current user, so a seeded
|
|
65
|
+
> `audit-full` entity fails install: *"Seed data for '<entity>': required column 'created_by'
|
|
66
|
+
> (cuid) is not present in seed records."* (The `audit` timestamps are fine — they default to
|
|
67
|
+
> `NOW()`.) `audit-full` is fully supported — when an entity uses it, pick one of two fixes:
|
|
68
|
+
>
|
|
69
|
+
> 1. **Don't seed that entity (preferred).** Keep `audit-full`, ship it with no seed file. This
|
|
70
|
+
> matches dForge-core — none of its modules seed user-tracked columns — and suits transactional
|
|
71
|
+
> entities (orders, movements) that don't need demo rows.
|
|
72
|
+
> 2. **Seed with the System user.** Set `"created_by": 0` and `"last_updated_by": 0` on every seed
|
|
73
|
+
> record. `user_id = 0` ('System') is a real, always-present row in every tenant (inserted by the
|
|
74
|
+
> `admin` dependency), so the FK resolves. Use this when you genuinely need `audit-full` **and**
|
|
75
|
+
> seed data on the same entity:
|
|
76
|
+
> ```json
|
|
77
|
+
> { "entityCode": "purchase_order", "records": [
|
|
78
|
+
> { "purchase_order_id": 1001, "order_number": "PO-2026-0001", "supplier_id": 2001,
|
|
79
|
+
> "created_by": 0, "last_updated_by": 0 }
|
|
80
|
+
> ] }
|
|
81
|
+
> ```
|
|
82
|
+
|
|
83
|
+
### `soft-delete`
|
|
84
|
+
|
|
85
|
+
Adds a soft-delete flag:
|
|
86
|
+
|
|
87
|
+
- `active` (boolean, default `true`, formula `"true"`)
|
|
88
|
+
|
|
89
|
+
When records are "deleted," they're marked `active = false` instead of being physically removed. This preserves FK integrity and allows recovery.
|
|
90
|
+
|
|
91
|
+
### `sorting`
|
|
92
|
+
|
|
93
|
+
Adds a manual sort-order column:
|
|
94
|
+
|
|
95
|
+
- `order_num` (int4, number, `flags: "VEM"`)
|
|
96
|
+
|
|
97
|
+
Use for entities where display order matters (menu items, checklist items, FAQ entries).
|
|
98
|
+
|
|
99
|
+
### `postable`
|
|
100
|
+
|
|
101
|
+
**Marker trait — adds no columns.** Indicates the entity supports document posting (e.g. invoices that transition from draft to posted state). The actual posting state is managed by `A`-type or `L`-type columns (accumulation/ledger column types).
|
|
102
|
+
|
|
103
|
+
Use in combination with `accumulation` or `ledger` traits for accounting/registry entities.
|
|
104
|
+
|
|
105
|
+
### `accumulation`
|
|
106
|
+
|
|
107
|
+
**Marker trait — adds no columns.** Indicates the entity is an accumulation register. Configuration lives in `A`-type column params (balance entity, dimensions, resources).
|
|
108
|
+
|
|
109
|
+
Advanced feature for accounting/registry modules.
|
|
110
|
+
|
|
111
|
+
### `ledger`
|
|
112
|
+
|
|
113
|
+
**Marker trait — adds no columns.** Indicates the entity uses double-entry bookkeeping. Configuration lives in `L`-type column params (lines, movement entity, balance entity).
|
|
114
|
+
|
|
115
|
+
Advanced feature for accounting modules.
|
|
116
|
+
|
|
117
|
+
### `period`
|
|
118
|
+
|
|
119
|
+
Adds a complete period-entity template with its own PK:
|
|
120
|
+
|
|
121
|
+
- `period_key` (varchar 20, `isPk: true`, `flags: "VEM"`) — the period identifier (e.g. "2026-Q1")
|
|
122
|
+
- `description` (varchar 100, `flags: "VEM"`)
|
|
123
|
+
- `closed` (boolean, default `false`, `flags: "VEM"`) — whether the period is closed
|
|
124
|
+
- `start_date` (date, `flags: "VEM"`)
|
|
125
|
+
- `end_date` (date, `flags: "VEM"`)
|
|
126
|
+
|
|
127
|
+
**Do not combine with `identity`** — the `period` trait provides its own PK (`period_key`).
|
|
128
|
+
|
|
129
|
+
## Which traits to use
|
|
130
|
+
|
|
131
|
+
| Entity type | Recommended traits |
|
|
132
|
+
|---|---|
|
|
133
|
+
| Most business entities (contacts, orders, products) | `["identity", "audit"]` |
|
|
134
|
+
| Documents / approval workflows (`purchase_order`, `invoice`) | `["identity", "audit"]` by default — dForge-core seeds these with plain `audit` |
|
|
135
|
+
| Entities that need per-row user attribution (who created/changed it) | `["identity", "audit-full"]` — if also seeded, set `created_by`/`last_updated_by: 0` per record (or don't seed it) |
|
|
136
|
+
| Lookup/enum tables (read-only after seeding) | `["identity"]` (no audit needed) |
|
|
137
|
+
| Entities with manual ordering (menu items, steps) | `["identity", "audit", "sorting"]` |
|
|
138
|
+
| Soft-deletable entities (users, accounts) | `["identity", "audit", "soft-delete"]` |
|
|
139
|
+
| Financial documents (invoices) | `["identity", "audit", "postable"]` |
|
|
140
|
+
| Accounting periods (fiscal quarters) | `["period"]` (no `identity` — has its own PK) |
|
|
141
|
+
| Junction/bridge tables | `["identity", "audit"]` |
|
|
142
|
+
|
|
143
|
+
**Default to `audit`.** Reach for `audit-full` when the user explicitly needs to track *who* changed a row. If such an entity is **also** seeded, handle its required `created_by`/`last_updated_by` with one of the two options above (don't seed it, or set both to the System user `0`).
|
|
144
|
+
|
|
145
|
+
## Rules
|
|
146
|
+
|
|
147
|
+
1. **Always declare `traits` as a JSON array**, not a string or object.
|
|
148
|
+
2. **Always include `identity` unless** the entity uses `period` (own PK) or has a manually-declared PK.
|
|
149
|
+
3. **Default to `audit`; use `audit-full` only when the user explicitly needs per-row user attribution.** `audit` is lighter (just timestamps). `audit-full` adds four user columns, two of which (`created_by`, `last_updated_by`) are **required** with no default. If an `audit-full` entity is **seeded**, every seed record must set `created_by` and `last_updated_by` to the System user `0` — otherwise install fails on the missing columns. (Prefer not seeding `audit-full` entities at all; see the `audit-full` section.)
|
|
150
|
+
4. **Don't redefine trait-provided columns.** If you declare `created_date` manually alongside `traits: ["audit"]`, the installer will error.
|
|
151
|
+
5. **Order doesn't matter** inside the `traits` array.
|
|
152
|
+
6. **`postable`, `accumulation`, `ledger` are marker traits** — they don't add columns, they flag the entity for advanced accounting behaviour. Only use them if you're building accounting/registry modules and understand the `A`/`L` column types.
|
|
153
|
+
7. **Don't invent trait names.** Only use the 9 listed above. Check `server/database/system-modules/metadata/traits.json` if in doubt.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Translations Reference
|
|
2
|
+
|
|
3
|
+
dForge modules support multi-language translations for user-facing labels — entity names, field labels, view titles, folder names, menu items, action labels and params, and report params/dataset captions.
|
|
4
|
+
|
|
5
|
+
Lives in: `translations/<locale>.json` (e.g. `translations/en-US.json`, `translations/de-DE.json`)
|
|
6
|
+
|
|
7
|
+
Locale codes use the `ll-CC` format (language-country): `en-US`, `de-DE`, `fr-FR`, `es-ES`, etc.
|
|
8
|
+
|
|
9
|
+
## Structure
|
|
10
|
+
|
|
11
|
+
Translation files mirror the module's content structure. Each translatable object has a `label` (and optionally `desc` for description):
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"entities": {
|
|
16
|
+
"contact": {
|
|
17
|
+
"label": "Contact",
|
|
18
|
+
"desc": "People at accounts",
|
|
19
|
+
"fields": {
|
|
20
|
+
"contact_id": { "label": "Contact ID" },
|
|
21
|
+
"first_name": { "label": "First Name" },
|
|
22
|
+
"last_name": { "label": "Last Name" },
|
|
23
|
+
"email": { "label": "Email" },
|
|
24
|
+
"phone": { "label": "Phone" },
|
|
25
|
+
"account": { "label": "Account" },
|
|
26
|
+
"owner_id": { "label": "Owner" },
|
|
27
|
+
"created_date": { "label": "Created Date" },
|
|
28
|
+
"last_updated": { "label": "Last Updated" },
|
|
29
|
+
"activities": { "label": "Activities" }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"folders": {
|
|
34
|
+
"crm": { "label": "Sales CRM" }
|
|
35
|
+
},
|
|
36
|
+
"views": {
|
|
37
|
+
"contacts": { "label": "Contacts" },
|
|
38
|
+
"contacts_list": { "label": "Contact List" },
|
|
39
|
+
"contacts_gallery": { "label": "Contact Cards" }
|
|
40
|
+
},
|
|
41
|
+
"menus": {
|
|
42
|
+
"crm_menu": {
|
|
43
|
+
"label": "Sales CRM",
|
|
44
|
+
"items": {
|
|
45
|
+
"customers": { "label": "Customers" },
|
|
46
|
+
"contacts": { "label": "Contacts" }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"actions": {
|
|
51
|
+
"create_quote_from_opp": {
|
|
52
|
+
"label": "Create Quote",
|
|
53
|
+
"desc": "Create a draft quote from this opportunity",
|
|
54
|
+
"params": {
|
|
55
|
+
"discount_pct": { "label": "Discount %" }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"reports": {
|
|
60
|
+
"sales_pipeline": {
|
|
61
|
+
"datasets": {
|
|
62
|
+
"opportunities": { "caption": "Open Opportunities" }
|
|
63
|
+
},
|
|
64
|
+
"params": {
|
|
65
|
+
"min_amount": { "label": "Minimum Amount" }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Top-level sections
|
|
73
|
+
|
|
74
|
+
| Section | Translates | Keys are |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `entities` | Entity labels (`label`, `desc`) and all field labels | Entity codes → `{ label, desc, fields: { field_code: { label } } }` |
|
|
77
|
+
| `folders` | Folder labels | Folder codes (root folder key from `ui/folders.json`) |
|
|
78
|
+
| `views` | Data view labels | View codes (keys from `ui/data_views.json`) |
|
|
79
|
+
| `menus` | Menu node labels (root + nested items) | Menu root key → `{ label, items: { item_code: { label } } }` matching `ui/menus.json` structure |
|
|
80
|
+
| `actions` | Action labels, descriptions, and param labels | Action codes (keys from `ui/actions.json`) → `{ label, desc, params: { param_cd: { label } } }` |
|
|
81
|
+
| `reports` | Report dataset captions and param labels | Report codes → `{ datasets: { ds_cd: { caption } }, params: { param_cd: { label } } }` |
|
|
82
|
+
|
|
83
|
+
### Sections that are NOT translated (write-time only)
|
|
84
|
+
|
|
85
|
+
- `roles` — role rows have no `res_id` column. The `roles` key may appear in shipped translation files (e.g. `modules/crm/translations/de-DE.json`) but is **explicitly skipped** by the installer. Role display name comes from `security/roles.json` `description` only. (Comment in [`TranslationRegistrar.cs:248`](../../../server/src/dForge.Admin/Services/ModuleInstall/TranslationRegistrar.cs#L248): "reserved for future use.")
|
|
86
|
+
- `settings` — completeness *is* checked by the validator when listed in `manifest.supportedLocales` (so you'll be forced to ship `settings.<cd>.label` for each declared locale), but the registrar doesn't display those translated labels at runtime. The English `label` in `settings.json` is what users see today.
|
|
87
|
+
- `print_templates` — not currently consumed by the registrar. Template labels come from `ui/print_templates.json`.
|
|
88
|
+
|
|
89
|
+
If you include these sections, the install succeeds (they're silently ignored) — but you won't see localized output for them, so don't expect it.
|
|
90
|
+
|
|
91
|
+
## German example (de-DE)
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"entities": {
|
|
96
|
+
"contact": {
|
|
97
|
+
"label": "Kontakt",
|
|
98
|
+
"desc": "Personen bei Konten",
|
|
99
|
+
"fields": {
|
|
100
|
+
"contact_id": { "label": "Kontakt-ID" },
|
|
101
|
+
"first_name": { "label": "Vorname" },
|
|
102
|
+
"last_name": { "label": "Nachname" },
|
|
103
|
+
"email": { "label": "E-Mail" },
|
|
104
|
+
"phone": { "label": "Telefon" },
|
|
105
|
+
"account": { "label": "Konto" },
|
|
106
|
+
"owner_id": { "label": "Besitzer" },
|
|
107
|
+
"created_date": { "label": "Erstellt am" },
|
|
108
|
+
"last_updated": { "label": "Zuletzt aktualisiert" }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"folders": {
|
|
113
|
+
"crm": { "label": "Vertrieb CRM" }
|
|
114
|
+
},
|
|
115
|
+
"views": {
|
|
116
|
+
"contacts": { "label": "Kontakte" }
|
|
117
|
+
},
|
|
118
|
+
"menus": {
|
|
119
|
+
"crm_menu": {
|
|
120
|
+
"label": "Vertrieb CRM",
|
|
121
|
+
"items": {
|
|
122
|
+
"customers": { "label": "Kunden" },
|
|
123
|
+
"contacts": { "label": "Kontakte" }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"actions": {
|
|
128
|
+
"create_quote_from_opp": { "label": "Angebot erstellen" }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Manifest declaration
|
|
134
|
+
|
|
135
|
+
Non-English locales are declared in `supportedLocales` (an array of `ll-CC` tags) — **not** a `translations` object. The manifest schema has no `translations` key and rejects it (`additionalProperties: false`). Translation files are auto-discovered at `translations/<locale>.json`. Ship `translations/en-US.json` as the English base, but do **not** list `en`/`en-US` in `supportedLocales` — it covers the non-English locales only (the schema rejects `en`/`en-US` entries).
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
"supportedLocales": ["de-DE", "fr-FR"]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Every listed locale MUST have a matching `translations/<locale>.json` with a label for each translatable resource, or install fails completeness validation.
|
|
142
|
+
|
|
143
|
+
## Rules
|
|
144
|
+
|
|
145
|
+
1. **Always provide `en-US`** as the base language. Other languages fall back to it for missing keys.
|
|
146
|
+
2. **Locale format is `ll-CC`** (e.g. `en-US`, `de-DE`, `fr-FR`) — language code + country code, hyphen-separated.
|
|
147
|
+
3. **Translate everything user-visible**: entity labels, every field label (including trait-provided fields like `created_date`, `last_updated`), view names, menu items, action labels, role descriptions, setting labels, folder names.
|
|
148
|
+
4. **Include virtual columns** (references, sets, formulas) in field translations — they appear in the UI just like physical columns.
|
|
149
|
+
5. **Menu translations mirror the `ui/menus.json` structure** — root key → `items` → nested items.
|
|
150
|
+
6. **Missing keys fall back** to the `en-US` value, then to the raw code name. So it's safe to ship partial translations — untranslated items show in English rather than breaking.
|
|
151
|
+
7. **Don't translate column codes** — only the `label` values. Codes stay as-is in all languages.
|
|
152
|
+
8. **File naming**: `translations/<locale>.json` (e.g. `de-DE.json`) — each non-English file must match a locale listed in `supportedLocales`.
|
|
153
|
+
|
|
154
|
+
## When to create translations
|
|
155
|
+
|
|
156
|
+
- **Always** create `translations/en-US.json` — even for English-only modules. It ensures all labels are explicitly declared rather than derived from column codes.
|
|
157
|
+
- **On request** create additional languages. The LLM can generate translations from the `en-US` file — the structure is identical, only `label` and `desc` values change.
|
|
158
|
+
- **Include all fields** in each language file, including trait-provided fields (`created_date`, `last_updated`, `created_by`, etc.) — users see these columns and they need labels.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Validation Checklist
|
|
2
|
+
|
|
3
|
+
Run through this checklist **before** declaring a module complete. Do it mentally if there's no validate CLI available; run `dforge validate .` if there is.
|
|
4
|
+
|
|
5
|
+
## Top install-blockers (scan first)
|
|
6
|
+
|
|
7
|
+
These five have each caused a real install failure. Check them before the section-by-section pass:
|
|
8
|
+
|
|
9
|
+
- [ ] **DSL dates** — `execute:` blocks use lowercase `now()`, never `TODAY()`/`NOW()` (formula-only; otherwise install fails `'TODAY' is not defined`)
|
|
10
|
+
- [ ] **Roll-up totals** — a sum over a child set is a Formula (`F`) column with `SUM([set].[field])`, **not** a Generated (`G`) column over a virtual `F`/`R`/`S` child (otherwise `db_error: column old.<field> does not exist`)
|
|
11
|
+
- [ ] **Rights keys** — actions/reports/folders use a **colon** (`action:x`, `report:x`, `folder:x`); entities bare or cross-module-dotted; deny by omitting the key, never `""`
|
|
12
|
+
- [ ] **Manifest** — no `translations` key (auto-discovered; non-English locales go in `supportedLocales`)
|
|
13
|
+
- [ ] **Column defaults** — set via `formula` / `numberSequence` / DSL, never a `defaultValue` key on an entity field
|
|
14
|
+
- [ ] **Seed + traits** — seeded `audit-full` entities set `created_by`/`last_updated_by: 0` (System user) in every record (or use `audit` / don't seed them); otherwise install fails on the missing user columns
|
|
15
|
+
|
|
16
|
+
## Manifest
|
|
17
|
+
|
|
18
|
+
- [ ] `manifest.json` exists at the module root
|
|
19
|
+
- [ ] `packageFormat` is set (usually `1`)
|
|
20
|
+
- [ ] `moduleId` is a valid UUID and unique (not copied from another module)
|
|
21
|
+
- [ ] `code` is lowercase, snake_case, letters+digits+underscores only
|
|
22
|
+
- [ ] `version` is valid semver
|
|
23
|
+
- [ ] `dbSchemaVersion` is valid semver
|
|
24
|
+
- [ ] `displayName` and `description` are present
|
|
25
|
+
- [ ] `dependencies` includes `admin` (almost always required)
|
|
26
|
+
- [ ] Every `entities` entry points to a file that exists
|
|
27
|
+
- [ ] Every other content pointer (`dataViews`, `menus`, `security`, etc.) points to existing files
|
|
28
|
+
|
|
29
|
+
## Entities
|
|
30
|
+
|
|
31
|
+
For each entity:
|
|
32
|
+
|
|
33
|
+
- [ ] Has `description`, `dbObject`, `toString`
|
|
34
|
+
- [ ] Has `traits: ["identity", "audit"]` (or a justified exception)
|
|
35
|
+
- [ ] `toString` template uses only column names that exist on the entity
|
|
36
|
+
- [ ] Every column has `fieldTypeCd` (except for formula columns which use `baseDatatypeCd` + `columnType: "F"`)
|
|
37
|
+
- [ ] Every `fieldTypeCd` value is from the official catalog (see `field-types.md`)
|
|
38
|
+
- [ ] Every column has `flags` (valid letters: V, I, E, M, H only — no U, S, or P; see `flags.md` for meanings)
|
|
39
|
+
- [ ] Every column has `orderNum`
|
|
40
|
+
- [ ] Every column has `description`
|
|
41
|
+
|
|
42
|
+
### FK+Reference pattern
|
|
43
|
+
|
|
44
|
+
- [ ] Every reference column (`columnType: "R"`) has a paired hidden FK column
|
|
45
|
+
- [ ] The hidden FK column has `flags: "EM"` (not `V`, not `I`)
|
|
46
|
+
- [ ] The reference column has `flags: "VEM"`, `fieldTypeCd: "lookup"`, and a `link` object
|
|
47
|
+
- [ ] `link.entity` points to an entity that exists (in this module or a dependency)
|
|
48
|
+
- [ ] `link.thisKey` matches the FK column name on this entity
|
|
49
|
+
- [ ] `link.otherKey` matches the PK column name on the target entity
|
|
50
|
+
- [ ] The entity's `references` block declares the FK constraint
|
|
51
|
+
|
|
52
|
+
### Formula columns
|
|
53
|
+
|
|
54
|
+
- [ ] `columnType: "F"`
|
|
55
|
+
- [ ] Has `baseDatatypeCd` (required)
|
|
56
|
+
- [ ] Has `formula`
|
|
57
|
+
- [ ] Uses only known formula functions (see `formulas.md`)
|
|
58
|
+
- [ ] Does NOT have `dbDatatype` (formula columns are virtual)
|
|
59
|
+
- [ ] Flags is usually `"V"` (read-only)
|
|
60
|
+
|
|
61
|
+
### Set columns
|
|
62
|
+
|
|
63
|
+
- [ ] `columnType: "S"`
|
|
64
|
+
- [ ] `fieldTypeCd: "grid"` (or another set renderer)
|
|
65
|
+
- [ ] Has `link` with `entity`, `thisKey`, `otherKey`
|
|
66
|
+
- [ ] `link.otherKey` points to a real FK column on the target entity
|
|
67
|
+
|
|
68
|
+
## Data views
|
|
69
|
+
|
|
70
|
+
- [ ] Every data view has `label`, `viewType`, and `dataSources`
|
|
71
|
+
- [ ] `dataSources` is an array, not an object
|
|
72
|
+
- [ ] Each source has `entityCode` and `columns`
|
|
73
|
+
- [ ] `entityCode` points to an entity that exists
|
|
74
|
+
- [ ] Column codes in `columns` all exist on the entity
|
|
75
|
+
- [ ] `viewType` is from the supported list (`grid`, `list`, `kanban`, `gallery`, `tree-grid`, `card`)
|
|
76
|
+
- [ ] Sort uses the view-def-root `order` key — a `string[]` like `["-created_date", "name"]` (leading `-` = descending), NOT `sort` / `[{column_cd, direction}]` (that object shape belongs to queries & reports, not data views)
|
|
77
|
+
|
|
78
|
+
## Menus
|
|
79
|
+
|
|
80
|
+
- [ ] Menus are **nested dictionaries** with `children`, NOT arrays
|
|
81
|
+
- [ ] Every node has `label`
|
|
82
|
+
- [ ] Leaf items have `itemType` (`V`, `R`, `D`, or `A`)
|
|
83
|
+
- [ ] Folder nodes do **not** have `itemType`
|
|
84
|
+
- [ ] Leaf items with `itemType: "V"` have `dataViewCode` (NOT `viewCode`)
|
|
85
|
+
- [ ] Every `dataViewCode` points to a data view that exists
|
|
86
|
+
- [ ] Every `reportCode` points to a report that exists
|
|
87
|
+
- [ ] `orderNum` is set on each node for deterministic ordering
|
|
88
|
+
|
|
89
|
+
## Security
|
|
90
|
+
|
|
91
|
+
- [ ] `security/roles.json` exists (at least one role)
|
|
92
|
+
- [ ] Every role has `description` and `rights` (both required; no `label` field — display name comes from `description`). The `roles.schema.json` sets `additionalProperties: false`, so any extra field (including `label`) is rejected at install time.
|
|
93
|
+
- [ ] The field name is `rights`, NOT `entityRights`
|
|
94
|
+
- [ ] Rights strings use only valid letters: `SIUDC` for entities, `E` for actions/reports/folders
|
|
95
|
+
- [ ] Entity codes in `rights` all reference real entities (in this module or dependencies)
|
|
96
|
+
- [ ] At least one role has `SIUDC` on every entity (typically an admin role)
|
|
97
|
+
|
|
98
|
+
## Actions
|
|
99
|
+
|
|
100
|
+
For each action:
|
|
101
|
+
|
|
102
|
+
- [ ] Registered in `ui/actions.json` with `label`, `script`, `executionMode`, `entityCode`
|
|
103
|
+
- [ ] DSL file exists at `logic/actions/<script>.dsl`, where `<script>` is the bare filename in the action's `script` field (no path, no `.dsl` extension)
|
|
104
|
+
- [ ] DSL file has `params:`, `canExecute:`, and `execute:` blocks
|
|
105
|
+
- [ ] `canExecute:` is a valid formula expression
|
|
106
|
+
- [ ] `execute:` uses only documented built-in functions
|
|
107
|
+
- [ ] Referenced entities exist
|
|
108
|
+
- [ ] Referenced parameters are declared in `params:`
|
|
109
|
+
|
|
110
|
+
## Settings
|
|
111
|
+
|
|
112
|
+
- [ ] `settings.json` exists if the module has any settings
|
|
113
|
+
- [ ] Every setting has `fieldTypeCd`
|
|
114
|
+
- [ ] Every setting has a `defaultValue` (or is explicitly nullable) — note the field is `defaultValue`, not `default`
|
|
115
|
+
- [ ] Field types used are from the supported subset (no `lookup`, no `grid`)
|
|
116
|
+
|
|
117
|
+
## Seed data
|
|
118
|
+
|
|
119
|
+
- [ ] Seed files are in `seed-data/` with numbered prefixes (`01-`, `02-`, …)
|
|
120
|
+
- [ ] Seed files are ordered by FK dependency (parents before children)
|
|
121
|
+
- [ ] Seeded `audit-full` entities supply `created_by`/`last_updated_by` — `audit-full` adds these as required (cuid, NOT NULL) columns. Either set both to the System user `0` in every seed record, switch the entity to `audit`, or don't seed it. (The `audit` timestamps need no value — they default to `NOW()`.)
|
|
122
|
+
- [ ] Each seed file has `entityCode` and `records`
|
|
123
|
+
- [ ] Every record has explicit values for NOT NULL columns
|
|
124
|
+
- [ ] PKs are explicit **numeric integers** (e.g. 1001, 1002 — NOT UUID strings, `cuid` is int8)
|
|
125
|
+
|
|
126
|
+
## Translations
|
|
127
|
+
|
|
128
|
+
- [ ] `translations/en-US.json` exists
|
|
129
|
+
- [ ] Has `entities` section with `label`, `desc`, and `fields` for every entity
|
|
130
|
+
- [ ] Every field (including trait-provided: `created_date`, `last_updated`, etc.) has a `label`
|
|
131
|
+
- [ ] Has `views` section with labels for every data view
|
|
132
|
+
- [ ] Has `menus` section matching the `ui/menus.json` structure
|
|
133
|
+
- [ ] Has `actions` section with labels for every action
|
|
134
|
+
- [ ] Has `roles` section with labels for every role
|
|
135
|
+
- [ ] Has `settings` section with labels for every setting (if settings exist)
|
|
136
|
+
- [ ] Has `folders` section with label for the root folder
|
|
137
|
+
- [ ] Additional language files (if any) have the same structure as `en-US.json`
|
|
138
|
+
- [ ] Manifest `supportedLocales` lists every non-English locale that has a `translations/<locale>.json` file (English is not listed)
|
|
139
|
+
|
|
140
|
+
## Versioning
|
|
141
|
+
|
|
142
|
+
- [ ] `version` in manifest is bumped from previous release (or `"0.1.0"` for new modules)
|
|
143
|
+
- [ ] `dbSchemaVersion` is bumped if any DB schema changed (new entity, new column, changed type)
|
|
144
|
+
- [ ] `dbSchemaVersion` is NOT bumped if only views/menus/actions/translations changed
|
|
145
|
+
|
|
146
|
+
## Cross-entity consistency
|
|
147
|
+
|
|
148
|
+
- [ ] Every menu item's `dataViewCode` has a matching entry in `ui/data_views.json`
|
|
149
|
+
- [ ] Every data view's `entityCode` has a matching entity in the module (or a dependency)
|
|
150
|
+
- [ ] Every role's `rights` object codes match real entities/actions/reports
|
|
151
|
+
- [ ] Every action's `entityCode` matches a real entity
|
|
152
|
+
- [ ] Every FK constraint in `references` blocks matches a real target entity
|
|
153
|
+
|
|
154
|
+
## After validation
|
|
155
|
+
|
|
156
|
+
- [ ] If a local `dforge validate .` CLI is available, run it and fix any errors
|
|
157
|
+
- [ ] Try to `dforge package .` to build the `.dforge` file — it should succeed
|
|
158
|
+
- [ ] If MCP is connected, run `dry_run_install` to see the generated SQL
|
|
159
|
+
- [ ] Only after dry-run looks clean, `install_module` or `install-to-dev`
|
|
160
|
+
|
|
161
|
+
## Red flags during review
|
|
162
|
+
|
|
163
|
+
If you see any of these, stop and investigate:
|
|
164
|
+
|
|
165
|
+
- An entity with no `traits` and no explicit PK column
|
|
166
|
+
- A reference column without a paired FK column (or vice versa)
|
|
167
|
+
- A column with `fieldTypeCd: "integer"` / `"datePicker"` / `"money"` / `"autocomplete"` / `"boolean"` (wrong names)
|
|
168
|
+
- Flags containing `U`, `S`, or `P` (not valid flag letters)
|
|
169
|
+
- Menus with `children: [...]` (array, not dict)
|
|
170
|
+
- Roles with `entityRights` (wrong key)
|
|
171
|
+
- Data views with root-level `entityCode` (should be inside `dataSources`)
|
|
172
|
+
- Formula columns without `baseDatatypeCd`
|
|
173
|
+
- DSL actions with JavaScript/Python syntax
|
|
174
|
+
- Seed data files without numbered prefixes
|
|
175
|
+
- Seed data using `"entity"` instead of `"entityCode"` (silent failure — installer reads empty entity code)
|
|
176
|
+
- Seed data PKs as UUID strings instead of numeric integers
|
|
177
|
+
- Duplicate column codes in the same entity
|
|
178
|
+
- `orderNum` missing or duplicated across columns
|
|
179
|
+
|
|
180
|
+
## When the checklist passes
|
|
181
|
+
|
|
182
|
+
Tell the user what you built, summarize the entity model (e.g. "7 entities: contact, account, opportunity with its line items, quote with lines, activity, product"), list the data views and actions, and describe the security roles. Then ask if they want to install it.
|