acumatica-cli 0.19.0__tar.gz → 0.26.0__tar.gz
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.
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/PKG-INFO +185 -56
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/README.md +184 -55
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/pyproject.toml +7 -2
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/pyproject.toml.orig +4 -1
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/bootstrap.py +35 -20
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/bootstrap_project.xml +344 -5
- acumatica_cli-0.26.0/src/acumatica_cli/cli.py +1897 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/client.py +35 -1
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/config.py +100 -49
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/extract.py +10 -1
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/inventory.py +1 -1
- acumatica_cli-0.26.0/src/acumatica_cli/matrix.py +155 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/reconcile.py +10 -4
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/run.py +69 -24
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/seed.py +141 -37
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/seed_catalog.yaml +117 -6
- acumatica_cli-0.26.0/src/acumatica_cli/snapshot_map.yaml +314 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/state.py +1 -1
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/README.md +19 -8
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/baseline/20-accounts.yaml +24 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/baseline/50-gl-preferences.yaml +4 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/config/bootstrap/segmented-key.yaml +10 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/config/master/05-numbering-sequences.yaml +82 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/20-in-preferences.yaml +9 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/56-so-preferences.yaml +2 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/57-po-preferences.yaml +3 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/config/master/60-ar-preferences.yaml +12 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/config/master/61-ap-preferences.yaml +12 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/62-ca-preferences.yaml +4 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/config/master/90-roles.yaml +214 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/config/master/91-users.yaml +16 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/env +15 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/matrix +9 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/overlays/README.md +51 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/overlays/default-24.200.001/README.md +11 -0
- acumatica_cli-0.26.0/src/acumatica_cli/templates/overlays/default-24.200.001/scenario/30-build.yaml +116 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/scenario/10-seed-capital.yaml +3 -3
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/scenario/20-buy.yaml +3 -3
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/scenario/30-build.yaml +1 -1
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/scenario/40-sell.yaml +4 -4
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/tenant.py +21 -5
- acumatica_cli-0.19.0/src/acumatica_cli/cli.py +0 -1242
- acumatica_cli-0.19.0/src/acumatica_cli/snapshot_map.yaml +0 -167
- acumatica_cli-0.19.0/src/acumatica_cli/target.py +0 -94
- acumatica_cli-0.19.0/src/acumatica_cli/templates/config/master/60-ar-preferences.yaml +0 -5
- acumatica_cli-0.19.0/src/acumatica_cli/templates/config/master/61-ap-preferences.yaml +0 -5
- acumatica_cli-0.19.0/src/acumatica_cli/templates/env +0 -16
- acumatica_cli-0.19.0/src/acumatica_cli/templates/target +0 -7
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/__init__.py +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/bootstrap_plugin.cs +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/firstlogin.py +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/models.py +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/output.py +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/baseline/10-subaccounts.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/baseline/40-ledger.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/baseline/60-ledger-company.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/baseline/90-uoms.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/bootstrap/company.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/bootstrap/credit-terms.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/bootstrap/features.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/10-reason-codes.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/30-availability-rules.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/40-posting-classes.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/50-warehouse.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/51-warehouse-locations.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/52-warehouse-defaults.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/53-tax-categories.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/54-item-classes.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/58-order-types.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/63-cash-account.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/64-payment-methods.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/65-statement-cycles.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/70-vendor-classes.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/71-customer-classes.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/75-vendors.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/76-customers.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/80-stock-items-parts.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/82-stock-items-kits.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/master/85-kit-specifications.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/setup/10-financial-year.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/setup/20-master-calendar.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/setup/30-open-periods.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/config/views/10-trial-balance.yaml +0 -0
- {acumatica_cli-0.19.0 → acumatica_cli-0.26.0}/src/acumatica_cli/templates/gitignore +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: acumatica-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.0
|
|
4
4
|
Summary: Acumatica ERP Config-as-Code: tenant provisioning, baseline config, and reference data
|
|
5
5
|
Author: Konstantin Borovik
|
|
6
6
|
Author-email: Konstantin Borovik <kb@lab5.ca>
|
|
@@ -32,6 +32,33 @@ Acumatica configuration normally lives in the web UI: wizards, screens, and manu
|
|
|
32
32
|
|
|
33
33
|
`acu` moves that configuration into YAML files in a git repo, so a tenant can be rebuilt from scratch, audited in a pull request, and checked for drift like any other infrastructure.
|
|
34
34
|
|
|
35
|
+
## What this tool is capable of
|
|
36
|
+
|
|
37
|
+
YAML in git is the source of truth; the live tenant is the target. `apply` is the only writer.
|
|
38
|
+
|
|
39
|
+
| You can | Command | Effect |
|
|
40
|
+
| ------- | ------- | ------ |
|
|
41
|
+
| Seed a tenant from YAML | `apply` | Idempotent PUT of `config/` into the live tenant |
|
|
42
|
+
| Detect drift | `diff` | Compare seed YAML to live (exit 2 when they diverge) |
|
|
43
|
+
| Pull live config into YAML | `extract` | Inverse of `apply` — GET into `config/{bootstrap,baseline,setup,master}/` |
|
|
44
|
+
| Run transaction scripts | `run` | Forward documents from `scenario/` (capital, buy, build, sell) |
|
|
45
|
+
| Capture derived balances | `state` | Inquire trial-balance etc. into `state/` (not seed) |
|
|
46
|
+
| Create and destroy tenants | `tenant` | SSH control plane: list / create / delete / recycle |
|
|
47
|
+
| Publish the Bootstrap contract | `bootstrap` | REST publish of AcuBootstrap (or `--export` zip for the UI) |
|
|
48
|
+
| Prove a cold rebuild | `check` | create, then apply, then run, then diff on a fresh tenant |
|
|
49
|
+
| Snapshot a site offline | `inventory` | SM203520 XML ZIP or `ac.exe export xml` writes `inventory/` |
|
|
50
|
+
| Cross-check snapshot vs seed | `reconcile` | `inventory/` plus optional `config/` writes `findings/` only |
|
|
51
|
+
| Dump the contract schema | `schema` | OpenAPI `swagger.json` for the pinned endpoint |
|
|
52
|
+
| Scaffold a data repo | `config init` | Full `config/`, `scenario/`, `matrix.yaml` tree |
|
|
53
|
+
| Preflight a target | `config check` | Discovery, secrets, matrix, REST, endpoints, SSH |
|
|
54
|
+
|
|
55
|
+
Seed YAML covers features, company, credit terms, subaccounts, chart of accounts, ledger, UOMs, financial year / calendar / periods, numbering sequences, inventory and distribution masters, roles, and users.
|
|
56
|
+
|
|
57
|
+
REST is the data plane (`apply`, `diff`, `run`, `extract`, `state`, `bootstrap`, `schema`).
|
|
58
|
+
SSH is the control plane (`tenant *`).
|
|
59
|
+
|
|
60
|
+
Hosted sites skip SSH.
|
|
61
|
+
|
|
35
62
|
## Quick start
|
|
36
63
|
|
|
37
64
|
```sh
|
|
@@ -39,23 +66,24 @@ uv tool install acumatica-cli
|
|
|
39
66
|
|
|
40
67
|
acu config init --host erp.example.com my-erp
|
|
41
68
|
cd my-erp # edit .env: set ACU_PASSWORD, ACU_TENANT
|
|
42
|
-
#
|
|
69
|
+
# pin+where = matrix.yaml cell (default_api + base_url)
|
|
43
70
|
# start from a brand-new empty tenant
|
|
44
71
|
|
|
45
|
-
acu config check # read-only preflight (incl.
|
|
46
|
-
acu tenant create --
|
|
72
|
+
acu config check # read-only preflight (incl. matrix.yaml)
|
|
73
|
+
acu tenant create --login DEV # create + bootstrap (SSH; --id optional)
|
|
47
74
|
# or hosted: acu --tenant DEV bootstrap
|
|
48
75
|
acu --tenant DEV apply config/ # seed config/{bootstrap,baseline,setup,master}/
|
|
49
76
|
acu --tenant DEV run scenario/ # once capital → buy → build → sell
|
|
50
77
|
acu --tenant DEV diff config/ # prove zero drift (exit 2 on drift)
|
|
51
78
|
acu --tenant DEV state # capture state/ trial-balance
|
|
52
|
-
acu --tenant DEV
|
|
79
|
+
acu check --yes --tenant DEV # cold lifecycle create→apply→run→diff (leave tenant)
|
|
53
80
|
```
|
|
54
81
|
|
|
55
82
|
Bare `apply` / `diff` (no path args) also prefer `config/` when those trees exist.
|
|
56
|
-
See [docs/demo-seed.md](docs/demo-seed.md) for the entity map, once-guard,
|
|
83
|
+
See [docs/demo-seed.md](docs/demo-seed.md) for the entity map, once-guard, apply-order notes, NumberingSequence vs prefs `*NumberingID`, curated *Preferences field depth (V41), and Role/User + password seed rules.
|
|
57
84
|
|
|
58
|
-
**Hosted Acumatica (no SSH):** the tenant already exists; set a blank `ACU_SSH=` in `.env
|
|
85
|
+
**Hosted Acumatica (no SSH):** the tenant already exists; set a blank `ACU_SSH=` in `.env`.
|
|
86
|
+
The scaffold omits the key — without it, acu defaults to `Administrator@<ACU_BASE_URL host>` for SSH boxes.
|
|
59
87
|
|
|
60
88
|
```sh
|
|
61
89
|
acu config init --host customer.acumatica.com my-erp
|
|
@@ -71,20 +99,23 @@ acu bootstrap --export AcuBootstrap.zip # import + publish on SM204505
|
|
|
71
99
|
## CLI map
|
|
72
100
|
|
|
73
101
|
```text
|
|
74
|
-
acu [--tenant NAME] [--url URL] [--ssh USER@HOST] [--api-version V]
|
|
102
|
+
acu [--cell ID] [--tenant NAME] [--url URL] [--ssh USER@HOST] [--api-version V]
|
|
75
103
|
[--username U] [--password P] [--version] [--completion [SHELL]]
|
|
76
104
|
│
|
|
77
105
|
├── tenant tenant CRUD (ac.exe over SSH — control plane)
|
|
78
106
|
│ ├── list CompanyID, sign-in name, internal CD, type
|
|
79
|
-
│ ├── create --
|
|
107
|
+
│ ├── create --login NAME [--id N] create + bootstrap; re-run to republish (SSH)
|
|
80
108
|
│ │ [--type SalesDemo|T100|U100] [--parent N] [--hidden] [--no-init]
|
|
81
|
-
│
|
|
109
|
+
│ │ omit --id → next free CompanyID (max list + 1)
|
|
110
|
+
│ ├── delete --id N | --login NAME [--yes]
|
|
111
|
+
│ │ delete the tenant and its data, recycle app pool
|
|
82
112
|
│ └── recycle [--yes] restart site app pool (tenant map + free API slots)
|
|
83
113
|
│
|
|
84
114
|
├── bootstrap [--export PATH] publish AcuBootstrap (REST); --export = offline zip
|
|
85
115
|
├── apply [--dry-run] [FILES...] push YAML via REST (idempotent PUT upserts)
|
|
86
116
|
├── diff [FILES...] drift check vs the live tenant (exit 2 on drift)
|
|
87
117
|
├── run [--dry-run] [FILES...] execute transaction scenario YAML (exit 1 on any miss)
|
|
118
|
+
├── check [--all] [--yes] [--tenant L] cold lifecycle create→apply→run→diff; leave tenant (V47)
|
|
88
119
|
├── state [--out DIR] [--diff] [--assert-unchanged] [--dry-run] [FILES...]
|
|
89
120
|
│ capture derived state into state/ (not seed)
|
|
90
121
|
├── extract [--out DIR] [--only NAME]... [--force] [--dry-run]
|
|
@@ -96,22 +127,41 @@ acu [--tenant NAME] [--url URL] [--ssh USER@HOST] [--api-version V]
|
|
|
96
127
|
├── schema [--out DIR] dump the endpoint's OpenAPI schema (swagger.json)
|
|
97
128
|
│
|
|
98
129
|
└── config configuration ops
|
|
99
|
-
├── init [--host HOST] [DIR] scaffold full data repo (config/, scenario/,
|
|
130
|
+
├── init [--host HOST] [DIR] scaffold full data repo (config/, scenario/, matrix.yaml)
|
|
100
131
|
├── show print the resolved config as a complete .env
|
|
101
|
-
└── check [--strict] preflight: discovery, secrets,
|
|
132
|
+
└── check [--strict] preflight: discovery, secrets, matrix, REST, endpoints, SSH
|
|
102
133
|
```
|
|
103
134
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
`
|
|
107
|
-
|
|
135
|
+
When you omit FILES:
|
|
136
|
+
|
|
137
|
+
- `apply` / `diff` prefer `config/<name>/` if any seed child exists under `config/`.
|
|
138
|
+
Otherwise they walk root `bootstrap/`, `baseline/`, `setup/`, then `master/` when present.
|
|
139
|
+
A path like `config/` expands those nested seed dirs in the same order.
|
|
140
|
+
- `run` defaults to `scenario/`.
|
|
141
|
+
- `state` defaults to `config/views/`; writes go to `state/` (`--out`).
|
|
142
|
+
|
|
143
|
+
Scenario YAML may use `${current_period}` (host-local `MMyyyy`) on steps, expect params, and `once.present` params.
|
|
144
|
+
`config/views` and `state` keep Period pinned — see [docs/demo-seed.md](docs/demo-seed.md#period-token-current_period-vs-pinned-views).
|
|
145
|
+
|
|
108
146
|
`extract` always writes under `config/{bootstrap,baseline,setup,master}/` (catalog-driven; never root SEED_DIRS).
|
|
109
|
-
|
|
110
|
-
`
|
|
111
|
-
|
|
147
|
+
|
|
148
|
+
`inventory` and `reconcile` are offline (no REST, SSH, or password).
|
|
149
|
+
`inventory` turns an SM203520 Settings XML ZIP or `ac.exe export xml` folder into `inventory/`.
|
|
150
|
+
|
|
151
|
+
`reconcile` compares `inventory/` to optional `config/` and writes `findings/` only.
|
|
152
|
+
It never writes seed.
|
|
153
|
+
|
|
154
|
+
Optional `snapshot_map.yaml` (data-repo root, or package defaults) maps DAC tables to catalog entities.
|
|
155
|
+
It normalizes the join: pad-trim, key/field aliases, Account/Sub FK CD resolve, enum label to code.
|
|
156
|
+
|
|
112
157
|
See [docs/demo-seed.md](docs/demo-seed.md).
|
|
158
|
+
|
|
113
159
|
`acu --completion` emits a completion script for bash, zsh, or fish — source it from your shell profile.
|
|
114
|
-
|
|
160
|
+
|
|
161
|
+
Run `acu --help` for the full mental model (workflow, planes, exit codes, command map).
|
|
162
|
+
That is enough for an agent to learn the tool without extra docs.
|
|
163
|
+
|
|
164
|
+
Run `acu <command> --help` (or `-h`) for flags, examples, and prerequisites.
|
|
115
165
|
|
|
116
166
|
### Dual readers, one writer
|
|
117
167
|
|
|
@@ -128,38 +178,49 @@ Do not confuse them with each other or with `state`:
|
|
|
128
178
|
|
|
129
179
|
`inventory/` and `findings/` are engagement outputs: not SEED_DIRS, never loaded by `apply`/`diff`, not scaffolded by `config init`.
|
|
130
180
|
Binary `.adb` snapshots are rejected (XML only).
|
|
181
|
+
|
|
131
182
|
See [docs/ac-exe.md](docs/ac-exe.md) for export / SM203520 notes and [docs/demo-seed.md](docs/demo-seed.md) for the extract/state/inventory map.
|
|
132
183
|
|
|
133
184
|
## The data repo
|
|
134
185
|
|
|
135
186
|
Your configuration lives in its own git repo.
|
|
136
|
-
`acu config init` scaffolds a **single full seed** under `config/` (
|
|
187
|
+
`acu config init` scaffolds a **single full seed** under `config/` (features, company, credit terms, expanded COA, masters) plus lifecycle `scenario/`, observer `config/views/`, and README.
|
|
188
|
+
|
|
189
|
+
The Bootstrap endpoint contract is package SoT (`bootstrap_project.xml` inside the CLI — `Bootstrap/1.4.0`).
|
|
190
|
+
`config init` never writes `project.xml`, and data repos must not keep one (a present file hard-errors on bootstrap/publish).
|
|
191
|
+
|
|
137
192
|
There is no `--flavor`.
|
|
138
193
|
|
|
139
194
|
| Path | What it holds |
|
|
140
195
|
| ---- | ------------- |
|
|
141
|
-
| `config/bootstrap/` | virgin-tenant config: features, company, credit terms
|
|
196
|
+
| `config/bootstrap/` | virgin-tenant config: features, company, credit terms (no `project.xml`) |
|
|
142
197
|
| `config/baseline/` | reference data: subaccounts, COA, ledger, UOMs |
|
|
143
198
|
| `config/setup/` | one-time actions: financial year, master calendar, open periods |
|
|
144
|
-
| `config/master/` | inventory/distribution masters (prefs, warehouse, items, parties) |
|
|
199
|
+
| `config/master/` | inventory/distribution masters: numbering (`05-…`) before prefs, warehouse, items, parties + Role/User (`90-roles` then `91-users`) |
|
|
145
200
|
| `scenario/` | lifecycle txns for `acu run`: once capital, then buy, build, sell |
|
|
146
201
|
| `config/views/` | observer views for `acu state` (`inquire:` / `entity:` / `gi:`; not SEED_DIRS) |
|
|
147
202
|
| `state/` | committed derived-state observations (evidence, not seed; money/qty fixed-point) |
|
|
148
203
|
| `inventory/` | engagement: offline snapshot tables from `acu inventory` (not seed; not SEED_DIRS) |
|
|
149
204
|
| `findings/` | engagement: `acu reconcile` cross-check output (never apply path) |
|
|
150
|
-
| `
|
|
151
|
-
| `.env` |
|
|
205
|
+
| `matrix.yaml` | multi-host pin+where: cells `id`+`erp`+`default_api`+`base_url` (V27); `--cell` selects |
|
|
206
|
+
| `.env` | secrets + optional where override (`ACU_*`); never Default API pin |
|
|
152
207
|
|
|
153
208
|
Legacy data repos may still keep root `bootstrap/`…`master/`; bare `apply`/`diff` prefer `config/` when present and never merge both trees.
|
|
154
209
|
|
|
155
210
|
Files in each directory apply alphabetically; the numbered prefixes (`10-`, `20-`, and so on) encode dependency order.
|
|
156
|
-
Commit `
|
|
211
|
+
Commit `matrix.yaml` with the seeds so every clone knows verified ERP line, Default API half, and REST where per cell.
|
|
157
212
|
|
|
158
213
|
Seed YAML is state: `apply` upserts it, `diff` proves it.
|
|
159
214
|
`acu extract` is the inverse of `apply`: GET live tenant rows into seed YAML under `config/{bootstrap,baseline,setup,master}/` (hard-cut).
|
|
160
|
-
|
|
215
|
+
|
|
216
|
+
Packaged `seed_catalog.yaml` is the sole extract registry (entity, endpoint, keys, file, strip/include, filter-split).
|
|
217
|
+
The demo entity map in [docs/demo-seed.md](docs/demo-seed.md) mirrors those catalog paths.
|
|
218
|
+
|
|
161
219
|
Features synthesize to `config/bootstrap/features.yaml`.
|
|
162
|
-
Existing files skip unless `--force`; empty live sets skip
|
|
220
|
+
Existing files skip unless `--force`; empty live sets skip.
|
|
221
|
+
|
|
222
|
+
Row failures continue (exit 1 only if any row failed).
|
|
223
|
+
Drift stays with `diff`.
|
|
163
224
|
|
|
164
225
|
```sh
|
|
165
226
|
acu --tenant DEV extract --out . --force # refresh config/** from live tenant
|
|
@@ -174,11 +235,13 @@ Dual-served entities (on both Bootstrap and Default) need an explicit `endpoint:
|
|
|
174
235
|
| Value | Resolves to |
|
|
175
236
|
| ----- | ----------- |
|
|
176
237
|
| omitted | `Default/<api_version>` for Default-only entities |
|
|
177
|
-
| `bootstrap` | active `Bootstrap/<ver>` from
|
|
238
|
+
| `bootstrap` | active `Bootstrap/<ver>` from the packaged contract only |
|
|
178
239
|
| `default` | `Default/<api_version>` — tracks the resolved API version |
|
|
179
|
-
| `Bootstrap/1.
|
|
240
|
+
| `Bootstrap/1.4.0` or `Default/25.200.001` | literal pin (ignores the resolved Default version) |
|
|
241
|
+
|
|
242
|
+
`api_version` resolves as `--api-version` flag, else active `matrix.yaml` cell `default_api`, else code default `25.200.001` (never `ACU_API_VERSION` in `.env`).
|
|
243
|
+
`base_url` resolves as `--url`, else `ACU_BASE_URL`, else active cell `base_url`.
|
|
180
244
|
|
|
181
|
-
`api_version` resolves as `--api-version` flag, else `target.yaml` `default_api` when present, else code default `25.200.001` (never `ACU_API_VERSION` in `.env`).
|
|
182
245
|
Prefer symbolic `default` over a pinned `Default/25.200.001` so the seed tree travels with the dataset pin.
|
|
183
246
|
|
|
184
247
|
## Installation
|
|
@@ -190,25 +253,28 @@ uv tool install acumatica-cli
|
|
|
190
253
|
```
|
|
191
254
|
|
|
192
255
|
`pipx install acumatica-cli` and `pip install acumatica-cli` work too.
|
|
193
|
-
|
|
256
|
+
|
|
257
|
+
Or clone and install editable for development:
|
|
194
258
|
|
|
195
259
|
```sh
|
|
196
|
-
|
|
260
|
+
git clone https://github.com/kborovik/acumatica-cli.git
|
|
261
|
+
cd acumatica-cli
|
|
262
|
+
gmake install # editable install as a global uv tool
|
|
197
263
|
```
|
|
198
264
|
|
|
199
265
|
Verify with `acu --version`.
|
|
200
266
|
|
|
201
267
|
## Configuration
|
|
202
268
|
|
|
203
|
-
|
|
204
|
-
(`
|
|
205
|
-
|
|
206
|
-
|
|
269
|
+
Secrets live in one `.env` file (`ACU_*` vars).
|
|
270
|
+
Non-secret **where** and the Default contract pin live in committed `matrix.yaml` (cell `base_url` + `default_api`).
|
|
271
|
+
|
|
272
|
+
Optional `ACU_BASE_URL` overrides cell where for ad-hoc probes.
|
|
207
273
|
|
|
208
274
|
```sh
|
|
209
|
-
ACU_BASE_URL
|
|
275
|
+
# ACU_BASE_URL optional when matrix.yaml cell carries base_url
|
|
210
276
|
ACU_TENANT=LAB5 # sign-in name of the tenant API sessions use
|
|
211
|
-
# ACU_SSH omitted → defaults to Administrator@
|
|
277
|
+
# ACU_SSH omitted → defaults to Administrator@ + resolved base_url host
|
|
212
278
|
# ACU_SSH= # hosted opt-out (blank key)
|
|
213
279
|
ACU_USER=admin # optional, defaults to admin
|
|
214
280
|
ACU_PASSWORD=... # required for live commands
|
|
@@ -218,34 +284,81 @@ There is no `ACU_API_VERSION` env key (unknown `ACU_*` vars are ignored).
|
|
|
218
284
|
Ad-hoc override: `acu --api-version 24.200.001 …` (version half only, never
|
|
219
285
|
`Default/25.200.001` — a full path would nest as `/entity/Default/Default/...`).
|
|
220
286
|
|
|
221
|
-
|
|
287
|
+
Committed `matrix.yaml` is the **sole data-repo pin+where registry** (1..N cells):
|
|
222
288
|
|
|
223
289
|
```yaml
|
|
224
|
-
|
|
225
|
-
|
|
290
|
+
cells:
|
|
291
|
+
- id: "default"
|
|
292
|
+
erp: "26.101.0225" # claimed product line/build
|
|
293
|
+
default_api: "25.200.001" # sources Instance.api_version when --api-version absent
|
|
294
|
+
base_url: "http://acu-dev1.vm.internal/AcumaticaERP"
|
|
226
295
|
```
|
|
227
296
|
|
|
228
|
-
|
|
229
|
-
`
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
297
|
+
`--cell <id>` selects a cell (omit means first cell).
|
|
298
|
+
When present, live commands source `api_version` from cell `default_api` and `base_url` from the cell when flag/env leave them unset.
|
|
299
|
+
|
|
300
|
+
`acu config check` reports `ok matrix (cell=...; api_version from default_api=...; ...)`.
|
|
301
|
+
Missing `matrix.yaml` only warns on `config check` unless you pass `--strict`.
|
|
302
|
+
|
|
303
|
+
`acu check` (lifecycle) **requires** matrix.
|
|
304
|
+
|
|
305
|
+
### Multi-host matrix (V44)
|
|
306
|
+
|
|
307
|
+
One **trunk seed** in the data repo serves every host.
|
|
308
|
+
Version fan-out is **not** long-running product branches (`acu-25r1`, `acu-26r1`, …).
|
|
309
|
+
|
|
310
|
+
| Piece | Role |
|
|
311
|
+
| ----- | ---- |
|
|
312
|
+
| Trunk seed | Canonical `config/` + `scenario/` (newest supported matrix) |
|
|
313
|
+
| `matrix.yaml` cells | Each host: `id`+`erp`+`default_api`+`base_url`; `--cell` / `acu check --all` |
|
|
314
|
+
| Optional overlays | Surgical seed deltas keyed by Default half (e.g. `overlays/default-24.200.001/`) |
|
|
315
|
+
| OpenAPI | Live `acu schema` dump only (gitignored); never multi-version swagger trees in package or data repo |
|
|
316
|
+
|
|
317
|
+
**Overlays** live under `overlays/default-<default_api>/` (scaffolded by `acu config init`).
|
|
318
|
+
No `--overlay` flag.
|
|
319
|
+
|
|
320
|
+
**Bare compose (pin auto):** when path args are omitted, `acu apply` / `acu diff` append overlay config seed dirs when present.
|
|
321
|
+
`acu run` replaces same-basename scenario files from the pin overlay.
|
|
322
|
+
|
|
323
|
+
Pin = resolved `api_version` (matrix cell `default_api`).
|
|
324
|
+
Explicit path args disable auto-compose.
|
|
325
|
+
|
|
326
|
+
```sh
|
|
327
|
+
# matrix cell default_api: 24.200.001 → uses overlays/default-24.200.001/
|
|
328
|
+
acu --cell lab25 apply
|
|
329
|
+
acu --cell lab25 run
|
|
330
|
+
acu --cell lab25 diff
|
|
331
|
+
|
|
332
|
+
# explicit path args (no auto) — later path wins same keys
|
|
333
|
+
acu apply config/ overlays/default-24.200.001/
|
|
334
|
+
acu diff config/ overlays/default-24.200.001/
|
|
335
|
+
|
|
336
|
+
# cold lifecycle every cell (SSH + tenant required); tenants left for inspect
|
|
337
|
+
acu check --all --yes --tenant LAB5
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Add a future half by creating `overlays/default-<new-half>/` with the
|
|
341
|
+
minimal rewrite; no long-running product branches and no multi-version
|
|
342
|
+
OpenAPI trees (V11/V44).
|
|
343
|
+
|
|
344
|
+
Sibling data-repo retirement of release branches:
|
|
345
|
+
[acumatica-gitops#2](https://github.com/kborovik/acumatica-gitops/issues/2).
|
|
233
346
|
|
|
234
347
|
Worth knowing:
|
|
235
348
|
|
|
236
|
-
- The file is found by walking up from the current directory, so any subdirectory of the data repo works.
|
|
237
|
-
- Without a `.env`, global flags plus the process environment supply the
|
|
238
|
-
- When `ACU_SSH` is **absent**, acu defaults to `Administrator@` + the
|
|
349
|
+
- The `.env` file is found by walking up from the current directory, so any subdirectory of the data repo works.
|
|
350
|
+
- Without a `.env`, global flags plus the process environment (and matrix cell where) supply the configuration.
|
|
351
|
+
- When `ACU_SSH` is **absent**, acu defaults to `Administrator@` + the resolved base_url hostname.
|
|
239
352
|
A **present blank** `ACU_SSH=` is the hosted opt-out.
|
|
240
|
-
Only `acu tenant`
|
|
241
|
-
- `acu config show` prints the resolved `.env` (password excluded; never `ACU_API_VERSION`) and comments
|
|
353
|
+
Only `acu tenant` / `acu check` require a non-empty value post-default.
|
|
354
|
+
- `acu config show` prints the resolved `.env` (password excluded; never `ACU_API_VERSION`) and comments active cell id/erp/default_api/base_url plus api_version source when `matrix.yaml` is present.
|
|
242
355
|
- Redirect it to turn resolved state into a working config: `acu config show > .env`.
|
|
243
356
|
|
|
244
357
|
Verify before touching anything live:
|
|
245
358
|
|
|
246
359
|
```sh
|
|
247
|
-
acu config check # discovery, secrets,
|
|
248
|
-
acu config check --strict # missing
|
|
360
|
+
acu config check # discovery, secrets, matrix, REST, endpoints, SSH
|
|
361
|
+
acu config check --strict # missing matrix.yaml becomes fail
|
|
249
362
|
acu apply --dry-run # show what would be written, write nothing
|
|
250
363
|
```
|
|
251
364
|
|
|
@@ -253,6 +366,7 @@ acu apply --dry-run # show what would be written, write nothing
|
|
|
253
366
|
|
|
254
367
|
Requires **GNU Make at least 3.82** — the Makefile uses `.ONESHELL`.
|
|
255
368
|
On macOS use Homebrew's `gmake` (`brew install make`); `/usr/bin/make` is 3.81 and fails the guard.
|
|
369
|
+
|
|
256
370
|
Elsewhere plain `make` is fine when it is GNU Make.
|
|
257
371
|
|
|
258
372
|
```sh
|
|
@@ -264,17 +378,30 @@ gmake check # offline gate: ruff, basedpyright strict, pytest
|
|
|
264
378
|
|
|
265
379
|
The default test suite is fully offline.
|
|
266
380
|
REST is faked with `httpx.MockTransport`, SSH with a monkeypatched `subprocess.run` — no live instance is needed.
|
|
381
|
+
|
|
267
382
|
`gmake check` must pass before every commit.
|
|
268
383
|
GitHub Actions runs the same gate on every push and pull request to `main`.
|
|
269
384
|
|
|
270
385
|
### Release
|
|
271
386
|
|
|
387
|
+
Human release notes live in root [`CHANGELOG.md`](CHANGELOG.md) (Keep a Changelog).
|
|
388
|
+
During development, append user-facing work under `## Unreleased` in `### Added` / `### Changed` / `### Fixed` as appropriate.
|
|
389
|
+
|
|
390
|
+
Empty Unreleased (no bullets) hard-fails the release — nothing to ship.
|
|
391
|
+
|
|
272
392
|
```sh
|
|
273
393
|
gmake release patch # or minor | major
|
|
274
394
|
```
|
|
275
395
|
|
|
276
|
-
|
|
277
|
-
|
|
396
|
+
`gmake release` is the sole release path (never local `gh release create`):
|
|
397
|
+
|
|
398
|
+
1. `gmake check` (ruff, basedpyright, offline pytest)
|
|
399
|
+
2. Fail if `## Unreleased` has no bullets
|
|
400
|
+
3. Bump `pyproject.toml` version (`major` | `minor` | `patch`)
|
|
401
|
+
4. Promote Unreleased body to `## [vX.Y.Z] - YYYY-MM-DD`, leave an empty `## Unreleased`
|
|
402
|
+
5. Commit `CHANGELOG.md` + `pyproject.toml` (+ lock if bumped) together, tag `vX.Y.Z`, push
|
|
403
|
+
|
|
404
|
+
GitHub Actions on tag `v*` re-runs CI, builds sdist+wheel, publishes to PyPI via OIDC trusted publishing, and creates a GitHub Release whose notes are the promoted CHANGELOG section for that tag (plus the artifacts).
|
|
278
405
|
|
|
279
406
|
### Live end-to-end tier
|
|
280
407
|
|
|
@@ -285,6 +412,7 @@ Configuration is one file: a decrypted `.env` at the repo root names the instanc
|
|
|
285
412
|
|
|
286
413
|
The tier is self-contained.
|
|
287
414
|
Each run scaffolds a synthetic single-org company from the packaged `acu config init` templates into a temporary directory, copies the real `.env` into it, and runs the installed `acu` binary from there — no data repo, no pre-existing fixtures on the instance.
|
|
415
|
+
|
|
288
416
|
Scratch tenants (`E2E`, `E2EA`, `E2EB`, `E2ESCEN`) are created on the way in and always deleted on the way out, so nothing persists.
|
|
289
417
|
The packaged full `config init` seed (under `config/`) is the only scaffold.
|
|
290
418
|
|
|
@@ -298,6 +426,7 @@ gmake e2e FILE=test_scenario_lifecycle # scenario + state focus
|
|
|
298
426
|
|
|
299
427
|
This project is licensed under the PolyForm Noncommercial License 1.0.0.
|
|
300
428
|
Noncommercial use is free under that license.
|
|
429
|
+
|
|
301
430
|
Commercial use requires a separate license — contact [lab5.ca](https://lab5.ca).
|
|
302
431
|
|
|
303
432
|
See [LICENSE](LICENSE) and [NOTICE](NOTICE).
|