@lotics/cli 0.315.18 → 0.316.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/examples.md CHANGED
@@ -1,17 +1,20 @@
1
1
  # Worked examples
2
2
 
3
- Each section is one treatment, cut from a complete model of an invented business: the job, the keys to
4
- read, where the treatment is wrong, and the excerpt — an app the check passes with no finding, as stated;
5
- the `records` of every entity it draws; those entities' fields it names, by what each holds and how it is
6
- drawn; and the templates it makes. Left out: labels, descriptions, `required`, `unique`, `default`, a link's
7
- pairing, how a computed field computes, and template contents. Choosing among them: the `design` reference.
8
- Every key: the `model` reference, at the pages each section names under **Keys**.
3
+ Each section is one treatment, cut from a complete workspace of an invented business: the job, the keys to
4
+ read, where the treatment is wrong, and the excerpt — the app's tree, the App node `set_app` takes as `body`,
5
+ which the check passes with no finding; the tables it draws as the items of a `set_tables` call, each with its
6
+ `title` and `records`; those tables' fields it names, by what each holds and how it is drawn; and the templates it makes.
7
+ Left out, for the page's size: a Section that carries nothing of the treatment, every `label` and `description` of a table or field, `required`, `unique`, `default`, a link's
8
+ `pair`, how a computed field computes, the `tables` and `templates` maps an App adds (ids from the
9
+ `set_tables` answer), and template contents — a call states each table's and field's `label`. Choosing
10
+ among them: the `design` reference. Every key: the `model` reference, at the pages each section names under
11
+ **Keys**.
9
12
 
10
13
  ## Calendar
11
14
 
12
15
  **The job.** Book visits on the days they happen, and see a week's work at a glance.
13
16
 
14
- **Read.** `register.layout: "calendar"`; each visit stands over its span because its `records` line holds two dates (`scheduled_for`, `ends_at`).
17
+ **Read.** `layout: "calendar"` on the List; each visit stands over its span because its `records` line holds two dates (`scheduled_for`, `ends_at`).
15
18
 
16
19
  **Keys.** `model/calendar`
17
20
 
@@ -19,76 +22,159 @@ Every key: the `model` reference, at the pages each section names under **Keys**
19
22
 
20
23
  ```json
21
24
  {
22
- "entities": [
25
+ "tables": [
23
26
  {
24
- "alias": "service_visit",
25
- "fields": [
26
- {"alias": "customer", "type": "select_record_link", "target_entity": "customer", "cardinality": "one"},
27
- {"alias": "scheduled_for", "type": "date", "format": "datetime"},
28
- {"alias": "ends_at", "type": "date", "format": "datetime"},
29
- {"alias": "engineer", "type": "select_record_link", "target_entity": "engineer", "cardinality": "one"},
30
- {
31
- "alias": "job",
27
+ "ref": "service_visit",
28
+ "title": "customer",
29
+ "fields": {
30
+ "customer": {"type": "link", "table": "customer", "multi": false},
31
+ "scheduled_for": {"type": "date", "format": "datetime"},
32
+ "ends_at": {"type": "date", "format": "datetime"},
33
+ "engineer": {"type": "link", "table": "engineer", "multi": false},
34
+ "job": {
32
35
  "type": "select",
33
36
  "options": [{"alias": "inspection", "color": "sky"}, {"alias": "repair", "color": "orange"}]
34
37
  },
35
- {"alias": "booked_on", "type": "date"},
36
- {"alias": "parts_ready_on", "type": "date"},
37
- {"alias": "arrived_on", "type": "date"},
38
- {"alias": "hours", "type": "number", "notation": {"style": "unit", "unit": "h"}},
39
- {"alias": "report", "type": "text", "format": "markdown"},
40
- {"alias": "completed_on", "type": "date"}
41
- ]
42
- }
43
- ],
44
- "records": {
45
- "service_visit": {
46
- "title": "customer",
47
- "subtitle": ["scheduled_for", "ends_at"],
48
- "status": {
49
- "milestones": ["booked_on", {"field": "parts_ready_on", "when": {"job": ["repair"]}}, "arrived_on", "completed_on"],
50
- "closed": ["completed_on"]
38
+ "stage": {
39
+ "type": "select",
40
+ "options": [
41
+ {"alias": "booked_on", "color": "blue"},
42
+ {"alias": "parts_ready_on", "color": "blue"},
43
+ {"alias": "arrived_on", "color": "blue"},
44
+ {"alias": "completed_on", "color": "green"}
45
+ ]
46
+ },
47
+ "booked_on": {"type": "date"},
48
+ "parts_ready_on": {"type": "date"},
49
+ "arrived_on": {"type": "date"},
50
+ "hours": {"type": "number", "notation": {"style": "unit", "unit": "h"}},
51
+ "report": {"type": "text", "format": "markdown"},
52
+ "completed_on": {"type": "date"}
51
53
  },
52
- "figure": "hours",
53
- "due": ["scheduled_for"]
54
+ "records": {
55
+ "subtitle": ["scheduled_for", "ends_at"],
56
+ "figure": "hours",
57
+ "due": ["scheduled_for"],
58
+ "status": {
59
+ "field": "stage",
60
+ "closed": ["completed_on"],
61
+ "dates": {
62
+ "booked_on": "booked_on",
63
+ "parts_ready_on": "parts_ready_on",
64
+ "arrived_on": "arrived_on",
65
+ "completed_on": "completed_on"
66
+ }
67
+ }
68
+ }
54
69
  }
55
- },
56
- "apps": [
57
- {
58
- "alias": "visits",
59
- "name": "Service visits",
60
- "icon": "calendar",
61
- "theme": {"color": "emerald"},
62
- "entity": "service_visit",
63
- "register": {
70
+ ],
71
+ "body": {
72
+ "type": "App",
73
+ "key": "visits",
74
+ "name": "Service visits",
75
+ "icon": "calendar",
76
+ "color": "emerald",
77
+ "children": [
78
+ {
79
+ "type": "List",
80
+ "entity": "service_visit",
81
+ "row": {"title": {"add": ["name", "credit_limit"]}},
64
82
  "columns": ["engineer", "job"],
65
83
  "filters": ["customer"],
66
84
  "sort": {"field": "scheduled_for"},
67
85
  "layout": "calendar",
68
- "create": ["customer", "job", "scheduled_for", "ends_at", "engineer"],
69
- "readings": [
70
- {"metric": "service_visit", "where": {"job": ["repair"]}, "label": "Repairs"},
71
- {"breakdown": "service_visit", "by": "engineer", "value": "hours"}
72
- ]
73
- },
74
- "record": {
75
- "sections": [
76
- {"title": "Visit", "at": ["booked_on", "parts_ready_on"], "fields": ["job"], "acts": ["reschedule"]},
77
- {"title": "Report", "at": ["arrived_on"], "fields": ["engineer", "report"], "acts": ["complete"]}
86
+ "create": [
87
+ "customer",
88
+ "job",
89
+ "scheduled_for",
90
+ "ends_at",
91
+ {"field": "engineer", "add": ["name", {"field": "base", "add": ["name", "city"]}]}
92
+ ],
93
+ "children": [
94
+ {"type": "Reading", "key": "register_reading_0", "label": "Repairs", "where": {"job": ["repair"]}},
95
+ {"type": "Reading", "key": "register_reading_1", "value": "hours", "by": "engineer"},
96
+ {
97
+ "type": "Record",
98
+ "children": [
99
+ {
100
+ "type": "Section",
101
+ "key": "visit",
102
+ "title": "Visit",
103
+ "at": ["booked_on", "parts_ready_on"],
104
+ "children": [
105
+ "job",
106
+ "booked_on",
107
+ "parts_ready_on",
108
+ {
109
+ "type": "Act",
110
+ "key": "reschedule",
111
+ "label": "Reschedule",
112
+ "asks": ["scheduled_for", "ends_at"],
113
+ "when": {"stage": ["booked_on", "parts_ready_on"]}
114
+ },
115
+ {
116
+ "type": "Act",
117
+ "key": "to_booked_on",
118
+ "label": "Booked",
119
+ "when": {"stage": {"empty": true}},
120
+ "asks": [{"field": "booked_on", "starts": "today"}],
121
+ "set": {"stage": "booked_on"}
122
+ },
123
+ {
124
+ "type": "Act",
125
+ "key": "to_parts_ready_on",
126
+ "label": "Parts ready",
127
+ "when": {"stage": ["booked_on"], "job": ["repair"]},
128
+ "asks": [{"field": "parts_ready_on", "starts": "today"}],
129
+ "set": {"stage": "parts_ready_on"}
130
+ },
131
+ {
132
+ "type": "Act",
133
+ "key": "to_arrived_on",
134
+ "label": "On site",
135
+ "when": {"stage": ["booked_on"], "job": ["inspection"]},
136
+ "asks": [{"field": "arrived_on", "starts": "today"}],
137
+ "set": {"stage": "arrived_on"}
138
+ },
139
+ {
140
+ "type": "Act",
141
+ "key": "to_arrived_on_2",
142
+ "label": "On site",
143
+ "when": {"stage": ["parts_ready_on"]},
144
+ "asks": [{"field": "arrived_on", "starts": "today"}],
145
+ "set": {"stage": "arrived_on"}
146
+ }
147
+ ]
148
+ },
149
+ {
150
+ "type": "Section",
151
+ "key": "report",
152
+ "title": "Report",
153
+ "at": ["arrived_on"],
154
+ "children": [
155
+ "engineer",
156
+ "arrived_on",
157
+ {"field": "report", "block": false},
158
+ {
159
+ "type": "Act",
160
+ "key": "complete",
161
+ "label": "Complete visit",
162
+ "requires": ["engineer"],
163
+ "asks": [
164
+ "hours",
165
+ {"input": "summary", "label": "What was done", "type": "long_text", "required": true}
166
+ ],
167
+ "set": {"report": {"input": "summary"}, "stage": "completed_on"},
168
+ "when": {"stage": ["arrived_on"]}
169
+ }
170
+ ]
171
+ }
172
+ ]
173
+ }
78
174
  ]
79
- },
80
- "acts": [
81
- {
82
- "alias": "complete",
83
- "label": "Complete visit",
84
- "requires": ["engineer"],
85
- "asks": ["hours", {"input": "summary", "label": "What was done", "type": "long_text", "required": true}],
86
- "set": {"report": "input:summary", "completed_on": "now"}
87
- },
88
- {"alias": "reschedule", "label": "Reschedule", "asks": ["scheduled_for", "ends_at"]}
89
- ]
90
- }
91
- ]
175
+ }
176
+ ]
177
+ }
92
178
  }
93
179
  ```
94
180
 
@@ -96,7 +182,7 @@ Every key: the `model` reference, at the pages each section names under **Keys**
96
182
 
97
183
  **The job.** Put each service visit on an engineer's day by the hour, and see at once who is free between visits.
98
184
 
99
- **Read.** `register.layout: "lanes"` with `lanes` naming the one-row link to the resource (the engineer); every engineer stands as a lane, empty or not.
185
+ **Read.** `layout` `lanes` with `by` naming the one-row link to the resource (the engineer); every engineer stands as a lane, empty or not.
100
186
 
101
187
  **Keys.** `model/lanes`
102
188
 
@@ -104,64 +190,141 @@ Every key: the `model` reference, at the pages each section names under **Keys**
104
190
 
105
191
  ```json
106
192
  {
107
- "entities": [
193
+ "tables": [
108
194
  {
109
- "alias": "service_visit",
110
- "fields": [
111
- {"alias": "customer", "type": "select_record_link", "target_entity": "customer", "cardinality": "one"},
112
- {"alias": "scheduled_for", "type": "date", "format": "datetime"},
113
- {"alias": "ends_at", "type": "date", "format": "datetime"},
114
- {"alias": "engineer", "type": "select_record_link", "target_entity": "engineer", "cardinality": "one"},
115
- {
116
- "alias": "job",
195
+ "ref": "service_visit",
196
+ "title": "customer",
197
+ "fields": {
198
+ "customer": {"type": "link", "table": "customer", "multi": false},
199
+ "scheduled_for": {"type": "date", "format": "datetime"},
200
+ "ends_at": {"type": "date", "format": "datetime"},
201
+ "engineer": {"type": "link", "table": "engineer", "multi": false},
202
+ "job": {
117
203
  "type": "select",
118
204
  "options": [{"alias": "inspection", "color": "sky"}, {"alias": "repair", "color": "orange"}]
119
205
  },
120
- {"alias": "booked_on", "type": "date"},
121
- {"alias": "parts_ready_on", "type": "date"},
122
- {"alias": "arrived_on", "type": "date"},
123
- {"alias": "hours", "type": "number", "notation": {"style": "unit", "unit": "h"}},
124
- {"alias": "completed_on", "type": "date"}
125
- ]
206
+ "stage": {
207
+ "type": "select",
208
+ "options": [
209
+ {"alias": "booked_on", "color": "blue"},
210
+ {"alias": "parts_ready_on", "color": "blue"},
211
+ {"alias": "arrived_on", "color": "blue"},
212
+ {"alias": "completed_on", "color": "green"}
213
+ ]
214
+ },
215
+ "booked_on": {"type": "date"},
216
+ "parts_ready_on": {"type": "date"},
217
+ "arrived_on": {"type": "date"},
218
+ "hours": {"type": "number", "notation": {"style": "unit", "unit": "h"}},
219
+ "completed_on": {"type": "date"}
220
+ },
221
+ "records": {
222
+ "subtitle": ["scheduled_for", "ends_at"],
223
+ "figure": "hours",
224
+ "due": ["scheduled_for"],
225
+ "status": {
226
+ "field": "stage",
227
+ "closed": ["completed_on"],
228
+ "dates": {
229
+ "booked_on": "booked_on",
230
+ "parts_ready_on": "parts_ready_on",
231
+ "arrived_on": "arrived_on",
232
+ "completed_on": "completed_on"
233
+ }
234
+ }
235
+ }
126
236
  },
127
237
  {
128
- "alias": "engineer",
129
- "fields": [
130
- {"alias": "name", "type": "text"},
131
- {"alias": "base", "type": "select_record_link", "target_entity": "warehouse", "cardinality": "one"}
132
- ]
238
+ "ref": "engineer",
239
+ "title": "name",
240
+ "fields": {"name": {"type": "text"}, "base": {"type": "link", "table": "warehouse", "multi": false}},
241
+ "records": {"subtitle": ["base"], "party": "person"}
133
242
  }
134
243
  ],
135
- "records": {
136
- "service_visit": {
137
- "title": "customer",
138
- "subtitle": ["scheduled_for", "ends_at"],
139
- "status": {
140
- "milestones": ["booked_on", {"field": "parts_ready_on", "when": {"job": ["repair"]}}, "arrived_on", "completed_on"],
141
- "closed": ["completed_on"]
142
- },
143
- "figure": "hours",
144
- "due": ["scheduled_for"]
145
- },
146
- "engineer": {"title": "name", "subtitle": ["base"], "party": "person"}
147
- },
148
- "apps": [
149
- {
150
- "alias": "dispatch",
151
- "name": "Dispatch",
152
- "icon": "wrench",
153
- "theme": {"color": "sky"},
154
- "entity": "service_visit",
155
- "register": {
244
+ "body": {
245
+ "type": "App",
246
+ "key": "dispatch",
247
+ "name": "Dispatch",
248
+ "icon": "wrench",
249
+ "color": "sky",
250
+ "children": [
251
+ {
252
+ "type": "List",
253
+ "entity": "service_visit",
254
+ "row": {
255
+ "title": {"add": ["name", "credit_limit"]},
256
+ "subtitle": [{"field": "scheduled_for", "edit": false}, {"field": "ends_at", "edit": false}]
257
+ },
156
258
  "columns": ["job"],
157
259
  "search": ["customer"],
158
- "layout": "lanes",
159
- "lanes": "engineer",
160
- "create": ["customer", "job"]
161
- },
162
- "record": {"door": "drawer", "sections": [{"title": "Booking", "fields": ["engineer", "job"]}]}
163
- }
164
- ]
260
+ "layout": {"lanes": {"by": "engineer"}},
261
+ "create": [
262
+ {"field": "engineer", "add": ["name", {"field": "base", "add": ["name", "city"]}]},
263
+ "scheduled_for",
264
+ "ends_at",
265
+ "customer",
266
+ "job"
267
+ ],
268
+ "children": [
269
+ {
270
+ "type": "Record",
271
+ "door": "drawer",
272
+ "children": [
273
+ {
274
+ "type": "Section",
275
+ "key": "booking",
276
+ "title": "Booking",
277
+ "children": [
278
+ "engineer",
279
+ "job",
280
+ {
281
+ "type": "Act",
282
+ "key": "to_booked_on",
283
+ "label": "Booked",
284
+ "when": {"stage": {"empty": true}},
285
+ "asks": [{"field": "booked_on", "starts": "today"}],
286
+ "set": {"stage": "booked_on"}
287
+ },
288
+ {
289
+ "type": "Act",
290
+ "key": "to_parts_ready_on",
291
+ "label": "Parts ready",
292
+ "when": {"stage": ["booked_on"], "job": ["repair"]},
293
+ "asks": [{"field": "parts_ready_on", "starts": "today"}],
294
+ "set": {"stage": "parts_ready_on"}
295
+ },
296
+ {
297
+ "type": "Act",
298
+ "key": "to_arrived_on",
299
+ "label": "On site",
300
+ "when": {"stage": ["booked_on"], "job": ["inspection"]},
301
+ "asks": [{"field": "arrived_on", "starts": "today"}],
302
+ "set": {"stage": "arrived_on"}
303
+ },
304
+ {
305
+ "type": "Act",
306
+ "key": "to_arrived_on_2",
307
+ "label": "On site",
308
+ "when": {"stage": ["parts_ready_on"]},
309
+ "asks": [{"field": "arrived_on", "starts": "today"}],
310
+ "set": {"stage": "arrived_on"}
311
+ },
312
+ {
313
+ "type": "Act",
314
+ "key": "to_completed_on",
315
+ "label": "Completed",
316
+ "when": {"stage": ["arrived_on"]},
317
+ "asks": [{"field": "completed_on", "starts": "today"}],
318
+ "set": {"stage": "completed_on"}
319
+ }
320
+ ]
321
+ }
322
+ ]
323
+ }
324
+ ]
325
+ }
326
+ ]
327
+ }
165
328
  }
166
329
  ```
