acumatica-cli 0.15.1__tar.gz → 0.15.3__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.
Files changed (97) hide show
  1. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/PKG-INFO +31 -12
  2. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/README.md +30 -11
  3. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/pyproject.toml +1 -1
  4. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/cli.py +52 -3
  5. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/baseline/10-subaccounts.yaml +7 -0
  6. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/config/baseline/20-accounts.yaml +0 -18
  7. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/baseline/40-ledger.yaml +6 -0
  8. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/baseline/50-gl-preferences.yaml +6 -0
  9. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/baseline/60-ledger-company.yaml +6 -0
  10. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/config/baseline/90-uoms.yaml +0 -5
  11. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/bootstrap/company.yaml +9 -0
  12. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/bootstrap/credit-terms.yaml +11 -0
  13. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/bootstrap/features.yaml +10 -0
  14. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/10-reason-codes.yaml +34 -0
  15. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/20-in-preferences.yaml +15 -0
  16. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/30-availability-rules.yaml +6 -0
  17. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/config/master/40-posting-classes.yaml +0 -12
  18. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/50-warehouse.yaml +8 -0
  19. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/51-warehouse-locations.yaml +18 -0
  20. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/52-warehouse-defaults.yaml +8 -0
  21. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/53-tax-categories.yaml +5 -0
  22. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/54-item-classes.yaml +23 -0
  23. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/56-so-preferences.yaml +8 -0
  24. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/57-po-preferences.yaml +7 -0
  25. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/58-order-types.yaml +8 -0
  26. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/60-ar-preferences.yaml +5 -0
  27. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/61-ap-preferences.yaml +5 -0
  28. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/62-ca-preferences.yaml +7 -0
  29. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/63-cash-account.yaml +10 -0
  30. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/64-payment-methods.yaml +17 -0
  31. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/65-statement-cycles.yaml +11 -0
  32. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/70-vendor-classes.yaml +24 -0
  33. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/71-customer-classes.yaml +14 -0
  34. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/config/master/75-vendors.yaml +0 -16
  35. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/config/master/76-customers.yaml +0 -9
  36. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/config/master/80-stock-items-parts.yaml +0 -18
  37. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/master/82-stock-items-kits.yaml +24 -0
  38. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/config/master/85-kit-specifications.yaml +0 -14
  39. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/setup/10-financial-year.yaml +8 -0
  40. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/setup/20-master-calendar.yaml +11 -0
  41. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/setup/30-open-periods.yaml +11 -0
  42. acumatica_cli-0.15.3/src/acumatica_cli/templates/config/views/10-trial-balance.yaml +15 -0
  43. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/scenario/10-seed-capital.yaml +0 -5
  44. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/scenario/20-buy.yaml +0 -32
  45. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/scenario/30-build.yaml +0 -20
  46. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/scenario/40-sell.yaml +0 -12
  47. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/baseline/10-subaccounts.yaml +0 -15
  48. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/baseline/40-ledger.yaml +0 -15
  49. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/baseline/50-gl-preferences.yaml +0 -18
  50. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/baseline/60-ledger-company.yaml +0 -27
  51. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/bootstrap/company.yaml +0 -15
  52. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/bootstrap/credit-terms.yaml +0 -17
  53. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/bootstrap/features.yaml +0 -26
  54. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/10-reason-codes.yaml +0 -49
  55. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/20-in-preferences.yaml +0 -34
  56. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/30-availability-rules.yaml +0 -16
  57. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/50-warehouse.yaml +0 -23
  58. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/51-warehouse-locations.yaml +0 -28
  59. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/52-warehouse-defaults.yaml +0 -13
  60. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/53-tax-categories.yaml +0 -14
  61. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/54-item-classes.yaml +0 -43
  62. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/56-so-preferences.yaml +0 -21
  63. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/57-po-preferences.yaml +0 -21
  64. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/58-order-types.yaml +0 -21
  65. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/60-ar-preferences.yaml +0 -15
  66. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/61-ap-preferences.yaml +0 -14
  67. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/62-ca-preferences.yaml +0 -18
  68. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/63-cash-account.yaml +0 -23
  69. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/64-payment-methods.yaml +0 -27
  70. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/65-statement-cycles.yaml +0 -23
  71. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/70-vendor-classes.yaml +0 -38
  72. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/71-customer-classes.yaml +0 -28
  73. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/master/82-stock-items-kits.yaml +0 -40
  74. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/setup/10-financial-year.yaml +0 -27
  75. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/setup/20-master-calendar.yaml +0 -28
  76. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/setup/30-open-periods.yaml +0 -33
  77. acumatica_cli-0.15.1/src/acumatica_cli/templates/config/views/10-trial-balance.yaml +0 -20
  78. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/__init__.py +0 -0
  79. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/bootstrap.py +0 -0
  80. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/bootstrap_plugin.cs +0 -0
  81. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/bootstrap_project.xml +0 -0
  82. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/client.py +0 -0
  83. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/config.py +0 -0
  84. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/extract.py +0 -0
  85. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/firstlogin.py +0 -0
  86. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/models.py +0 -0
  87. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/output.py +0 -0
  88. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/run.py +0 -0
  89. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/seed.py +0 -0
  90. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/seed_catalog.yaml +0 -0
  91. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/state.py +0 -0
  92. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/target.py +0 -0
  93. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/README.md +0 -0
  94. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/env +0 -0
  95. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/gitignore +0 -0
  96. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/templates/target +0 -0
  97. {acumatica_cli-0.15.1 → acumatica_cli-0.15.3}/src/acumatica_cli/tenant.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: acumatica-cli
