@dforge-core/dforge-mcp 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/server.js +295 -294
- package/package.json +2 -2
- package/resources/schemas/data-views.schema.json +175 -4
- package/resources/schemas/deps.schema.json +84 -0
- package/resources/schemas/entity.schema.json +35 -5
- package/resources/schemas/manifest.schema.json +4 -2
- package/resources/schemas/reports.schema.json +30 -3
- package/resources/schemas/traits.schema.json +32 -0
- package/skills/dforge-mcp-author/references/reports.md +160 -284
|
@@ -1,152 +1,114 @@
|
|
|
1
1
|
# Reports Reference
|
|
2
2
|
|
|
3
|
-
Reports are **query-driven visualizations** — charts,
|
|
3
|
+
Reports are **query-driven visualizations** — charts, KPI cards, pivot tables, tables — backed by datasets. Unlike data views (which target one entity), a report can combine **multiple datasets** and lay out several panels.
|
|
4
4
|
|
|
5
|
-
Lives in: `ui/reports.json`
|
|
5
|
+
Lives in: `ui/reports.json` (map of `report_code` → report). Not listed in `manifest.json` — the install pipeline picks up `ui/reports.json` automatically.
|
|
6
6
|
SP files: `logic/reports/rpt_*.sql`
|
|
7
7
|
|
|
8
8
|
## Structure
|
|
9
9
|
|
|
10
|
+
A report has `description`, a `datasets` map, a `layout` object (`{ panels: [...] }`), and optional `parameters`. Panels reference datasets by code via `datasetCd`.
|
|
11
|
+
|
|
10
12
|
```json
|
|
11
13
|
{
|
|
12
14
|
"sales_pipeline": {
|
|
13
15
|
"description": "Open opportunities by stage",
|
|
14
16
|
"datasets": {
|
|
15
|
-
"
|
|
17
|
+
"pipeline": {
|
|
16
18
|
"caption": "Pipeline Data",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"aggregations": {
|
|
27
|
-
"opp_count": { "func": "count", "column": "*" },
|
|
28
|
-
"total_value": { "func": "sum", "column": "total_amount" }
|
|
19
|
+
"datasetType": "Q",
|
|
20
|
+
"query": {
|
|
21
|
+
"entityCd": "opportunity",
|
|
22
|
+
"columns": ["stage", "amount", "lead_source", "customer.name"],
|
|
23
|
+
"filter": {
|
|
24
|
+
"g": "and",
|
|
25
|
+
"i": [ { "c": "stage", "o": "nIn", "v": ["Closed Won", "Closed Lost"] } ]
|
|
26
|
+
},
|
|
27
|
+
"sort": [ { "c": "amount", "d": "desc" } ]
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
},
|
|
32
|
-
"layout":
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"categoryCol": "stage",
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"datasetCd": "ds_pipeline",
|
|
46
|
-
"title": "Pipeline Detail"
|
|
47
|
-
}
|
|
48
|
-
],
|
|
49
|
-
"params": {
|
|
50
|
-
"owner_filter": {
|
|
51
|
-
"fieldTypeCd": "user",
|
|
52
|
-
"label": "Filter by Owner",
|
|
53
|
-
"required": false
|
|
54
|
-
},
|
|
55
|
-
"start_date": {
|
|
56
|
-
"fieldTypeCd": "date",
|
|
57
|
-
"label": "Start Date",
|
|
58
|
-
"required": false,
|
|
59
|
-
"default": "=STARTMONTH()"
|
|
60
|
-
}
|
|
31
|
+
"layout": {
|
|
32
|
+
"panels": [
|
|
33
|
+
{
|
|
34
|
+
"vizType": "chart",
|
|
35
|
+
"datasetCd": "pipeline",
|
|
36
|
+
"title": "Pipeline Value by Stage",
|
|
37
|
+
"config": { "chartType": "bar", "categoryCol": "stage", "valueCol": "amount", "agg": "sum", "chartSize": "l" }
|
|
38
|
+
},
|
|
39
|
+
{ "vizType": "table", "datasetCd": "pipeline", "title": "Pipeline Detail" }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"parameters": {
|
|
43
|
+
"min_amount": { "fieldTypeCd": "number", "label": "Min Amount", "required": false, "default": 0 }
|
|
61
44
|
}
|
|
62
45
|
}
|
|
63
46
|
}
|
|
64
47
|
```
|
|
65
48
|
|
|
49
|
+
**Aggregation happens in the viz, not the dataset.** A dataset selects raw columns; the chart/KPI config aggregates them (`agg`, `metrics`). There is no dataset-level `groupBy`/`aggregations` — do not use them.
|
|
50
|
+
|
|
66
51
|
## Dataset types
|
|
67
52
|
|
|
68
53
|
### Entity query dataset (most common)
|
|
69
54
|
|
|
70
|
-
|
|
55
|
+
`datasetType: "Q"` (the default) with a `query` object using the platform query builder. Filters use the standard JSON filter format (see `references/filters.md`).
|
|
71
56
|
|
|
72
57
|
```json
|
|
73
|
-
"
|
|
58
|
+
"sales": {
|
|
74
59
|
"caption": "Sales Data",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"avg_value": { "func": "avg", "column": "total_amount" }
|
|
84
|
-
}
|
|
60
|
+
"datasetType": "Q",
|
|
61
|
+
"query": {
|
|
62
|
+
"entityCd": "opportunity",
|
|
63
|
+
"columns": ["stage", "amount", "close_date", "customer.name"],
|
|
64
|
+
"filter": { "c": "stage", "o": "ne", "v": "Closed Lost" },
|
|
65
|
+
"sort": [ { "c": "close_date", "d": "desc" } ]
|
|
66
|
+
},
|
|
67
|
+
"columnsDef": { "amount": { "label": "Deal Value" }, "customer.name": { "label": "Account" } }
|
|
85
68
|
}
|
|
86
69
|
```
|
|
87
70
|
|
|
88
|
-
|
|
71
|
+
- `query.entityCd` — source entity code (**not** `entityCode`).
|
|
72
|
+
- `query.columns` — column codes; supports dot navigation (`customer.name`).
|
|
73
|
+
- `query.filter` — canonical filter (`{c,o,v}` or `{g,i:[...]}`); use `@param_code` to reference a parameter.
|
|
74
|
+
- `query.sort` — `[{ c, d }]` where `d` ∈ `asc | desc`.
|
|
75
|
+
- `columnsDef` — optional per-column display overrides (`{ label, visible, width }`).
|
|
89
76
|
|
|
90
77
|
### Stored procedure dataset (developer path)
|
|
91
78
|
|
|
92
|
-
For
|
|
79
|
+
For SQL the query builder can't express — window functions, CTEs, cross-schema joins, conditional aggregation, multi-result sets.
|
|
93
80
|
|
|
94
81
|
```json
|
|
95
|
-
"
|
|
82
|
+
"aging": {
|
|
96
83
|
"caption": "AR Aging",
|
|
97
84
|
"datasetType": "S",
|
|
98
|
-
"
|
|
85
|
+
"spCd": "rpt_ar_aging",
|
|
99
86
|
"params": {
|
|
100
|
-
"as_of_date": {
|
|
101
|
-
|
|
102
|
-
"label": "As of Date",
|
|
103
|
-
"required": true,
|
|
104
|
-
"default": "=NOW()"
|
|
105
|
-
},
|
|
106
|
-
"customer_id": {
|
|
107
|
-
"fieldTypeCd": "lookup",
|
|
108
|
-
"label": "Customer",
|
|
109
|
-
"required": false,
|
|
110
|
-
"params": { "entityCd": "customer" }
|
|
111
|
-
}
|
|
87
|
+
"as_of_date": { "fieldTypeCd": "date", "label": "As of Date", "required": true, "default": "=NOW()" },
|
|
88
|
+
"customer_id": { "fieldTypeCd": "lookup", "label": "Customer", "required": false, "params": { "entityCd": "account" } }
|
|
112
89
|
},
|
|
113
90
|
"columnsDef": {
|
|
114
|
-
"customer_name": {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"baseDatatypeCd": "string",
|
|
118
|
-
"width": 200
|
|
119
|
-
},
|
|
120
|
-
"current_amount": {
|
|
121
|
-
"label": "Current",
|
|
122
|
-
"fieldTypeCd": "number",
|
|
123
|
-
"baseDatatypeCd": "number",
|
|
124
|
-
"format": "#,##0.00",
|
|
125
|
-
"width": 120
|
|
126
|
-
},
|
|
127
|
-
"days_30": {
|
|
128
|
-
"label": "1-30 Days",
|
|
129
|
-
"fieldTypeCd": "number",
|
|
130
|
-
"baseDatatypeCd": "number",
|
|
131
|
-
"format": "#,##0.00",
|
|
132
|
-
"width": 120
|
|
133
|
-
},
|
|
134
|
-
"total": {
|
|
135
|
-
"label": "Total",
|
|
136
|
-
"fieldTypeCd": "number",
|
|
137
|
-
"baseDatatypeCd": "number",
|
|
138
|
-
"format": "#,##0.00",
|
|
139
|
-
"width": 130
|
|
140
|
-
}
|
|
91
|
+
"customer_name": { "label": "Customer", "fieldTypeCd": "text", "baseDatatypeCd": "string", "width": 200 },
|
|
92
|
+
"current_amount": { "label": "Current", "fieldTypeCd": "number", "baseDatatypeCd": "number", "width": 120 },
|
|
93
|
+
"total": { "label": "Total", "fieldTypeCd": "number", "baseDatatypeCd": "number", "width": 130 }
|
|
141
94
|
}
|
|
142
95
|
}
|
|
143
96
|
```
|
|
144
97
|
|
|
145
98
|
Key differences from entity datasets:
|
|
146
|
-
- `datasetType: "S"
|
|
147
|
-
- `
|
|
148
|
-
- `columnsDef` — **required** —
|
|
149
|
-
- Params can be
|
|
99
|
+
- `datasetType: "S"`.
|
|
100
|
+
- `spCd` — the stored-procedure code (the function name **without** the schema prefix; resolved to `sp_id` at install). (**Not** `sp` or `procedureName`.)
|
|
101
|
+
- `columnsDef` — **required** — the platform can't infer columns from a function.
|
|
102
|
+
- Params can be per-dataset or per-report.
|
|
103
|
+
|
|
104
|
+
**Multi-result-set SPs** map extra datasets to the same function via `parentDatasetCd` (the dataset that owns the SP call) + `parentRef` (the named refcursor):
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
"datasets": {
|
|
108
|
+
"summary": { "caption": "Summary", "datasetType": "S", "spCd": "rpt_department_overview", "columnsDef": { } },
|
|
109
|
+
"by_role": { "caption": "By Role", "datasetType": "S", "parentDatasetCd": "summary", "parentRef": "employee_breakdown", "columnsDef": { } }
|
|
110
|
+
}
|
|
111
|
+
```
|
|
150
112
|
|
|
151
113
|
### The PostgreSQL function
|
|
152
114
|
|
|
@@ -159,240 +121,154 @@ CREATE OR REPLACE FUNCTION crm.rpt_ar_aging(
|
|
|
159
121
|
p_as_of_date date DEFAULT NULL, -- User parameter
|
|
160
122
|
p_customer_id bigint DEFAULT NULL -- User parameter (optional)
|
|
161
123
|
)
|
|
162
|
-
RETURNS TABLE (
|
|
163
|
-
customer_name text,
|
|
164
|
-
current_amount numeric,
|
|
165
|
-
days_30 numeric,
|
|
166
|
-
days_60 numeric,
|
|
167
|
-
days_90 numeric,
|
|
168
|
-
over_90 numeric,
|
|
169
|
-
total numeric
|
|
170
|
-
)
|
|
124
|
+
RETURNS TABLE ( customer_name text, current_amount numeric, total numeric )
|
|
171
125
|
LANGUAGE sql STABLE
|
|
172
126
|
AS $$
|
|
173
|
-
SELECT
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
-- ... more columns ...
|
|
178
|
-
SUM(i.amount_due) AS total
|
|
179
|
-
FROM fin.invoice i
|
|
180
|
-
JOIN crm.account c ON c.account_id = i.customer_id
|
|
181
|
-
WHERE i.status != 'Paid'
|
|
182
|
-
AND (p_customer_id IS NULL OR i.customer_id = p_customer_id)
|
|
183
|
-
GROUP BY c.account_name
|
|
184
|
-
ORDER BY total DESC;
|
|
127
|
+
SELECT c.account_name, SUM(...) , SUM(i.amount_due)
|
|
128
|
+
FROM fin.invoice i JOIN crm.account c ON c.account_id = i.customer_id
|
|
129
|
+
WHERE i.status <> 'Paid' AND (p_customer_id IS NULL OR i.customer_id = p_customer_id)
|
|
130
|
+
GROUP BY c.account_name ORDER BY 3 DESC;
|
|
185
131
|
$$;
|
|
186
132
|
```
|
|
187
133
|
|
|
188
134
|
**Rules for SP functions:**
|
|
189
|
-
- First two params are **always** `p_folder_uid uuid` and `p_user_id bigint` — injected by the platform
|
|
190
|
-
- User params come after,
|
|
191
|
-
-
|
|
192
|
-
- Use `
|
|
193
|
-
- Use the module's schema prefix: `crm.rpt_*`, `fin.rpt_*`, etc.
|
|
194
|
-
- Use `STABLE` volatility (read-only, enables PostgreSQL optimization)
|
|
195
|
-
- **Security is your responsibility** — filter by `p_folder_uid` and `p_user_id` where needed
|
|
196
|
-
|
|
197
|
-
### Multi-result-set SPs
|
|
198
|
-
|
|
199
|
-
For reports with multiple related datasets from one query:
|
|
200
|
-
|
|
201
|
-
```json
|
|
202
|
-
"datasets": {
|
|
203
|
-
"summary": {
|
|
204
|
-
"caption": "Department Summary",
|
|
205
|
-
"datasetType": "S",
|
|
206
|
-
"sp": "rpt_department_overview",
|
|
207
|
-
"spCursor": "department_summary",
|
|
208
|
-
"columnsDef": { /* ... */ }
|
|
209
|
-
},
|
|
210
|
-
"by_role": {
|
|
211
|
-
"caption": "By Role",
|
|
212
|
-
"datasetType": "S",
|
|
213
|
-
"sp": "rpt_department_overview",
|
|
214
|
-
"spCursor": "employee_breakdown",
|
|
215
|
-
"columnsDef": { /* ... */ }
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
Multiple datasets share the same `sp` (function called once). `spCursor` maps each dataset to a named refcursor returned by the function.
|
|
135
|
+
- First two params are **always** `p_folder_uid uuid` and `p_user_id bigint` — injected by the platform.
|
|
136
|
+
- User params come after, `DEFAULT NULL` for optional ones (order matches `params` declaration order).
|
|
137
|
+
- `RETURNS TABLE (...)` for a single set; `RETURNS SETOF refcursor` for multi-set (mapped via `parentRef`).
|
|
138
|
+
- Use the module's schema prefix (`crm.rpt_*`), `STABLE` volatility, and filter by `p_folder_uid` / `p_user_id` where needed — **security is your responsibility**.
|
|
221
139
|
|
|
222
140
|
## Parameters
|
|
223
141
|
|
|
224
|
-
Parameters prompt the user
|
|
225
|
-
|
|
226
|
-
### Report-level parameters
|
|
227
|
-
|
|
228
|
-
Defined in the report's `params` object:
|
|
142
|
+
Parameters prompt the user before running the report. Declare them per-report (`parameters`) or per-dataset (`params`).
|
|
229
143
|
|
|
230
144
|
```json
|
|
231
|
-
"
|
|
232
|
-
"start_date": {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
"required": true,
|
|
236
|
-
"default": "=STARTMONTH()"
|
|
237
|
-
},
|
|
238
|
-
"end_date": {
|
|
239
|
-
"fieldTypeCd": "date",
|
|
240
|
-
"label": "End Date",
|
|
241
|
-
"required": true,
|
|
242
|
-
"default": "=NOW()"
|
|
243
|
-
},
|
|
244
|
-
"region": {
|
|
245
|
-
"fieldTypeCd": "dropdown",
|
|
246
|
-
"label": "Region",
|
|
247
|
-
"required": false,
|
|
248
|
-
"params": {
|
|
249
|
-
"options": ["All", "North", "South", "East", "West"]
|
|
250
|
-
}
|
|
251
|
-
},
|
|
252
|
-
"customer": {
|
|
253
|
-
"fieldTypeCd": "lookup",
|
|
254
|
-
"label": "Customer",
|
|
255
|
-
"required": false,
|
|
256
|
-
"params": { "entityCd": "account" }
|
|
257
|
-
}
|
|
145
|
+
"parameters": {
|
|
146
|
+
"start_date": { "fieldTypeCd": "date", "label": "Start Date", "required": true, "default": "=STARTMONTH()" },
|
|
147
|
+
"region": { "fieldTypeCd": "dropdown", "label": "Region", "params": { "options": ["All", "North", "South"] } },
|
|
148
|
+
"customer": { "fieldTypeCd": "lookup", "label": "Customer", "params": { "entityCd": "account" } }
|
|
258
149
|
}
|
|
259
150
|
```
|
|
260
151
|
|
|
261
|
-
### Parameter properties
|
|
262
|
-
|
|
263
152
|
| Property | Description |
|
|
264
153
|
|---|---|
|
|
265
|
-
| `fieldTypeCd` |
|
|
154
|
+
| `fieldTypeCd` | Control type: `date`, `datetime`, `text`, `number`, `dropdown`, `lookup`, `user`, `checkbox` |
|
|
266
155
|
| `label` | Display label in the parameter dialog |
|
|
267
156
|
| `required` | Whether the user must fill this before running |
|
|
268
|
-
| `default` |
|
|
269
|
-
| `params` |
|
|
157
|
+
| `default` | Plain value or `=`-prefixed formula (`"=NOW()"`, `"=STARTMONTH()"`, `"=TODAY()"`) |
|
|
158
|
+
| `params` | Extra config — `options` for dropdowns, `entityCd` for lookups |
|
|
159
|
+
| `orderNum` | Display order in the parameter form (falls back to declaration order) |
|
|
160
|
+
|
|
161
|
+
Reference a parameter with `@param_code` inside a query filter value:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
"filter": { "g": "and", "i": [ { "c": "created_date", "o": "grEq", "v": "@start_date" } ] }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
SP params are passed positionally after the two required system params, in `params` declaration order.
|
|
270
168
|
|
|
271
|
-
|
|
169
|
+
## Layout
|
|
272
170
|
|
|
273
|
-
|
|
171
|
+
`layout` is an **object** — `{ "panels": [ ... ] }` (not a bare array). Each panel binds a `vizType` to a `datasetCd`:
|
|
274
172
|
|
|
275
173
|
```json
|
|
276
|
-
"
|
|
277
|
-
"
|
|
278
|
-
|
|
279
|
-
{ "
|
|
280
|
-
{ "
|
|
174
|
+
"layout": {
|
|
175
|
+
"panels": [
|
|
176
|
+
{ "vizType": "chart", "datasetCd": "pipeline", "title": "By Stage", "config": { "chartType": "bar", "categoryCol": "stage", "valueCol": "amount", "agg": "sum", "chartSize": "l" } },
|
|
177
|
+
{ "vizType": "kpi", "datasetCd": "pipeline", "title": "Pipeline KPIs", "config": { "metrics": [ { "column": "amount", "agg": "sum", "label": "Open Pipeline" } ] } },
|
|
178
|
+
{ "vizType": "table", "datasetCd": "pipeline", "title": "Detail" }
|
|
281
179
|
]
|
|
282
180
|
}
|
|
283
181
|
```
|
|
284
182
|
|
|
285
|
-
###
|
|
183
|
+
### Visualization types
|
|
286
184
|
|
|
287
|
-
|
|
185
|
+
The panel `vizType` is one of `table` / `chart` / `kpi` / `pivot` (also `tree`, `markdown`). **Chart type is set via `config.chartType`** — the panel `vizType` is always `"chart"` for any chart.
|
|
288
186
|
|
|
289
|
-
|
|
187
|
+
| `vizType` | Description | Config |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| `table` | Tabular data with sort/filter | optional `groupRules`, `aggregations`, `colorRules` |
|
|
190
|
+
| `chart` | Any chart — kind chosen by `config.chartType` | see below |
|
|
191
|
+
| `kpi` | One or more metric cards | `{ metrics: [ ... ] }` — see below |
|
|
192
|
+
| `pivot` | Pivot table | `rowFields`, `columnFields`, `values` |
|
|
290
193
|
|
|
291
|
-
|
|
194
|
+
**`config.chartType`** ∈ `bar` · `horizontalBar` · `stackedBar` · `combo` · `line` · `area` · `pie` · `doughnut` · `scatter` · `bubble` · `funnel` · `heatmap`.
|
|
292
195
|
|
|
293
|
-
|
|
294
|
-
params:
|
|
295
|
-
new_stage: dropdown required "New Stage"
|
|
296
|
-
note: textarea "Note"
|
|
297
|
-
amount: number required "Amount"
|
|
298
|
-
```
|
|
196
|
+
Chart config: `{ chartType, categoryCol, valueCol, agg, seriesCol?, sizeCol?, chartSize? ('sm'|'m'|'l'|'xl'), clickAction?, showTrend?, series? }`. `agg` ∈ `sum|avg|min|max|count`.
|
|
299
197
|
|
|
300
|
-
|
|
198
|
+
### KPI config (metrics)
|
|
301
199
|
|
|
302
|
-
|
|
200
|
+
`config.metrics` is an array; each metric is one of two modes:
|
|
303
201
|
|
|
304
|
-
|
|
202
|
+
**Aggregation metric** — one column reduced by one aggregation:
|
|
305
203
|
|
|
306
|
-
|
|
204
|
+
```json
|
|
205
|
+
{ "column": "amount", "agg": "sum", "display": "value", "label": "Open Pipeline" }
|
|
206
|
+
```
|
|
307
207
|
|
|
308
|
-
|
|
208
|
+
Optional: `display` (`value|gauge|progress|sparkline|icon`), `label`, `target`/`min`/`max`, `icon`, `sparklineDimension`.
|
|
309
209
|
|
|
310
|
-
|
|
210
|
+
**Formula metric** — an expression over named aggregation *inputs* (ratios, derived numbers):
|
|
311
211
|
|
|
312
|
-
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"label": "Avg Deal Size",
|
|
215
|
+
"formula": "[total] / [n]",
|
|
216
|
+
"inputs": [
|
|
217
|
+
{ "alias": "total", "column": "amount", "agg": "sum" },
|
|
218
|
+
{ "alias": "n", "column": "amount", "agg": "count" }
|
|
219
|
+
],
|
|
220
|
+
"format": { "style": "number", "decimals": 0 }
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
- `formula` references each input by `[alias]`. Missing alias → `0`; non-finite (÷0, NaN) → blank.
|
|
225
|
+
- `format.style` ∈ `number|percent`; `decimals` sets fraction digits. **Omit `format` (Auto)** to inherit the *first input column's own formatter* — a money formula then reads as money.
|
|
226
|
+
|
|
227
|
+
### Cross-source metrics & overlay series
|
|
313
228
|
|
|
314
|
-
|
|
229
|
+
A report already loads **multiple datasets**, so a KPI formula input or a chart overlay series can aggregate over a **sibling** dataset — reference it by its dataset code via `source` (omit = the panel's own `datasetCd`).
|
|
230
|
+
|
|
231
|
+
**Cross-dataset KPI** (pipeline vs leads):
|
|
315
232
|
|
|
316
233
|
```json
|
|
317
|
-
"
|
|
318
|
-
{
|
|
319
|
-
"
|
|
320
|
-
"
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
"categoryCol": "stage",
|
|
324
|
-
"valueCol": "total_value",
|
|
325
|
-
"agg": "sum",
|
|
326
|
-
"chartSize": "l"
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
"vizType": "grid",
|
|
331
|
-
"datasetCd": "ds_pipeline",
|
|
332
|
-
"title": "Detail"
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
"vizType": "kpi",
|
|
336
|
-
"datasetCd": "ds_pipeline",
|
|
337
|
-
"title": "Total Value",
|
|
338
|
-
"config": {
|
|
339
|
-
"valueCol": "total_value",
|
|
340
|
-
"format": "currency"
|
|
341
|
-
}
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
"vizType": "pie",
|
|
345
|
-
"datasetCd": "ds_pipeline",
|
|
346
|
-
"title": "Distribution",
|
|
347
|
-
"config": {
|
|
348
|
-
"categoryCol": "stage",
|
|
349
|
-
"valueCol": "opp_count"
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
]
|
|
234
|
+
{ "vizType": "kpi", "datasetCd": "deals", "config": { "metrics": [
|
|
235
|
+
{ "label": "Total Funnel Value", "formula": "[pipeline] + [leadval]", "inputs": [
|
|
236
|
+
{ "alias": "pipeline", "column": "amount", "agg": "sum" },
|
|
237
|
+
{ "alias": "leadval", "column": "estimated_value", "agg": "sum", "source": "leads" }
|
|
238
|
+
] }
|
|
239
|
+
] } }
|
|
353
240
|
```
|
|
354
241
|
|
|
355
|
-
|
|
242
|
+
**Cross-source chart overlay** — `config.series` (a single object **or** an array) adds series aggregated from other datasets, aligned on a shared category axis. Supported on `bar`/`horizontalBar`/`line`/`area`. Categories are outer-joined; a missing bucket fills `0` (bar) or gaps with `null` (line/area). Keep each series' `categoryCol` matching the primary axis unless a different axis is intended.
|
|
356
243
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
| `line` | Line chart | `categoryCol`, `valueCol`, `agg` |
|
|
364
|
-
| `area` | Area chart | Same as `line` |
|
|
365
|
-
| `pie` | Pie chart | `categoryCol`, `valueCol` |
|
|
366
|
-
| `doughnut` | Doughnut chart | Same as `pie` |
|
|
367
|
-
| `scatter` | Scatter plot | `xCol`, `yCol` |
|
|
368
|
-
| `combo` | Combined bar + line | `categoryCol`, `barCol`, `lineCol` |
|
|
369
|
-
| `kpi` | Single value card | `valueCol`, `format` (optional: gauge, progress, sparkline, icon variants) |
|
|
370
|
-
| `pivot` | Pivot table | `rowCols`, `colCols`, `valueCols`, `aggFunc` |
|
|
244
|
+
```json
|
|
245
|
+
{ "vizType": "chart", "datasetCd": "deals", "config": {
|
|
246
|
+
"chartType": "bar", "categoryCol": "lead_source", "valueCol": "amount", "agg": "sum",
|
|
247
|
+
"series": { "source": "leads", "categoryCol": "lead_source", "valueCol": "estimated_value", "agg": "sum", "label": "Lead Value" }
|
|
248
|
+
} }
|
|
249
|
+
```
|
|
371
250
|
|
|
372
|
-
|
|
251
|
+
Dashboard KPI/chart *tiles* get the same support via a `sources` map in the tile config (authored with "Add source" in the tile dialog) — not part of a module's `ui/reports.json`.
|
|
373
252
|
|
|
374
|
-
Chart
|
|
253
|
+
### Chart size
|
|
375
254
|
|
|
376
|
-
|
|
|
377
|
-
|---|---|---|---|
|
|
378
|
-
| `config.chartSize` | string | `sm`, `m`, `l`, `xl` | Optional size preset for chart height. When omitted, the default is `m`. |
|
|
255
|
+
`config.chartSize` ∈ `sm | m | l | xl` controls the default chart height (`m` when omitted).
|
|
379
256
|
|
|
380
257
|
## Grants
|
|
381
258
|
|
|
382
|
-
|
|
259
|
+
Grant report access with the `E` right:
|
|
383
260
|
|
|
384
261
|
```json
|
|
385
|
-
"rights": {
|
|
386
|
-
"report.sales_pipeline": "E"
|
|
387
|
-
}
|
|
262
|
+
"rights": { "report.sales_pipeline": "E" }
|
|
388
263
|
```
|
|
389
264
|
|
|
390
265
|
## Common mistakes
|
|
391
266
|
|
|
392
|
-
-
|
|
393
|
-
-
|
|
394
|
-
-
|
|
395
|
-
-
|
|
396
|
-
-
|
|
397
|
-
-
|
|
267
|
+
- Using a bare `"layout": [ ... ]` array — `layout` is an **object**: `"layout": { "panels": [ ... ] }`.
|
|
268
|
+
- Setting the chart kind as `vizType` (`"vizType": "bar"`) — the panel `vizType` is `"chart"`; the kind goes in `config.chartType`.
|
|
269
|
+
- Using `entityCode` / `groupBy` / `aggregations` on a dataset — use `query.entityCd` + `query.columns`, and aggregate in the viz (`agg`, `metrics`).
|
|
270
|
+
- Using `sp` / `procedureName` for an SP dataset — the field is `spCd`.
|
|
271
|
+
- Forgetting `columnsDef` on an SP dataset — **required**.
|
|
272
|
+
- Forgetting to grant `E` on the report in at least one role — it becomes invisible.
|
|
273
|
+
- Forgetting `p_folder_uid` / `p_user_id` as the first two SP function params — the call fails.
|
|
398
274
|
- Referencing a parameter as `$param` — use `@param_code` in filters.
|