167
330
 
@@ -169,7 +332,7 @@ Every key: the `model` reference, at the pages each section names under **Keys**
169
332
 
170
333
  **The job.** Read every hire as a bar from the day the machine goes out to the day it is due back, against today.
171
334
 
172
- **Read.** `register.layout: "gantt"` and `register.gantt.start`; with no end stated, the bar ends at the entity's first `due`.
335
+ **Read.** `layout` `gantt` with its `start`; with no end stated, the bar ends at the entity's first `due`. The day a hire comes back (`ngay_tra`) is stamped by the status's `dates`, so no act sets it.
173
336
 
174
337
  **Keys.** `model/gantt`
175
338
 
@@ -177,19 +340,14 @@ Every key: the `model` reference, at the pages each section names under **Keys**
177
340
 
178
341
  ```json
179
342
  {
180
- "entities": [
343
+ "tables": [
181
344
  {
182
- "alias": "phieu_thue",
183
- "fields": [
184
- {
185
- "alias": "khach_hang",
186
- "type": "select_record_link",
187
- "target_entity": "khach_hang",
188
- "cardinality": "one"
189
- },
190
- {"alias": "so_phieu", "type": "text"},
191
- {
192
- "alias": "trang_thai",
345
+ "ref": "phieu_thue",
346
+ "title": "khach_hang",
347
+ "fields": {
348
+ "khach_hang": {"type": "link", "table": "khach_hang", "multi": false},
349
+ "so_phieu": {"type": "text"},
350
+ "trang_thai": {
193
351
  "type": "select",
194
352
  "options": [
195
353
  {"alias": "moi", "color": "slate"},
@@ -198,12 +356,11 @@ Every key: the `model` reference, at the pages each section names under **Keys**
198
356
  {"alias": "huy", "color": "gray"}
199
357
  ]
200
358
  },
201
- {"alias": "phu_trach", "type": "select_member"},
202
- {"alias": "ngay_giao", "type": "date"},
203
- {"alias": "han_tra", "type": "date"},
204
- {"alias": "ngay_tra", "type": "date"},
205
- {
206
- "alias": "tinh_trang_tra",
359
+ "phu_trach": {"type": "member"},
360
+ "ngay_giao": {"type": "date"},
361
+ "han_tra": {"type": "date"},
362
+ "ngay_tra": {"type": "date"},
363
+ "tinh_trang_tra": {
207
364
  "type": "select",
208
365
  "options": [
209
366
  {"alias": "tot", "color": "green"},
@@ -211,62 +368,81 @@ Every key: the `model` reference, at the pages each section names under **Keys**
211
368
  {"alias": "hu_hong", "color": "red"}
212
369
  ]
213
370
  },
214
- {"alias": "so_ngay", "type": "formula"},
215
- {"alias": "tong_tien", "type": "rollup"}
216
- ]
371
+ "so_ngay": {"type": "formula"},
372
+ "tong_tien": {"type": "rollup"}
373
+ },
374
+ "records": {
375
+ "subtitle": ["so_phieu"],
376
+ "figure": "tong_tien",
377
+ "due": ["han_tra"],
378
+ "status": {"field": "trang_thai", "closed": ["da_tra", "huy"], "dates": {"da_tra": "ngay_tra"}}
379
+ }
217
380
  }
218
381
  ],
219
- "records": {
220
- "phieu_thue": {
221
- "title": "khach_hang",
222
- "subtitle": ["so_phieu"],
223
- "status": {"field": "trang_thai", "closed": ["da_tra", "huy"], "history": "lich_su_phieu"},
224
- "figure": "tong_tien",
225
- "due": ["han_tra"]
226
- }
227
- },
228
- "apps": [
229
- {
230
- "alias": "tien_do_thue",
231
- "name": "Tiến độ cho thuê",
232
- "icon": "calendar-range",
233
- "theme": {"color": "teal"},
234
- "entity": "phieu_thue",
235
- "register": {
382
+ "body": {
383
+ "type": "App",
384
+ "key": "tien_do_thue",
385
+ "name": "Tiến độ cho thuê",
386
+ "icon": "calendar-range",
387
+ "color": "teal",
388
+ "children": [
389
+ {
390
+ "type": "List",
391
+ "entity": "phieu_thue",
392
+ "row": {"title": {"add": true}},
236
393
  "columns": ["phu_trach"],
237
394
  "filters": ["phu_trach", "khach_hang"],
238
- "layout": "gantt",
239
- "gantt": {"start": "ngay_giao"},
240
- "remove": false,
395
+ "layout": {"gantt": {"start": "ngay_giao"}},
241
396
  "create": false,
242
- "readings": [
243
- {"metric": "phieu_thue", "where": {"trang_thai": ["dang_thue"]}, "label": "Phiếu đang cho thuê"},
397
+ "remove": false,
398
+ "children": [
399
+ {
400
+ "type": "Reading",
401
+ "key": "register_reading_0",
402
+ "label": "Phiếu đang cho thuê",
403
+ "where": {"trang_thai": ["dang_thue"]}
404
+ },
244
405
  {
245
- "metric": "phieu_thue",
406
+ "type": "Reading",
407
+ "key": "register_reading_1",
408
+ "label": "Tiền thuê đang chạy",
246
409
  "value": "tong_tien",
247
- "where": {"trang_thai": ["dang_thue"]},
248
- "label": "Tiền thuê đang chạy"
410
+ "where": {"trang_thai": ["dang_thue"]}
411
+ },
412
+ {
413
+ "type": "Record",
414
+ "children": [
415
+ {
416
+ "type": "Section",
417
+ "key": "phieu_thue",
418
+ "title": "Phiếu thuê",
419
+ "children": ["ngay_giao", "han_tra"]
420
+ },
421
+ {
422
+ "type": "Section",
423
+ "key": "dang_thue",
424
+ "title": "Đang thuê",
425
+ "at": ["dang_thue"],
426
+ "children": [
427
+ "so_ngay",
428
+ "ngay_tra",
429
+ {
430
+ "type": "Act",
431
+ "key": "nhan_tra",
432
+ "label": "Nhận máy trả",
433
+ "when": {"trang_thai": ["dang_thue"]},
434
+ "asks": ["tinh_trang_tra"],
435
+ "set": {"trang_thai": "da_tra"},
436
+ "confirm": true
437
+ }
438
+ ]
439
+ }
440
+ ]
249
441
  }
250
442
  ]
251
- },
252
- "record": {
253
- "sections": [
254
- {"title": "Phiếu thuê", "fields": ["ngay_giao", "han_tra"]},
255
- {"title": "Đang thuê", "at": ["dang_thue"], "fields": ["so_ngay", "ngay_tra"], "acts": ["nhan_tra"]}
256
- ]
257
- },
258
- "acts": [
259
- {
260
- "alias": "nhan_tra",
261
- "label": "Nhận máy trả",
262
- "when": {"trang_thai": ["dang_thue"]},
263
- "asks": ["tinh_trang_tra"],
264
- "set": {"trang_thai": "da_tra", "ngay_tra": "now"},
265
- "confirm": true
266
- }
267
- ]
268
- }
269
- ]
443
+ }
444
+ ]
445
+ }
270
446
  }
271
447
  ```
272
448
 
@@ -274,7 +450,7 @@ Every key: the `model` reference, at the pages each section names under **Keys**
274
450
 
275
451
  **The job.** See who works which shift on each day of the week or the month.
276
452
 
277
- **Read.** `register.layout: "roster"` over the people; `register.roster` names the child whose rows fill the days — one link back and a date on its line.
453
+ **Read.** `layout: "roster"` over the people; the child List (`lich_truc`) is the rows that fill the days — one link back and a date on its line.
278
454
 
279
455
  **Keys.** `model/roster`
280
456
 
@@ -282,13 +458,13 @@ Every key: the `model` reference, at the pages each section names under **Keys**
282
458
 
283
459
  ```json
284
460
  {
285
- "entities": [
461
+ "tables": [
286
462
  {
287
- "alias": "nha_si",
288
- "fields": [
289
- {"alias": "ho_ten", "type": "text"},
290
- {
291
- "alias": "chuyen_mon",
463
+ "ref": "nha_si",
464
+ "title": "ho_ten",
465
+ "fields": {
466
+ "ho_ten": {"type": "text"},
467
+ "chuyen_mon": {
292
468
  "type": "select",
293
469
  "options": [
294
470
  {"alias": "tong_quat", "color": "blue"},
@@ -297,15 +473,16 @@ Every key: the `model` reference, at the pages each section names under **Keys**
297
473
  {"alias": "phuc_hinh", "color": "orange"}
298
474
  ]
299
475
  },
300
- {"alias": "dien_thoai", "type": "text"}
301
- ]
476
+ "dien_thoai": {"type": "text"}
477
+ },
478
+ "records": {"subtitle": ["chuyen_mon"], "party": "person"}
302
479
  },
303
480
  {
304
- "alias": "lich_truc",
305
- "fields": [
306
- {"alias": "nha_si", "type": "select_record_link", "target_entity": "nha_si", "cardinality": "one"},
307
- {
308
- "alias": "ca",
481
+ "ref": "lich_truc",
482
+ "title": "ca",
483
+ "fields": {
484
+ "nha_si": {"type": "link", "table": "nha_si", "multi": false},
485
+ "ca": {
309
486
  "type": "select",
310
487
  "options": [
311
488
  {"alias": "sang", "color": "blue"},
@@ -314,30 +491,37 @@ Every key: the `model` reference, at the pages each section names under **Keys**
314
491
  {"alias": "nghi", "color": "gray"}
315
492
  ]
316
493
  },
317
- {"alias": "ngay", "type": "date"},
318
- {"alias": "ghe", "type": "select_record_link", "target_entity": "ghe", "cardinality": "one"}
319
- ]
494
+ "ngay": {"type": "date"},
495
+ "ghe": {"type": "link", "table": "ghe", "multi": false}
496
+ },
497
+ "records": {"subtitle": ["ngay", "ghe"]}
320
498
  }
321
499
  ],
322
- "records": {
323
- "nha_si": {"title": "ho_ten", "subtitle": ["chuyen_mon"], "party": "person"},
324
- "lich_truc": {"title": "ca", "subtitle": ["ngay", "ghe"]}
325
- },
326
- "apps": [
327
- {
328
- "alias": "lich_truc",
329
- "name": "Lịch trực",
330
- "icon": "users",
331
- "theme": {"color": "indigo"},
332
- "entity": "nha_si",
333
- "register": {
500
+ "body": {
501
+ "type": "App",
502
+ "key": "lich_truc",
503
+ "name": "Lịch trực",
504
+ "icon": "users",
505
+ "color": "indigo",
506
+ "children": [
507
+ {
508
+ "type": "List",
509
+ "entity": "nha_si",
334
510
  "filters": ["chuyen_mon"],
335
511
  "layout": "roster",
336
- "roster": "lich_truc",
337
- "create": ["ho_ten", "chuyen_mon", "dien_thoai"]
512
+ "create": ["ho_ten", "chuyen_mon", "dien_thoai"],
513
+ "children": [
514
+ {
515
+ "type": "List",
516
+ "entity": "lich_truc",
517
+ "row": {"subtitle": ["ngay", {"field": "ghe", "add": ["ten", "phong"]}]},
518
+ "create": true,
519
+ "remove": true
520
+ }
521
+ ]
338
522
  }
339
- }
340
- ]
523
+ ]
524
+ }
341
525
  }
342
526
  ```
343
527
 
@@ -345,29 +529,24 @@ Every key: the `model` reference, at the pages each section names under **Keys**
345
529
 
346
530
  **The job.** Find an item by its picture, and count what the shelf holds.
347
531
 
348
- **Read.** `register.layout: "cards"` over rows whose `records.image` is a photo; `scope` keeps the app inside one warehouse at a time.
532
+ **Read.** `layout: "cards"` over rows whose `records.image` is a photo; `scope` keeps the app inside one warehouse at a time.
349
533
 
350
- **Keys.** `model/cards`, `model/records`, `model/apps`
534
+ **Keys.** `model/cards`, `model/records`, `model/app-node`
351
535
 
352
536
  **Not when.** Rows with no picture — cards of words are a table drawn worse.
353
537
 
354
538
  ```json
355
539
  {
356
- "entities": [
540
+ "tables": [
357
541
  {
358
- "alias": "stock_item",
359
- "fields": [
360
- {"alias": "name", "type": "text"},
361
- {
362
- "alias": "warehouse",
363
- "type": "select_record_link",
364
- "target_entity": "warehouse",
365
- "cardinality": "one"
366
- },
367
- {"alias": "photo", "type": "files"},
368
- {"alias": "sku", "type": "text"},
369
- {
370
- "alias": "category",
542
+ "ref": "stock_item",
543
+ "title": "name",
544
+ "fields": {
545
+ "name": {"type": "text"},
546
+ "warehouse": {"type": "link", "table": "warehouse", "multi": false},
547
+ "photo": {"type": "files"},
548
+ "sku": {"type": "text"},
549
+ "category": {
371
550
  "type": "select",
372
551
  "options": [
373
552
  {"alias": "pumps", "color": "blue"},
@@ -376,87 +555,126 @@ Every key: the `model` reference, at the pages each section names under **Keys**
376
555
  {"alias": "motors", "color": "violet"}
377
556
  ]
378
557
  },
379
- {"alias": "on_hand", "type": "number"},
380
- {"alias": "minimum", "type": "number"},
381
- {"alias": "price", "type": "number", "notation": {"style": "currency", "currency": "USD"}},
382
- {"alias": "supplier_page", "type": "text", "format": "link"},
383
- {"alias": "last_counted", "type": "date"},
384
- {"alias": "counted_by", "type": "select_member"},
385
- {"alias": "count_note", "type": "text"}
386
- ]
558
+ "on_hand": {"type": "number"},
559
+ "minimum": {"type": "number"},
560
+ "price": {"type": "number", "notation": {"style": "currency", "currency": "USD"}},
561
+ "supplier_page": {"type": "text", "format": "link"},
562
+ "last_counted": {"type": "date"},
563
+ "counted_by": {"type": "member"},
564
+ "count_note": {"type": "text"}
565
+ },
566
+ "records": {
567
+ "subtitle": ["sku", "category"],
568
+ "image": "photo",
569
+ "figure": "on_hand",
570
+ "limits": {"on_hand": 400},
571
+ "gates": {"on_hand": "minimum"}
572
+ }
387
573
  },
388
574
  {
389
- "alias": "order_line",
390
- "fields": [
391
- {"alias": "item", "type": "select_record_link", "target_entity": "stock_item", "cardinality": "one"},
392
- {"alias": "order", "type": "select_record_link", "target_entity": "order", "cardinality": "one"},
393
- {"alias": "quantity", "type": "number"},
394
- {"alias": "shipped", "type": "number"},
395
- {"alias": "picked", "type": "number"},
396
- {"alias": "unit_price", "type": "number", "notation": {"style": "currency", "currency": "USD"}},
397
- {"alias": "amount", "type": "formula"},
398
- {"alias": "category", "type": "lookup"}
399
- ]
400
- }
401
- ],
402
- "records": {
403
- "stock_item": {
404
- "title": "name",
405
- "subtitle": ["sku", "category"],
406
- "image": "photo",
407
- "figure": "on_hand",
408
- "limits": {"on_hand": 400},
409
- "gates": {"on_hand": "minimum"}
410
- },
411
- "order_line": {
575
+ "ref": "order_line",
412
576
  "title": "item",
413
- "subtitle": ["order", "unit_price"],
414
- "figure": "amount",
415
- "limits": {"shipped": "quantity", "picked": "quantity"}
577
+ "fields": {
578
+ "item": {"type": "link", "table": "stock_item", "multi": false},
579
+ "order": {"type": "link", "table": "order", "multi": false},
580
+ "quantity": {"type": "number"},
581
+ "shipped": {"type": "number"},
582
+ "picked": {"type": "number"},
583
+ "unit_price": {"type": "number", "notation": {"style": "currency", "currency": "USD"}},
584
+ "amount": {"type": "formula", "notation": {"style": "currency", "currency": "USD"}},
585
+ "settled": {"type": "formula"},
586
+ "category": {"type": "lookup"}
587
+ },
588
+ "records": {
589
+ "subtitle": ["order", "unit_price"],
590
+ "figure": "amount",
591
+ "limits": {"shipped": "quantity", "picked": "quantity"}
592
+ }
416
593
  }
417
- },
418
- "apps": [
419
- {
420
- "alias": "stock",
421
- "name": "Stock",
422
- "icon": "boxes",
423
- "theme": {"color": "teal"},
424
- "entity": "stock_item",
425
- "scope": "warehouse",
426
- "register": {
594
+ ],
595
+ "body": {
596
+ "type": "App",
597
+ "key": "stock",
598
+ "name": "Stock",
599
+ "icon": "boxes",
600
+ "color": "teal",
601
+ "scope": "warehouse",
602
+ "children": [
603
+ {
604
+ "type": "List",
605
+ "entity": "stock_item",
427
606
  "columns": ["price"],
428
607
  "filters": ["on_hand"],
429
608
  "search": ["name", "sku"],
430
609
  "layout": "cards",
431
- "create": false,
432
- "export": true,
433
- "readings": [{"breakdown": "stock_item", "by": "category", "value": "on_hand"}]
434
- },
435
- "record": {
436
- "sections": [
437
- {"title": "Item", "fields": ["price", "minimum", "supplier_page"]},
438
- {"title": "Last count", "fields": ["last_counted", "counted_by", "count_note"], "acts": ["count"]},
439
- {"title": "On order", "blocks": [{"rows": "order_line", "columns": ["quantity"]}]}
440
- ]
441
- },
442
- "acts": [
443
- {
444
- "alias": "count",
445
- "label": "Count stock",
446
- "asks": [
447
- {"input": "counted", "label": "Units on the shelf", "type": "number", "required": true},
448
- {"input": "remark", "label": "Remark", "type": "long_text"}
449
- ],
450
- "set": {
451
- "on_hand": "input:counted",
452
- "last_counted": "now",
453
- "counted_by": "me",
454
- "count_note": "input:remark"
610
+ "children": [
611
+ {"type": "Reading", "key": "register_reading_0", "value": "on_hand", "by": "category"},
612
+ {"type": "Export", "key": "export"},
613
+ {
614
+ "type": "Record",
615
+ "children": [
616
+ {
617
+ "type": "Section",
618
+ "key": "item",
619
+ "title": "Item",
620
+ "children": ["price", "minimum", "supplier_page"]
621
+ },
622
+ {
623
+ "type": "Section",
624
+ "key": "last_count",
625
+ "title": "Last count",
626
+ "children": [
627
+ "last_counted",
628
+ "counted_by",
629
+ "count_note",
630
+ {
631
+ "type": "Act",
632
+ "key": "count",
633
+ "label": "Count stock",
634
+ "asks": [
635
+ {"input": "counted", "label": "Units on the shelf", "type": "number", "required": true},
636
+ {"input": "remark", "label": "Remark", "type": "long_text"}
637
+ ],
638
+ "set": {
639
+ "on_hand": {"input": "counted"},
640
+ "last_counted": {"now": true},
641
+ "counted_by": {"me": true},
642
+ "count_note": {"input": "remark"}
643
+ }
644
+ }
645
+ ]
646
+ },
647
+ {
648
+ "type": "Section",
649
+ "key": "on_order",
650
+ "title": "On order",
651
+ "children": [
652
+ {
653
+ "type": "List",
654
+ "entity": "order_line",
655
+ "row": {
656
+ "subtitle": [
657
+ {
658
+ "field": "order",
659
+ "add": [{"field": "customer", "add": ["name", "credit_limit"]}, "reference"]
660
+ },
661
+ "unit_price"
662
+ ]
663
+ },
664
+ "columns": ["quantity"],
665
+ "children": [
666
+ {"type": "Check", "field": "settled", "on": ["update"]},
667
+ {"type": "Record", "children": ["item", "order", "quantity", "shipped", "unit_price"]}
668
+ ]
669
+ }
670
+ ]
671
+ }
672
+ ]
455
673
  }
456
- }
457
- ]
458
- }
459
- ]
674
+ ]
675
+ }
676
+ ]
677
+ }
460
678
  }
461
679
  ```
