runlify 0.0.794 → 0.0.796
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/build/cli.js +8 -2
- package/build/projectsGeneration/builders/generationPaths.js +44 -1
- package/build/projectsGeneration/generateProject/back/src/generateBackSrc.js +11 -2
- package/build/projectsGeneration/generateProject/fileHandlers.js +6 -2
- package/build/projectsGeneration/generateProject/front/generateFrontSrc.js +10 -1
- package/build/projectsGeneration/generateProject/front/translations/generateFrontSrcTranslations.js +33 -1
- package/build/projectsGeneration/generateProject/generateBackClickHouseBootstrap.js +1 -4
- package/build/projectsGeneration/generateProject/utils.js +2 -2
- package/build/projectsGeneration/generators/fileTemplates/back/clickhouse/jobs.js +41 -4
- package/build/projectsGeneration/generators/fileTemplates/back/roles/customPermissions.js +12 -0
- package/build/projectsGeneration/generators/fileTemplates/back/roles/customUiPermissions.js +13 -0
- package/build/projectsGeneration/generators/fileTemplates/back/roles/initPermissions.js +31 -0
- package/build/projectsGeneration/generators/fileTemplates/back/roles/uiPermissions.js +24 -0
- package/build/projectsGeneration/generators/fileTemplates/ui/NotFoundPage.js +6 -0
- package/build/projectsGeneration/generators/fileTemplates/ui/PermissionPage.js +6 -0
- package/build/projectsGeneration/generators/fileTemplates/ui/environment/src/routes.js +5 -5
- package/build/projectsGeneration/generators/fileTemplates/ui/getDefaultMenu.js +5 -1
- package/build/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nIndexTmpl.js +35 -0
- package/build/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nTypesTmpl.js +33 -0
- package/build/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nValidationTmpl.js +38 -0
- package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityCreate/DefaultEntityCreate.js +16 -5
- package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityEdit/DefaultEntityEdit.js +27 -11
- package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityList/DefaultEntityFilter.js +13 -4
- package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityList/DefaultEntityList.js +14 -7
- package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityShow/DefaultMainTab.js +13 -4
- package/build/projectsGeneration/generators/fileTemplates/ui/pages/EntityShow/DependencyTab.js +12 -3
- package/build/projectsGeneration/generators/fileTemplates/ui/resources.js +21 -8
- package/build/projectsGeneration/generators/fileTemplates/ui/utils/permissions.js +89 -0
- package/build/projectsGeneration/generators/ui/getShowComponent.js +2 -2
- package/build/types/projectsGeneration/builders/generationPaths.d.ts +42 -0
- package/build/types/projectsGeneration/generateProject/fileHandlers.d.ts +1 -0
- package/build/types/projectsGeneration/generateProject/types.d.ts +1 -1
- package/build/types/projectsGeneration/generateProject/utils.d.ts +1 -1
- package/build/types/projectsGeneration/generators/fileTemplates/back/roles/customPermissions.d.ts +2 -0
- package/build/types/projectsGeneration/generators/fileTemplates/back/roles/customUiPermissions.d.ts +2 -0
- package/build/types/projectsGeneration/generators/fileTemplates/back/roles/initPermissions.d.ts +2 -0
- package/build/types/projectsGeneration/generators/fileTemplates/back/roles/uiPermissions.d.ts +3 -0
- package/build/types/projectsGeneration/generators/fileTemplates/ui/NotFoundPage.d.ts +2 -0
- package/build/types/projectsGeneration/generators/fileTemplates/ui/PermissionPage.d.ts +2 -0
- package/build/types/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nIndexTmpl.d.ts +2 -0
- package/build/types/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nTypesTmpl.d.ts +3 -0
- package/build/types/projectsGeneration/generators/fileTemplates/ui/i18n/skeleton/uiI18nValidationTmpl.d.ts +2 -0
- package/build/types/projectsGeneration/generators/fileTemplates/ui/pages/EntityEdit/DefaultEntityEdit.d.ts +1 -0
- package/build/types/projectsGeneration/generators/fileTemplates/ui/utils/permissions.d.ts +2 -0
- package/docs/01-overview.md +172 -0
- package/docs/02-system-meta-builder.md +405 -0
- package/docs/03-entity-types.md +471 -0
- package/docs/04-fields.md +357 -0
- package/docs/05-custom-methods.md +249 -0
- package/docs/06-storage.md +129 -0
- package/docs/07-backend-file-graph.md +231 -0
- package/docs/08-frontend-file-graph.md +260 -0
- package/docs/09-options.md +213 -0
- package/docs/10-modules.md +164 -0
- package/docs/11-permissions.md +231 -0
- package/docs/INDEX.md +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
# Entity Types
|
|
2
|
+
|
|
3
|
+
> **Load this file when:** you need to choose which entity type to use, understand
|
|
4
|
+
> what each type provides, or configure entity-level behaviour (deletable, auditable,
|
|
5
|
+
> multitenancy, storage, permissions, etc.).
|
|
6
|
+
>
|
|
7
|
+
> Related: [02-system-meta-builder.md](./02-system-meta-builder.md) ·
|
|
8
|
+
> [04-fields.md](./04-fields.md) · [06-storage.md](./06-storage.md)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Choosing an entity type
|
|
13
|
+
|
|
14
|
+
| Type | Use when | Deletable by default | Auto-added fields |
|
|
15
|
+
|------|----------|---------------------|-------------------|
|
|
16
|
+
| `Catalog` | Reference / lookup data (statuses, categories, countries) | No | `id`, `search` |
|
|
17
|
+
| `Document` | Business transactions (orders, invoices, payments) | No | `id`, `date`, `search` |
|
|
18
|
+
| `InfoRegistry` | Accounting-style state snapshots (inventory levels, balances at a point in time) | No | `id`, + registrar fields if `registrarDepended=true` |
|
|
19
|
+
| `SumRegistry` | Accounting-style running totals (sum of amounts per dimension) | No | `id`, `date`, + registrar fields if `registrarDepended=true` |
|
|
20
|
+
| `Report` | Read-only report pages (no CRUD, no storage) | — | none |
|
|
21
|
+
|
|
22
|
+
All savable entity types (`Catalog`, `Document`, `InfoRegistry`, `SumRegistry`) inherit
|
|
23
|
+
from `BaseSavableEntityBuilder` and share the same core API described below.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## BaseSavableEntityBuilder — shared API
|
|
28
|
+
|
|
29
|
+
All methods below are available on every savable entity.
|
|
30
|
+
|
|
31
|
+
### Default state
|
|
32
|
+
|
|
33
|
+
| Property | Default |
|
|
34
|
+
|----------|---------|
|
|
35
|
+
| `deletable` | `false` |
|
|
36
|
+
| `editable` | `true` |
|
|
37
|
+
| `auditable` | `true` |
|
|
38
|
+
| `creatableByUser` | `true` |
|
|
39
|
+
| `updatableByUser` | `true` |
|
|
40
|
+
| `removableByUser` | `true` |
|
|
41
|
+
| `exportableByUser` | `true` |
|
|
42
|
+
| `searchEnabled` | `true` |
|
|
43
|
+
| `storage` | `POSTGRES` |
|
|
44
|
+
| `multitenancy` | `'none'` |
|
|
45
|
+
| `database` | `'main'` |
|
|
46
|
+
| `sortField` | `'id'` |
|
|
47
|
+
| `sortOrder` | `'DESC'` |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### Access control {#access-control}
|
|
52
|
+
|
|
53
|
+
#### `setCreatableByUser(value?: boolean): this`
|
|
54
|
+
|
|
55
|
+
Controls whether users can create records via the UI and GraphQL mutation.
|
|
56
|
+
Defaults to `true`.
|
|
57
|
+
|
|
58
|
+
#### `setUpdatableByUser(value?: boolean): this`
|
|
59
|
+
|
|
60
|
+
Controls whether users can update records. Defaults to `true`.
|
|
61
|
+
|
|
62
|
+
#### `setRemovableByUser(value?: boolean): this`
|
|
63
|
+
|
|
64
|
+
Controls whether users can delete records. Defaults to `true`.
|
|
65
|
+
|
|
66
|
+
**Anti-pattern — hiding entity from UI by setting `setNotUpdatableByUser` on every field:**
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
// WRONG: makes fields optional in generated types, conflicts with setRequired()
|
|
70
|
+
entity.addField('name').setRequired().setNotUpdatableByUser()
|
|
71
|
+
entity.addField('status').setRequired().setNotUpdatableByUser()
|
|
72
|
+
// ... repeat for every field
|
|
73
|
+
|
|
74
|
+
// CORRECT: disable at the entity level
|
|
75
|
+
entity
|
|
76
|
+
.setCreatableByUser(false)
|
|
77
|
+
.setUpdatableByUser(false)
|
|
78
|
+
.setRemovableByUser(false)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`setNotUpdatableByUser()` on a field marks that field as **not required in update input
|
|
82
|
+
types**. When combined with `setRequired()`, the field is required in the DB but not in
|
|
83
|
+
the update GraphQL input — which is the correct semantic for immutable fields. Using it
|
|
84
|
+
globally to hide an entity from the UI is the wrong tool.
|
|
85
|
+
|
|
86
|
+
#### `setExportableByUser(value?: boolean): this`
|
|
87
|
+
|
|
88
|
+
Controls whether users can export records (e.g. to CSV). Defaults to `true`.
|
|
89
|
+
|
|
90
|
+
See [11-permissions.md](./11-permissions.md) for how access control actually works —
|
|
91
|
+
both the backend-enforced `<entity>.<action>` permissions and the frontend-only
|
|
92
|
+
`ui.<entity>.list` permission.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### Deletion and editing {#deletion-and-editing}
|
|
97
|
+
|
|
98
|
+
#### `setDeletable(value?: boolean): this`
|
|
99
|
+
|
|
100
|
+
Allows physical deletion of records. Default `false` — most entities use soft-delete or
|
|
101
|
+
are not deletable at all.
|
|
102
|
+
|
|
103
|
+
#### `setEditable(value?: boolean): this`
|
|
104
|
+
|
|
105
|
+
Controls whether the edit UI form is shown. Default `true`.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### Auditing {#auditing}
|
|
110
|
+
|
|
111
|
+
#### `setAuditable(value?: boolean): this`
|
|
112
|
+
|
|
113
|
+
Enables audit logging (recording who created/updated/deleted records). Default `true`.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### Caching {#caching}
|
|
118
|
+
|
|
119
|
+
#### `setCacheable(value?: boolean): this`
|
|
120
|
+
|
|
121
|
+
Enables in-memory caching for this entity. Default `false`. Use for small, rarely
|
|
122
|
+
changing reference data (e.g. currencies, countries).
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Storage {#storage}
|
|
127
|
+
|
|
128
|
+
#### `setStorage(storage: StorageType): this`
|
|
129
|
+
|
|
130
|
+
Sets the storage engine. See [06-storage.md](./06-storage.md) for all options.
|
|
131
|
+
|
|
132
|
+
When set to an external-search storage, automatically:
|
|
133
|
+
- Forces `id` type to `string` with `cuid()` auto-generation
|
|
134
|
+
- Creates an `externalSearchTrackings` helper entity
|
|
135
|
+
|
|
136
|
+
#### `setDatabase(name: string): this`
|
|
137
|
+
|
|
138
|
+
Assigns this entity to a named database. Default `'main'`.
|
|
139
|
+
The database must be registered first via `system.addDatabase(name)`.
|
|
140
|
+
See [02-system-meta-builder.md#addDatabase](./02-system-meta-builder.md#addDatabase).
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### Search {#search}
|
|
145
|
+
|
|
146
|
+
#### `setSearchEnabled(value?: boolean): this`
|
|
147
|
+
|
|
148
|
+
Enables/disables full-text search for this entity. Default `true` for Catalog/Document,
|
|
149
|
+
`false` for InfoRegistry.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
### Sorting {#sorting}
|
|
154
|
+
|
|
155
|
+
#### `setSort(field: string, order?: 'ASC' | 'DESC'): this`
|
|
156
|
+
|
|
157
|
+
Sets the default sort order for list queries. The field must exist on the entity.
|
|
158
|
+
Default: `'id'`, `'DESC'`.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### Unique constraints & indexes {#constraints-and-indexes}
|
|
163
|
+
|
|
164
|
+
#### `addUniqueConstraint(fields: string[]): this`
|
|
165
|
+
|
|
166
|
+
Adds a multi-column unique constraint. All field names must exist on the entity.
|
|
167
|
+
|
|
168
|
+
#### `addIndex({ fields, type }: Index): this`
|
|
169
|
+
|
|
170
|
+
Adds a database index.
|
|
171
|
+
|
|
172
|
+
| `type` | `IndexType` value | Notes |
|
|
173
|
+
|--------|------------------|-------|
|
|
174
|
+
| BTree | `IndexType.BTree` | Default; supports multi-column |
|
|
175
|
+
| Hash | `IndexType.Hash` | Single-column only |
|
|
176
|
+
| GIN | `IndexType.Gin` | For full-text / array search |
|
|
177
|
+
|
|
178
|
+
**Anti-pattern — multi-column Hash index:**
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
// WRONG: throws at build() time
|
|
182
|
+
entity.addIndex({ fields: ['statusId', 'date'], type: IndexType.Hash })
|
|
183
|
+
|
|
184
|
+
// CORRECT: use BTree for multi-column
|
|
185
|
+
entity.addIndex({ fields: ['statusId', 'date'], type: IndexType.BTree })
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### Multitenancy {#multitenancy}
|
|
191
|
+
|
|
192
|
+
#### `setMultitenancy(multitenancy, commonElementsVisibleToAll?): this`
|
|
193
|
+
|
|
194
|
+
| Value | Behaviour |
|
|
195
|
+
|-------|-----------|
|
|
196
|
+
| `'none'` | No tenant isolation (default) |
|
|
197
|
+
| `'optional'` | Records may belong to a tenant or be global. Pass `commonElementsVisibleToAll` to control whether global records are visible to all tenants |
|
|
198
|
+
| `'required'` | Every record must belong to a tenant. `tenantId` is auto-populated from the current user's profile |
|
|
199
|
+
|
|
200
|
+
Automatically adds a `tenantId` link field to `tenants` entity when `!== 'none'`.
|
|
201
|
+
|
|
202
|
+
**Anti-pattern — `commonElementsVisibleToAll` with non-optional multitenancy:**
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
// WRONG: throws at build() time
|
|
206
|
+
entity.setMultitenancy('required', true)
|
|
207
|
+
|
|
208
|
+
// CORRECT: commonElementsVisibleToAll is only valid for 'optional'
|
|
209
|
+
entity.setMultitenancy('optional', true)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
### Predefined data {#predefined-data}
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
addPredefinedElements(rows: Record<string, any>[]) // seeds data in all environments
|
|
218
|
+
setPredefinedElements(rows: Record<string, any>[]) // replaces (not appends) seed data
|
|
219
|
+
addDevPredefinedElements(rows: Record<string, any>[]) // seeds data in dev environment only
|
|
220
|
+
setDevPredefinedElements(rows: Record<string, any>[])
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
### Data cleanup {#data-cleanup}
|
|
226
|
+
|
|
227
|
+
#### `setClearDBAfter(count: number, unit: DateUnit): this`
|
|
228
|
+
|
|
229
|
+
Automatically deletes records older than `count` `unit`s.
|
|
230
|
+
|
|
231
|
+
`DateUnit` values: `'year' | 'month' | 'day' | 'hour' | 'minute'`
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
### UI / menu {#ui-menu}
|
|
236
|
+
|
|
237
|
+
#### `setExcludeFromCommonMenu(exclude?: boolean): this`
|
|
238
|
+
|
|
239
|
+
Prevents this entity from appearing in the auto-generated sidebar menu. Default `false`.
|
|
240
|
+
|
|
241
|
+
#### `getForms(): FormsBuilder`
|
|
242
|
+
|
|
243
|
+
Returns the UI forms builder for customising list/show/create/edit form fields and
|
|
244
|
+
their display order. See [08-frontend-file-graph.md](./08-frontend-file-graph.md).
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
### Key field {#key-field}
|
|
249
|
+
|
|
250
|
+
#### `getKey(): IdFieldBuilder`
|
|
251
|
+
|
|
252
|
+
Returns the `id` field builder. Use to change the key type:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
entity.getKey().setType('string') // cuid() auto-generated string id
|
|
256
|
+
entity.getKey().setType('int') // autoincrement integer
|
|
257
|
+
entity.getKey().setType('bigint') // autoincrement bigint
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Default type is `int` (autoincrement).
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### Title field {#title-field}
|
|
265
|
+
|
|
266
|
+
#### `setTitleFieldByName(fieldName: string): this`
|
|
267
|
+
|
|
268
|
+
Sets which field is shown as the record's display name in dropdowns and relation fields.
|
|
269
|
+
Default is `id`. Usually set to a `name` or `title` scalar field.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### Fields {#fields}
|
|
274
|
+
|
|
275
|
+
All field methods are described in [04-fields.md](./04-fields.md).
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
addField(name, title?, options?) // ScalarFieldBuilder
|
|
279
|
+
addLinkField(entity, name, title?) // LinkFieldBuilder — name MUST end in 'Id'
|
|
280
|
+
addFileField(name, title?) // LinkFieldBuilder to 'files' (plain file)
|
|
281
|
+
addImageField(name, title?) // LinkFieldBuilder to 'files' (image)
|
|
282
|
+
addViewLinkField(entity, name, title?) // ViewLinkFieldBuilder (read-only join)
|
|
283
|
+
delField(name) // removes a field
|
|
284
|
+
getFiled(name) // returns a field builder or throws
|
|
285
|
+
getFieldIfExist(name) // returns a field builder or undefined
|
|
286
|
+
getLinkFileds() // returns all LinkFieldBuilders
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### Custom methods & models on an entity {#entity-methods}
|
|
292
|
+
|
|
293
|
+
Entities can have their own custom GraphQL operations in addition to the standard CRUD.
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
entity.addMethod(name, methodType, title?) // MethodType.Query | Mutation
|
|
297
|
+
entity.createGeneralModel(name, title?) // model for input or output
|
|
298
|
+
entity.createInputModel(name, title?) // input-only model
|
|
299
|
+
entity.createOutputModel(name, title?) // output-only model
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
See [05-custom-methods.md](./05-custom-methods.md).
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Catalog {#catalog}
|
|
307
|
+
|
|
308
|
+
Created via `system.addCatalog(name, title?, options?)`.
|
|
309
|
+
|
|
310
|
+
**Additional auto-added fields:** `search` (hidden, string, not updatable by user).
|
|
311
|
+
|
|
312
|
+
**Additional properties:**
|
|
313
|
+
|
|
314
|
+
| Property | Default | Setter |
|
|
315
|
+
|----------|---------|--------|
|
|
316
|
+
| `deletable` | `false` | `setDeletable()` |
|
|
317
|
+
| `editable` | `true` | `setEditable()` |
|
|
318
|
+
|
|
319
|
+
Use for: statuses, categories, countries, currencies, units of measure, any lookup table.
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Document {#document}
|
|
324
|
+
|
|
325
|
+
Created via `system.addDocument(name, title?, options?)`.
|
|
326
|
+
|
|
327
|
+
**Additional auto-added fields:** `date` (datetime, required, defaults to `new Date()`)
|
|
328
|
+
and `search` (hidden string).
|
|
329
|
+
|
|
330
|
+
**Additional methods:**
|
|
331
|
+
|
|
332
|
+
#### `addRegistry(registryName: string)`
|
|
333
|
+
|
|
334
|
+
Links this document to an InfoRegistry or SumRegistry that it drives.
|
|
335
|
+
`registryName` is the name of the registry entity.
|
|
336
|
+
|
|
337
|
+
#### `setSharded(value?: boolean): this`
|
|
338
|
+
|
|
339
|
+
When `true`, additionally adds hidden fields `repostRequired` (bool, default `true`) and
|
|
340
|
+
`deleteRequired` (bool, default `false`) used for sharded accounting workflows.
|
|
341
|
+
|
|
342
|
+
Use for: orders, invoices, payments, events, anything that represents a business
|
|
343
|
+
transaction at a point in time.
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## InfoRegistry {#inforegistry}
|
|
348
|
+
|
|
349
|
+
Created via `system.addInfoRegistry(name, registrarDepended, title?, options?)`.
|
|
350
|
+
|
|
351
|
+
An information register stores **state snapshots** — the value of something at a
|
|
352
|
+
specific moment. Think of it as a time-series table of dimension→resource pairs.
|
|
353
|
+
|
|
354
|
+
**Search disabled by default.**
|
|
355
|
+
|
|
356
|
+
**When `registrarDepended = true`**, auto-adds:
|
|
357
|
+
- `registrarTypeId` (string link to `entities`)
|
|
358
|
+
- `registrarId` (int or string, configurable via `RegistryOptions.registrarIdType`)
|
|
359
|
+
- `row` (int, default `1`)
|
|
360
|
+
- Unique constraint on `[registrarTypeId, registrarId, row]`
|
|
361
|
+
|
|
362
|
+
**Dimension vs Resource:**
|
|
363
|
+
|
|
364
|
+
| Role | Meaning | Methods |
|
|
365
|
+
|------|---------|---------|
|
|
366
|
+
| **Dimension** | What you're measuring (product, warehouse, account) | `addDimension`, `addDimensionLinkField`, `addDimensionViewLinkField` |
|
|
367
|
+
| **Resource** | The value being stored (quantity, amount, flag) | `addResource`, `addResourceLinkField`, `addResourceViewLinkField` |
|
|
368
|
+
|
|
369
|
+
Auto-generated unique constraint: all dimension field names combined.
|
|
370
|
+
|
|
371
|
+
#### `setPeriod(period: InfoRegistryPeriod): this`
|
|
372
|
+
|
|
373
|
+
| Value | Auto-added field |
|
|
374
|
+
|-------|-----------------|
|
|
375
|
+
| `'notPeriodic'` | none |
|
|
376
|
+
| `'second'` | `date` (datetime) |
|
|
377
|
+
| `'day'` | `date` (date) |
|
|
378
|
+
| `'month'` | `date` (date) |
|
|
379
|
+
| `'year'` | `date` (date) |
|
|
380
|
+
|
|
381
|
+
#### `delDimension(name) / delResource(name)`
|
|
382
|
+
|
|
383
|
+
Removes a dimension or resource field.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## SumRegistry {#sumregistry}
|
|
388
|
+
|
|
389
|
+
Created via `system.addSumRegistry(name, registrarDepended, title?, options?)`.
|
|
390
|
+
|
|
391
|
+
A totals register stores **running aggregated sums** across dimensions. Think of it as
|
|
392
|
+
a materialized aggregate table.
|
|
393
|
+
|
|
394
|
+
`RegistryOptions`:
|
|
395
|
+
|
|
396
|
+
| Field | Type | Default | Description |
|
|
397
|
+
|-------|------|---------|-------------|
|
|
398
|
+
| `registrarIdType` | `'int' \| 'string'` | `'int'` | Type of the `registrarId` field |
|
|
399
|
+
| `sharded` | `boolean` | `undefined` | Use `ViewLinkField` instead of `LinkField` for `registrarTypeId` |
|
|
400
|
+
|
|
401
|
+
**When `registrarDepended = true`**, auto-adds:
|
|
402
|
+
- `date` (datetime, required)
|
|
403
|
+
- `registrarTypeId` (string link/view-link to `entities`)
|
|
404
|
+
- `registrarId` (int or string)
|
|
405
|
+
- `row` (int, default `1`)
|
|
406
|
+
- Unique constraint on `[registrarTypeId, registrarId, row]`
|
|
407
|
+
- **Also sets `creatableByUser(false)`, `updatableByUser(false)`, `removableByUser(false)`**
|
|
408
|
+
|
|
409
|
+
Same dimension/resource API as `InfoRegistry`.
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Report {#report}
|
|
414
|
+
|
|
415
|
+
Created via `system.addReport(name, title?, options?)`.
|
|
416
|
+
|
|
417
|
+
A report is a **read-only UI page** with no storage, no CRUD GraphQL, and no Prisma
|
|
418
|
+
model. It generates a page and a `read` permission automatically — see
|
|
419
|
+
[11-permissions.md](./11-permissions.md) for how permissions are structured and enforced.
|
|
420
|
+
|
|
421
|
+
Reports do not inherit from `BaseSavableEntityBuilder` — they have no fields, no
|
|
422
|
+
methods, and no storage configuration.
|
|
423
|
+
|
|
424
|
+
Use for: dashboards, custom analytics pages, external data views.
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Anti-patterns
|
|
429
|
+
|
|
430
|
+
### Using `Document` for reference data
|
|
431
|
+
|
|
432
|
+
**Wrong:** creating a `Document` to store countries or categories.
|
|
433
|
+
|
|
434
|
+
**Why:** Documents auto-add a `date` field and are designed for transactions. Reference
|
|
435
|
+
data has no meaningful transaction date.
|
|
436
|
+
|
|
437
|
+
**Correct:** use `Catalog` for lookup / reference data.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
### Using `Catalog` for transactional data
|
|
442
|
+
|
|
443
|
+
**Wrong:** creating a `Catalog` to store orders.
|
|
444
|
+
|
|
445
|
+
**Why:** Catalogs have no `date` field, no registry linking, and no sharding support
|
|
446
|
+
designed for transactions.
|
|
447
|
+
|
|
448
|
+
**Correct:** use `Document` for transactional entities.
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
### Forgetting `system.addDatabase()` before `setDatabase()`
|
|
453
|
+
|
|
454
|
+
**Wrong:**
|
|
455
|
+
```ts
|
|
456
|
+
const orders = system.addDocument('orders')
|
|
457
|
+
orders.setDatabase('archive') // throws at build(): 'archive' is not registered
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Correct:**
|
|
461
|
+
```ts
|
|
462
|
+
system.addDatabase('archive')
|
|
463
|
+
const orders = system.addDocument('orders')
|
|
464
|
+
orders.setDatabase('archive')
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
> **Examples:** this file intentionally omits code examples. Add real usage examples
|
|
470
|
+
> from the consuming project here. A reference metadata file is typically at
|
|
471
|
+
> `src/meta/metadata.ts` in the meta project.
|