@datarails-ai/dr-cli 0.0.1-locked → 1.6.70

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.
Files changed (58) hide show
  1. package/README.md +608 -0
  2. package/dist/chunk-DWMCBGQH.js +242 -0
  3. package/dist/cli.js +64468 -0
  4. package/dist/kb-6UWI7Q32.js +21 -0
  5. package/dist/scopes-map.json +4412 -0
  6. package/dist/skills/connector-sandbox/SKILL.md +95 -0
  7. package/dist/skills/datarails-connect-workato/SKILL.md +140 -0
  8. package/dist/skills/datarails-connectors-onboard-connector/SKILL.md +813 -0
  9. package/dist/skills/dr-cli/SKILL.md +659 -0
  10. package/dist/skills/dr-cli/reference/admin.md +796 -0
  11. package/dist/skills/dr-cli/reference/budget-builder.md +151 -0
  12. package/dist/skills/dr-cli/reference/calc-vals.md +73 -0
  13. package/dist/skills/dr-cli/reference/calcmodels.md +76 -0
  14. package/dist/skills/dr-cli/reference/cash.md +61 -0
  15. package/dist/skills/dr-cli/reference/dashboards-widgets.md +85 -0
  16. package/dist/skills/dr-cli/reference/datamappers.md +150 -0
  17. package/dist/skills/dr-cli/reference/desk.md +472 -0
  18. package/dist/skills/dr-cli/reference/filebox-documents.md +373 -0
  19. package/dist/skills/dr-cli/reference/forecast.md +51 -0
  20. package/dist/skills/dr-cli/reference/integrations.md +57 -0
  21. package/dist/skills/dr-cli/reference/lookups.md +168 -0
  22. package/dist/skills/dr-cli/reference/metrics.md +512 -0
  23. package/dist/skills/dr-cli/reference/multi-product.md +180 -0
  24. package/dist/skills/dr-cli/reference/reconciliation-mec.md +76 -0
  25. package/dist/skills/dr-cli/reference/rh-config.md +84 -0
  26. package/dist/skills/dr-cli/reference/safety.md +143 -0
  27. package/dist/skills/dr-cli/reference/spend-control.md +206 -0
  28. package/dist/skills/dr-cli/reference/templates.md +163 -0
  29. package/dist/skills/dr-cli/reference/workflows.md +137 -0
  30. package/dist/skills/dr-error-enrichment/SKILL.md +80 -0
  31. package/dist/skills/dr-error-enrichment/reference/core.md +257 -0
  32. package/dist/skills/dr-error-enrichment/reference/groundcover.md +84 -0
  33. package/dist/skills/dr-error-enrichment/reference/jira-bugs.md +219 -0
  34. package/dist/skills/dr-error-enrichment/reference/safety-backup.md +122 -0
  35. package/dist/skills/dr-error-enrichment-author/SKILL.md +219 -0
  36. package/dist/skills/dr-error-enrichment-improve/SKILL.md +218 -0
  37. package/dist/skills/dr-error-enrichment-maintain/SKILL.md +237 -0
  38. package/dist/skills/dr-error-enrichment-reconcile/SKILL.md +167 -0
  39. package/dist/skills/dr-filebox-api-setup/SKILL.md +170 -0
  40. package/dist/skills/dr-switch-user/SKILL.md +395 -0
  41. package/dist/skills/dr-validate/SKILL.md +392 -0
  42. package/dist/skills/dr-validate/report-template.html +950 -0
  43. package/dist/skills/fx-conversion-matrix/SKILL.md +763 -0
  44. package/dist/skills/fx-conversion-matrix/scripts/_args.mjs +52 -0
  45. package/dist/skills/fx-conversion-matrix/scripts/_xlsx.mjs +241 -0
  46. package/dist/skills/fx-conversion-matrix/scripts/build-matrix.mjs +366 -0
  47. package/dist/skills/fx-conversion-matrix/scripts/gen-mappers.mjs +171 -0
  48. package/dist/skills/fx-conversion-matrix/scripts/rates-to-xlsx.mjs +172 -0
  49. package/dist/skills/fx-onboard/SKILL.md +325 -0
  50. package/dist/skills/mec-table-validation/SKILL.md +383 -0
  51. package/dist/skills/plugins.json +24 -0
  52. package/dist/skills/validate-workato-migration/SKILL.md +167 -0
  53. package/dist/skills/validate-workato-migration/reference/gotchas.md +16 -0
  54. package/dist/skills/validate-workato-migration/reference/staging-orgs.md +26 -0
  55. package/dist/skills/validate-workato-migration/scripts/compare_files.py +255 -0
  56. package/dist/zendeskKb-JPSHVOPH.js +243 -0
  57. package/package.json +56 -9
  58. package/scripts/postinstall.mjs +91 -0