462
680
 
@@ -464,23 +682,23 @@ Every key: the `model` reference, at the pages each section names under **Keys**
464
682
 
465
683
  **The job.** Keep each customer account — who they are, what they ordered, what was said to them.
466
684
 
467
- **Read.** `records.customer.party: "organization"` and its `image` (the logo); a `timeline` of touches, a `trend` of its orders, and its papers `expect`ed by kind.
685
+ **Read.** `records.customer.party: "organization"` and its `image` (the logo); a List with `layout: "timeline"` for the touches, a Reading `by` the day placed for its orders, and its papers `expect`ed by kind.
468
686
 
469
- **Keys.** `model/records`, `model/timeline-and-agenda`, `model/blocks`, `model/reading-blocks`
687
+ **Keys.** `model/records`, `model/layouts`, `model/list-node`, `model/reading-node`
470
688
 
471
689
  **Not when.** A list of customers is not a job by itself: the customer is a link on the desk whose rows name it, unless the job is the account itself.
472
690
 
473
691
  ```json
474
692
  {
475
- "entities": [
693
+ "tables": [
476
694
  {
477
- "alias": "customer",
478
- "fields": [
479
- {"alias": "name", "type": "text"},
480
- {"alias": "logo", "type": "files"},
481
- {"alias": "code", "type": "text"},
482
- {
483
- "alias": "tier",
695
+ "ref": "customer",
696
+ "title": "name",
697
+ "fields": {
698
+ "name": {"type": "text"},
699
+ "logo": {"type": "files"},
700
+ "code": {"type": "text"},
701
+ "tier": {
484
702
  "type": "select",
485
703
  "options": [
486
704
  {"alias": "key", "color": "violet"},
@@ -488,14 +706,14 @@ Every key: the `model` reference, at the pages each section names under **Keys**
488
706
  {"alias": "lapsed", "color": "gray"}
489
707
  ]
490
708
  },
491
- {"alias": "phone", "type": "text"},
492
- {"alias": "website", "type": "text", "format": "link"},
493
- {"alias": "account_manager", "type": "select_member"},
494
- {"alias": "credit_limit", "type": "number", "notation": {"style": "currency", "currency": "USD"}},
495
- {"alias": "outstanding", "type": "rollup"},
496
- {"alias": "over_limit", "type": "formula"},
497
- {
498
- "alias": "papers_needed",
709
+ "phone": {"type": "text"},
710
+ "website": {"type": "text", "format": "link"},
711
+ "account_manager": {"type": "member"},
712
+ "credit_limit": {"type": "number", "notation": {"style": "currency", "currency": "USD"}},
713
+ "orders": {"type": "link", "table": "order", "multi": true},
714
+ "outstanding": {"type": "rollup"},
715
+ "over_limit": {"type": "formula"},
716
+ "papers_needed": {
499
717
  "type": "select",
500
718
  "options": [
501
719
  {"alias": "credit_application", "color": "blue"},
@@ -504,42 +722,75 @@ Every key: the `model` reference, at the pages each section names under **Keys**
504
722
  ],
505
723
  "multi": true
506
724
  },
507
- {"alias": "notes", "type": "text", "format": "markdown"}
508
- ]
725
+ "notes": {"type": "text", "format": "markdown"}
726
+ },
727
+ "records": {
728
+ "subtitle": ["tier"],
729
+ "image": "logo",
730
+ "party": "organization",
731
+ "figure": "outstanding",
732
+ "limits": {"outstanding": "credit_limit"}
733
+ }
509
734
  },
510
735
  {
511
- "alias": "order",
512
- "fields": [
513
- {"alias": "customer", "type": "select_record_link", "target_entity": "customer", "cardinality": "one"},
514
- {"alias": "reference", "type": "text"},
515
- {
516
- "alias": "stage",
736
+ "ref": "order",
737
+ "title": "reference",
738
+ "fields": {
739
+ "customer": {"type": "link", "table": "customer", "multi": false},
740
+ "reference": {"type": "text"},
741
+ "stage": {
517
742
  "type": "select",
518
743
  "options": [
519
744
  {"alias": "draft", "color": "slate"},
520
745
  {"alias": "confirmed", "color": "blue"},
521
746
  {"alias": "picking", "color": "amber"},
522
747
  {"alias": "shipped", "color": "indigo"},
523
- {"alias": "delivered", "color": "green"},
748
+ {"alias": "received", "color": "green"},
524
749
  {"alias": "cancelled", "color": "rose"}
525
750
  ]
526
751
  },
527
- {"alias": "placed", "type": "date"},
528
- {"alias": "due_date", "type": "date"},
529
- {"alias": "total", "type": "rollup"},
530
- {"alias": "balance", "type": "formula"},
531
- {"alias": "units_ordered", "type": "rollup"},
532
- {"alias": "units_shipped", "type": "rollup"},
533
- {"alias": "units_picked", "type": "rollup"},
534
- {"alias": "notes", "type": "text", "format": "markdown"},
535
- {"alias": "paid_in_full", "type": "formula"}
536
- ]
752
+ "placed": {"type": "date"},
753
+ "due_date": {"type": "date"},
754
+ "handler": {"type": "member"},
755
+ "priority": {"type": "boolean"},
756
+ "lines": {"type": "link", "table": "order_line", "multi": true},
757
+ "payments": {"type": "link", "table": "payment", "multi": true},
758
+ "total": {"type": "rollup"},
759
+ "balance": {"type": "formula", "notation": {"style": "currency", "currency": "USD"}},
760
+ "units_ordered": {"type": "rollup"},
761
+ "units_shipped": {"type": "rollup"},
762
+ "units_picked": {"type": "rollup"},
763
+ "carrier": {"type": "text"},
764
+ "tracking_no": {"type": "text"},
765
+ "shipped_on": {"type": "date"},
766
+ "cancellation_reason": {"type": "text"},
767
+ "cancelled_by": {"type": "member"},
768
+ "replaced_by": {"type": "link", "table": "order", "multi": false},
769
+ "attachments": {"type": "files"},
770
+ "delivery_note": {"type": "files"},
771
+ "notes": {"type": "text", "format": "markdown"},
772
+ "paid_in_full": {"type": "formula"},
773
+ "has_payments": {"type": "formula"},
774
+ "reminder_ref": {"type": "text"}
775
+ },
776
+ "records": {
777
+ "subtitle": ["customer", "placed"],
778
+ "figure": "total",
779
+ "limits": {"units_shipped": "units_ordered", "units_picked": "units_ordered"},
780
+ "due": ["due_date"],
781
+ "status": {
782
+ "field": "stage",
783
+ "closed": ["received", "cancelled"],
784
+ "dates": {"shipped": "shipped_on"},
785
+ "history": "order_stage"
786
+ }
787
+ }
537
788
  },
538
789
  {
539
- "alias": "customer_paper",
540
- "fields": [
541
- {
542
- "alias": "kind",
790
+ "ref": "customer_paper",
791
+ "title": "kind",
792
+ "fields": {
793
+ "kind": {
543
794
  "type": "select",
544
795
  "options": [
545
796
  {"alias": "credit_application", "color": "blue"},
@@ -547,192 +798,246 @@ Every key: the `model` reference, at the pages each section names under **Keys**
547
798
  {"alias": "vat_certificate", "color": "teal"}
548
799
  ]
549
800
  },
550
- {"alias": "customer", "type": "select_record_link", "target_entity": "customer", "cardinality": "one"},
551
- {"alias": "received_on", "type": "date"},
552
- {"alias": "file", "type": "files"}
553
- ]
801
+ "customer": {"type": "link", "table": "customer", "multi": false},
802
+ "received_on": {"type": "date"},
803
+ "file": {"type": "files"}
804
+ },
805
+ "records": {"image": "file"}
554
806
  },
555
807
  {
556
- "alias": "touch",
557
- "fields": [
558
- {"alias": "said", "type": "text"},
559
- {"alias": "customer", "type": "select_record_link", "target_entity": "customer", "cardinality": "one"},
560
- {"alias": "by", "type": "select_member"}
561
- ]
808
+ "ref": "touch",
809
+ "title": "said",
810
+ "fields": {
811
+ "said": {"type": "text"},
812
+ "customer": {"type": "link", "table": "customer", "multi": false},
813
+ "on": {"type": "date"},
814
+ "by": {"type": "member"}
815
+ }
562
816
  },
563
817
  {
564
- "alias": "service_visit",
565
- "fields": [
566
- {"alias": "customer", "type": "select_record_link", "target_entity": "customer", "cardinality": "one"},
567
- {"alias": "scheduled_for", "type": "date", "format": "datetime"},
568
- {"alias": "ends_at", "type": "date", "format": "datetime"},
569
- {"alias": "engineer", "type": "select_record_link", "target_entity": "engineer", "cardinality": "one"},
570
- {
571
- "alias": "job",
818
+ "ref": "service_visit",
819
+ "title": "customer",
820
+ "fields": {
821
+ "customer": {"type": "link", "table": "customer", "multi": false},
822
+ "scheduled_for": {"type": "date", "format": "datetime"},
823
+ "ends_at": {"type": "date", "format": "datetime"},
824
+ "engineer": {"type": "link", "table": "engineer", "multi": false},
825
+ "job": {
572
826
  "type": "select",
573
827
  "options": [{"alias": "inspection", "color": "sky"}, {"alias": "repair", "color": "orange"}]
574
828
  },
575
- {"alias": "booked_on", "type": "date"},
576
- {"alias": "parts_ready_on", "type": "date"},
577
- {"alias": "arrived_on", "type": "date"},
578
- {"alias": "hours", "type": "number", "notation": {"style": "unit", "unit": "h"}},
579
- {"alias": "report", "type": "text", "format": "markdown"},
580
- {"alias": "completed_on", "type": "date"}
581
- ]
582
- }
583
- ],
584
- "records": {
585
- "customer": {
586
- "title": "name",
587
- "subtitle": ["tier"],
588
- "image": "logo",
589
- "party": "organization",
590
- "figure": "outstanding",
591
- "limits": {"outstanding": "credit_limit"}
592
- },
593
- "order": {
594
- "title": "reference",
595
- "subtitle": ["customer", "placed"],
596
- "status": {"field": "stage", "closed": ["delivered", "cancelled"], "history": "order_stage"},
597
- "figure": "total",
598
- "limits": {"units_shipped": "units_ordered", "units_picked": "units_ordered"},
599
- "due": ["due_date"]
600
- },
601
- "customer_paper": {"title": "kind", "image": "file"},
602
- "touch": {"title": "said"},
603
- "service_visit": {
604
- "title": "customer",
605
- "subtitle": ["scheduled_for", "ends_at"],
606
- "status": {
607
- "milestones": ["booked_on", {"field": "parts_ready_on", "when": {"job": ["repair"]}}, "arrived_on", "completed_on"],
608
- "closed": ["completed_on"]
829
+ "stage": {
830
+ "type": "select",
831
+ "options": [
832
+ {"alias": "booked_on", "color": "blue"},
833
+ {"alias": "parts_ready_on", "color": "blue"},
834
+ {"alias": "arrived_on", "color": "blue"},
835
+ {"alias": "completed_on", "color": "green"}
836
+ ]
837
+ },
838
+ "booked_on": {"type": "date"},
839
+ "parts_ready_on": {"type": "date"},
840
+ "arrived_on": {"type": "date"},
841
+ "hours": {"type": "number", "notation": {"style": "unit", "unit": "h"}},
842
+ "report": {"type": "text", "format": "markdown"},
843
+ "completed_on": {"type": "date"}
609
844
  },
610
- "figure": "hours",
611
- "due": ["scheduled_for"]
845
+ "records": {
846
+ "subtitle": ["scheduled_for", "ends_at"],
847
+ "figure": "hours",
848
+ "due": ["scheduled_for"],
849
+ "status": {
850
+ "field": "stage",
851
+ "closed": ["completed_on"],
852
+ "dates": {
853
+ "booked_on": "booked_on",
854
+ "parts_ready_on": "parts_ready_on",
855
+ "arrived_on": "arrived_on",
856
+ "completed_on": "completed_on"
857
+ }
858
+ }
859
+ }
612
860
  }
613
- },
614
- "apps": [
615
- {
616
- "alias": "customers",
617
- "name": "Customers",
618
- "icon": "users",
619
- "theme": {"color": "violet"},
620
- "entity": "customer",
621
- "reads": "shared",
622
- "register": {
861
+ ],
862
+ "body": {
863
+ "type": "App",
864
+ "key": "customers",
865
+ "name": "Customers",
866
+ "icon": "users",
867
+ "color": "violet",
868
+ "children": [
869
+ {
870
+ "type": "List",
871
+ "entity": "customer",
623
872
  "columns": ["phone", "account_manager"],
624
873
  "filters": ["account_manager"],
625
874
  "sort": {"field": "name"},
626
875
  "opens": {"tier": ["key", "standard"]},
627
876
  "create": ["name", "code", "credit_limit"],
628
- "readings": [
629
- {"breakdown": "customer", "by": "tier", "value": "outstanding"},
630
- {"breakdown": "customer", "by": "account_manager", "value": "outstanding"}
631
- ]
632
- },
633
- "record": {
634
- "door": "drawer",
635
- "sections": [
636
- {"title": "Contact", "fields": ["phone", "website"]},
637
- {
638
- "title": "Account",
639
- "fields": ["code", "account_manager", "credit_limit"],
640
- "acts": ["reassign", "lapse", "reinstate"]
641
- },
877
+ "children": [
878
+ {"type": "Reading", "key": "register_reading_0", "value": "outstanding", "by": "tier"},
879
+ {"type": "Reading", "key": "register_reading_1", "value": "outstanding", "by": "account_manager"},
880
+ {"type": "Check", "field": "over_limit"},
642
881
  {
643
- "title": "Orders",
644
- "blocks": [
882
+ "type": "Record",
883
+ "door": "drawer",
884
+ "children": [
885
+ {"type": "Section", "key": "contact", "title": "Contact", "children": ["phone", "website"]},
645
886
  {
646
- "trend": "order",
647
- "over": "placed",
648
- "value": "total",
649
- "where": {"stage": ["confirmed", "picking", "shipped", "delivered"]},
650
- "label": "Order value"
887
+ "type": "Section",
888
+ "key": "account",
889
+ "title": "Account",
890
+ "children": [
891
+ "code",
892
+ "account_manager",
893
+ "credit_limit",
894
+ {
895
+ "type": "Act",
896
+ "key": "reassign",
897
+ "label": "Reassign",
898
+ "asks": [{"input": "to", "label": "Account manager", "type": "member", "required": true}],
899
+ "set": {"account_manager": {"input": "to"}}
900
+ },
901
+ {
902
+ "type": "Act",
903
+ "key": "lapse",
904
+ "label": "Mark lapsed",
905
+ "when": {"tier": ["key", "standard"]},
906
+ "set": {"tier": "lapsed"},
907
+ "confirm": true
908
+ },
909
+ {
910
+ "type": "Act",
911
+ "key": "reinstate",
912
+ "label": "Reinstate",
913
+ "when": {"tier": ["lapsed"]},
914
+ "asks": [
915
+ {
916
+ "input": "back_to",
917
+ "label": "Back to",
918
+ "type": "select",
919
+ "options": [
920
+ {"alias": "key", "label": "Key account", "color": "violet"},
921
+ {"alias": "standard", "label": "Standard", "color": "sky"}
922
+ ],
923
+ "required": true
924
+ }
925
+ ],
926
+ "set": {"tier": {"input": "back_to"}}
927
+ }
928
+ ]
651
929
  },
652
930
  {
653
- "rows": "order",
654
- "via": "customer",
655
- "title": "Open orders",
656
- "columns": ["due_date"],
657
- "create": false,
658
- "where": {"stage": ["draft", "confirmed", "picking", "shipped"]}
931
+ "type": "Section",
932
+ "key": "orders",
933
+ "title": "Orders",
934
+ "children": [
935
+ {
936
+ "type": "Reading",
937
+ "key": "block_reading_0",
938
+ "label": "Order value",
939
+ "entity": "order",
940
+ "value": "total",
941
+ "where": {"stage": ["confirmed", "picking", "shipped", "received"]},
942
+ "by": "placed"
943
+ },
944
+ {
945
+ "type": "List",
946
+ "entity": "order",
947
+ "where": {"stage": ["draft", "confirmed", "picking", "shipped"]},
948
+ "label": "Open orders",
949
+ "columns": ["due_date"],
950
+ "create": false,
951
+ "children": [
952
+ {"type": "Check", "field": "paid_in_full", "on": ["update"]},
953
+ {"type": "Check", "field": "has_payments"},
954
+ {
955
+ "type": "Record",
956
+ "children": [
957
+ "due_date",
958
+ "handler",
959
+ {"field": "priority", "edit": false},
960
+ {
961
+ "field": "lines",
962
+ "add": [
963
+ {
964
+ "field": "item",
965
+ "add": ["name", {"field": "warehouse", "add": ["name", "city"]}, "sku", "category"]
966
+ },
967
+ "quantity"
968
+ ]
969
+ },
970
+ {"field": "payments", "add": true},
971
+ {"field": "carrier", "edit": false},
972
+ {"field": "tracking_no", "edit": false},
973
+ {"field": "shipped_on", "edit": false},
974
+ {"field": "cancellation_reason", "edit": false},
975
+ {"field": "cancelled_by", "edit": false},
976
+ {"field": "replaced_by", "edit": false},
977
+ {"field": "attachments", "edit": false},
978
+ "delivery_note",
979
+ "notes",
980
+ {"field": "reminder_ref", "edit": false}
981
+ ]
982
+ }
983
+ ]
984
+ },
985
+ {
986
+ "type": "List",
987
+ "key": "order_2",
988
+ "entity": "order",
989
+ "where": {"stage": ["shipped", "received"], "paid_in_full": false},
990
+ "label": "Shipped, still owed",
991
+ "columns": ["balance"],
992
+ "create": false,
993
+ "children": [{"type": "Record", "children": ["due_date"]}]
994
+ },
995
+ {
996
+ "type": "List",
997
+ "key": "block_reading_3",
998
+ "entity": "service_visit",
999
+ "app": "visits",
1000
+ "label": "Service visits",
1001
+ "columns": ["engineer", "job"],
1002
+ "children": [
1003
+ {
1004
+ "type": "Act",
1005
+ "key": "clear_visit_hours",
1006
+ "label": "Clear hours",
1007
+ "set": {"hours": null},
1008
+ "confirm": true
1009
+ },
1010
+ {"type": "Record", "edit": false, "children": ["report", "arrived_on", "completed_on"]}
1011
+ ]
1012
+ }
1013
+ ]
659
1014
  },
660
1015
  {
661
- "rows": "order",
662
- "via": "customer",
663
- "title": "Shipped, still owed",
664
- "columns": ["balance"],
665
- "create": false,
666
- "where": {"stage": ["shipped", "delivered"], "paid_in_full": false}
1016
+ "type": "Section",
1017
+ "key": "account_papers",
1018
+ "title": "Account papers",
1019
+ "children": [
1020
+ {
1021
+ "type": "List",
1022
+ "entity": "customer_paper",
1023
+ "expect": {"field": "kind", "of": "papers_needed"},
1024
+ "columns": ["received_on"]
1025
+ }
1026
+ ]
667
1027
  },
668
1028
  {
669
- "list": "service_visit",
670
- "columns": ["engineer", "job"],
671
- "app": "visits",
672
- "fields": ["report", "arrived_on", "completed_on"],
673
- "label": "Service visits"
674
- }
675
- ]
676
- },
677
- {
678
- "title": "Account papers",
679
- "blocks": [
680
- {
681
- "rows": "customer_paper",
682
- "via": "customer",
683
- "columns": ["received_on"],
684
- "expect": "kind",
685
- "of": "papers_needed"
686
- }
1029
+ "type": "Section",
1030
+ "key": "contact_log",
1031
+ "title": "Contact log",
1032
+ "children": [{"type": "List", "entity": "touch", "layout": "timeline"}]
1033
+ },
1034
+ {"type": "Section", "key": "notes", "title": "Notes", "children": ["notes"]}
687
1035
  ]
688
- },
689
- {"title": "Contact log", "blocks": [{"timeline": "touch", "via": "customer"}]},
690
- {"title": "Notes", "blocks": [{"text": "notes"}]}
1036
+ }
691
1037
  ]
692
- },
693
- "acts": [
694
- {
695
- "alias": "lapse",
696
- "label": "Mark lapsed",
697
- "when": {"tier": ["key", "standard"]},
698
- "set": {"tier": "lapsed"},
699
- "confirm": true
700
- },
701
- {
702
- "alias": "reinstate",
703
- "label": "Reinstate",
704
- "when": {"tier": ["lapsed"]},
705
- "asks": [
706
- {
707
- "input": "back_to",
708
- "label": "Back to",
709
- "type": "select",
710
- "options": [
711
- {"alias": "key", "label": "Key account", "color": "violet"},
712
- {"alias": "standard", "label": "Standard", "color": "sky"}
713
- ],
714
- "required": true
715
- }
716
- ],
717
- "set": {"tier": "input:back_to"}
718
- },
719
- {
720
- "alias": "reassign",
721
- "label": "Reassign",
722
- "asks": [{"input": "to", "label": "Account manager", "type": "member", "required": true}],
723
- "set": {"account_manager": "input:to"}
724
- },
725
- {
726
- "alias": "clear_visit_hours",
727
- "label": "Clear hours",
728
- "of": "service_visit",
729
- "set": {"hours": null},
730
- "confirm": true
731
- }
732
- ],
733
- "checks": [{"field": "over_limit"}]
734
- }
735
- ]
1038
+ }
1039
+ ]
1040
+ }
736
1041
  }
737
1042
  ```