3
- Version: 0.15.1
3
+ Version: 0.15.3
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>
@@ -108,7 +108,8 @@ Run `acu <command> --help` for details on any command.
108
108
  ## The data repo
109
109
 
110
110
  Your configuration lives in its own git repo.
111
- `acu config init` scaffolds a **single full seed** under `config/` (Bootstrap `project.xml` at `Bootstrap/1.0.0`, expanded COA, masters) plus lifecycle `scenario/`, observer `config/views/`, and README. There is no `--flavor`.
111
+ `acu config init` scaffolds a **single full seed** under `config/` (Bootstrap `project.xml` at `Bootstrap/1.0.0`, expanded COA, masters) plus lifecycle `scenario/`, observer `config/views/`, and README.
112
+ There is no `--flavor`.
112
113
 
113
114
  | Path | What it holds |
114
115
  | ---- | ------------- |
@@ -129,7 +130,10 @@ The scaffolded `.gitignore` keeps `.env` out of git — store it encrypted (for
129
130
  Commit `target.yaml` with the seeds so every clone knows the verified ERP line and Default API generation.
130
131
 
131
132
  Seed YAML is state: `apply` upserts it, `diff` proves it.
132
- `acu extract` is the inverse of `apply`: GET live tenant rows into seed YAML under `config/{bootstrap,baseline,setup,master}/` (hard-cut). Packaged `seed_catalog.yaml` is the sole extract registry (entity, endpoint, keys, file, strip/include, filter-split); the demo entity map in [docs/demo-seed.md](docs/demo-seed.md) mirrors those catalog paths. Features synthesize to `config/bootstrap/features.yaml`. Existing files skip unless `--force`; empty live sets skip; row failures continue (exit 1 only if any row failed — drift stays with `diff`).
133
+ `acu extract` is the inverse of `apply`: GET live tenant rows into seed YAML under `config/{bootstrap,baseline,setup,master}/` (hard-cut).
134
+ Packaged `seed_catalog.yaml` is the sole extract registry (entity, endpoint, keys, file, strip/include, filter-split); the demo entity map in [docs/demo-seed.md](docs/demo-seed.md) mirrors those catalog paths.
135
+ Features synthesize to `config/bootstrap/features.yaml`.
136
+ Existing files skip unless `--force`; empty live sets skip; row failures continue (exit 1 only if any row failed — drift stays with `diff`).
133
137
 
134
138
  ```sh
135
139
  acu --tenant DEV extract --out . --force # refresh config/** from live tenant
@@ -138,15 +142,24 @@ acu --tenant DEV apply config/ # replay extracted seed
138
142
  acu --tenant DEV diff config/ # expect exit 0
139
143
  ```
140
144
 
141
- **Migration (T115–T120 extract hard-cut):** extract no longer writes root `bootstrap/` / `baseline/` / `setup/` / `master/`. Paths are always `config/…`. There is no `--layout`. Move any root-layout extract output under `config/`, or re-extract into a modern data repo. Catalog rename: `extract_manifest.yaml` becomes `seed_catalog.yaml` (package data only; operators do not author it).
145
+ **Migration (T115–T120 extract hard-cut):** extract no longer writes root `bootstrap/` / `baseline/` / `setup/` / `master/`.
146
+ Paths are always `config/…`.
147
+ There is no `--layout`.
148
+ Move any root-layout extract output under `config/`, or re-extract into a modern data repo.
149
+ Catalog rename: `extract_manifest.yaml` becomes `seed_catalog.yaml` (package data only; operators do not author it).
142
150
 
143
151
  Scenario YAML is different — it describes transactions that flow forward (not extractable seed).
144
152
  `acu run` executes each step in order (`put`, `action`, `wait`, `get`), captures server-assigned document numbers into `${var}` references for later steps, and checks `expect:` assertions as deltas against a pre-run snapshot, so additive scenarios re-run safely on a warm tenant.
145
153
  `once: true` scenarios declare a `present` inquire-absolute gate; when the probe already holds, the CLI prints `skip <path> (once: already present)` and runs neither steps nor expects (Owner Capital does not restack).
146
154
 
147
- `acu state` is the third observation path: it captures live derived state (balances) into `state/` for git review. It is not `extract` (config seed) and not `diff` (desired vs actual config). Packaged golden is trial-balance via contract `inquire:` (`EndingBalance` fixed-point); inventory-summary is not golden this pass. `gi:` stays optional when a GI is V12-verified and **Expose via OData** is on (`params` fail-closed vs `$metadata`). After a cold `acu run scenario/ && acu state`, warm `acu run scenario/10-seed-capital.yaml && acu state --assert-unchanged` is the once-class gate (full scenario re-run is additive and moves cash observations on the TB).
155
+ `acu state` is the third observation path: it captures live derived state (balances) into `state/` for git review.
156
+ It is not `extract` (config seed) and not `diff` (desired vs actual config).
157
+ Packaged golden is trial-balance via contract `inquire:` (`EndingBalance` fixed-point); inventory-summary is not golden this pass. `gi:` stays optional when a GI is V12-verified and **Expose via OData** is on (`params` fail-closed vs `$metadata`).
158
+ After a cold `acu run scenario/ && acu state`, warm `acu run scenario/10-seed-capital.yaml && acu state --assert-unchanged` is the once-class gate (full scenario re-run is additive and moves cash observations on the TB).
148
159
 
149
- **Migration (T112–T114 hard-cut):** CLI verb is `state` only (no `snapshot` alias). Bare defaults are `config/views/` (views) and `state/` (observations). Prior `config/snapshot/` and root `snapshot/` / `snapshots/` are not fallbacks — move views to `config/views/` or pass explicit path args.
160
+ **Migration (T112–T114 hard-cut):** CLI verb is `state` only (no `snapshot` alias).
161
+ Bare defaults are `config/views/` (views) and `state/` (observations).
162
+ Prior `config/snapshot/` and root `snapshot/` / `snapshots/` are not fallbacks — move views to `config/views/` or pass explicit path args.
150
163
 
151
164
  ### Seed `endpoint:` symbols
152
165
 
@@ -169,7 +182,8 @@ Requires Python 3.14 or newer.
169
182
  uv tool install acumatica-cli
170
183
  ```
171
184
 
172
- `pipx install acumatica-cli` and `pip install acumatica-cli` work too. For the latest development version straight from the main branch:
185
+ `pipx install acumatica-cli` and `pip install acumatica-cli` work too.
186
+ For the latest development version straight from the main branch:
173
187
 
174
188
  ```sh
175
189
  uv tool install git+https://github.com/kborovik/acumatica-cli.git
@@ -237,10 +251,13 @@ On a hosted instance, publish AcuBootstrap with `acu bootstrap`, then seed with
237
251
 
238
252
  ## SSH setup (control plane)
239
253
 
240
- `acu tenant` runs commands on the Windows guest through plain `ssh`. Two things about this setup are not obvious, and both are hard requirements.
254
+ `acu tenant` runs commands on the Windows guest through plain `ssh`.
255
+ Two things about this setup are not obvious, and both are hard requirements.
241
256
 
242
- **1. The default SSH shell on the Windows guest must be PowerShell.**
243
- `acu` sends PowerShell syntax over the wire, and every one of those commands fails under `cmd.exe`, the Windows OpenSSH default. Switch it once, in an elevated PowerShell on the guest:
257
+ **1.
258
+ The default SSH shell on the Windows guest must be PowerShell.**
259
+ `acu` sends PowerShell syntax over the wire, and every one of those commands fails under `cmd.exe`, the Windows OpenSSH default.
260
+ Switch it once, in an elevated PowerShell on the guest:
244
261
 
245
262
  ```powershell
246
263
  New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell `
@@ -248,9 +265,11 @@ New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell `
248
265
  -PropertyType String -Force
249
266
  ```
250
267
 
251
- **2. Authentication must be key-based and non-interactive.**
268
+ **2.
269
+ Authentication must be key-based and non-interactive.**
252
270
  `acu` connects with `BatchMode=yes`, so it will never answer a password prompt.
253
- Because the default user is `Administrator` (an administrators-group member), Windows OpenSSH reads the key from the *machine-wide* file `C:\ProgramData\ssh\administrators_authorized_keys` — **not** from `~\.ssh\authorized_keys` like on Linux. On the guest:
271
+ Because the default user is `Administrator` (an administrators-group member), Windows OpenSSH reads the key from the *machine-wide* file `C:\ProgramData\ssh\administrators_authorized_keys` — **not** from `~\.ssh\authorized_keys` like on Linux.
272
+ On the guest:
254
273
 
255
274
  Install + start the server (once):
256
275
 
@@ -90,7 +90,8 @@ Run `acu <command> --help` for details on any command.
90
90
  ## The data repo
91
91
 
92
92
  Your configuration lives in its own git repo.
93
- `acu config init` scaffolds a **single full seed** under `config/` (Bootstrap `project.xml` at `Bootstrap/1.0.0`, expanded COA, masters) plus lifecycle `scenario/`, observer `config/views/`, and README. There is no `--flavor`.
93
+ `acu config init` scaffolds a **single full seed** under `config/` (Bootstrap `project.xml` at `Bootstrap/1.0.0`, expanded COA, masters) plus lifecycle `scenario/`, observer `config/views/`, and README.
94
+ There is no `--flavor`.
94
95
 
95
96
  | Path | What it holds |
96
97
  | ---- | ------------- |
@@ -111,7 +112,10 @@ The scaffolded `.gitignore` keeps `.env` out of git — store it encrypted (for
111
112
  Commit `target.yaml` with the seeds so every clone knows the verified ERP line and Default API generation.
112
113
 
113
114
  Seed YAML is state: `apply` upserts it, `diff` proves it.
114
- `acu extract` is the inverse of `apply`: GET live tenant rows into seed YAML under `config/{bootstrap,baseline,setup,master}/` (hard-cut). Packaged `seed_catalog.yaml` is the sole extract registry (entity, endpoint, keys, file, strip/include, filter-split); the demo entity map in [docs/demo-seed.md](docs/demo-seed.md) mirrors those catalog paths. Features synthesize to `config/bootstrap/features.yaml`. Existing files skip unless `--force`; empty live sets skip; row failures continue (exit 1 only if any row failed — drift stays with `diff`).
115
+ `acu extract` is the inverse of `apply`: GET live tenant rows into seed YAML under `config/{bootstrap,baseline,setup,master}/` (hard-cut).
116
+ Packaged `seed_catalog.yaml` is the sole extract registry (entity, endpoint, keys, file, strip/include, filter-split); the demo entity map in [docs/demo-seed.md](docs/demo-seed.md) mirrors those catalog paths.
117
+ Features synthesize to `config/bootstrap/features.yaml`.
118
+ Existing files skip unless `--force`; empty live sets skip; row failures continue (exit 1 only if any row failed — drift stays with `diff`).
115
119
 
116
120
  ```sh
117
121
  acu --tenant DEV extract --out . --force # refresh config/** from live tenant
@@ -120,15 +124,24 @@ acu --tenant DEV apply config/ # replay extracted seed
120
124
  acu --tenant DEV diff config/ # expect exit 0
121
125
  ```
122
126
 
123
- **Migration (T115–T120 extract hard-cut):** extract no longer writes root `bootstrap/` / `baseline/` / `setup/` / `master/`. Paths are always `config/…`. There is no `--layout`. Move any root-layout extract output under `config/`, or re-extract into a modern data repo. Catalog rename: `extract_manifest.yaml` becomes `seed_catalog.yaml` (package data only; operators do not author it).
127
+ **Migration (T115–T120 extract hard-cut):** extract no longer writes root `bootstrap/` / `baseline/` / `setup/` / `master/`.
128
+ Paths are always `config/…`.
129
+ There is no `--layout`.
130
+ Move any root-layout extract output under `config/`, or re-extract into a modern data repo.
131
+ Catalog rename: `extract_manifest.yaml` becomes `seed_catalog.yaml` (package data only; operators do not author it).
124
132
 
125
133
  Scenario YAML is different — it describes transactions that flow forward (not extractable seed).
126
134
  `acu run` executes each step in order (`put`, `action`, `wait`, `get`), captures server-assigned document numbers into `${var}` references for later steps, and checks `expect:` assertions as deltas against a pre-run snapshot, so additive scenarios re-run safely on a warm tenant.
127
135
  `once: true` scenarios declare a `present` inquire-absolute gate; when the probe already holds, the CLI prints `skip <path> (once: already present)` and runs neither steps nor expects (Owner Capital does not restack).
128
136
 
129
- `acu state` is the third observation path: it captures live derived state (balances) into `state/` for git review. It is not `extract` (config seed) and not `diff` (desired vs actual config). Packaged golden is trial-balance via contract `inquire:` (`EndingBalance` fixed-point); inventory-summary is not golden this pass. `gi:` stays optional when a GI is V12-verified and **Expose via OData** is on (`params` fail-closed vs `$metadata`). After a cold `acu run scenario/ && acu state`, warm `acu run scenario/10-seed-capital.yaml && acu state --assert-unchanged` is the once-class gate (full scenario re-run is additive and moves cash observations on the TB).
137
+ `acu state` is the third observation path: it captures live derived state (balances) into `state/` for git review.
138
+ It is not `extract` (config seed) and not `diff` (desired vs actual config).
139
+ Packaged golden is trial-balance via contract `inquire:` (`EndingBalance` fixed-point); inventory-summary is not golden this pass. `gi:` stays optional when a GI is V12-verified and **Expose via OData** is on (`params` fail-closed vs `$metadata`).
140
+ After a cold `acu run scenario/ && acu state`, warm `acu run scenario/10-seed-capital.yaml && acu state --assert-unchanged` is the once-class gate (full scenario re-run is additive and moves cash observations on the TB).
130
141
 
131
- **Migration (T112–T114 hard-cut):** CLI verb is `state` only (no `snapshot` alias). Bare defaults are `config/views/` (views) and `state/` (observations). Prior `config/snapshot/` and root `snapshot/` / `snapshots/` are not fallbacks — move views to `config/views/` or pass explicit path args.
142
+ **Migration (T112–T114 hard-cut):** CLI verb is `state` only (no `snapshot` alias).
143
+ Bare defaults are `config/views/` (views) and `state/` (observations).
144
+ Prior `config/snapshot/` and root `snapshot/` / `snapshots/` are not fallbacks — move views to `config/views/` or pass explicit path args.
132
145
 
133
146
  ### Seed `endpoint:` symbols
134
147
 
@@ -151,7 +164,8 @@ Requires Python 3.14 or newer.
151
164
  uv tool install acumatica-cli
152
165
  ```
153
166
 
154
- `pipx install acumatica-cli` and `pip install acumatica-cli` work too. For the latest development version straight from the main branch:
167
+ `pipx install acumatica-cli` and `pip install acumatica-cli` work too.
168
+ For the latest development version straight from the main branch:
155
169
 
156
170
  ```sh
157
171
  uv tool install git+https://github.com/kborovik/acumatica-cli.git
@@ -219,10 +233,13 @@ On a hosted instance, publish AcuBootstrap with `acu bootstrap`, then seed with
219
233
 
220
234
  ## SSH setup (control plane)
221
235
 
222
- `acu tenant` runs commands on the Windows guest through plain `ssh`. Two things about this setup are not obvious, and both are hard requirements.
236
+ `acu tenant` runs commands on the Windows guest through plain `ssh`.
237
+ Two things about this setup are not obvious, and both are hard requirements.
223
238
 
224
- **1. The default SSH shell on the Windows guest must be PowerShell.**
225
- `acu` sends PowerShell syntax over the wire, and every one of those commands fails under `cmd.exe`, the Windows OpenSSH default. Switch it once, in an elevated PowerShell on the guest:
239
+ **1.
240
+ The default SSH shell on the Windows guest must be PowerShell.**
241
+ `acu` sends PowerShell syntax over the wire, and every one of those commands fails under `cmd.exe`, the Windows OpenSSH default.
242
+ Switch it once, in an elevated PowerShell on the guest:
226
243
 
227
244
  ```powershell
228
245
  New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell `
@@ -230,9 +247,11 @@ New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell `
230
247
  -PropertyType String -Force
231
248
  ```
232
249
 
233
- **2. Authentication must be key-based and non-interactive.**
250
+ **2.
251
+ Authentication must be key-based and non-interactive.**
234
252
  `acu` connects with `BatchMode=yes`, so it will never answer a password prompt.
235
- Because the default user is `Administrator` (an administrators-group member), Windows OpenSSH reads the key from the *machine-wide* file `C:\ProgramData\ssh\administrators_authorized_keys` — **not** from `~\.ssh\authorized_keys` like on Linux. On the guest:
253
+ Because the default user is `Administrator` (an administrators-group member), Windows OpenSSH reads the key from the *machine-wide* file `C:\ProgramData\ssh\administrators_authorized_keys` — **not** from `~\.ssh\authorized_keys` like on Linux.
254
+ On the guest:
236
255
 
237
256
  Install + start the server (once):
238
257
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "acumatica-cli"
3
- version = "0.15.1"
3
+ version = "0.15.3"
4
4
  description = "Acumatica ERP Config-as-Code: tenant provisioning, baseline config, and reference data"
5
5
  authors = [{ name = "Konstantin Borovik", email = "kb@lab5.ca" }]
6
6
  license = "PolyForm-Noncommercial-1.0.0"
@@ -155,18 +155,67 @@ def pass_instance[**P, R](f: Callable[Concatenate[Instance, P], R]) -> Callable[
155
155
  return new_func
156
156
 
157
157
 
158
+ def _transport_target(
159
+ exc: httpx.TransportError, *, target: str | None = None
160
+ ) -> str | None:
161
+ """Prefer explicit target (e.g. Instance.base_url); else request URL."""
162
+ if target:
163
+ return target
164
+ request = getattr(exc, "request", None)
165
+ if request is not None:
166
+ return str(request.url)
167
+ return None
168
+
169
+
170
+ def _format_transport_error(
171
+ exc: httpx.TransportError, *, target: str | None = None
172
+ ) -> str:
173
+ """Friendly one-line body for connect/timeout/TLS (V9 / T123).
174
+
175
+ Never surface raw httpx/OS dumps (e.g. ``[Errno 61] Connection refused``).
176
+ """
177
+ raw = str(exc).lower()
178
+ if "ssl" in raw or "certificate" in raw or "tls" in raw:
179
+ kind = "TLS error"
180
+ elif isinstance(exc, httpx.TimeoutException):
181
+ kind = "request timed out"
182
+ elif isinstance(exc, httpx.ConnectError):
183
+ kind = "cannot connect"
184
+ elif isinstance(exc, httpx.NetworkError):
185
+ kind = "network error"
186
+ else:
187
+ kind = "transport error"
188
+ where = _transport_target(exc, target=target)
189
+ hint = "check ACU_BASE_URL / network / instance up"
190
+ if where:
191
+ return f"{kind} to {where} ({hint})"
192
+ return f"{kind} ({hint})"
193
+
194
+
195
+ def _format_failure(exc: BaseException, *, target: str | None = None) -> str:
196
+ """Map expected failures to one greppable error line body (V9).
197
+
198
+ Transport/network class → friendly rewrite; other RuntimeError / HTTPError
199
+ keep ``str(exc)`` (server ``exceptionMessage`` path unchanged).
200
+ """
201
+ if isinstance(exc, httpx.TransportError):
202
+ return _format_transport_error(exc, target=target)
203
+ return str(exc)
204
+
205
+
158
206
  def main() -> None:
159
207
  """Entry point: run the CLI, mapping expected failures to one-line errors.
160
208
 
161
209
  RuntimeError (SSH/ac.exe, REST, first-login) and httpx transport errors
162
210
  print `x message` and exit 1; ACU_DEBUG=1 re-raises for the traceback.
211
+ Transport/network failures use a friendly rewrite (V9), not raw dumps.
163
212
  """
164
213
  try:
165
214
  cli()
166
215
  except (RuntimeError, httpx.HTTPError) as exc:
167
216
  if os.environ.get("ACU_DEBUG"):
168
217
  raise
169
- output.error(str(exc))
218
+ output.error(_format_failure(exc))
170
219
  raise SystemExit(1) from exc
171
220
 
172
221
 
@@ -546,7 +595,7 @@ def _probe_rest(inst: Instance, claimed_erp: str | None) -> bool:
546
595
  output.data(f"ok rest ({inst.base_url}, tenant {inst.tenant})")
547
596
  return _probe_entity_root(client, inst, claimed_erp)
548
597
  except (RuntimeError, httpx.HTTPError) as exc:
549
- output.data(f"fail rest: {exc}")
598
+ output.data(f"fail rest: {_format_failure(exc, target=inst.base_url)}")
550
599
  return False
551
600
 
552
601
 
@@ -558,7 +607,7 @@ def _probe_entity_root(
558
607
  try:
559
608
  endpoints, live_build = client.entity_root()
560
609
  except (RuntimeError, httpx.HTTPError) as exc:
561
- output.data(f"fail endpoints: {exc}")
610
+ output.data(f"fail endpoints: {_format_failure(exc, target=inst.base_url)}")
562
611
  endpoints, live_build = [], None
563
612
  ok = False
564
613
  else:
@@ -0,0 +1,7 @@
1
+ entity: Subaccount
2
+ key: SubaccountCD
3
+ records:
4
+ - Active: true
5
+ Description: Default
6
+ Secured: false
7
+ SubaccountCD: '000000'
@@ -1,21 +1,3 @@
1
- # Chart of accounts (GL202500), designed for LAB5 Electronics Inc. (lab5.ca demo tenant seed) - 23 accounts covering GL foundation plus the
2
- # AP/AR/IN/SO/PO posting resolution the buy-build-sell scenario needs.
3
- # Field shape live-verified on 26.101.0225 by the earlier SalesDemo
4
- # replay (T24). Stripped per that work: internal AccountID, AccountClass
5
- # (no Default-endpoint entity), AccountGroup (PM - absent on a fresh
6
- # tenant, PUT 422s), RevaluationRateType, and the server-derived
7
- # ChartOfAccountsOrder + CashAccount (PUT-tolerated but the server keeps
8
- # its own derivation, so any sourced value diffs dirty forever - B11).
9
- # CurrencyID only on denominated cash accounts (the SalesDemo extract
10
- # carried it on 11 cash accounts only). Equity accounts are Type:
11
- # Liability - the Account.Type list has no Equity value.
12
- #
13
- # APPLY ORDER (dir expansion sorts *.yaml alphabetically; numbered
14
- # prefixes encode order): after 10-subaccounts.yaml; before
15
- # 32-gl-preferences.yaml, which references 32000/33000 below. FX
16
- # gain/loss accounts (83xxx/84000) intentionally absent - USD-only
17
- # tenant; add them only if AR/AP release proves to need a financial
18
- # currency row (plan risk R4).
19
1
  entity: Account
20
2
  key: AccountCD
21
3
  records:
@@ -0,0 +1,6 @@
1
+ entity: Ledger
2
+ key: LedgerID
3
+ records:
4
+ - LedgerID: ACTUAL
5
+ Description: Actual Ledger
6
+ Type: Actual
@@ -0,0 +1,6 @@
1
+ entity: GLPreferences
2
+ key: RetEarnAccountID
3
+ endpoint: bootstrap
4
+ records:
5
+ - RetEarnAccountID: '32000'
6
+ YtdNetIncAccountID: '33000'
@@ -0,0 +1,6 @@
1
+ entity: LedgerCompany
2
+ key: [LedgerCD, OrganizationID]
3
+ endpoint: bootstrap
4
+ records:
5
+ - LedgerCD: ACTUAL
6
+ OrganizationID: LAB5
@@ -1,8 +1,3 @@
1
- # Units of measure (CS203500) via the Default endpoint - EA for stock
2
- # items; BOX second unit; PIECE/HOUR retained from scaffold.
3
- # KG + LITER: company packaging base UOMs (Companies Weight/Volume UOM;
4
- # Stock Items Packaging tab inherits them). Must exist before
5
- # 91-company-packaging.yaml sets them on the org.
6
1
  entity: UnitsOfMeasure
7
2
  key: UnitID
8
3
  records:
@@ -0,0 +1,9 @@
1
+ entity: Company
2
+ key: AcctCD
3
+ endpoint: bootstrap
4
+ records:
5
+ - AcctCD: LAB5
6
+ AcctName: LAB5 Electronics Inc.
7
+ OrganizationType: Without Branches
8
+ BaseCuryID: USD
9
+ CountryID: US
@@ -0,0 +1,11 @@
1
+ entity: CreditTerms
2
+ key: TermsID
3
+ endpoint: bootstrap
4
+ records:
5
+ - TermsID: NET30
6
+ Descr: Net 30 Days
7
+ VisibleTo: All
8
+ DueType: Fixed Number of Days
9
+ DayDue00: 30
10
+ DiscType: Fixed Number of Days
11
+ DiscPercent: 0
@@ -0,0 +1,10 @@
1
+ - FinancialModule
2
+ - FinancialStandard
3
+ - DistributionModule
4
+ - Inventory
5
+ - Branch
6
+ - MultiCompany
7
+ - SubAccount
8
+ - KitAssemblies
9
+ - Warehouse
10
+ - WarehouseLocation
@@ -0,0 +1,34 @@
1
+ entity: ReasonCode
2
+ key: ReasonCodeID
3
+ endpoint: bootstrap
4
+ records:
5
+ - ReasonCodeID: INISSUE
6
+ Descr: Inventory issue
7
+ Usage: Issue
8
+ AccountID: '50300'
9
+ SubID: '000000'
10
+ - ReasonCodeID: INRECEIPT
11
+ Descr: Inventory receipt
12
+ Usage: Receipt
13
+ AccountID: '50300'
14
+ SubID: '000000'
15
+ - ReasonCodeID: INADJUST
16
+ Descr: Inventory adjustment
17
+ Usage: Adjustment
18
+ AccountID: '50300'
19
+ SubID: '000000'
20
+ - ReasonCodeID: INPI
21
+ Descr: Physical inventory variance
22
+ Usage: Adjustment
23
+ AccountID: '50300'
24
+ SubID: '000000'
25
+ - ReasonCodeID: INASSEMBLY
26
+ Descr: Kit assembly
27
+ Usage: Assembly/Disassembly
28
+ AccountID: '50300'
29
+ SubID: '000000'
30
+ - ReasonCodeID: PORETURN
31
+ Descr: Vendor return
32
+ Usage: Vendor Return
33
+ AccountID: '50300'
34
+ SubID: '000000'
@@ -0,0 +1,15 @@
1
+ entity: INPreferences
2
+ key: HoldEntry
3
+ endpoint: bootstrap
4
+ records:
5
+ - HoldEntry: false
6
+ INProgressAcctID: '12300'
7
+ INProgressSubID: '000000'
8
+ INTransitAcctID: '12400'
9
+ INTransitSubID: '000000'
10
+ TransitBranchID: LAB5
11
+ UpdateGL: true
12
+ IssuesReasonCode: INISSUE
13
+ ReceiptReasonCode: INRECEIPT
14
+ AdjustmentReasonCode: INADJUST
15
+ PIReasonCode: INPI
@@ -0,0 +1,6 @@
1
+ entity: AvailabilityCalculationRule
2
+ key: AvailabilitySchemeID
3
+ endpoint: bootstrap
4
+ records:
5
+ - AvailabilitySchemeID: DEFAULT
6
+ Description: Default availability calculation rule
@@ -1,15 +1,3 @@
1
- # Posting classes (IN206000) via the custom Bootstrap endpoint (symbolic endpoint: bootstrap)
2
- # (acumatica-cli#10, T61) - no Default-endpoint entity, and StockItem
3
- # posting resolution needs a class. PARTS books inventory to 12100,
4
- # KITS to 12200; both share sales 40000, COGS 50000, PO accrual 20200,
5
- # PPV 50100, LCV 50200, subaccount 000000 everywhere. The *AcctDefault
6
- # source combos are left to their server defaults on first save - if
7
- # posting resolution surprises during the scenario, claim them here
8
- # with the live-read stored words.
9
- #
10
- # APPLY ORDER (dir expansion sorts *.yaml alphabetically; numbered
11
- # prefixes encode order): after 20-accounts.yaml (all six accounts) and
12
- # 10-subaccounts.yaml; before 54-item-classes.yaml.
13
1
  entity: PostingClass
14
2
  key: PostClassID
15
3
  endpoint: bootstrap
@@ -0,0 +1,8 @@
1
+ entity: Warehouse
2
+ key: SiteCD
3
+ endpoint: bootstrap
4
+ records:
5
+ - SiteCD: WH01
6
+ Descr: Main warehouse
7
+ Active: true
8
+ CountryID: US
@@ -0,0 +1,18 @@
1
+ entity: Warehouse
2
+ key: WarehouseID
3
+ endpoint: default
4
+ detail_keys:
5
+ Locations: LocationID
6
+ records:
7
+ - WarehouseID: WH01
8
+ ReceivingLocationID: MAIN
9
+ ShippingLocationID: MAIN
10
+ RMALocationID: MAIN
11
+ Locations:
12
+ - LocationID: MAIN
13
+ Description: Main storage
14
+ Active: true
15
+ ReceiptsAllowed: true
16
+ SalesAllowed: true
17
+ TransfersAllowed: true
18
+ AssemblyAllowed: true
@@ -0,0 +1,8 @@
1
+ entity: Warehouse
2
+ key: WarehouseID
3
+ endpoint: default
4
+ records:
5
+ - WarehouseID: WH01
6
+ ReceivingLocationID: MAIN
7
+ ShippingLocationID: MAIN
8
+ RMALocationID: MAIN
@@ -0,0 +1,5 @@
1
+ entity: TaxCategory
2
+ key: TaxCategoryID
3
+ records:
4
+ - TaxCategoryID: EXEMPT
5
+ Description: Tax exempt
@@ -0,0 +1,23 @@
1
+ entity: ItemClass
2
+ key: ClassID
3
+ records:
4
+ - ClassID: PARTS
5
+ Description: Purchased components
6
+ StockItem: true
7
+ ItemType: Finished Good
8
+ ValuationMethod: Average
9
+ BaseUOM: EA
10
+ AvailabilityCalculationRule: DEFAULT
11
+ PostingClass: PARTS
12
+ TaxCategoryID: EXEMPT
13
+ DefaultWarehouseID: WH01
14
+ - ClassID: KITS
15
+ Description: Assembled gateway kits
16
+ StockItem: true
17
+ ItemType: Finished Good
18
+ ValuationMethod: Average
19
+ BaseUOM: EA
20
+ AvailabilityCalculationRule: DEFAULT
21
+ PostingClass: KITS
22
+ TaxCategoryID: EXEMPT
23
+ DefaultWarehouseID: WH01
@@ -0,0 +1,8 @@
1
+ entity: SOPreferences
2
+ key: DefaultOrderType
3
+ endpoint: bootstrap
4
+ records:
5
+ - DefaultOrderType: SO
6
+ HoldShipments: false
7
+ RequireShipmentTotal: false
8
+ AutoReleaseIN: true
@@ -0,0 +1,7 @@
1
+ entity: POPreferences
2
+ key: HoldReceipts
3
+ endpoint: bootstrap
4
+ records:
5
+ - HoldReceipts: false
6
+ RCReturnReasonCodeID: PORETURN
7
+ AutoReleaseIN: true
@@ -0,0 +1,8 @@
1
+ entity: OrderType
2
+ key: OrderType
3
+ endpoint: bootstrap
4
+ records:
5
+ - OrderType: SO
6
+ Active: true
7
+ FreightAcctID: '40100'
8
+ FreightSubID: '000000'
@@ -0,0 +1,5 @@
1
+ entity: ARPreferences
2
+ key: HoldEntry
3
+ endpoint: bootstrap
4
+ records:
5
+ - HoldEntry: false
@@ -0,0 +1,5 @@
1
+ entity: APPreferences
2
+ key: HoldEntry
3
+ endpoint: bootstrap
4
+ records:
5
+ - HoldEntry: false
@@ -0,0 +1,7 @@
1
+ entity: CAPreferences
2
+ key: TransitAcctId
3
+ endpoint: bootstrap
4
+ records:
5
+ - TransitAcctId: '10200'
6
+ TransitSubID: '000000'
7
+ HoldEntry: false
@@ -0,0 +1,10 @@
1
+ entity: CashAccount
2
+ key: CashAccountCD
3
+ endpoint: bootstrap
4
+ records:
5
+ - CashAccountCD: '10100'
6
+ Descr: Checking Account
7
+ AccountID: '10100'
8
+ SubID: '000000'
9
+ BranchID: LAB5
10
+ Active: true
@@ -0,0 +1,17 @@
1
+ entity: PaymentMethod
2
+ key: PaymentMethodID
3
+ detail_keys:
4
+ AllowedCashAccounts: CashAccount
5
+ records:
6
+ - PaymentMethodID: WIRE
7
+ Description: Wire transfer
8
+ Active: true
9
+ MeansOfPayment: Cash/Check
10
+ UseInAP: true
11
+ UseInAR: true
12
+ AllowedCashAccounts:
13
+ - CashAccount: '10100'
14
+ UseInAP: true
15
+ UseInAR: true
16
+ APDefault: true
17
+ ARDefault: true
@@ -0,0 +1,11 @@
1
+ entity: StatementCycle
2
+ key: StatementCycleId
3
+ endpoint: bootstrap
4
+ records:
5
+ - StatementCycleId: MONTHLY
6
+ Descr: Monthly statements
7
+ PrepareOn: Fixed Day of Month
8
+ Day00: 1
9
+ AgeDays00: 30
10
+ AgeDays01: 60
11
+ AgeDays02: 90