@@ -0,0 +1,373 @@
1
+ # Fileboxes & documents
2
+
3
+ ## Filebox — read
4
+
5
+ `filebox list`, `filebox get`, `filebox versions`, `filebox tags`,
6
+ `filebox sheets`, `filebox preview`, `filebox shares <doc_id>` (current user +
7
+ group shares). Names are HTML-decoded before render.
8
+
9
+ `filebox versions <doc_id>` lists **active** versions by default (unchanged).
10
+ To see archived (auto_save) versions, pass `--archived` (archived only) or
11
+ `--all` (active + archived); both add an `ARCHIVED` column. This is the only
12
+ way to discover an archived version's id from the CLI — archived rows are
13
+ absent from the default listing and from `list --include-versions`.
14
+
15
+ `--included` narrows any of those to the versions whose **`include_file` flag is
16
+ on** and adds an `INCLUDED` column. The flag is the server's intent, not proof
17
+ of delivered rows: turning it on only queues an ETL scan, so a version can be
18
+ listed here before (or without) its rows landing in the mapped tables — track
19
+ that separately with `dr scans status <table_id>`. Change the flag with `filebox
20
+ set-version-include` (below).
21
+
22
+ ## Filebox — create / upload
23
+
24
+ - `filebox create` — empty filebox + date tag. `--folder` makes a folder filebox
25
+ for nesting (no date tag). `--no-date` allocates the doc with no date tag.
26
+ `--scenario <s>` sets scenario **at create time** (the only working way to set
27
+ it — see update below). `--date-sub-type <t>` sets the initial tag config;
28
+ `--parent <id>` nests it.
29
+ - `filebox upload` — one-shot create + populate. **Known failure on
30
+ freshly-bootstrapped tenants:** returns 401 even with a valid JWT (re-running
31
+ `dr login` does not help). Workaround: `filebox create --no-date` to allocate
32
+ the doc id, then `filebox upload-version <id>` to push the data.
33
+ - `filebox upload-version <id>` — push a new version. Accepts `--date <date>
34
+ --scenario <name>` to validate-then-tag at upload time in one call (planning
35
+ fileboxes — same validation and read-back confirmation as
36
+ `set-version-cycle`).
37
+ - `filebox download` — download document content.
38
+
39
+ ## Filebox — update / config
40
+
41
+ - `filebox update <id> [--name <s>] [--payload-file <p>]` — PUT editable fields:
42
+ `name`, `parent`, `auto_save`, `date_format`, `use_type`,
43
+ `is_present_in_dashboard_reports`. **`scenario` and `tags_config_str` are NOT
44
+ editable here** — the server returns 200 but silently drops them. Use
45
+ `set-tag-config` for tags; set scenario at create time via
46
+ `filebox create --scenario`.
47
+ - `filebox set-tag-config <doc_id> --add NAME [--add …] [--remove NAME …]` —
48
+ merge semantics (adds/removes against the existing config, not full-replace);
49
+ tag names matched case-insensitively against the tenant tag pool.
50
+ - `filebox set-parent <doc_id> --parent <pid> | --detach` — `--detach` moves to
51
+ root; mutually exclusive with `--parent`.
52
+ - `filebox archive` (two-step; sets `is_archive`, recoverable until async
53
+ cleanup), `filebox archive-version <vid>` (two-step),
54
+ `filebox restore-version <vid>` (no confirm — non-destructive).
55
+ - Both `archive-version` / `restore-version` also accept the version by its
56
+ per-filebox **number** instead of the global id: `restore-version --doc
57
+ <doc_id> --version-number <n>` or `--latest-archived` (un-archive the
58
+ newest archived version); `archive-version --doc <doc_id> --version-number
59
+ <n>` or `--latest-active`. The CLI maps number → global id client-side
60
+ (there is no by-number endpoint). Success output names both, e.g.
61
+ `Restored version 12 (id 90001).` Typical flow: `filebox versions <doc>
62
+ --archived` to find it, then `restore-version --doc <doc> --version-number
63
+ <n>`.
64
+ - `filebox lock | unlock | lock-date-tag | unlock-date-tag | locked-date-tags` —
65
+ filebox / per-date-tag locks. `lock-date-tag`/`unlock-date-tag` take a
66
+ dimension flag (`--month 2026-04`, `--day 2026-04-15`, …) and resolve the
67
+ target epoch from the filebox's **actual version tags** (`tags[].value`), so
68
+ the lock matches real versions — a period with no matching version errors
69
+ loudly instead of silently no-op'ing (DR-51292). Pass `--value <epoch>` to
70
+ lock a raw stored value directly (from `filebox versions <id> --json`).
71
+ - `filebox get-auto-calc-settings`, `filebox set-option`, `filebox set-tag-config`,
72
+ `filebox share`, `filebox shares`, `filebox set-parent` — see `--help`.
73
+
74
+ ### Version include-in-database
75
+
76
+ - `filebox set-version-include <vid> --include on|off [--json]` — write the
77
+ per-version **Include in database** flag (`include_file`), the switch that
78
+ decides whether that version's rows reach its mapped tables. Read it with
79
+ `filebox versions <doc_id> --included`.
80
+ - Address it like `archive-version`: by global version id, or `--doc <doc_id>`
81
+ with `--version-number <n>` / `--latest-active`, or `--doc <doc_id>
82
+ --all-except-latest` for the bulk sweep (every active version behind the
83
+ latest). The sweep never **targets** the latest — but that is not a promise
84
+ that something still feeds the table: if the latest is itself excluded, an
85
+ exclude sweep leaves nothing included, and the plan step says so in its
86
+ `latest:` line.
87
+ - **Two-step confirm in both directions**, because both change table data:
88
+ `--include off` deletes that version's rows from every table it feeds,
89
+ `--include on` queues an ETL scan. The plan step prints the current → new
90
+ flag per version and, for the bulk sweep, every target plus the kept latest.
91
+ - A version already at the requested value is reported and skipped — the
92
+ server answers 200 without writing in that case, so a blind PUT looks like a
93
+ change it never made. Same guard catches the server's other silent
94
+ early-return: including a version whose filebox has an active budget split
95
+ for that dimension leaves the flag alone, and the CLI errors instead of
96
+ claiming success.
97
+ - Only reaches **non-archived** versions of **non-config** fileboxes (the
98
+ endpoint's queryset filters both) — a 404 usually means one of those, not a
99
+ bad id. `versions --all` shows which.
100
+ - `--all-except-latest` reads "latest" as the **single highest per-filebox
101
+ version number**, and refuses (rather than guessing) when two active
102
+ versions tie on it. On a filebox whose versions carry **dimension tags**
103
+ (per-department splits and the like) that is the newest upload overall, not
104
+ the newest per dimension — so the sweep would exclude the current version of
105
+ every *other* dimension. Read the plan's target list before confirming; for
106
+ those fileboxes address versions individually.
107
+ - Typical cleanup on a CoA-style filebox where stale monthly snapshots are all
108
+ still feeding the table: `filebox versions <doc> --included` to see the
109
+ damage, then `filebox set-version-include --doc <doc> --all-except-latest
110
+ --include off`.
111
+
112
+ ### Version tagging
113
+
114
+ - `filebox set-version-date-tag <vid> --doc <id> --month|--quarter|--day|--year|--week …
115
+ [--json]` — set the date tag on one version (resolves the tag id from the doc's
116
+ config; other tags untouched).
117
+ - `filebox set-version-workflow <vid> --doc <id> --status <Not Started|In Progress|Pending|Approved|Rejected>`
118
+ — set the workflow LIST-tag value (setting `Approved` requires `approve_workflow`).
119
+ - `filebox set-version-file-status <vid> --doc <id> --status <Draft|Final> [--json]`
120
+ — set the File Status LIST-tag value (other tags untouched). This is how a
121
+ version uploaded with `upload-version` (always Draft) is made Final without the
122
+ UI. Final needs `assign_final`. Only one version per period can be Final: if
123
+ another one already is, the command also needs `override_final`, and the server
124
+ moves that version to Draft (the output names it). The status is read back and
125
+ re-applied once if it did not stick. The write queues a rescan of the version,
126
+ so mapped tables show the new `File Status` only after it finishes.
127
+ - `filebox set-version-cycle <vid> --doc <id> --date <date> --scenario <name>
128
+ [--date-tag-name <n>] [--json]` — **forecast/budget (planning) fileboxes only.**
129
+ Sets the cycle date tag and the scenario LIST-tag together (both flags
130
+ required), mirroring the planning UI. **Validates the `(date, scenario)` pair
131
+ against `dr forecast scenarios` before writing**, so a version can never be
132
+ tagged into a non-existent cycle/scenario — create the cycle/scenario first
133
+ with `forecast scenarios create`. `--date-tag-name` disambiguates when a budget
134
+ filebox exposes more than one DATE tag.
135
+ **The tag is confirmed by reading the version back**, because a 2xx from the
136
+ write does not prove it stuck: on a filebox with an active auto-calculate
137
+ cascade the recalculation can replace the version or its tags moments later,
138
+ which used to leave an untagged version behind a successful command
139
+ (DR-52508). The read waits ~2s first — an immediate read sees a tag that is
140
+ about to be clobbered. If the pair is not there the command re-applies it
141
+ once, and only then fails, naming what the server actually holds and the
142
+ command to re-run. **This adds ~2s to a tagged write** (~4s when a re-apply
143
+ is needed); `set-version-cycle` therefore runs on the 5-minute call budget,
144
+ not the 30s default.
145
+ The date tag is compared **by period, never by exact epoch** — the server
146
+ normalizes what it is sent (`use_end_of_period_for_date`) and stored values
147
+ carry a noon/midnight offset, so equality would fail writes that succeeded
148
+ (same reason as `lock-date-tag`, DR-51292). Granularity comes from the date
149
+ tag's `sub_type`/name; a forecast filebox's `Forecast (New)` tag has none, so
150
+ only the tag's presence is checked there. The scenario tag is compared
151
+ exactly.
152
+ On an upload the confirmed row is also cross-checked against the upload
153
+ response (version number, name, filebox) — if the response only carried a
154
+ `latest_version_id` the cascade had already moved, the tag went to the wrong
155
+ version and the command says so instead of confirming.
156
+ `--json` always reports `verified: true|false`, and still emits the record
157
+ (with `error`) when the tag fails, so a scripted retry has the version id.
158
+ Cases the command deliberately does **not** fail on, since the write itself
159
+ returned 2xx: a version it cannot read back, and a confirmation cut short by
160
+ the call budget — both give `verified: false` plus an `unverified_reason` and
161
+ a stderr warning.
162
+
163
+ ### Share / options / sync
164
+
165
+ - `filebox share <doc_id> --user EMAIL:ROLE [--user …]` — ROLE is
166
+ `ADMIN|CONTRIBUTOR|VIEWER` or numeric `1|2|3`. Discover email candidates via
167
+ `organizations contacts`; groups via `organizations permission-groups`.
168
+ - `filebox set-option <doc_id> --bank-integration on|off [--option KEY=VALUE …]` —
169
+ only allow-listed keys are accepted (currently `is_bank_integration`); unknown
170
+ keys are rejected client-side (the server silently accepts dead keys).
171
+ - `filebox data-query get|set|clear|sync <fb_id>` — read/write/delete/poke the
172
+ Workato data-query. `get` shows `sync_interval` (cron) + `last_sync`; `set
173
+ --payload-file` PUTs a create/update body; `clear` is two-step.
174
+ - `filebox data-query sync <fb_id> [--date YYYY-MM-DD]` — trigger an async
175
+ sync, optionally scoped to specific period(s). `--date` is repeatable and
176
+ takes one plain calendar date — the CLI reads the filebox's own DATE
177
+ tag(s) from `tags_config` and truncates the date to whatever granularity
178
+ each one uses, so `--date 2026-07-01` syncs "July 2026" on a monthly
179
+ filebox, "Q3 2026" on a quarterly one, "2026" on a yearly one, or the
180
+ exact day/ISO week on a daily/weekly one — no need to know or care which
181
+ granularity a given filebox uses. A filebox with more than one DATE tag
182
+ resolves all of them into one sync target. Repeat `--date` to backfill
183
+ several periods in one call (one sync target per value). With no `--date`,
184
+ the sync targets whatever period the connector resolves as
185
+ current/default. **`--tag <json>`** still exists as an escape hatch for
186
+ tag dimensions `--date` doesn't cover (e.g. LIST tags like Plan/Forecast)
187
+ — it forwards the given string completely unvalidated to
188
+ `POST /api/documents/<id>/data_query_sync` as one `multiple_tags` element.
189
+ The server does an unguarded `json.loads()` + `tag['id']`/`tag['name']`
190
+ dict access on each element, so a malformed value throws an uncaught
191
+ exception that surfaces as a **bare HTTP 500 with no error body** — if you
192
+ ever need it directly, the required shape is
193
+ `[{"id":<tag_id>,"name":"<TagName>","value":<epoch_in_target_period>}]`,
194
+ where `id`/`name` come from that tenant's `dr filebox tags` (not a
195
+ constant — varies per tenant) and the epoch just needs to fall within the
196
+ target period (the server normalizes it). Prefer `--date`; reach for raw
197
+ `--tag` only when it doesn't cover your case.
198
+ - `filebox data-query set-sync-interval <fb_id> --cron "<expr>"` — change how
199
+ often the data-query polls (Workato-backed **and** connector-platform /
200
+ `DatarailsConnector` fileboxes both use this cron). **Use this for sync
201
+ frequency, not `set-auto-calc-frequency`.** The cron is stored/evaluated as
202
+ **5-field UTC** — an optional 6th field is folded into UTC, so `0 */3 * * *`
203
+ fires every 3h on UTC boundaries, not local time. Known behavior: any PUT to
204
+ the data-query resets `status` from `SYNCED` to `null` (write = "sync state
205
+ uncertain until next run"); it heals on the next cron fire — do not try to
206
+ "fix" it.
207
+ - **`--offsets` / `sync_offset_array`.** Every scheduled run syncs a set of
208
+ **period offsets** stored as `sync_offset_array`: `0` = the filebox's
209
+ current period, `-1` = the previous one, etc.
210
+ (`DataIntegrationQuery.get_default_tags_with_offset_array` iterates them; the
211
+ `{{ date_tag }}` placeholders in a connector Sync-query render against each
212
+ offset's period — so offsets, not the cron, decide *which* periods a run
213
+ pulls). `set-sync-interval` resolves them so it **never PUTs an empty array**
214
+ (the server's write serializer is `ListField(allow_empty=False)` and 400s on
215
+ `[]`, which is what a freshly-created data-query has): explicit `--offsets`
216
+ wins, else the existing non-empty array is preserved, else it defaults to
217
+ `[0]` ("re-fetch the current period each run"). So the bare command now works
218
+ on a data-query that never had offsets:
219
+ ```bash
220
+ dr filebox data-query set-sync-interval <fb_id> --cron "0 */3 * * *" # -> offsets [0]
221
+ dr filebox data-query set-sync-interval <fb_id> --cron "0 */3 * * *" --offsets 0,-1
222
+ ```
223
+ `--offsets` is a comma-separated integer list; server constraints (validated
224
+ client-side first): non-empty, **max 10** items, each int in **[-100, 100]**.
225
+ (If you hand-build a `data-query set --payload-file` body instead, you must
226
+ include a non-empty `sync_offset_array` yourself — that path does no
227
+ defaulting.)
228
+ - `filebox set-auto-calc-frequency <doc_id> --cron "<expr>" [--enable]` —
229
+ two-step. Controls Datarails **auto-calculate** scheduling, a **different**
230
+ feature from the Workato sync cron. `--enable` bootstraps auto-calc if it's
231
+ currently off. (`set-sync-frequency` is a deprecated alias that warns.)
232
+ - `filebox validation-rules options` — fetch the rule schema. `filebox
233
+ validation-rules set <doc_uuid> [flags] | --file <path>` — idempotent
234
+ create/update; **takes the document UUID, not the integer id.**
235
+
236
+ ### Push a filebox to other organizations
237
+
238
+ `filebox push <id> --to <orgs> [--override]` — two-step confirm, max 50
239
+ destinations per run. **Only the filebox's latest version travels.**
240
+
241
+ 1. `organizations push-targets` lists the eligible destinations. `--to` takes
242
+ their ids or names, comma-separated or repeated. A destination that isn't on
243
+ that list is rejected and **nothing** is pushed.
244
+ - An all-digit token is only ever matched against ids, so an organization
245
+ *named* e.g. `2024` must be addressed by its id. Same for a name
246
+ containing a comma — `--to` splits on commas before matching.
247
+ 2. Run without `--confirm-token` for the plan, then re-run with the token.
248
+
249
+ The push carries the filebox's data mappers, data queries, functions,
250
+ ex-tables, dynamic ranges and LUTs with it. If any of those can't be cloned
251
+ into a given destination, **that whole destination is skipped** — the others
252
+ still run.
253
+
254
+ There is no flag for pushing full version history. The endpoint accepts one, but
255
+ only the newer of the server's two export implementations reads it — a
256
+ destination on the older path takes the request and clones the latest version
257
+ alone, with nothing in the response to say so, and the pushing user cannot read
258
+ the destination's flags to find out. To move earlier versions, upload them in the
259
+ destination or use the web UI, which sends the same field with the same caveat.
260
+
261
+ **`--override` off does not guarantee a new filebox.** When the destination org
262
+ has the alias-matching behavior enabled server-side, a source filebox whose
263
+ alias *and* tags match an existing destination filebox pushes a new version into
264
+ that filebox instead of creating one. `--override` additionally targets the
265
+ filebox a previous push created (matched by source id); if both match, both
266
+ receive a version. To be certain what happened, check the destination after the
267
+ push.
268
+
269
+ Queued and returns no job id — results arrive as in-app notifications. Confirm
270
+ with `filebox list --account <destination>`.
271
+
272
+ ## Documents
273
+
274
+ - `documents sheets <doc_id> --template <tid>` — sheet names on the doc's latest
275
+ version, scoped to a target template's mapping context.
276
+ - `documents support-api set <doc_id> --on|--off` — toggle the doc's
277
+ `support_api` flag. The server rejects turning it **off** while a
278
+ Workato/WorkatoBased connection is in play.
279
+ - `documents calculate <doc_id> --year|--quarter|--month|--day|--week … [--backward] [--no-roll]`
280
+ — human-readable target (e.g. `--month 2026-04`); the CLI resolves the matching
281
+ tag and posts calculate. Errors list the doc's available dimensions when the
282
+ requested one is missing.
283
+ - `documents debug-zip <doc_version_id> --output <path>.zip [--type published_item|presentation]`
284
+ — **`<doc_version_id>` is a DocumentVersion id, not a doc id** (get it from
285
+ `filebox versions <doc_id>`). `--type` defaults to `published_item`. A 400
286
+ "no debug file" is surfaced verbatim.
287
+ - `documents auto-calculate <file.xlsx> [--out <path>] [--poll-interval <s>] [--timeout <s>]`
288
+ — **filebox-less** one-shot calc (DR-48445). Uploads a raw `.xlsx` to
289
+ `POST /api/documents/auto_calculate`, polls
290
+ `GET /api/documents/auto_calculate/<request_id>`, and writes the calculated
291
+ workbook to disk (default `<name>_auto_calculated.xlsx` next to the input).
292
+ Distinct from `filebox get-auto-calc-settings` / `set-auto-calc-frequency`,
293
+ which schedule auto-calc **on an existing filebox** — this one needs no
294
+ document at all. The result is one-shot: the server discards its copy once
295
+ downloaded, so a re-run means re-uploading. `--poll-interval` defaults to 2s,
296
+ `--timeout` to 300s; a server-side calc failure surfaces the BE error (e.g.
297
+ `incorrect formula syntax`) verbatim.
298
+ - `documents add-functions <file.xlsx> --function <id> [--function <id> …] [--out <path>] [--poll-interval <s>] [--timeout <s>]`
299
+ — **filebox-less** function registration (DR-50743): the CLI sibling of
300
+ `auto-calculate`. This is the **"add a function to an Excel file"** command —
301
+ it lives under `documents`, **not** under `functions` (which only read/write a
302
+ function's definition). Each `--function <id>` is resolved to its canonical
303
+ name (`GET /api/functions/<id>`); the workbook + the `[{id,name}]` list are
304
+ uploaded to `POST /api/documents/add_functions`, polled, and the result is
305
+ written to disk (default `<name>_functions_added.xlsx` next to the input).
306
+ Fails fast **before any upload** on an empty `--function` list or an unknown
307
+ id. `--poll-interval` defaults to 2s, `--timeout` to 300s. Same one-shot
308
+ contract as `auto-calculate` — the server discards its copy once downloaded,
309
+ so a re-run means re-uploading. To find the ids, `functions list`.
310
+ - `documents add-extables <file.xlsx> --extable <id> [--extable <id> …] [--out <path>] [--poll-interval <s>] [--timeout <s>]`
311
+ — **filebox-less** ExTable registration (DR-50832): the ExTable counterpart of
312
+ `add-functions`. This is the **"add an ExTable to an Excel file"** command; it
313
+ lives under `documents`, **not** under `extables` (which only
314
+ `list|get|clone|delete`). Each `--extable <id>` is resolved to its name from
315
+ the tenant; the workbook + list are uploaded to
316
+ `POST /api/documents/add_extables`, polled, and written to disk (default
317
+ `<name>_extables_added.xlsx` next to the input). Same one-shot contract as
318
+ `auto-calculate` / `add-functions`. To find the ids, `extables list`.
319
+ - `documents add-dynamic-ranges <file.xlsx> --range <id> [--range <id> …] [--formula <formula>] [--preserve-static-values] [--out <path>] [--poll-interval <s>] [--timeout <s>]`
320
+ — **filebox-less** Dynamic Range registration (DR-50909): the Dynamic Range
321
+ counterpart of `add-extables`. This is the **"add a Dynamic Range to an Excel
322
+ file"** command; it lives under `documents`, **not** under `dynamic-ranges`
323
+ (which `list|get|create|update|delete|data`). Each `--range <id>` is resolved
324
+ to its name from the tenant (`GET /api/templates/dynamic_range/<id>` — the
325
+ per-id endpoint, so **no `--table` is needed**); the workbook + `[{id,name}]`
326
+ list are uploaded to `POST /api/documents/add_dynamic_ranges`, polled, and
327
+ written to disk (default `<name>_dynamic_ranges_added.xlsx` next to the input).
328
+ Unlike ExTables/functions, the **server resolves and pushes each range's row
329
+ data to xl-calculate itself** (dynamic ranges have no pull endpoint) — the CLI
330
+ only supplies `{id, name}`.
331
+ - **Formula column (optional, server PR #6772):** attach a formula column to
332
+ the range with `--formula "<formula>"`, e.g. `--formula "=DR.QTD(rev)"`.
333
+ One formula per range and **no id prefix**, so `--formula` requires **exactly
334
+ one `--range`** (to formula-annotate several ranges, run the command once per
335
+ range). The string is **verbatim pass-through** — the server does no
336
+ resolution and forwards it to xl-calculate, which draws the formula column
337
+ next to the range list. A range added with no `--formula` is drawn
338
+ **list-only** (no `formula_columns` key sent). Because the formula is
339
+ caller-supplied, the CLI does not derive it from a Datarails function — pass
340
+ the literal Excel formula you want. **The formula must invoke a recognized
341
+ Datarails `DR.*` function** as its leading function (e.g. `DR.GET`, `DR.YTD`,
342
+ `DR.QTD`, `DR.MTD`, `DR.YTG`, `DR.QTG`, `DR.MTG`, `DR.ROLLINGMONTHS`,
343
+ `DR.CUMULATIVE`, and the rest of the `NewCustomFunctions.cs` table) —
344
+ validated case-insensitively, tolerating a leading `=` / `=@`. A typo or a
345
+ plain Excel formula (`=SUM(...)`) is rejected before any upload.
346
+ - **Preserve static values (optional, DR-52682):** pass
347
+ `--preserve-static-values` to register every `--range` with
348
+ `Preserve_Static_Values: true` in the workbook's `dr_control ->
349
+ _dr_dynamic_ranges` section. That flag is what keeps **hand-entered values**
350
+ in a dynamic range when it refreshes; without it a refresh or category
351
+ update can **wipe values the user typed**, which on a budget template is the
352
+ whole deliverable. Omitting the flag sends no key, which is **not** the same
353
+ as safe: a range the uploaded workbook already carries keeps its stored
354
+ value, but a range being registered for the **first time** — the normal case
355
+ for this command — gets xl-calculate's `false` default. So on a budget file
356
+ you are building from scratch, **pass the flag**; leaving it off ships the
357
+ exact data-loss risk DR-52682 is about. The CLI never sends an explicit
358
+ `false`, so re-running without the flag will not clear a range that already
359
+ has it set. Requires a server that accepts the field (monolith PR #7437) —
360
+ older servers silently ignore it.
361
+ - Same one-shot contract as `auto-calculate` / `add-functions` /
362
+ `add-extables`. To find the ids, `dynamic-ranges list --table <template_id>`.
363
+
364
+ > **Lookups (LUTs)** moved to their own reference — see
365
+ > [reference/lookups.md](lookups.md) for `lut …` (both filebox-sourced and
366
+ > table-sourced). A filebox document is the *source* for a filebox-sourced LUT,
367
+ > but the lookup commands live there now.
368
+
369
+ ## Customer-facing docs (KB)
370
+
371
+ Help Center → **"Getting Data into Datarails"** → Filebox. Find current articles with `dr kb search "filebox"` (or
372
+ browse `dr kb categories` / `dr kb sections`). Cite the article; don't
373
+ paraphrase from memory.
@@ -0,0 +1,51 @@
1
+ # Forecast management (Planning)
2
+
3
+ Date inputs accept `YYYY-MM-DD` or a 10-digit Unix timestamp.
4
+
5
+ **Two planning-scenario families exist: `Forecast` and `Budget`.** The list
6
+ commands default to **`Forecast`**, so budget cycles are invisible unless you
7
+ pass `--scenario Budget`. The same scenario NAME can exist in both families, so
8
+ omitting the flag returns a plausible wrong answer rather than nothing. A budget
9
+ file that "isn't in the Budget module" is often one whose cycle was read from
10
+ the wrong family. When working on a budget, pass it every time.
11
+
12
+ Read:
13
+
14
+ - `forecast cycles list [--year] [--scenario Forecast|Budget]`,
15
+ `forecast cycles get <date>`,
16
+ `forecast cycles documents <date> [--with-names]`.
17
+ - `forecast scenarios list [--cycle <date>] [--year <Y>] [--scenario Forecast|Budget]`,
18
+ `forecast scenarios get <id> [--no-versions] [--no-splits] [--no-collection-processes]`,
19
+ `forecast scenarios versions <id>`.
20
+
21
+ Write (all two-step confirm):
22
+
23
+ - `forecast cycles lock|unlock|set-default <date>`.
24
+ - `forecast cycles roll-forward <src> --to <dest> [--raw-tags <json>]`.
25
+ - `forecast scenarios create --cycle <date> --name <value> [--scenario <family>]
26
+ [--default] [--locked]` — `--scenario` picks the planning family and defaults
27
+ to `Forecast`. **Pass `--scenario Budget` to create a budget cycle**; without
28
+ it the row lands in the Forecast family under a budget-looking name and the
29
+ Budget module never shows it.
30
+ - `forecast scenarios delete|lock|unlock|set-default <id>`.
31
+ - `forecast scenarios clone <src-id> --to-cycle <dest-date> --to-scenario <name>
32
+ [--raw-tags <json>]` — the destination is a `(cycle, scenario-name)` **pair**;
33
+ `--to-scenario` is required unless `--raw-tags` is given (they're mutually
34
+ exclusive). `--to` is a deprecated alias for `--to-cycle`. **Empty-source
35
+ guard:** clone refuses (non-zero exit) if the source scenario has no versions —
36
+ use `scenarios create` for an empty shell.
37
+ - `forecast scenarios clone-from-budget --source-budget-cycle <id>
38
+ --to-cycle <dest> --to-scenario <name> [--documents <ids>] [--raw-tags <json>]`
39
+ — same `--to-cycle`/`--to-scenario`/`--raw-tags` rules.
40
+ - `forecast scenarios bulk-edit --cycle <date> [--add a,b] [--delete c,d]`.
41
+
42
+ `clone` and `clone-from-budget` auto-build the destination date-tag + scenario-
43
+ name-tag; `--raw-tags '[{"id":…,"value":…}, …]'` overrides for edge cases. The
44
+ CLI does not pre-check destination-name collisions — the server is authoritative
45
+ (appends versions or rejects; the response is surfaced verbatim).
46
+
47
+ ## Customer-facing docs (KB)
48
+
49
+ Help Center → **"Planning"** category (Forecast / Budget). Find current articles
50
+ with `dr kb search "forecast cycle scenario"` (or browse `dr kb categories` /
51
+ `dr kb sections`). Cite the article; don't paraphrase from memory.
@@ -0,0 +1,57 @@
1
+ # Integrations & company data sources
2
+
3
+ ## Customer connectors (`integrations`, alias `integration`)
4
+
5
+ Read:
6
+
7
+ - `integrations list`, `integrations get` (`--show-secrets` for cleartext config),
8
+ `integrations types`, `integrations status`, `integrations query-status`,
9
+ `integrations errors`.
10
+
11
+ Write:
12
+
13
+ - `integrations add` (payload-file), `integrations sync`,
14
+ `integrations test-connection`, `integrations update`,
15
+ `integrations delete` (two-step confirm).
16
+ - `integrations revoke <id>` (two-step confirm; Tier A on prod) — Disconnect a
17
+ `DatarailsConnector` OAuth data source: `POST /api/v2/data_integrations/<id>/revoke`.
18
+ The grant is cleared locally first (`oauth_tokens`/`oauth_pending` dropped,
19
+ `authorised=false`; `oauth` and `oauth_secrets` kept, so reconnecting needs no
20
+ re-setup), then revoked at the vendor best-effort via `oauth.revoke_url`
21
+ (`revoke_auth_style`, `revoke_body` describe the request). A failed vendor revoke
22
+ never undoes the disconnect — read `vendor_revoke` in the output: anything but
23
+ `revoked` may leave the grant live at the vendor. Legacy vendors are refused.
24
+ - `integrations share users` / `integrations share groups`.
25
+
26
+ ## IT-admin file-sync (`integrations it`)
27
+
28
+ File-sync connectors only — OneDrive / SharePoint / Egnyte. Output redacts
29
+ secrets by default.
30
+
31
+ - `integrations it list`, `integrations it get` (`--show-secrets` or `--json` to
32
+ expose creds).
33
+ - `integrations it add` (payload-file), `integrations it update` (payload-file or
34
+ `--set-config KEY=VAL`), `integrations it delete` (two-step confirm),
35
+ `integrations it resync`.
36
+
37
+ ## Entities — company data sources (`entities`, alias `companies`)
38
+
39
+ The connected accounting systems (QuickBooks Online/Desktop, Xero, NetSuite) —
40
+ i.e. integration data sources at the company level.
41
+
42
+ - `entities list`, `entities qbd-availability` (discoverability for `create qbd`),
43
+ `entities netsuite-connections` (discoverability for `create netsuite`).
44
+ - `entities create qbo|xero|qbd|netsuite` — **source system required up-front**
45
+ (ask "QBO, Xero, QBD, or NetSuite?"). `qbo`/`xero` require `--credential <s>`
46
+ (OAuth token) plus optional `--group --first --ignore-credential-conflict`;
47
+ `qbd` requires `--company-files <list>`; `netsuite` requires `--connection <id>`.
48
+ - `entities rename <doc_ids> --alias <s>` (CSV doc ids),
49
+ `entities rename-group <doc_ids> --group <s>` (move into a group).
50
+ - `entities delete <doc_ids>` — two-step confirm.
51
+ - `entities mark-completed <doc_id>` — IT-support override for a stuck state.
52
+
53
+ ## Customer-facing docs (KB)
54
+
55
+ Help Center → **"Integrations"** category. Find current articles with
56
+ `dr kb search "integration <system>"` (or browse `dr kb categories` /
57
+ `dr kb sections`). Cite the article; don't paraphrase from memory.
@@ -0,0 +1,168 @@
1
+ # Lookups (LUTs)
2
+
3
+ Command group `lut` (alias `lookups`). A LUT enriches a target table by joining
4
+ on a key and pulling in one or more value columns. The data feeding a lookup
5
+ comes from **one of two sources** — a filebox document, or another table — see
6
+ [Two source types](#two-source-types) below.
7
+
8
+ `lut list` is by template — pass `--table <template_id>` (get it from
9
+ `templates list`).
10
+
11
+ ## Read
12
+
13
+ - `lut list --table <tid>` — LUTs configured on a template.
14
+ - `lut list-by-doc <doc_id>` — LUTs sourced from a given filebox document.
15
+ - `lut data <doc_id> <lut_id>` — a LUT's mapped key→value data.
16
+ - `lut unmapped [--all] [--limit N]` — **filebox** LUT documents with unmapped
17
+ items (a.k.a. `lut_misses`). Table lookups have `document: null` and never
18
+ appear here — they don't surface misses (see [Choosing a type](#choosing-a-type)).
19
+
20
+ ## Create
21
+
22
+ - `lut create --table <tid> --payload-file <p>` — **filebox-sourced** LUT
23
+ (`document: <id>`), body supplied as JSON. See [Two source types](#two-source-types)
24
+ for the positional-keys / name-override behavior.
25
+ - `lut create-table-lookup --table <tid> --source-table <sid> --keys A,B --fields C,D`
26
+ — **table-sourced** LUT: a lookup on `--table` fed by columns from another
27
+ table (`--source-table`). Flag-based, no payload file. `--keys` and `--fields`
28
+ are comma-separated **source** column names; `--key-len` defaults to the count
29
+ of `--keys`; optional `--name`, `--order` (default 0), `--rescan-option`
30
+ (default `LATEST_IN_DIM`), `--no-check-misses`, `--no-validate-loops`,
31
+ `--json`. The source table must differ from the target.
32
+
33
+ ## Update / delete / order
34
+
35
+ - `lut update-table-lookup <id> --table <tid> [--source-table <sid>] [--keys A,B]
36
+ [--fields C,D] [--key-len <n>] [--name <n>] [--order <n>] [--rescan-option <opt>]
37
+ [--check-misses true|false] [--no-validate-loops] [--json]` — **table lookups.**
38
+ Overlays only the flags you pass onto the lookup's current config and PUTs the
39
+ full shape. `--table` (the target) is required to locate the LUT — there is no
40
+ per-id fetch. Prefer this over `lut update` for table lookups.
41
+ - `lut update <id> --payload-file <p>` — generic update (either source type). For
42
+ a **table** lookup the server re-runs full validation on every PUT, so a raw
43
+ payload MUST re-send `source_template_id` + non-empty `keys`/`fields` even to
44
+ change one field (no partial update) — which is why `update-table-lookup`
45
+ exists. Use this raw path for filebox lookups.
46
+ - `lut delete <id>` — two-step confirm (works for either source type).
47
+ - `lut reorder [--ids a,b,c | --payload-file <p>]` — reorder LUTs under a template.
48
+
49
+ ## Mapping data / export
50
+
51
+ - `lut save-mappings <doc_id> --file <path>` — the FE "Save" action, and the way
52
+ to resolve unmapped items / seed a lookup from the CLI.
53
+
54
+ `--file` holds a **bare JSON array** of sheet objects. Not an object — the
55
+ endpoint deserializes with `many=True`, so a top-level object comes back as
56
+ `400 {"non_field_errors":["Expected a list of items but got type \"dict\"."]}`.
57
+
58
+ ```json
59
+ [
60
+ {
61
+ "sheet_name": "USER to DR Mapping",
62
+ "document_version_id": 9001,
63
+ "create": [
64
+ [
65
+ {"column_header": "USER_TO_DR_ACC_KEY", "value": "4000"},
66
+ {"column_header": "Account Group L2", "value": "Revenue"}
67
+ ]
68
+ ]
69
+ }
70
+ ]
71
+ ```
72
+
73
+ - A **row** is a list of `{column_header, value}` cells — key columns first,
74
+ in `header` order. A flat array of values is rejected.
75
+ - `document_version_id` must be the document's **latest** version
76
+ (`filebox versions <doc_id>`), else the server answers
77
+ `"DocumentVersion is not the latest"`.
78
+ - **`create` only.** `update` and `delete` are refused: the server addresses
79
+ their rows by a position it computes wrongly — `delete_rows` deletes by
80
+ indexes it never adjusts after each removal, and `update_rows` reads a
81
+ sheet-ordered, matched-only index map by the caller's array position — so
82
+ either can write to or remove the wrong rows and still report success.
83
+ Neither depends on key length. Edit or remove existing rows in the web app.
84
+ - **Leave `header` out.** The CLI reads the sheet's own row 1 and sends that.
85
+ A `header` that differs from row 1 makes the server *rename* the columns in
86
+ the customer's file, so a supplied one is checked against the sheet and
87
+ refused on mismatch; `--allow-header-rename` is the deliberate override.
88
+ - A bucket may also be an object keyed by row (the shape the web app holds in
89
+ memory); the CLI flattens it. Omitted buckets default to `[]`.
90
+ - Every cell's `column_header` must appear in the sheet's header. Everything
91
+ is validated locally first, so a malformed body fails naming the sheet index
92
+ and field instead of costing a round-trip.
93
+ - **The write is async and the command waits for it.** The server runs the
94
+ save as a Celery task and answers `409 {task_id, cache_key}` after 0.5s —
95
+ which any payload that actually writes a row will hit, since the task
96
+ reopens the workbook and recalculates it through the Xl service. The CLI
97
+ re-POSTs to `?cache_key=…` until the task reports a result, then prints the
98
+ server's own message (`Mapping has been saved. Version number is <id>`).
99
+ `--poll-interval <s>` and `--timeout <s>` (default 2 / 300) tune that. On
100
+ timeout the write is still running server-side — re-running the same
101
+ command rejoins it rather than starting a second one.
102
+ - **Unrecognized sheet keys are passed through, not rejected** (so a payload
103
+ captured from the browser keeps `key_len` etc.). A misspelled `"crate"`
104
+ therefore saves nothing and still returns success — the command's
105
+ `N created, N updated, N deleted` summary is how you catch it.
106
+
107
+ Assembling a payload from unmapped items, end to end:
108
+
109
+ ```bash
110
+ dr lut unmapped --server US # which documents have misses
111
+ dr lut list-by-doc <doc_id> --server US --json # lut id, sheet_name, keys, fields
112
+ dr filebox versions <doc_id> --server US # latest version id
113
+ ```
114
+
115
+ Each miss in `lut_misses` becomes one `create` row: the key cell first, then
116
+ one cell per field. Column names come from `list-by-doc`'s `keys` + `fields`;
117
+ don't put them in a `header` field — the CLI reads the sheet's own.
118
+
119
+ - `lut export <doc_id> --output <path>` — downloads the document's LUT data as
120
+ `.xlsx`.
121
+
122
+ ## Choosing a type
123
+
124
+ Both types join a **source** to the target table on shared key field(s) — like
125
+ `VLOOKUP` but with support for multiple keys — and pull extra columns in. Pick
126
+ by where the source lives and what you need:
127
+
128
+ | If… | Use | Command |
129
+ |---|---|---|
130
+ | Source is an uploaded mapping **file** (e.g. a simple Chart of Accounts) | Filebox lookup | `lut create` |
131
+ | Source is **another table** already in the environment (e.g. a large dataset) | Table lookup | `lut create-table-lookup` |
132
+ | You need **unmapped-item detection** (misses surfaced) | Filebox lookup only | `lut create` |
133
+ | The key is **not** in the source file's leading columns | Table lookup (no position constraint) | `lut create-table-lookup` |
134
+
135
+ Deciding factors:
136
+
137
+ - **Unmapped items.** A **filebox lookup surfaces unmapped items** — raw-data
138
+ keys with no match in the source — via `dr lut unmapped`, the Home Page Data
139
+ Status, and `lut_misses`. A **table lookup does NOT surface unmapped items**:
140
+ table-sourced lookups have `document: null`, so they never appear in
141
+ `dr lut unmapped`; monitor misses via a dashboard instead. If data-integrity
142
+ alerting matters, prefer a filebox lookup.
143
+ - **Key column position.** A filebox lookup resolves keys **positionally** — the
144
+ key field(s) must be the **leading column(s)** of the source file. A table
145
+ lookup honors **named** keys/fields, so the key can be any column.
146
+
147
+ ## The two shapes on the wire
148
+
149
+ - **Filebox-sourced** (`document: <id>`, `source_template_id` omitted) — created
150
+ by `lut create` from a `--payload-file`. The engine **resolves keys
151
+ positionally** from the source file's header row (first `key_len` columns →
152
+ keys, rest → fields; the JSON `keys`/`fields` arrays are ignored), and
153
+ **overrides the requested `name`** with the source filebox's name. After
154
+ `create`, the CLI re-fetches and warns on a shape/name mismatch; the fix is to
155
+ reshape/rename the source file, then `lut update`.
156
+ - **Table-sourced** (`source_template_id: <sid>`, `document: null`) — created by
157
+ `lut create-table-lookup`. The write field is `source_template_id` (NOT the
158
+ read-only `source_template`); the named `--keys`/`--fields` are honored
159
+ verbatim (no positional resolution). The source table must differ from the
160
+ target. The endpoint returns 202 (data materializes asynchronously) — rerun
161
+ `lut list --table <tid>` to confirm the row; joined values populate only after
162
+ the target table is rescanned.
163
+
164
+ ## Customer-facing docs (KB)
165
+
166
+ Help Center → **"Getting Data into Datarails"** → Lookups. Find current articles
167
+ with `dr kb search "lookup"` (or browse `dr kb categories` / `dr kb sections`).
168
+ Cite the article; don't paraphrase from memory.