738
1043
 
@@ -740,33 +1045,27 @@ Every key: the `model` reference, at the pages each section names under **Keys**
740
1045
 
741
1046
  **The job.** Gather an applicant's papers into one case, make the forms it needs, and file it on time.
742
1047
 
743
- **Read.** `expect` blocks for the papers brought (one line per kind, the missing ones empty), an act's `templates` for the forms made (ticked and made at once, kept in `into`), and `intake` reading the papers brought.
1048
+ **Read.** Lists with `expect` for the papers brought (one line per kind, the missing ones empty), an Act's `templates` for the forms made (ticked and made at once, kept in `into`), and `intake` reading the papers brought.
744
1049
 
745
- **Keys.** `model/blocks`, `model/papers-and-recordings`
1050
+ **Keys.** `model/list-node`, `model/act-node`
746
1051
 
747
1052
  **Not when.** A record that needs no papers, or one paper made once — a single `template` act.
748
1053
 
749
1054
  ```json
750
1055
  {
751
- "entities": [
1056
+ "tables": [
752
1057
  {
753
- "alias": "ho_so",
754
- "fields": [
755
- {
756
- "alias": "khach_hang",
757
- "type": "select_record_link",
758
- "target_entity": "khach_hang",
759
- "cardinality": "one"
760
- },
761
- {"alias": "du_an", "type": "select_record_link", "target_entity": "du_an", "cardinality": "one"},
762
- {"alias": "ma_ho_so", "type": "autonumber"},
763
- {
764
- "alias": "dich_vu",
1058
+ "ref": "ho_so",
1059
+ "title": "khach_hang",
1060
+ "fields": {
1061
+ "khach_hang": {"type": "link", "table": "khach_hang", "multi": false},
1062
+ "du_an": {"type": "link", "table": "du_an", "multi": false},
1063
+ "ma_ho_so": {"type": "autonumber"},
1064
+ "dich_vu": {
765
1065
  "type": "select",
766
1066
  "options": [{"alias": "tron_goi", "color": "blue"}, {"alias": "lam_ho_so", "color": "violet"}]
767
1067
  },
768
- {
769
- "alias": "doi_tuong",
1068
+ "doi_tuong": {
770
1069
  "type": "select",
771
1070
  "options": [
772
1071
  {"alias": "thu_nhap_thap", "color": "sky"},
@@ -777,19 +1076,17 @@ Every key: the `model` reference, at the pages each section names under **Keys**
777
1076
  {"alias": "nguoi_co_cong", "color": "rose"}
778
1077
  ]
779
1078
  },
780
- {
781
- "alias": "tinh_trang_nha_o",
1079
+ "tinh_trang_nha_o": {
782
1080
  "type": "select",
783
1081
  "options": [{"alias": "chua_co_nha", "color": "gray"}, {"alias": "nha_chat", "color": "orange"}]
784
1082
  },
785
- {"alias": "giay_chung_nhan_so", "type": "text"},
786
- {"alias": "dien_tich_nha", "type": "number"},
787
- {"alias": "sale", "type": "select_member"},
788
- {"alias": "xu_ly", "type": "select_member"},
789
- {"alias": "can_bo_sung", "type": "boolean"},
790
- {"alias": "phi_dich_vu", "type": "number", "notation": {"style": "currency", "currency": "VND"}},
791
- {
792
- "alias": "loai_can",
1083
+ "giay_chung_nhan_so": {"type": "text"},
1084
+ "dien_tich_nha": {"type": "number"},
1085
+ "sale": {"type": "member"},
1086
+ "xu_ly": {"type": "member"},
1087
+ "can_bo_sung": {"type": "boolean"},
1088
+ "phi_dich_vu": {"type": "number", "notation": {"style": "currency", "currency": "VND"}},
1089
+ "loai_can": {
793
1090
  "type": "select",
794
1091
  "options": [
795
1092
  {"alias": "studio", "color": "sky"},
@@ -798,52 +1095,70 @@ Every key: the `model` reference, at the pages each section names under **Keys**
798
1095
  {"alias": "ba_pn", "color": "violet"}
799
1096
  ]
800
1097
  },
801
- {"alias": "khoi", "type": "select_record_link", "target_entity": "khoi_nha", "cardinality": "one"},
802
- {"alias": "can_so", "type": "number"},
803
- {"alias": "tang", "type": "number"},
804
- {"alias": "hoa_hong_sale", "type": "number", "notation": {"style": "currency", "currency": "VND"}},
805
- {"alias": "ngay_thu_phi", "type": "date"},
806
- {"alias": "ngay_nhan_giay_to", "type": "date"},
807
- {"alias": "ngay_lap_ho_so", "type": "date"},
808
- {"alias": "ngay_nop_cdt", "type": "date"},
809
- {"alias": "ngay_cdt_duyet", "type": "date"},
810
- {"alias": "ngay_so_xd_duyet", "type": "date"},
811
- {"alias": "ngay_boc_tham", "type": "date"},
812
- {"alias": "ngay_ky_hdmb", "type": "date"},
813
- {"alias": "ngay_ban_giao", "type": "date"},
814
- {"alias": "bo_ho_so", "type": "files"},
815
- {"alias": "hop_dong_mua_ban", "type": "files"},
816
- {"alias": "giay_to", "type": "select_record_link", "target_entity": "giay_to", "cardinality": "many"},
817
- {
818
- "alias": "thanh_vien",
819
- "type": "select_record_link",
820
- "target_entity": "thanh_vien",
821
- "cardinality": "many"
822
- },
823
- {
824
- "alias": "phieu_thu",
825
- "type": "select_record_link",
826
- "target_entity": "phieu_thu",
827
- "cardinality": "many"
1098
+ "giai_doan": {
1099
+ "type": "select",
1100
+ "options": [
1101
+ {"alias": "ngay_thu_phi", "color": "blue"},
1102
+ {"alias": "ngay_nhan_giay_to", "color": "blue"},
1103
+ {"alias": "ngay_lap_ho_so", "color": "blue"},
1104
+ {"alias": "ngay_nop_cdt", "color": "blue"},
1105
+ {"alias": "ngay_cdt_duyet", "color": "blue"},
1106
+ {"alias": "ngay_so_xd_duyet", "color": "blue"},
1107
+ {"alias": "ngay_boc_tham", "color": "blue"},
1108
+ {"alias": "ngay_ky_hdmb", "color": "green"},
1109
+ {"alias": "ngay_ban_giao", "color": "green"}
1110
+ ]
828
1111
  },
829
- {"alias": "dien_thoai", "type": "lookup"},
830
- {"alias": "cccd", "type": "lookup"},
831
- {"alias": "ngay_sinh", "type": "lookup"},
832
- {"alias": "dia_chi", "type": "lookup"},
833
- {"alias": "da_thu", "type": "rollup"},
834
- {"alias": "chua_thu_phi", "type": "formula"},
835
- {"alias": "con_no_phi", "type": "formula"},
836
- {"alias": "vuot_thu_nhap", "type": "formula"},
837
- {"alias": "da_hoan_tat", "type": "formula"}
838
- ]
1112
+ "ngay_thu_phi": {"type": "date"},
1113
+ "ngay_nhan_giay_to": {"type": "date"},
1114
+ "ngay_lap_ho_so": {"type": "date"},
1115
+ "ngay_nop_cdt": {"type": "date"},
1116
+ "ngay_cdt_duyet": {"type": "date"},
1117
+ "ngay_so_xd_duyet": {"type": "date"},
1118
+ "ngay_boc_tham": {"type": "date"},
1119
+ "ngay_ky_hdmb": {"type": "date"},
1120
+ "ngay_ban_giao": {"type": "date"},
1121
+ "bo_ho_so": {"type": "files"},
1122
+ "giay_to": {"type": "link", "table": "giay_to", "multi": true},
1123
+ "thanh_vien": {"type": "link", "table": "thanh_vien", "multi": true},
1124
+ "phieu_thu": {"type": "link", "table": "phieu_thu", "multi": true},
1125
+ "dien_thoai": {"type": "lookup"},
1126
+ "cccd": {"type": "lookup"},
1127
+ "ngay_sinh": {"type": "lookup"},
1128
+ "dia_chi": {"type": "lookup"},
1129
+ "da_thu": {"type": "rollup"},
1130
+ "chua_thu_phi": {"type": "formula"},
1131
+ "con_no_phi": {"type": "formula"},
1132
+ "vuot_thu_nhap": {"type": "formula"},
1133
+ "da_hoan_tat": {"type": "formula"}
1134
+ },
1135
+ "records": {
1136
+ "subtitle": ["dich_vu"],
1137
+ "figure": "phi_dich_vu",
1138
+ "status": {
1139
+ "field": "giai_doan",
1140
+ "closed": ["ngay_ky_hdmb", "ngay_ban_giao"],
1141
+ "dates": {
1142
+ "ngay_thu_phi": "ngay_thu_phi",
1143
+ "ngay_nhan_giay_to": "ngay_nhan_giay_to",
1144
+ "ngay_lap_ho_so": "ngay_lap_ho_so",
1145
+ "ngay_nop_cdt": "ngay_nop_cdt",
1146
+ "ngay_cdt_duyet": "ngay_cdt_duyet",
1147
+ "ngay_so_xd_duyet": "ngay_so_xd_duyet",
1148
+ "ngay_boc_tham": "ngay_boc_tham",
1149
+ "ngay_ky_hdmb": "ngay_ky_hdmb",
1150
+ "ngay_ban_giao": "ngay_ban_giao"
1151
+ }
1152
+ }
1153
+ }
839
1154
  },
840
1155
  {
841
- "alias": "thanh_vien",
842
- "fields": [
843
- {"alias": "ho_ten", "type": "text"},
844
- {"alias": "ho_so", "type": "select_record_link", "target_entity": "ho_so", "cardinality": "one"},
845
- {
846
- "alias": "quan_he",
1156
+ "ref": "thanh_vien",
1157
+ "title": "ho_ten",
1158
+ "fields": {
1159
+ "ho_ten": {"type": "text"},
1160
+ "ho_so": {"type": "link", "table": "ho_so", "multi": false},
1161
+ "quan_he": {
847
1162
  "type": "select",
848
1163
  "options": [
849
1164
  {"alias": "nguoi_dung_don", "color": "blue"},
@@ -853,16 +1168,17 @@ Every key: the `model` reference, at the pages each section names under **Keys**
853
1168
  {"alias": "khac", "color": "gray"}
854
1169
  ]
855
1170
  },
856
- {"alias": "ngay_sinh", "type": "date"},
857
- {"alias": "cccd", "type": "text"},
858
- {"alias": "thu_nhap", "type": "number", "notation": {"style": "currency", "currency": "VND"}}
859
- ]
1171
+ "ngay_sinh": {"type": "date"},
1172
+ "cccd": {"type": "text"},
1173
+ "thu_nhap": {"type": "number", "notation": {"style": "currency", "currency": "VND"}}
1174
+ },
1175
+ "records": {"subtitle": ["quan_he", "cccd"], "party": "person", "figure": "thu_nhap"}
860
1176
  },
861
1177
  {
862
- "alias": "giay_to",
863
- "fields": [
864
- {
865
- "alias": "loai",
1178
+ "ref": "giay_to",
1179
+ "title": "loai",
1180
+ "fields": {
1181
+ "loai": {
866
1182
  "type": "select",
867
1183
  "options": [
868
1184
  {"alias": "don_dang_ky", "color": "blue"},
@@ -873,18 +1189,18 @@ Every key: the `model` reference, at the pages each section names under **Keys**
873
1189
  {"alias": "hon_nhan", "color": "indigo"}
874
1190
  ]
875
1191
  },
876
- {"alias": "ho_so", "type": "select_record_link", "target_entity": "ho_so", "cardinality": "one"},
877
- {"alias": "ngay_nhan", "type": "date"},
878
- {"alias": "can_sua", "type": "text"},
879
- {"alias": "tep", "type": "files"},
880
- {"alias": "phai_sua", "type": "formula"}
881
- ]
1192
+ "ho_so": {"type": "link", "table": "ho_so", "multi": false},
1193
+ "ngay_nhan": {"type": "date"},
1194
+ "tep": {"type": "files"},
1195
+ "phai_sua": {"type": "formula"}
1196
+ },
1197
+ "records": {"image": "tep", "starts": {"ngay_nhan": "today"}}
882
1198
  },
883
1199
  {
884
- "alias": "phieu_thu",
885
- "fields": [
886
- {
887
- "alias": "khoan",
1200
+ "ref": "phieu_thu",
1201
+ "title": "khoan",
1202
+ "fields": {
1203
+ "khoan": {
888
1204
  "type": "select",
889
1205
  "options": [
890
1206
  {"alias": "dat_coc", "color": "blue"},
@@ -892,18 +1208,16 @@ Every key: the `model` reference, at the pages each section names under **Keys**
892
1208
  {"alias": "phi_ho_so", "color": "violet"}
893
1209
  ]
894
1210
  },
895
- {"alias": "ho_so", "type": "select_record_link", "target_entity": "ho_so", "cardinality": "one"},
896
- {"alias": "so_tien", "type": "number", "notation": {"style": "currency", "currency": "VND"}},
897
- {
898
- "alias": "hinh_thuc",
1211
+ "ho_so": {"type": "link", "table": "ho_so", "multi": false},
1212
+ "so_tien": {"type": "number", "notation": {"style": "currency", "currency": "VND"}},
1213
+ "hinh_thuc": {
899
1214
  "type": "select",
900
1215
  "options": [
901
1216
  {"alias": "tien_mat", "color": "green", "mark": {"kind": "icon", "name": "banknote"}},
902
1217
  {"alias": "chuyen_khoan", "color": "blue", "mark": {"kind": "icon", "name": "arrow-right-left"}}
903
1218
  ]
904
1219
  },
905
- {
906
- "alias": "trang_thai",
1220
+ "trang_thai": {
907
1221
  "type": "select",
908
1222
  "options": [
909
1223
  {"alias": "cho_thu", "color": "amber"},
@@ -911,41 +1225,15 @@ Every key: the `model` reference, at the pages each section names under **Keys**
911
1225
  {"alias": "huy", "color": "gray"}
912
1226
  ]
913
1227
  },
914
- {"alias": "ngay_thu", "type": "date"},
915
- {"alias": "nguoi_thu", "type": "select_member"},
916
- {"alias": "da_chot", "type": "formula"}
917
- ]
918
- }
919
- ],
920
- "records": {
921
- "ho_so": {
922
- "title": "khach_hang",
923
- "subtitle": ["dich_vu"],
924
- "status": {
925
- "milestones": [
926
- {"field": "ngay_thu_phi", "when": {"dich_vu": ["lam_ho_so"]}},
927
- "ngay_nhan_giay_to",
928
- {"field": "ngay_lap_ho_so", "when": {"dich_vu": ["tron_goi"]}},
929
- {"field": "ngay_nop_cdt", "when": {"dich_vu": ["tron_goi"]}},
930
- {"field": "ngay_cdt_duyet", "when": {"dich_vu": ["tron_goi"]}},
931
- {"field": "ngay_so_xd_duyet", "when": {"dich_vu": ["tron_goi"]}},
932
- {"field": "ngay_boc_tham", "when": {"dich_vu": ["tron_goi"]}},
933
- {"field": "ngay_ky_hdmb", "when": {"dich_vu": ["tron_goi"]}},
934
- {"field": "ngay_ban_giao", "when": {"dich_vu": ["lam_ho_so"]}}
935
- ],
936
- "closed": ["ngay_ky_hdmb", "ngay_ban_giao"]
1228
+ "ngay_thu": {"type": "date"}
937
1229
  },
938
- "figure": "phi_dich_vu"
939
- },
940
- "thanh_vien": {"title": "ho_ten", "subtitle": ["quan_he", "cccd"], "party": "person", "figure": "thu_nhap"},
941
- "giay_to": {"title": "loai", "image": "tep", "starts": {"ngay_nhan": "today"}},
942
- "phieu_thu": {
943
- "title": "khoan",
944
- "subtitle": ["hinh_thuc", "ngay_thu"],
945
- "status": {"field": "trang_thai", "closed": ["da_thu", "huy"]},
946
- "figure": "so_tien"
1230
+ "records": {
1231
+ "subtitle": ["hinh_thuc", "ngay_thu"],
1232
+ "figure": "so_tien",
1233
+ "status": {"field": "trang_thai", "closed": ["da_thu", "huy"]}
1234
+ }
947
1235
  }
948
- },
1236
+ ],
949
1237
  "templates": [
950
1238
  {"alias": "don_dang_ky", "type": "html", "label": "Đơn đăng ký mua nhà ở xã hội"},
951
1239
  {"alias": "mau_02_chua_co_nha", "type": "html", "label": "Mẫu 02 — Xác nhận chưa có nhà ở"},
@@ -953,205 +1241,190 @@ Every key: the `model` reference, at the pages each section names under **Keys**
953
1241
  {"alias": "mau_05_thu_nhap", "type": "html", "label": "Mẫu 05 — Xác nhận thu nhập"},
954
1242
  {"alias": "danh_sach_nop", "type": "html", "label": "Danh sách hồ sơ nộp chủ đầu tư"}
955
1243
  ],
956
- "apps": [
957
- {
958
- "alias": "ho_so",
959
- "name": "Hồ sơ nhà ở xã hội",
960
- "icon": "folder-open",
961
- "theme": {"color": "blue"},
962
- "entity": "ho_so",
963
- "scope": "du_an",
964
- "register": {
1244
+ "body": {
1245
+ "type": "App",
1246
+ "key": "ho_so",
1247
+ "name": "Hồ sơ nhà ở xã hội",
1248
+ "icon": "folder-open",
1249
+ "color": "blue",
1250
+ "scope": "du_an",
1251
+ "children": [
1252
+ {
1253
+ "type": "List",
1254
+ "entity": "ho_so",
1255
+ "row": {"title": {"add": true}},
965
1256
  "columns": ["xu_ly", "can_bo_sung", "doi_tuong"],
966
1257
  "filters": ["xu_ly", "can_bo_sung"],
967
1258
  "search": ["khach_hang", "ma_ho_so", "dien_thoai"],
968
1259
  "create": ["khach_hang", "dich_vu", "doi_tuong", "tinh_trang_nha_o", "loai_can", "phi_dich_vu", "sale"],
969
- "export": true,
970
- "readings": [
971
- {"metric": "ho_so", "where": {"can_bo_sung": true}, "label": "Chờ khách bổ sung"},
972
- {"metric": "ho_so", "value": "da_thu", "over": "ngay_nhan_giay_to", "label": "Phí đã thu"},
973
- {"breakdown": "ho_so", "by": "doi_tuong"}
974
- ]
975
- },
976
- "record": {
977
- "sections": [
1260
+ "children": [
978
1261
  {
979
- "title": "Người đứng đơn",
980
- "fields": [
981
- "ma_ho_so",
982
- "dien_thoai",
983
- "cccd",
984
- "ngay_sinh",
985
- "dia_chi",
986
- "doi_tuong",
987
- "tinh_trang_nha_o",
988
- "sale",
989
- "xu_ly",
990
- "can_bo_sung"
991
- ],
992
- "blocks": [
993
- {
994
- "rows": "thanh_vien",
995
- "title": "Hộ gia đình",
996
- "columns": ["ngay_sinh"],
997
- "create": ["ho_ten", "quan_he", "ngay_sinh", "cccd", "thu_nhap"]
998
- },
999
- {
1000
- "rows": "giay_to",
1001
- "title": "Giấy tờ cá nhân",
1002
- "columns": ["ngay_nhan"],
1003
- "expect": "loai",
1004
- "only": ["can_cuoc", "hon_nhan"]
1005
- }
1006
- ],
1007
- "acts": ["doc_giay_to", "giao_xu_ly", "chuyen_du_an"]
1262
+ "type": "Reading",
1263
+ "key": "register_reading_0",
1264
+ "label": "Chờ khách bổ sung",
1265
+ "where": {"can_bo_sung": true}
1008
1266
  },
1009
1267
  {
1010
- "title": "Nhà hiện có",
1011
- "fields": ["giay_chung_nhan_so", "dien_tich_nha"],
1012
- "when": {"tinh_trang_nha_o": ["nha_chat"]}
1268
+ "type": "Reading",
1269
+ "key": "register_reading_1",
1270
+ "label": "Phí đã thu",
1271
+ "value": "da_thu",
1272
+ "over": "ngay_nhan_giay_to"
1013
1273
  },
1274
+ {"type": "Reading", "key": "register_reading_2", "by": "doi_tuong"},
1275
+ {"type": "Export", "key": "export"},
1276
+ {"type": "Check", "field": "chua_thu_phi", "on": ["nop_cdt"]},
1277
+ {"type": "Check", "field": "con_no_phi"},
1278
+ {"type": "Check", "field": "vuot_thu_nhap"},
1279
+ {"type": "Check", "field": "da_hoan_tat", "on": ["doc_giay_to"]},
1014
1280
  {
1015
- "title": "Phí dịch vụ",
1016
- "blocks": [
1281
+ "type": "Record",
1282
+ "children": [
1017
1283
  {
1018
- "rows": "phieu_thu",
1019
- "title": "Phiếu thu",
1020
- "columns": ["so_tien"],
1021
- "create": ["khoan", "so_tien", "hinh_thuc"],
1022
- "where": {"khoan": ["dat_coc", "tat_toan"]},
1023
- "expect": "khoan",
1024
- "when": {"dich_vu": ["tron_goi"]}
1284
+ "type": "Section",
1285
+ "key": "nguoi_dung_don",
1286
+ "title": "Người đứng đơn",
1287
+ "children": [
1288
+ "ma_ho_so",
1289
+ "dien_thoai",
1290
+ "cccd",
1291
+ "ngay_sinh",
1292
+ "dia_chi",
1293
+ "doi_tuong",
1294
+ "tinh_trang_nha_o",
1295
+ "sale",
1296
+ "xu_ly",
1297
+ "can_bo_sung",
1298
+ "ngay_ky_hdmb",
1299
+ "ngay_ban_giao",
1300
+ {
1301
+ "type": "List",
1302
+ "entity": "thanh_vien",
1303
+ "label": "Hộ gia đình",
1304
+ "columns": ["ngay_sinh"],
1305
+ "create": ["ho_ten", "quan_he", "ngay_sinh", "cccd", "thu_nhap"]
1306
+ },
1307
+ {
1308
+ "type": "List",
1309
+ "entity": "giay_to",
1310
+ "expect": {"field": "loai", "only": ["can_cuoc", "hon_nhan"]},
1311
+ "label": "Giấy tờ cá nhân",
1312
+ "columns": ["ngay_nhan"],
1313
+ "children": [{"type": "Check", "field": "phai_sua"}]
1314
+ },
1315
+ {
1316
+ "type": "Act",
1317
+ "key": "doc_giay_to",
1318
+ "label": "Đọc giấy tờ",
1319
+ "intake": "giay_to",
1320
+ "fills": [
1321
+ "doi_tuong",
1322
+ "tinh_trang_nha_o",
1323
+ "giay_chung_nhan_so",
1324
+ "dien_tich_nha",
1325
+ "khach_hang.ho_ten",
1326
+ "khach_hang.cccd",
1327
+ "khach_hang.ngay_sinh",
1328
+ "khach_hang.dia_chi",
1329
+ "thanh_vien"
1330
+ ]
1331
+ },
1332
+ {
1333
+ "type": "Act",
1334
+ "key": "giao_xu_ly",
1335
+ "label": "Giao người xử lý",
1336
+ "asks": [{"input": "nguoi", "label": "Người xử lý", "type": "member", "required": true}],
1337
+ "set": {"xu_ly": {"input": "nguoi"}}
1338
+ },
1339
+ {
1340
+ "type": "Act",
1341
+ "key": "chuyen_du_an",
1342
+ "label": "Chuyển dự án",
1343
+ "asks": [
1344
+ {
1345
+ "input": "du_an_moi",
1346
+ "label": "Sang dự án",
1347
+ "type": "link",
1348
+ "entity": "du_an",
1349
+ "required": true,
1350
+ "add": ["ten_du_an", "chu_dau_tu", "so_can"]
1351
+ }
1352
+ ],
1353
+ "set": {"du_an": {"input": "du_an_moi"}},
1354
+ "confirm": true
1355
+ }
1356
+ ]
1025
1357
  },
1026
1358
  {
1027
- "rows": "phieu_thu",
1028
- "title": "Phiếu thu",
1029
- "columns": ["so_tien"],
1030
- "create": ["khoan", "so_tien", "hinh_thuc"],
1031
- "where": {"khoan": ["phi_ho_so"]},
1032
- "expect": "khoan",
1033
- "when": {"dich_vu": ["lam_ho_so"]}
1034
- }
1035
- ]
1036
- },
1037
- {
1038
- "title": "Giấy tờ",
1039
- "at": ["ngay_thu_phi"],
1040
- "blocks": [
1041
- {
1042
- "rows": "giay_to",
1043
- "title": "Giấy tờ",
1044
- "columns": ["ngay_nhan"],
1045
- "create": ["loai", "ngay_nhan", "can_sua", "tep"],
1046
- "expect": "loai",
1047
- "only": ["don_dang_ky", "giay_doi_tuong", "xac_nhan_nha_o", "xac_nhan_thu_nhap"]
1359
+ "type": "Section",
1360
+ "key": "bo_ho_so",
1361
+ "title": "Bộ hồ sơ",
1362
+ "at": ["ngay_nhan_giay_to", "ngay_lap_ho_so"],
1363
+ "children": [
1364
+ "ngay_nhan_giay_to",
1365
+ "ngay_lap_ho_so",
1366
+ {
1367
+ "type": "Act",
1368
+ "key": "tao_giay_to",
1369
+ "label": "Tạo giấy tờ",
1370
+ "templates": [
1371
+ {"template": "don_dang_ky"},
1372
+ {"template": "mau_02_chua_co_nha", "when": {"tinh_trang_nha_o": ["chua_co_nha"]}},
1373
+ {"template": "mau_03_nha_chat", "when": {"tinh_trang_nha_o": ["nha_chat"]}},
1374
+ {"template": "mau_05_thu_nhap", "when": {"doi_tuong": ["thu_nhap_thap"]}}
1375
+ ],
1376
+ "into": "bo_ho_so",
1377
+ "when": {"giai_doan": ["ngay_nhan_giay_to", "ngay_lap_ho_so"]}
1378
+ },
1379
+ {
1380
+ "type": "Act",
1381
+ "key": "nop_cdt",
1382
+ "label": "Nộp chủ đầu tư",
1383
+ "when": {"dich_vu": ["tron_goi"], "giai_doan": ["ngay_lap_ho_so"]},
1384
+ "requires": ["bo_ho_so", "loai_can"],
1385
+ "asks": ["loai_can"],
1386
+ "set": {"giai_doan": "ngay_nop_cdt"},
1387
+ "confirm": true
1388
+ },
1389
+ {
1390
+ "type": "Act",
1391
+ "key": "to_ngay_nhan_giay_to",
1392
+ "label": "Đã nhận giấy tờ",
1393
+ "when": {"giai_doan": {"empty": true}, "dich_vu": ["tron_goi"]},
1394
+ "asks": [{"field": "ngay_nhan_giay_to", "starts": "today"}],
1395
+ "set": {"giai_doan": "ngay_nhan_giay_to"}
1396
+ },
1397
+ {
1398
+ "type": "Act",
1399
+ "key": "to_ngay_lap_ho_so",
1400
+ "label": "Đã lập hồ sơ",
1401
+ "when": {"giai_doan": ["ngay_nhan_giay_to"], "dich_vu": ["tron_goi"]},
1402
+ "asks": [{"field": "ngay_lap_ho_so", "starts": "today"}],
1403
+ "set": {"giai_doan": "ngay_lap_ho_so"}
1404
+ },
1405
+ {
1406
+ "type": "Act",
1407
+ "key": "to_ngay_ban_giao",
1408
+ "label": "Đã bàn giao hồ sơ",
1409
+ "when": {"giai_doan": ["ngay_nhan_giay_to"], "dich_vu": ["lam_ho_so"]},
1410
+ "asks": [{"field": "ngay_ban_giao", "starts": "today"}],
1411
+ "set": {"giai_doan": "ngay_ban_giao"}
1412
+ }
1413
+ ]
1048
1414
  }
1049
1415
  ]
1050
1416
  },
1051
1417
  {
1052
- "title": "Bộ hồ sơ",
1053
- "at": ["ngay_nhan_giay_to", "ngay_lap_ho_so"],
1054
- "acts": ["tao_giay_to", "nop_cdt"]
1055
- },
1056
- {
1057
- "title": "Chủ đầu tư",
1058
- "at": ["ngay_nop_cdt", "ngay_cdt_duyet", "ngay_so_xd_duyet"],
1059
- "fields": ["loai_can"],
1060
- "when": {"dich_vu": ["tron_goi"]}
1061
- },
1062
- {
1063
- "title": "Căn hộ",
1064
- "at": ["ngay_boc_tham"],
1065
- "fields": ["khoi", "can_so", "tang", "hoa_hong_sale"],
1066
- "blocks": [{"files": ["hop_dong_mua_ban"]}],
1067
- "when": {"dich_vu": ["tron_goi"]}
1418
+ "type": "Act",
1419
+ "key": "in_danh_sach",
1420
+ "label": "In danh sách nộp chủ đầu tư",
1421
+ "on": "view",
1422
+ "template": "danh_sach_nop"
1068
1423
  }
1069
1424
  ]
1070
- },
1071
- "acts": [
1072
- {
1073
- "alias": "doc_giay_to",
1074
- "label": "Đọc giấy tờ",
1075
- "intake": "giay_to",
1076
- "fills": [
1077
- "doi_tuong",
1078
- "tinh_trang_nha_o",
1079
- "giay_chung_nhan_so",
1080
- "dien_tich_nha",
1081
- "khach_hang.ho_ten",
1082
- "khach_hang.cccd",
1083
- "khach_hang.ngay_sinh",
1084
- "khach_hang.dia_chi",
1085
- "thanh_vien"
1086
- ]
1087
- },
1088
- {
1089
- "alias": "tao_giay_to",
1090
- "label": "Tạo giấy tờ",
1091
- "templates": [
1092
- {"template": "don_dang_ky"},
1093
- {"template": "mau_02_chua_co_nha", "when": {"tinh_trang_nha_o": ["chua_co_nha"]}},
1094
- {"template": "mau_03_nha_chat", "when": {"tinh_trang_nha_o": ["nha_chat"]}},
1095
- {"template": "mau_05_thu_nhap", "when": {"doi_tuong": ["thu_nhap_thap"]}}
1096
- ],
1097
- "into": "bo_ho_so"
1098
- },
1099
- {
1100
- "alias": "nop_cdt",
1101
- "label": "Nộp chủ đầu tư",
1102
- "when": {"dich_vu": ["tron_goi"]},
1103
- "requires": ["bo_ho_so", "loai_can"],
1104
- "asks": ["loai_can"],
1105
- "set": {"ngay_nop_cdt": "now"},
1106
- "confirm": true
1107
- },
1108
- {
1109
- "alias": "giao_xu_ly",
1110
- "label": "Giao người xử lý",
1111
- "asks": [{"input": "nguoi", "label": "Người xử lý", "type": "member", "required": true}],
1112
- "set": {"xu_ly": "input:nguoi"}
1113
- },
1114
- {
1115
- "alias": "chuyen_du_an",
1116
- "label": "Chuyển dự án",
1117
- "asks": [{"input": "du_an_moi", "label": "Sang dự án", "type": "link", "entity": "du_an", "required": true}],
1118
- "set": {"du_an": "input:du_an_moi"},
1119
- "confirm": true
1120
- },
1121
- {
1122
- "alias": "in_danh_sach",
1123
- "label": "In danh sách nộp chủ đầu tư",
1124
- "on": "view",
1125
- "template": "danh_sach_nop"
1126
- },
1127
- {
1128
- "alias": "xac_nhan_thu",
1129
- "label": "Xác nhận đã thu",
1130
- "of": "phieu_thu",
1131
- "when": {"trang_thai": ["cho_thu"]},
1132
- "asks": ["hinh_thuc"],
1133
- "set": {"trang_thai": "da_thu", "ngay_thu": "now", "nguoi_thu": "me"},
1134
- "confirm": true
1135
- },
1136
- {
1137
- "alias": "huy_phieu",
1138
- "label": "Hủy phiếu",
1139
- "of": "phieu_thu",
1140
- "when": {"trang_thai": ["cho_thu"]},
1141
- "set": {"trang_thai": "huy"},
1142
- "danger": true
1143
- }
1144
- ],
1145
- "checks": [
1146
- {"field": "chua_thu_phi", "blocks": ["nop_cdt"]},
1147
- {"field": "con_no_phi"},
1148
- {"field": "vuot_thu_nhap"},
1149
- {"field": "da_hoan_tat", "blocks": ["delete", "doc_giay_to"]},
1150
- {"field": "da_chot", "of": "phieu_thu", "blocks": ["edit"]},
1151
- {"field": "phai_sua", "of": "giay_to"}
1152
- ]
1153
- }
1154
- ]
1425
+ }
1426
+ ]
1427
+ }
1155
1428
  }
1156
1429
  ```
1157
1430
 
@@ -1159,22 +1432,22 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1159
1432
 
1160
1433
  **The job.** Call a lead from their record, and keep what was said as a touch on its timeline.
1161
1434
 
1162
- **Read.** An act's `record` (into the `timeline` child: audio, transcript, when and who) and `fills` (the touch's fields an agent fills from the transcript).
1435
+ **Read.** An Act's `record` (into the `timeline` child: audio, transcript, when and who) and `fills` (the touch's fields an agent fills from the transcript).
1163
1436
 
1164
- **Keys.** `model/papers-and-recordings`, `model/timeline-and-agenda`
1437
+ **Keys.** `model/act-node`, `model/layouts`
1165
1438
 
1166
1439
  **Not when.** Calls made and logged elsewhere — a `timeline` with its own add keeps the note.
1167
1440
 
1168
1441
  ```json
1169
1442
  {
1170
- "entities": [
1443
+ "tables": [
1171
1444
  {
1172
- "alias": "khach_tiem_nang",
1173
- "fields": [
1174
- {"alias": "ho_ten", "type": "text"},
1175
- {"alias": "ten_cong_ty", "type": "text"},
1176
- {
1177
- "alias": "vai_tro",
1445
+ "ref": "khach_tiem_nang",
1446
+ "title": "ho_ten",
1447
+ "fields": {
1448
+ "ho_ten": {"type": "text"},
1449
+ "ten_cong_ty": {"type": "text"},
1450
+ "vai_tro": {
1178
1451
  "type": "select",
1179
1452
  "options": [
1180
1453
  {"alias": "chu_doanh_nghiep", "color": "emerald"},
@@ -1184,8 +1457,7 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1184
1457
  {"alias": "chua_ro", "color": "zinc"}
1185
1458
  ]
1186
1459
  },
1187
- {
1188
- "alias": "loai_hinh",
1460
+ "loai_hinh": {
1189
1461
  "type": "select",
1190
1462
  "options": [
1191
1463
  {"alias": "thuong_mai", "color": "blue"},
@@ -1196,8 +1468,7 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1196
1468
  {"alias": "chua_ro", "color": "zinc"}
1197
1469
  ]
1198
1470
  },
1199
- {
1200
- "alias": "quy_mo",
1471
+ "quy_mo": {
1201
1472
  "type": "select",
1202
1473
  "options": [
1203
1474
  {"alias": "q_1_10", "color": "sky"},
@@ -1207,13 +1478,12 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1207
1478
  {"alias": "chua_ro", "color": "zinc"}
1208
1479
  ]
1209
1480
  },
1210
- {"alias": "so_dien_thoai", "type": "text"},
1211
- {"alias": "zalo", "type": "text"},
1212
- {"alias": "email", "type": "text"},
1213
- {"alias": "trang_ca_nhan", "type": "text", "format": "link"},
1214
- {"alias": "anh", "type": "files"},
1215
- {
1216
- "alias": "trang_thai",
1481
+ "so_dien_thoai": {"type": "text"},
1482
+ "zalo": {"type": "text"},
1483
+ "email": {"type": "text"},
1484
+ "trang_ca_nhan": {"type": "text", "format": "link"},
1485
+ "anh": {"type": "files"},
1486
+ "trang_thai": {
1217
1487
  "type": "select",
1218
1488
  "options": [
1219
1489
  {"alias": "moi", "color": "gray"},
@@ -1223,8 +1493,7 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1223
1493
  {"alias": "loai", "color": "red"}
1224
1494
  ]
1225
1495
  },
1226
- {
1227
- "alias": "ly_do_loai",
1496
+ "ly_do_loai": {
1228
1497
  "type": "select",
1229
1498
  "options": [
1230
1499
  {"alias": "khong_phu_hop", "color": "orange"},
@@ -1235,31 +1504,48 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1235
1504
  {"alias": "trung", "color": "zinc"}
1236
1505
  ]
1237
1506
  },
1238
- {"alias": "vi_sao_du_dieu_kien", "type": "text"},
1239
- {"alias": "phu_trach", "type": "select_member"},
1240
- {"alias": "nguon", "type": "select_record_link", "target_entity": "kenh", "cardinality": "one"},
1241
- {"alias": "lien_he", "type": "select_record_link", "target_entity": "lien_he", "cardinality": "one"},
1242
- {"alias": "cong_ty", "type": "select_record_link", "target_entity": "cong_ty", "cardinality": "one"},
1243
- {"alias": "ngay_chuyen_doi", "type": "date", "format": "datetime", "timezone": "Asia/Ho_Chi_Minh"},
1244
- {"alias": "ghi_chu", "type": "text", "format": "markdown"},
1245
- {"alias": "tin_hieu", "type": "select_record_link", "target_entity": "tin_hieu"},
1246
- {"alias": "buoc_tiep", "type": "select_record_link", "target_entity": "buoc_tiep"},
1247
- {"alias": "tuong_tac", "type": "select_record_link", "target_entity": "tuong_tac"},
1248
- {"alias": "do_nong", "type": "formula"},
1249
- {"alias": "han_buoc_tiep", "type": "rollup"},
1250
- {"alias": "khong_lien_he", "type": "formula"}
1251
- ]
1507
+ "vi_sao_du_dieu_kien": {"type": "text"},
1508
+ "phu_trach": {"type": "member"},
1509
+ "nguon": {"type": "link", "table": "kenh", "multi": false},
1510
+ "lien_he": {"type": "link", "table": "lien_he", "multi": false},
1511
+ "cong_ty": {"type": "link", "table": "cong_ty", "multi": false},
1512
+ "ngay_chuyen_doi": {"type": "date", "format": "datetime", "timezone": "Asia/Ho_Chi_Minh"},
1513
+ "ghi_chu": {"type": "text", "format": "markdown"},
1514
+ "tin_hieu": {"type": "link", "table": "tin_hieu", "multi": true},
1515
+ "buoc_tiep": {"type": "link", "table": "buoc_tiep", "multi": true},
1516
+ "tuong_tac": {"type": "link", "table": "tuong_tac", "multi": true},
1517
+ "do_nong": {
1518
+ "type": "formula",
1519
+ "options": [
1520
+ {"alias": "nong", "label": "Nóng", "color": "orange"},
1521
+ {"alias": "nguoi", "label": "Nguội", "color": "sky"}
1522
+ ]
1523
+ },
1524
+ "han_buoc_tiep": {"type": "rollup"},
1525
+ "khong_lien_he": {"type": "formula"}
1526
+ },
1527
+ "records": {
1528
+ "subtitle": ["ten_cong_ty"],
1529
+ "image": "anh",
1530
+ "party": "person",
1531
+ "due": ["han_buoc_tiep"],
1532
+ "status": {
1533
+ "field": "trang_thai",
1534
+ "closed": ["da_chuyen_doi", "loai"],
1535
+ "dates": {"da_chuyen_doi": "ngay_chuyen_doi"}
1536
+ }
1537
+ }
1252
1538
  },
1253
1539
  {
1254
- "alias": "tin_hieu",
1255
- "fields": [
1256
- {"alias": "tom_tat", "type": "text"},
1257
- {"alias": "noi_dung", "type": "text", "format": "markdown"},
1258
- {"alias": "anh_chup", "type": "files"},
1259
- {"alias": "lien_ket", "type": "text", "format": "link"},
1260
- {"alias": "kenh", "type": "select_record_link", "target_entity": "kenh", "cardinality": "one"},
1261
- {
1262
- "alias": "loai",
1540
+ "ref": "tin_hieu",
1541
+ "title": "tom_tat",
1542
+ "fields": {
1543
+ "tom_tat": {"type": "text"},
1544
+ "noi_dung": {"type": "text", "format": "markdown"},
1545
+ "anh_chup": {"type": "files"},
1546
+ "lien_ket": {"type": "text", "format": "link"},
1547
+ "kenh": {"type": "link", "table": "kenh", "multi": false},
1548
+ "loai": {
1263
1549
  "type": "select",
1264
1550
  "options": [
1265
1551
  {"alias": "hoi_dich_vu", "color": "emerald"},
@@ -1271,16 +1557,17 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1271
1557
  {"alias": "nhieu", "color": "stone"}
1272
1558
  ]
1273
1559
  },
1274
- {"alias": "ngay_dang", "type": "date", "timezone": "Asia/Ho_Chi_Minh"},
1275
- {"alias": "ngay_xay_ra", "type": "formula"}
1276
- ]
1560
+ "ngay_dang": {"type": "date", "timezone": "Asia/Ho_Chi_Minh"},
1561
+ "ngay_xay_ra": {"type": "formula"}
1562
+ },
1563
+ "records": {"subtitle": ["kenh", "ngay_xay_ra"], "image": "anh_chup"}
1277
1564
  },
1278
1565
  {
1279
- "alias": "buoc_tiep",
1280
- "fields": [
1281
- {"alias": "vi_sao_luc_nay", "type": "text"},
1282
- {
1283
- "alias": "viec",
1566
+ "ref": "buoc_tiep",
1567
+ "title": "vi_sao_luc_nay",
1568
+ "fields": {
1569
+ "vi_sao_luc_nay": {"type": "text"},
1570
+ "viec": {
1284
1571
  "type": "select",
1285
1572
  "options": [
1286
1573
  {"alias": "binh_luan", "color": "blue", "mark": {"kind": "icon", "name": "message-square"}},
@@ -1291,10 +1578,9 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1291
1578
  {"alias": "gui_bang_gia", "color": "amber", "mark": {"kind": "icon", "name": "receipt"}}
1292
1579
  ]
1293
1580
  },
1294
- {"alias": "ban_nhap", "type": "text", "format": "markdown"},
1295
- {"alias": "han", "type": "date", "format": "datetime", "timezone": "Asia/Ho_Chi_Minh"},
1296
- {
1297
- "alias": "tinh_trang",
1581
+ "ban_nhap": {"type": "text", "format": "markdown"},
1582
+ "han": {"type": "date", "format": "datetime", "timezone": "Asia/Ho_Chi_Minh"},
1583
+ "tinh_trang": {
1298
1584
  "type": "select",
1299
1585
  "options": [
1300
1586
  {"alias": "cho", "color": "blue"},
@@ -1302,15 +1588,21 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1302
1588
  {"alias": "bo_qua", "color": "zinc"}
1303
1589
  ]
1304
1590
  }
1305
- ]
1591
+ },
1592
+ "records": {
1593
+ "subtitle": ["viec"],
1594
+ "mark": "viec",
1595
+ "due": ["han"],
1596
+ "status": {"field": "tinh_trang", "closed": ["xong", "bo_qua"]}
1597
+ }
1306
1598
  },
1307
1599
  {
1308
- "alias": "tuong_tac",
1309
- "fields": [
1310
- {"alias": "tom_tat", "type": "text"},
1311
- {"alias": "thoi_diem", "type": "date", "format": "datetime", "timezone": "Asia/Ho_Chi_Minh"},
1312
- {
1313
- "alias": "qua",
1600
+ "ref": "tuong_tac",
1601
+ "title": "tom_tat",
1602
+ "fields": {
1603
+ "tom_tat": {"type": "text"},
1604
+ "thoi_diem": {"type": "date", "format": "datetime", "timezone": "Asia/Ho_Chi_Minh"},
1605
+ "qua": {
1314
1606
  "type": "select",
1315
1607
  "options": [
1316
1608
  {"alias": "goi_dien", "color": "teal", "mark": {"kind": "icon", "name": "phone"}},
@@ -1321,8 +1613,7 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1321
1613
  {"alias": "khac", "color": "zinc", "mark": {"kind": "icon", "name": "ellipsis"}}
1322
1614
  ]
1323
1615
  },
1324
- {
1325
- "alias": "phan_hoi",
1616
+ "phan_hoi": {
1326
1617
  "type": "select",
1327
1618
  "options": [
1328
1619
  {"alias": "dang_cho", "color": "blue"},
@@ -1333,146 +1624,174 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1333
1624
  {"alias": "sai_so", "color": "stone"}
1334
1625
  ]
1335
1626
  },
1336
- {"alias": "ghi_am", "type": "files"},
1337
- {"alias": "nguyen_van", "type": "text", "format": "markdown"},
1338
- {"alias": "hen_lai", "type": "date", "timezone": "Asia/Ho_Chi_Minh"},
1339
- {"alias": "nguoi_thuc_hien", "type": "select_member"}
1340
- ]
1627
+ "ghi_am": {"type": "files"},
1628
+ "nguyen_van": {"type": "text", "format": "markdown"},
1629
+ "hen_lai": {"type": "date", "timezone": "Asia/Ho_Chi_Minh"},
1630
+ "nguoi_thuc_hien": {"type": "member"}
1631
+ },
1632
+ "records": {"subtitle": ["qua", "phan_hoi"]}
1341
1633
  }
1342
1634
  ],
1343
- "records": {
1344
- "khach_tiem_nang": {
1345
- "title": "ho_ten",
1346
- "subtitle": ["ten_cong_ty"],
1347
- "image": "anh",
1348
- "party": "person",
1349
- "status": {"field": "trang_thai", "closed": ["da_chuyen_doi", "loai"], "history": "lich_su_trang_thai"},
1350
- "due": ["han_buoc_tiep"]
1351
- },
1352
- "tin_hieu": {"title": "tom_tat", "subtitle": ["kenh", "ngay_xay_ra"], "image": "anh_chup"},
1353
- "buoc_tiep": {
1354
- "title": "vi_sao_luc_nay",
1355
- "subtitle": ["viec"],
1356
- "mark": "viec",
1357
- "status": {"field": "tinh_trang", "closed": ["xong", "bo_qua"]},
1358
- "due": ["han"]
1359
- },
1360
- "tuong_tac": {"title": "tom_tat", "subtitle": ["qua", "phan_hoi"]}
1361
- },
1362
- "apps": [
1363
- {
1364
- "alias": "khach_tiem_nang",
1365
- "name": "Khách tiềm năng",
1366
- "icon": "radar",
1367
- "theme": {"color": "violet"},
1368
- "entity": "khach_tiem_nang",
1369
- "register": {
1635
+ "body": {
1636
+ "type": "App",
1637
+ "key": "khach_tiem_nang",
1638
+ "name": "Khách tiềm năng",
1639
+ "icon": "radar",
1640
+ "color": "violet",
1641
+ "children": [
1642
+ {
1643
+ "type": "List",
1644
+ "entity": "khach_tiem_nang",
1370
1645
  "columns": ["phu_trach", "han_buoc_tiep"],
1371
1646
  "filters": ["phu_trach"],
1372
- "create": ["ho_ten", "trang_ca_nhan", "so_dien_thoai", "ten_cong_ty", "nguon"],
1373
- "readings": [
1374
- {"breakdown": "khach_tiem_nang", "by": "phu_trach"},
1375
- {"trend": "khach_tiem_nang", "over": "ngay_chuyen_doi", "label": "Khách chuyển đổi"}
1376
- ]
1377
- },
1378
- "record": {
1379
- "sections": [
1647
+ "create": [
1648
+ "ho_ten",
1649
+ "trang_ca_nhan",
1650
+ "so_dien_thoai",
1651
+ "ten_cong_ty",
1652
+ {"field": "nguon", "add": ["ten", "loai"]}
1653
+ ],
1654
+ "children": [
1655
+ {"type": "Reading", "key": "register_reading_0", "by": "phu_trach"},
1380
1656
  {
1381
- "title": "Đánh giá",
1382
- "at": ["moi"],
1383
- "fields": ["vai_tro", "loai_hinh", "quy_mo", "vi_sao_du_dieu_kien", "do_nong"],
1384
- "blocks": [
1385
- {
1386
- "rows": "tin_hieu",
1387
- "title": "Tín hiệu",
1388
- "columns": ["loai", "noi_dung"],
1389
- "create": ["tom_tat", "anh_chup", "loai", "noi_dung", "kenh", "lien_ket", "ngay_dang"]
1390
- }
1391
- ],
1392
- "acts": ["danh_gia_dat"]
1657
+ "type": "Reading",
1658
+ "key": "register_reading_1",
1659
+ "label": "Khách chuyển đổi",
1660
+ "by": "ngay_chuyen_doi"
1393
1661
  },
1662
+ {"type": "Check", "field": "khong_lien_he", "resolve": "da_lien_he"},
1394
1663
  {
1395
- "title": "Liên hệ",
1396
- "at": ["du_dieu_kien"],
1397
- "fields": ["so_dien_thoai", "phu_trach", "trang_ca_nhan", "zalo", "email", "ghi_chu"],
1398
- "blocks": [
1664
+ "type": "Record",
1665
+ "children": [
1666
+ {
1667
+ "type": "Section",
1668
+ "key": "danh_gia",
1669
+ "title": "Đánh giá",
1670
+ "at": ["moi"],
1671
+ "children": [
1672
+ "vai_tro",
1673
+ "loai_hinh",
1674
+ "quy_mo",
1675
+ "vi_sao_du_dieu_kien",
1676
+ "do_nong",
1677
+ {
1678
+ "type": "List",
1679
+ "entity": "tin_hieu",
1680
+ "label": "Tín hiệu",
1681
+ "columns": ["loai", "noi_dung"],
1682
+ "create": ["tom_tat", "anh_chup", "loai", "noi_dung", "kenh", "lien_ket", "ngay_dang"]
1683
+ },
1684
+ {
1685
+ "type": "Act",
1686
+ "key": "danh_gia_dat",
1687
+ "label": "Đủ điều kiện",
1688
+ "when": {"trang_thai": ["moi"]},
1689
+ "requires": ["vi_sao_du_dieu_kien"],
1690
+ "set": {"trang_thai": "du_dieu_kien"}
1691
+ }
1692
+ ]
1693
+ },
1399
1694
  {
1400
- "rows": "buoc_tiep",
1401
- "title": "Bước tiếp theo",
1402
- "columns": ["ban_nhap"],
1403
- "create": ["vi_sao_luc_nay", "viec", "han", "ban_nhap"]
1695
+ "type": "Section",
1696
+ "key": "lien_he",
1697
+ "title": "Liên hệ",
1698
+ "at": ["du_dieu_kien"],
1699
+ "children": [
1700
+ "so_dien_thoai",
1701
+ "phu_trach",
1702
+ "trang_ca_nhan",
1703
+ "zalo",
1704
+ "email",
1705
+ {"field": "ghi_chu", "block": false},
1706
+ {
1707
+ "type": "List",
1708
+ "entity": "buoc_tiep",
1709
+ "label": "Bước tiếp theo",
1710
+ "columns": ["ban_nhap"],
1711
+ "create": ["vi_sao_luc_nay", "viec", "han", "ban_nhap"],
1712
+ "children": [
1713
+ {
1714
+ "type": "Act",
1715
+ "key": "lam_xong",
1716
+ "label": "Đã làm",
1717
+ "when": {"tinh_trang": ["cho"]},
1718
+ "set": {"tinh_trang": "xong"}
1719
+ }
1720
+ ]
1721
+ },
1722
+ {
1723
+ "type": "List",
1724
+ "entity": "tuong_tac",
1725
+ "label": "Tương tác",
1726
+ "layout": "timeline",
1727
+ "create": ["tom_tat", "qua", "phan_hoi", "thoi_diem", "hen_lai"]
1728
+ },
1729
+ {
1730
+ "type": "Act",
1731
+ "key": "ghi_cuoc_goi",
1732
+ "label": "Ghi âm cuộc gọi",
1733
+ "when": {"trang_thai": ["du_dieu_kien"]},
1734
+ "fills": ["tom_tat", "phan_hoi"],
1735
+ "record": {
1736
+ "into": "tuong_tac",
1737
+ "audio": "ghi_am",
1738
+ "transcript": "nguyen_van",
1739
+ "at": "thoi_diem",
1740
+ "by": "nguoi_thuc_hien"
1741
+ }
1742
+ },
1743
+ {
1744
+ "type": "Act",
1745
+ "key": "da_lien_he",
1746
+ "label": "Đánh dấu đã liên hệ",
1747
+ "when": {"trang_thai": ["du_dieu_kien"]},
1748
+ "set": {"trang_thai": "da_lien_he"}
1749
+ }
1750
+ ]
1404
1751
  },
1405
1752
  {
1406
- "timeline": "tuong_tac",
1407
- "title": "Tương tác",
1408
- "create": ["tom_tat", "qua", "phan_hoi", "thoi_diem", "hen_lai"]
1753
+ "type": "Section",
1754
+ "key": "chuyen_doi",
1755
+ "title": "Chuyển đổi",
1756
+ "at": ["da_lien_he"],
1757
+ "children": [
1758
+ "nguon",
1759
+ {"field": "lien_he", "edit": false},
1760
+ {"field": "cong_ty", "edit": false},
1761
+ {
1762
+ "type": "Act",
1763
+ "key": "chuyen_doi",
1764
+ "label": "Chuyển đổi",
1765
+ "when": {"trang_thai": ["da_lien_he"]},
1766
+ "workflow": "chuyen_doi_khach",
1767
+ "confirm": true
1768
+ }
1769
+ ]
1770
+ },
1771
+ {
1772
+ "type": "Section",
1773
+ "key": "loai",
1774
+ "title": "Loại",
1775
+ "at": ["loai"],
1776
+ "children": [
1777
+ "ly_do_loai",
1778
+ {
1779
+ "type": "Act",
1780
+ "key": "loai",
1781
+ "label": "Loại",
1782
+ "when": {"trang_thai": ["moi", "du_dieu_kien", "da_lien_he"]},
1783
+ "asks": ["ly_do_loai"],
1784
+ "set": {"trang_thai": "loai"},
1785
+ "danger": true
1786
+ }
1787
+ ]
1409
1788
  }
1410
- ],
1411
- "acts": ["ghi_cuoc_goi", "da_lien_he"]
1412
- },
1413
- {
1414
- "title": "Chuyển đổi",
1415
- "at": ["da_lien_he"],
1416
- "fields": ["nguon", "lien_he", "cong_ty"],
1417
- "acts": ["chuyen_doi"]
1418
- },
1419
- {"title": "Loại", "at": ["loai"], "fields": ["ly_do_loai"], "acts": ["loai"]}
1789
+ ]
1790
+ }
1420
1791
  ]
1421
- },
1422
- "acts": [
1423
- {
1424
- "alias": "danh_gia_dat",
1425
- "label": "Đủ điều kiện",
1426
- "when": {"trang_thai": ["moi"]},
1427
- "requires": ["vi_sao_du_dieu_kien"],
1428
- "set": {"trang_thai": "du_dieu_kien"}
1429
- },
1430
- {
1431
- "alias": "ghi_cuoc_goi",
1432
- "label": "Ghi âm cuộc gọi",
1433
- "when": {"trang_thai": ["du_dieu_kien"]},
1434
- "record": {
1435
- "into": "tuong_tac",
1436
- "audio": "ghi_am",
1437
- "transcript": "nguyen_van",
1438
- "at": "thoi_diem",
1439
- "by": "nguoi_thuc_hien"
1440
- },
1441
- "fills": ["tom_tat", "phan_hoi"]
1442
- },
1443
- {
1444
- "alias": "da_lien_he",
1445
- "label": "Đánh dấu đã liên hệ",
1446
- "when": {"trang_thai": ["du_dieu_kien"]},
1447
- "set": {"trang_thai": "da_lien_he"}
1448
- },
1449
- {
1450
- "alias": "chuyen_doi",
1451
- "label": "Chuyển đổi",
1452
- "when": {"trang_thai": ["da_lien_he"]},
1453
- "workflow": "chuyen_doi_khach",
1454
- "writes": ["lien_he", "cong_ty"],
1455
- "confirm": true
1456
- },
1457
- {
1458
- "alias": "loai",
1459
- "label": "Loại",
1460
- "when": {"trang_thai": ["moi", "du_dieu_kien", "da_lien_he"]},
1461
- "asks": ["ly_do_loai"],
1462
- "set": {"trang_thai": "loai"},
1463
- "danger": true
1464
- },
1465
- {
1466
- "alias": "lam_xong",
1467
- "label": "Đã làm",
1468
- "of": "buoc_tiep",
1469
- "when": {"tinh_trang": ["cho"]},
1470
- "set": {"tinh_trang": "xong"}
1471
- }
1472
- ],
1473
- "checks": [{"field": "khong_lien_he", "resolve": "da_lien_he"}]
1474
- }
1475
- ]
1792
+ }
1793
+ ]
1794
+ }
1476
1795
  }
1477
1796
  ```
1478
1797
 
@@ -1480,22 +1799,22 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1480
1799
 
1481
1800
  **The job.** Raise a payment request, see it through approval, and read what is waiting at the top of the list.
1482
1801
 
1483
- **Read.** `records.de_nghi.status` with its `history` (each move: when and who), sections staged by `at`, and `register.readings` leading with the headline number.
1802
+ **Read.** `records.de_nghi.status` with its `history` (each move: when and who), Sections staged by `at`, and the List's Readings leading with the headline number. The history stays because the business reads who signed each move; where only the day a stage was reached matters, the status stamps a date on the row instead (the Gantt above).
1484
1803
 
1485
- **Keys.** `model/status`, `model/record-page`, `model/readings`
1804
+ **Keys.** `model/status`, `model/section-node`, `model/reading-node`
1486
1805
 
1487
1806
  **Not when.** Rows that never move — a reference list (a price list, a catalog) has no status and no headline number.
1488
1807
 
1489
1808
  ```json
1490
1809
  {
1491
- "entities": [
1810
+ "tables": [
1492
1811
  {
1493
- "alias": "de_nghi",
1494
- "fields": [
1495
- {"alias": "noi_dung", "type": "text"},
1496
- {"alias": "so_de_nghi", "type": "text"},
1497
- {
1498
- "alias": "loai",
1812
+ "ref": "de_nghi",
1813
+ "title": "noi_dung",
1814
+ "fields": {
1815
+ "noi_dung": {"type": "text"},
1816
+ "so_de_nghi": {"type": "text"},
1817
+ "loai": {
1499
1818
  "type": "select",
1500
1819
  "options": [
1501
1820
  {"alias": "thanh_toan_ncc", "color": "violet"},
@@ -1505,17 +1824,11 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1505
1824
  {"alias": "hoan_ung", "color": "teal"}
1506
1825
  ]
1507
1826
  },
1508
- {"alias": "du_an", "type": "select_record_link", "target_entity": "du_an", "cardinality": "one"},
1509
- {
1510
- "alias": "nha_cung_cap",
1511
- "type": "select_record_link",
1512
- "target_entity": "nha_cung_cap",
1513
- "cardinality": "one"
1514
- },
1515
- {"alias": "so_tien", "type": "number", "notation": {"style": "currency", "currency": "VND"}},
1516
- {"alias": "han_thanh_toan", "type": "date"},
1517
- {
1518
- "alias": "trang_thai",
1827
+ "du_an": {"type": "link", "table": "du_an", "multi": false},
1828
+ "nha_cung_cap": {"type": "link", "table": "nha_cung_cap", "multi": false},
1829
+ "so_tien": {"type": "number", "notation": {"style": "currency", "currency": "VND"}},
1830
+ "han_thanh_toan": {"type": "date"},
1831
+ "trang_thai": {
1519
1832
  "type": "select",
1520
1833
  "options": [
1521
1834
  {"alias": "nhap", "color": "slate"},
@@ -1526,101 +1839,146 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1526
1839
  {"alias": "tu_choi", "color": "rose"}
1527
1840
  ]
1528
1841
  },
1529
- {"alias": "nguoi_de_nghi", "type": "select_member"},
1530
- {"alias": "chung_tu", "type": "files"},
1531
- {"alias": "nguoi_duyet", "type": "select_member"},
1532
- {"alias": "duyet_luc", "type": "date", "format": "datetime"},
1533
- {"alias": "ly_do_tu_choi", "type": "text"},
1534
- {"alias": "phieu_chi", "type": "files"},
1535
- {"alias": "ngan_sach_du_an", "type": "lookup"},
1536
- {"alias": "da_duyet_du_an", "type": "lookup"},
1537
- {"alias": "vuot_ngan_sach", "type": "formula"},
1538
- {"alias": "sat_ngan_sach", "type": "formula"},
1539
- {"alias": "da_gui", "type": "formula"}
1540
- ]
1541
- }
1542
- ],
1543
- "records": {
1544
- "de_nghi": {
1545
- "title": "noi_dung",
1546
- "subtitle": ["du_an"],
1547
- "status": {
1548
- "field": "trang_thai",
1549
- "closed": ["da_chi", "tu_choi"],
1550
- "history": "lich_su_duyet",
1551
- "stages": {
1552
- "nhap": {"owner": "nguoi_de_nghi"},
1553
- "cho_duyet": {"owner": {"role": "giam_doc"}, "within": 1},
1554
- "cho_chu_tich": {"owner": {"role": "chu_tich"}, "within": 2},
1555
- "da_duyet": {"owner": {"role": "ke_toan"}, "within": 2}
1556
- }
1842
+ "nguoi_de_nghi": {"type": "member"},
1843
+ "chung_tu": {"type": "files"},
1844
+ "nguoi_duyet": {"type": "member"},
1845
+ "duyet_luc": {"type": "date", "format": "datetime"},
1846
+ "ly_do_tu_choi": {"type": "text"},
1847
+ "phieu_chi": {"type": "files"},
1848
+ "ngan_sach_du_an": {"type": "lookup"},
1849
+ "da_duyet_du_an": {"type": "lookup"},
1850
+ "vuot_ngan_sach": {"type": "formula"},
1851
+ "sat_ngan_sach": {"type": "formula"},
1852
+ "da_gui": {"type": "formula"},
1853
+ "nhap_at": {"type": "date", "format": "datetime"},
1854
+ "cho_duyet_at": {"type": "date", "format": "datetime"},
1855
+ "cho_chu_tich_at": {"type": "date", "format": "datetime"},
1856
+ "da_duyet_at": {"type": "date", "format": "datetime"}
1557
1857
  },
1558
- "figure": "so_tien",
1559
- "limits": {"da_duyet_du_an": "ngan_sach_du_an"},
1560
- "due": ["han_thanh_toan"],
1561
- "starts": {"nguoi_de_nghi": "me"}
1858
+ "records": {
1859
+ "subtitle": ["du_an"],
1860
+ "figure": "so_tien",
1861
+ "limits": {"da_duyet_du_an": "ngan_sach_du_an"},
1862
+ "due": ["han_thanh_toan"],
1863
+ "starts": {"nguoi_de_nghi": "me"},
1864
+ "status": {
1865
+ "field": "trang_thai",
1866
+ "closed": ["da_chi", "tu_choi"],
1867
+ "dropped": ["tu_choi"],
1868
+ "dates": {
1869
+ "nhap": "nhap_at",
1870
+ "cho_duyet": "cho_duyet_at",
1871
+ "cho_chu_tich": "cho_chu_tich_at",
1872
+ "da_duyet": "da_duyet_at"
1873
+ },
1874
+ "history": "lich_su_duyet",
1875
+ "stages": {
1876
+ "nhap": {"owner": "nguoi_de_nghi"},
1877
+ "cho_duyet": {"owner": {"group": "<id of the giam_doc group>"}, "within": 1},
1878
+ "cho_chu_tich": {
1879
+ "owner": {"group": "<id of the chu_tich group>"},
1880
+ "within": 2,
1881
+ "when": {"so_tien": {"gt": 200000000}}
1882
+ },
1883
+ "da_duyet": {"owner": {"group": "<id of the ke_toan group>"}, "within": 2}
1884
+ }
1885
+ }
1886
+ }
1562
1887
  }
1563
- },
1564
- "apps": [
1565
- {
1566
- "alias": "de_nghi_chi",
1567
- "name": "Đề nghị thanh toán",
1568
- "icon": "hand-coins",
1569
- "theme": {"color": "amber"},
1570
- "entity": "de_nghi",
1571
- "register": {
1888
+ ],
1889
+ "body": {
1890
+ "type": "App",
1891
+ "key": "de_nghi_chi",
1892
+ "name": "Đề nghị thanh toán",
1893
+ "icon": "hand-coins",
1894
+ "color": "amber",
1895
+ "children": [
1896
+ {
1897
+ "type": "List",
1898
+ "entity": "de_nghi",
1899
+ "row": {"subtitle": [{"field": "du_an", "add": ["ten", "ma_du_an", "chu_dau_tu", "ngan_sach"]}]},
1572
1900
  "columns": ["nguoi_de_nghi"],
1573
1901
  "filters": ["nguoi_de_nghi", "du_an"],
1574
1902
  "group": "du_an",
1575
- "create": ["noi_dung", "loai", "du_an", "nha_cung_cap", "so_tien", "han_thanh_toan", "nguoi_de_nghi"],
1576
- "readings": [
1903
+ "create": [
1904
+ "noi_dung",
1905
+ "loai",
1906
+ "du_an",
1907
+ {"field": "nha_cung_cap", "add": true},
1908
+ "so_tien",
1909
+ "han_thanh_toan",
1910
+ "nguoi_de_nghi"
1911
+ ],
1912
+ "children": [
1577
1913
  {
1578
- "metric": "de_nghi",
1914
+ "type": "Reading",
1915
+ "key": "register_reading_0",
1916
+ "label": "Đã duyệt, chờ chi",
1579
1917
  "value": "so_tien",
1580
- "where": {"trang_thai": ["da_duyet"]},
1581
- "label": "Đã duyệt, chờ chi"
1582
- },
1583
- {"trend": "de_nghi", "over": "han_thanh_toan", "value": "so_tien"}
1584
- ]
1585
- },
1586
- "record": {
1587
- "door": "drawer",
1588
- "sections": [
1589
- {
1590
- "title": "Đề nghị",
1591
- "at": ["nhap"],
1592
- "fields": ["so_de_nghi", "loai", "nha_cung_cap", "han_thanh_toan", "nguoi_de_nghi"],
1593
- "blocks": [{"files": ["chung_tu"], "title": "Chứng từ kèm"}],
1594
- "acts": ["gui_duyet"]
1918
+ "where": {"trang_thai": ["da_duyet"]}
1595
1919
  },
1920
+ {"type": "Reading", "key": "register_reading_1", "value": "so_tien", "by": "han_thanh_toan"},
1921
+ {"type": "Check", "field": "da_gui", "on": ["update"]},
1922
+ {"type": "Check", "field": "vuot_ngan_sach"},
1923
+ {"type": "Check", "field": "sat_ngan_sach"},
1596
1924
  {
1597
- "title": "Duyệt",
1598
- "at": ["cho_duyet", "cho_chu_tich"],
1599
- "fields": ["nguoi_duyet", "duyet_luc", "ly_do_tu_choi"],
1600
- "acts": ["rut_lai"]
1601
- },
1602
- {"title": "Đã chi", "at": ["da_chi"], "blocks": [{"files": ["phieu_chi"], "title": "Phiếu chi"}]}
1925
+ "type": "Record",
1926
+ "door": "drawer",
1927
+ "children": [
1928
+ {
1929
+ "type": "Section",
1930
+ "key": "de_nghi",
1931
+ "title": "Đề nghị",
1932
+ "at": ["nhap"],
1933
+ "children": [
1934
+ "so_de_nghi",
1935
+ "loai",
1936
+ "nha_cung_cap",
1937
+ "han_thanh_toan",
1938
+ "nguoi_de_nghi",
1939
+ {"field": "chung_tu", "label": "Chứng từ kèm"},
1940
+ {
1941
+ "type": "Act",
1942
+ "key": "gui_duyet",
1943
+ "label": "Gửi duyệt",
1944
+ "when": {"trang_thai": ["nhap"]},
1945
+ "requires": ["chung_tu", "nguoi_de_nghi"],
1946
+ "set": {"trang_thai": "cho_duyet"}
1947
+ }
1948
+ ]
1949
+ },
1950
+ {
1951
+ "type": "Section",
1952
+ "key": "duyet",
1953
+ "title": "Duyệt",
1954
+ "at": ["cho_duyet", "cho_chu_tich"],
1955
+ "children": [
1956
+ {"field": "nguoi_duyet", "edit": false},
1957
+ {"field": "duyet_luc", "edit": false},
1958
+ {"field": "ly_do_tu_choi", "edit": false},
1959
+ {
1960
+ "type": "Act",
1961
+ "key": "rut_lai",
1962
+ "label": "Rút lại",
1963
+ "when": {"trang_thai": ["cho_duyet", "cho_chu_tich"]},
1964
+ "set": {"trang_thai": "nhap"},
1965
+ "confirm": true
1966
+ }
1967
+ ]
1968
+ },
1969
+ {
1970
+ "type": "Section",
1971
+ "key": "da_chi",
1972
+ "title": "Đã chi",
1973
+ "at": ["da_chi"],
1974
+ "children": [{"field": "phieu_chi", "label": "Phiếu chi", "edit": false}]
1975
+ }
1976
+ ]
1977
+ }
1603
1978
  ]
1604
- },
1605
- "acts": [
1606
- {
1607
- "alias": "gui_duyet",
1608
- "label": "Gửi duyệt",
1609
- "when": {"trang_thai": ["nhap"]},
1610
- "requires": ["chung_tu", "nguoi_de_nghi"],
1611
- "set": {"trang_thai": "cho_duyet"}
1612
- },
1613
- {
1614
- "alias": "rut_lai",
1615
- "label": "Rút lại",
1616
- "when": {"trang_thai": ["cho_duyet", "cho_chu_tich"]},
1617
- "set": {"trang_thai": "nhap"},
1618
- "confirm": true
1619
- }
1620
- ],
1621
- "checks": [{"field": "da_gui", "blocks": ["edit"]}, {"field": "vuot_ngan_sach"}, {"field": "sat_ngan_sach"}]
1622
- }
1623
- ]
1979
+ }
1980
+ ]
1981
+ }
1624
1982
  }
1625
1983
  ```
1626
1984
 
@@ -1628,35 +1986,26 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1628
1986
 
1629
1987
  **The job.** The owner's question — what came in, what went out, where it went, and what is still unmatched — answered over one period.
1630
1988
 
1631
- **Read.** `dashboard`: `metric`s with `over` (the period windows them), a `trend`, `breakdown`s, and a `list` of rows to act on opening in their app.
1989
+ **Read.** An App of Readings and no List: Readings with `over` (the period windows them), a Reading `by` the day, Readings `by` a field, and a List of rows to act on that opens in its `app`.
1632
1990
 
1633
- **Keys.** `model/dashboards`, `model/readings`
1991
+ **Keys.** `model/app-node`, `model/reading-node`
1634
1992
 
1635
- **Not when.** One job's headline numbers — those lead that job's register as its `readings`.
1993
+ **Not when.** One job's headline numbers — those lead that job's List as its Readings.
1636
1994
 
1637
1995
  ```json
1638
1996
  {
1639
- "entities": [
1997
+ "tables": [
1640
1998
  {
1641
- "alias": "giao_dich",
1642
- "fields": [
1643
- {"alias": "noi_dung", "type": "text"},
1644
- {"alias": "ngay", "type": "date"},
1645
- {
1646
- "alias": "loai",
1647
- "type": "select",
1648
- "options": [{"alias": "thu", "color": "green"}, {"alias": "chi", "color": "rose"}]
1649
- },
1650
- {"alias": "so_tien", "type": "number", "notation": {"style": "currency", "currency": "VND"}},
1651
- {"alias": "bien_dong", "type": "formula"},
1652
- {
1653
- "alias": "doi_tuong",
1654
- "type": "select_record_link",
1655
- "target_entity": "doi_tuong",
1656
- "cardinality": "one"
1657
- },
1658
- {
1659
- "alias": "danh_muc",
1999
+ "ref": "giao_dich",
2000
+ "title": "noi_dung",
2001
+ "fields": {
2002
+ "noi_dung": {"type": "text"},
2003
+ "ngay": {"type": "date"},
2004
+ "loai": {"type": "select", "options": [{"alias": "thu", "color": "green"}, {"alias": "chi", "color": "rose"}]},
2005
+ "so_tien": {"type": "number", "notation": {"style": "currency", "currency": "VND"}},
2006
+ "bien_dong": {"type": "formula", "notation": {"style": "currency", "currency": "VND"}},
2007
+ "doi_tuong": {"type": "link", "table": "doi_tuong", "multi": false},
2008
+ "danh_muc": {
1660
2009
  "type": "select",
1661
2010
  "options": [
1662
2011
  {"alias": "doanh_thu", "color": "green"},
@@ -1669,121 +2018,133 @@ Every key: the `model` reference, at the pages each section names under **Keys**
1669
2018
  {"alias": "khac", "color": "gray"}
1670
2019
  ]
1671
2020
  },
1672
- {"alias": "sao_ke", "type": "select_record_link", "target_entity": "sao_ke", "cardinality": "one"},
1673
- {"alias": "hoa_don", "type": "select_record_link", "target_entity": "hoa_don", "cardinality": "many"},
1674
- {
1675
- "alias": "trang_thai",
2021
+ "sao_ke": {"type": "link", "table": "sao_ke", "multi": false},
2022
+ "hoa_don": {"type": "link", "table": "hoa_don", "multi": true},
2023
+ "trang_thai": {
1676
2024
  "type": "select",
1677
2025
  "options": [
1678
2026
  {"alias": "chua_khop", "color": "amber"},
1679
2027
  {"alias": "da_khop", "color": "green"},
1680
2028
  {"alias": "khong_can_hd", "color": "slate"}
1681
2029
  ]
1682
- }
1683
- ]
2030
+ },
2031
+ "khop_luc": {"type": "date", "format": "datetime"}
2032
+ },
2033
+ "records": {
2034
+ "subtitle": ["ngay"],
2035
+ "figure": "bien_dong",
2036
+ "status": {"field": "trang_thai", "closed": ["da_khop", "khong_can_hd"], "dates": {"da_khop": "khop_luc"}}
2037
+ }
1684
2038
  },
1685
2039
  {
1686
- "alias": "hoa_don",
1687
- "fields": [
1688
- {
1689
- "alias": "doi_tuong",
1690
- "type": "select_record_link",
1691
- "target_entity": "doi_tuong",
1692
- "cardinality": "one"
1693
- },
1694
- {"alias": "so_hoa_don", "type": "text"},
1695
- {"alias": "ngay_hoa_don", "type": "date"},
1696
- {"alias": "so_ngay_qua_han", "type": "formula"},
1697
- {
1698
- "alias": "chieu",
2040
+ "ref": "hoa_don",
2041
+ "title": "doi_tuong",
2042
+ "fields": {
2043
+ "doi_tuong": {"type": "link", "table": "doi_tuong", "multi": false},
2044
+ "so_hoa_don": {"type": "text"},
2045
+ "ngay_hoa_don": {"type": "date"},
2046
+ "so_ngay_qua_han": {"type": "formula", "notation": {"style": "unit", "unit": "day"}},
2047
+ "chieu": {
1699
2048
  "type": "select",
1700
2049
  "options": [{"alias": "ban_ra", "color": "green"}, {"alias": "mua_vao", "color": "violet"}]
1701
2050
  },
1702
- {"alias": "tong_cong", "type": "formula"},
1703
- {"alias": "thue_suat", "type": "formula"},
1704
- {"alias": "tep", "type": "files"},
1705
- {
1706
- "alias": "giao_dich",
1707
- "type": "select_record_link",
1708
- "target_entity": "giao_dich",
1709
- "cardinality": "many"
1710
- }
1711
- ]
2051
+ "tong_cong": {"type": "formula", "notation": {"style": "currency", "currency": "VND"}},
2052
+ "thue_suat": {"type": "formula", "notation": {"style": "unit", "unit": "percent"}},
2053
+ "tep": {"type": "files"},
2054
+ "giao_dich": {"type": "link", "table": "giao_dich", "multi": true}
2055
+ },
2056
+ "records": {"subtitle": ["so_hoa_don", "ngay_hoa_don"], "image": "tep", "figure": "tong_cong"}
1712
2057
  }
1713
2058
  ],
1714
- "records": {
1715
- "giao_dich": {
1716
- "title": "noi_dung",
1717
- "subtitle": ["ngay"],
1718
- "status": {"field": "trang_thai", "closed": ["da_khop", "khong_can_hd"], "history": "lich_su_khop"},
1719
- "figure": "bien_dong"
1720
- },
1721
- "hoa_don": {"title": "doi_tuong", "subtitle": ["so_hoa_don", "ngay_hoa_don"], "image": "tep", "figure": "tong_cong"}
1722
- },
1723
- "apps": [
1724
- {
1725
- "alias": "dong_tien",
1726
- "name": "Dòng tiền",
1727
- "icon": "chart-line",
1728
- "theme": {"color": "emerald"},
1729
- "dashboard": [
1730
- {
1731
- "metric": "giao_dich",
1732
- "value": "so_tien",
1733
- "where": {"loai": ["thu"]},
1734
- "over": "ngay",
1735
- "label": "Tiền vào"
1736
- },
1737
- {
1738
- "metric": "giao_dich",
1739
- "value": "so_tien",
1740
- "where": {"loai": ["chi"]},
1741
- "over": "ngay",
1742
- "better": "down",
1743
- "label": "Tiền ra"
1744
- },
1745
- {"trend": "giao_dich", "over": "ngay", "value": "bien_dong", "label": "Dòng tiền ròng"},
1746
- {
1747
- "breakdown": "giao_dich",
1748
- "by": "danh_muc",
1749
- "value": "so_tien",
1750
- "where": {"loai": ["chi"]},
1751
- "over": "ngay",
1752
- "label": "Chi theo danh mục"
1753
- },
1754
- {
1755
- "breakdown": "giao_dich",
1756
- "by": "doi_tuong",
1757
- "value": "so_tien",
1758
- "where": {"loai": ["thu"]},
1759
- "over": "ngay",
1760
- "label": "Thu theo khách hàng"
1761
- },
1762
- {
1763
- "pivot": "hoa_don",
1764
- "rows": "doi_tuong",
1765
- "columns": {"field": "so_ngay_qua_han", "tiers": [1, 8, 16, 31]},
1766
- "value": "tong_cong",
1767
- "where": {"chieu": ["ban_ra"]},
1768
- "label": "Công nợ bán ra theo tuổi nợ"
1769
- },
1770
- {
1771
- "metric": "hoa_don",
1772
- "value": "so_ngay_qua_han",
1773
- "average": true,
1774
- "where": {"chieu": ["ban_ra"]},
1775
- "label": "Số ngày quá hạn bình quân"
1776
- },
1777
- {"metric": "hoa_don", "value": "thue_suat", "average": true, "label": "Thuế suất bình quân"},
1778
- {
1779
- "list": "giao_dich",
1780
- "where": {"trang_thai": ["chua_khop"]},
1781
- "columns": ["sao_ke"],
1782
- "app": "doi_chieu",
1783
- "label": "Dòng chưa khớp"
1784
- }
1785
- ]
1786
- }
1787
- ]
2059
+ "body": {
2060
+ "type": "App",
2061
+ "key": "dong_tien",
2062
+ "name": "Dòng tiền",
2063
+ "icon": "chart-line",
2064
+ "color": "emerald",
2065
+ "children": [
2066
+ {
2067
+ "type": "Reading",
2068
+ "key": "dashboard_reading_0",
2069
+ "label": "Tiền vào",
2070
+ "entity": "giao_dich",
2071
+ "value": "so_tien",
2072
+ "where": {"loai": ["thu"]},
2073
+ "over": "ngay"
2074
+ },
2075
+ {
2076
+ "type": "Reading",
2077
+ "key": "dashboard_reading_1",
2078
+ "label": "Tiền ra",
2079
+ "entity": "giao_dich",
2080
+ "value": "so_tien",
2081
+ "where": {"loai": ["chi"]},
2082
+ "over": "ngay",
2083
+ "better": "down"
2084
+ },
2085
+ {
2086
+ "type": "Reading",
2087
+ "key": "dashboard_reading_2",
2088
+ "label": "Dòng tiền ròng",
2089
+ "entity": "giao_dich",
2090
+ "value": "bien_dong",
2091
+ "by": "ngay"
2092
+ },
2093
+ {
2094
+ "type": "Reading",
2095
+ "key": "dashboard_reading_3",
2096
+ "label": "Chi theo danh mục",
2097
+ "entity": "giao_dich",
2098
+ "value": "so_tien",
2099
+ "where": {"loai": ["chi"]},
2100
+ "by": "danh_muc",
2101
+ "over": "ngay"
2102
+ },
2103
+ {
2104
+ "type": "Reading",
2105
+ "key": "dashboard_reading_4",
2106
+ "label": "Thu theo khách hàng",
2107
+ "entity": "giao_dich",
2108
+ "value": "so_tien",
2109
+ "where": {"loai": ["thu"]},
2110
+ "by": "doi_tuong",
2111
+ "over": "ngay"
2112
+ },
2113
+ {
2114
+ "type": "Reading",
2115
+ "key": "dashboard_reading_5",
2116
+ "label": "Công nợ bán ra theo tuổi nợ",
2117
+ "entity": "hoa_don",
2118
+ "value": "tong_cong",
2119
+ "where": {"chieu": ["ban_ra"]},
2120
+ "by": "doi_tuong",
2121
+ "across": {"field": "so_ngay_qua_han", "tiers": [1, 8, 16, 31]}
2122
+ },
2123
+ {
2124
+ "type": "Reading",
2125
+ "key": "dashboard_reading_6",
2126
+ "label": "Số ngày quá hạn bình quân",
2127
+ "entity": "hoa_don",
2128
+ "value": {"avg": "so_ngay_qua_han"},
2129
+ "where": {"chieu": ["ban_ra"]}
2130
+ },
2131
+ {
2132
+ "type": "Reading",
2133
+ "key": "dashboard_reading_7",
2134
+ "label": "Thuế suất bình quân",
2135
+ "entity": "hoa_don",
2136
+ "value": {"avg": "thue_suat"}
2137
+ },
2138
+ {
2139
+ "type": "List",
2140
+ "key": "dashboard_reading_8",
2141
+ "entity": "giao_dich",
2142
+ "where": {"trang_thai": ["chua_khop"]},
2143
+ "app": "doi_chieu",
2144
+ "label": "Dòng chưa khớp",
2145
+ "columns": ["sao_ke"]
2146
+ }
2147
+ ]
2148
+ }
1788
2149
  }
1789
2150
  ```