acumatica-cli 0.13.0__tar.gz → 0.13.1__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 (77) hide show
  1. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/PKG-INFO +27 -29
  2. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/README.md +26 -28
  3. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/pyproject.toml +1 -1
  4. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/cli.py +14 -27
  5. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/config.py +63 -164
  6. acumatica_cli-0.13.1/src/acumatica_cli/templates/README.md +49 -0
  7. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/baseline/20-accounts.yaml +2 -2
  8. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/baseline/90-uoms.yaml +9 -0
  9. acumatica_cli-0.13.1/src/acumatica_cli/templates/config/baseline/91-company-packaging.yaml +14 -0
  10. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/bootstrap/company.yaml +1 -1
  11. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/bootstrap/features.yaml +1 -1
  12. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/51-warehouse-locations.yaml +8 -6
  13. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/53-tax-categories.yaml +1 -1
  14. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/75-vendors.yaml +1 -1
  15. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/80-stock-items-parts.yaml +22 -0
  16. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/82-stock-items-kits.yaml +10 -0
  17. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates}/scenario/10-seed-capital.yaml +2 -2
  18. acumatica_cli-0.13.1/src/acumatica_cli/templates/scenario/20-buy.yaml +361 -0
  19. acumatica_cli-0.13.1/src/acumatica_cli/templates/scenario/30-build.yaml +136 -0
  20. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates}/scenario/40-sell.yaml +1 -1
  21. acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution/README.md +0 -87
  22. acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution/scenario/20-buy-gateways.yaml +0 -112
  23. acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution/scenario/30-build.yaml +0 -5
  24. acumatica_cli-0.13.0/src/acumatica_cli/templates/finance/baseline/20-accounts.yaml +0 -94
  25. acumatica_cli-0.13.0/src/acumatica_cli/templates/finance/baseline/60-ledger-company.yaml +0 -27
  26. acumatica_cli-0.13.0/src/acumatica_cli/templates/finance/baseline/90-uoms.yaml +0 -16
  27. acumatica_cli-0.13.0/src/acumatica_cli/templates/finance/bootstrap/company.yaml +0 -15
  28. acumatica_cli-0.13.0/src/acumatica_cli/templates/finance/bootstrap/features.yaml +0 -16
  29. acumatica_cli-0.13.0/src/acumatica_cli/templates/finance/setup/30-open-periods.yaml +0 -33
  30. acumatica_cli-0.13.0/src/acumatica_cli/templates/finance/snapshot/10-trial-balance.yaml +0 -20
  31. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/__init__.py +0 -0
  32. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/bootstrap.py +0 -0
  33. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/bootstrap_plugin.cs +0 -0
  34. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/bootstrap_project.xml +0 -0
  35. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/client.py +0 -0
  36. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/extract.py +0 -0
  37. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/extract_manifest.yaml +0 -0
  38. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/firstlogin.py +0 -0
  39. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/models.py +0 -0
  40. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/output.py +0 -0
  41. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/run.py +0 -0
  42. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/seed.py +0 -0
  43. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/snapshot.py +0 -0
  44. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/src/acumatica_cli/target.py +0 -0
  45. {acumatica_cli-0.13.0/src/acumatica_cli/templates/finance → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/baseline/10-subaccounts.yaml +0 -0
  46. {acumatica_cli-0.13.0/src/acumatica_cli/templates/finance → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/baseline/40-ledger.yaml +0 -0
  47. {acumatica_cli-0.13.0/src/acumatica_cli/templates/finance → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/baseline/50-gl-preferences.yaml +0 -0
  48. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/baseline/60-ledger-company.yaml +0 -0
  49. {acumatica_cli-0.13.0/src/acumatica_cli/templates/finance → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/bootstrap/credit-terms.yaml +0 -0
  50. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/10-reason-codes.yaml +0 -0
  51. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/20-in-preferences.yaml +0 -0
  52. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/30-availability-rules.yaml +0 -0
  53. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/40-posting-classes.yaml +0 -0
  54. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/50-warehouse.yaml +0 -0
  55. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/52-warehouse-defaults.yaml +0 -0
  56. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/54-item-classes.yaml +0 -0
  57. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/56-so-preferences.yaml +0 -0
  58. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/57-po-preferences.yaml +0 -0
  59. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/58-order-types.yaml +0 -0
  60. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/60-ar-preferences.yaml +0 -0
  61. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/61-ap-preferences.yaml +0 -0
  62. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/62-ca-preferences.yaml +0 -0
  63. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/63-cash-account.yaml +0 -0
  64. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/64-payment-methods.yaml +0 -0
  65. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/65-statement-cycles.yaml +0 -0
  66. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/70-vendor-classes.yaml +0 -0
  67. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/71-customer-classes.yaml +0 -0
  68. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/76-customers.yaml +0 -0
  69. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/master/85-kit-specifications.yaml +0 -0
  70. {acumatica_cli-0.13.0/src/acumatica_cli/templates/finance → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/setup/10-financial-year.yaml +0 -0
  71. {acumatica_cli-0.13.0/src/acumatica_cli/templates/finance → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/setup/20-master-calendar.yaml +0 -0
  72. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/setup/30-open-periods.yaml +0 -0
  73. {acumatica_cli-0.13.0/src/acumatica_cli/templates/distribution → acumatica_cli-0.13.1/src/acumatica_cli/templates/config}/snapshot/10-trial-balance.yaml +0 -0
  74. {acumatica_cli-0.13.0/src/acumatica_cli/templates/finance → acumatica_cli-0.13.1/src/acumatica_cli/templates}/env +0 -0
  75. {acumatica_cli-0.13.0/src/acumatica_cli/templates/finance → acumatica_cli-0.13.1/src/acumatica_cli/templates}/gitignore +0 -0
  76. {acumatica_cli-0.13.0/src/acumatica_cli/templates/finance → acumatica_cli-0.13.1/src/acumatica_cli/templates}/target +0 -0
  77. {acumatica_cli-0.13.0 → acumatica_cli-0.13.1}/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.13.0
3
+ Version: 0.13.1
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>
@@ -38,27 +38,23 @@ Acumatica configuration normally lives in the web UI: wizards, screens, and manu
38
38
  uv tool install acumatica-cli
39
39
 
40
40
  acu config init --host erp.example.com my-erp
41
- cd my-erp # edit .env: set ACU_PASSWORD
41
+ cd my-erp # edit .env: set ACU_PASSWORD, ACU_TENANT
42
42
  # keep ACU_API_VERSION in sync with target.yaml
43
+ # start from a brand-new empty tenant
43
44
 
44
45
  acu config check # read-only preflight (incl. target.yaml)
45
46
  acu tenant create --id 3 --login DEV # create the tenant + bootstrap it (needs SSH)
46
- acu --tenant DEV apply # seed bootstrap/, baseline/, setup/ (, master/)
47
- acu --tenant DEV diff # prove zero drift (exit 2 on drift)
47
+ # or hosted: acu --tenant DEV bootstrap
48
+ acu --tenant DEV apply config/ # seed config/{bootstrap,baseline,setup,master}/
49
+ acu --tenant DEV run scenario/ # once capital → buy → build → sell
50
+ acu --tenant DEV diff config/ # prove zero drift (exit 2 on drift)
51
+ acu --tenant DEV snapshot # capture state/ trial-balance
52
+ # warm: capital once-skips; Owner Capital stays 50000 (not 100000)
53
+ acu --tenant DEV run scenario/
48
54
  ```
49
55
 
50
- **Distribution demo seed** (inventory, warehouse, items, vendors/customers, lifecycle scenarios):
51
-
52
- ```sh
53
- acu config init --flavor distribution --host erp.example.com my-dist
54
- cd my-dist # edit .env; start from an empty tenant
55
- acu config check && acu bootstrap \
56
- && acu apply config/ && acu run scenario/ && acu diff config/
57
- # warm re-run: capital once-skips; Owner Capital stays 50000 (not 100000)
58
- acu run scenario/
59
- ```
60
-
61
- See [docs/distribution.md](docs/distribution.md) for the entity map, once-guard, and apply-order notes.
56
+ Bare `apply` / `diff` (no path args) also prefer `config/` when those trees exist.
57
+ See [docs/demo-seed.md](docs/demo-seed.md) for the entity map, once-guard, and apply-order notes.
62
58
 
63
59
  **Hosted Acumatica (no SSH):** the tenant already exists; leave `ACU_SSH` blank.
64
60
 
@@ -67,8 +63,8 @@ acu config init --host customer.acumatica.com my-erp
67
63
  cd my-erp # edit .env: ACU_TENANT, ACU_PASSWORD; ACU_SSH=
68
64
  acu config check # REST preflight; ssh probe is skipped
69
65
  acu --tenant DEV bootstrap # publish AcuBootstrap via REST only
70
- acu --tenant DEV apply
71
- acu --tenant DEV diff
66
+ acu --tenant DEV apply config/
67
+ acu --tenant DEV diff config/
72
68
  # offline UI fallback when REST publish is blocked:
73
69
  acu bootstrap --export AcuBootstrap.zip # import + publish on SM204505
74
70
  ```
@@ -96,8 +92,7 @@ acu [--tenant NAME] [--url URL] [--ssh USER@HOST] [--api-version V]
96
92
  ├── schema [--out DIR] dump the endpoint's OpenAPI schema (swagger.json)
97
93
  │
98
94
  └── config configuration ops
99
- ├── init [--host HOST] [--flavor distribution] [DIR]
100
- │ scaffold a data repo (.env, target.yaml, example YAML)
95
+ ├── init [--host HOST] [DIR] scaffold full data repo (config/, scenario/, target.yaml)
101
96
  ├── show print the resolved config as a complete .env
102
97
  └── check [--strict] preflight: discovery, secrets, target, REST, endpoints, SSH
103
98
  ```
@@ -112,21 +107,22 @@ Run `acu <command> --help` for details on any command.
112
107
  ## The data repo
113
108
 
114
109
  Your configuration lives in its own git repo.
115
- `acu config init` scaffolds finance-minimal seeds at the **root** (Bootstrap `project.xml` at `Bootstrap/1.0.0`, no `master/`, no scenario).
116
- `acu config init --flavor distribution` scaffolds the full demo under `config/` (same contract identity, expanded COA, masters) plus lifecycle `scenario/` and README.
110
+ `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/snapshot/`, and README. There is no `--flavor`.
117
111
 
118
112
  | Path | What it holds |
119
113
  | ---- | ------------- |
120
- | `bootstrap/` / `config/bootstrap/` | virgin-tenant config: features, company, credit terms, `project.xml` |
121
- | `baseline/` / `config/baseline/` | reference data: subaccounts, COA, ledger, UOMs |
122
- | `setup/` / `config/setup/` | one-time actions: financial year, master calendar, open periods |
123
- | `config/master/` | distribution masters (prefs, warehouse, items, parties); flavor only |
124
- | `scenario/` | lifecycle txns for `acu run`: once capital, then buy, build stub, sell |
114
+ | `config/bootstrap/` | virgin-tenant config: features, company, credit terms, `project.xml` |
115
+ | `config/baseline/` | reference data: subaccounts, COA, ledger, UOMs, packaging |
116
+ | `config/setup/` | one-time actions: financial year, master calendar, open periods |
117
+ | `config/master/` | inventory/distribution masters (prefs, warehouse, items, parties) |
118
+ | `scenario/` | lifecycle txns for `acu run`: once capital, then buy, build, sell |
125
119
  | `config/snapshot/` | observer views for `acu snapshot` (`inquire:` / `entity:` / `gi:`; not SEED_DIRS) |
126
120
  | `state/` | committed derived-state observations (evidence, not seed; money/qty fixed-point) |
127
121
  | `target.yaml` | committed verified matrix: `erp` + `default_api` (what, not where) |
128
122
  | `.env` | where to apply and who signs in, every key an `ACU_*` variable |
129
123
 
124
+ Legacy data repos may still keep root `bootstrap/`…`master/`; bare `apply`/`diff` prefer `config/` when present and never merge both trees.
125
+
130
126
  Files in each directory apply alphabetically; the numbered prefixes (`10-`, `20-`, and so on) encode dependency order.
131
127
  The scaffolded `.gitignore` keeps `.env` out of git — store it encrypted (for example as `.env.gpg`) and decrypt once per clone.
132
128
  Commit `target.yaml` with the seeds so every clone knows the verified ERP line and Default API generation.
@@ -306,11 +302,13 @@ Configuration is one file: a decrypted `.env` at the repo root names the instanc
306
302
 
307
303
  The tier is self-contained.
308
304
  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.
309
- Scratch tenants (`E2E`, `E2EA`, `E2EB`) are created on the way in and always deleted on the way out, so nothing persists.
305
+ Scratch tenants (`E2E`, `E2EA`, `E2EB`, `E2ESCEN`) are created on the way in and always deleted on the way out, so nothing persists.
306
+ The packaged full `config init` seed (under `config/`) is the only scaffold.
310
307
 
311
308
  ```sh
312
309
  gmake e2e # whole tier, about 20 minutes
313
- gmake e2e FILE=test_provision_lifecycle # one file, by stem or path
310
+ gmake e2e FILE=test_provision_lifecycle # apply/diff focus
311
+ gmake e2e FILE=test_scenario_lifecycle # scenario + snapshot focus
314
312
  ```
315
313
 
316
314
  ## License
@@ -20,27 +20,23 @@ Acumatica configuration normally lives in the web UI: wizards, screens, and manu
20
20
  uv tool install acumatica-cli
21
21
 
22
22
  acu config init --host erp.example.com my-erp
23
- cd my-erp # edit .env: set ACU_PASSWORD
23
+ cd my-erp # edit .env: set ACU_PASSWORD, ACU_TENANT
24
24
  # keep ACU_API_VERSION in sync with target.yaml
25
+ # start from a brand-new empty tenant
25
26
 
26
27
  acu config check # read-only preflight (incl. target.yaml)
27
28
  acu tenant create --id 3 --login DEV # create the tenant + bootstrap it (needs SSH)
28
- acu --tenant DEV apply # seed bootstrap/, baseline/, setup/ (, master/)
29
- acu --tenant DEV diff # prove zero drift (exit 2 on drift)
29
+ # or hosted: acu --tenant DEV bootstrap
30
+ acu --tenant DEV apply config/ # seed config/{bootstrap,baseline,setup,master}/
31
+ acu --tenant DEV run scenario/ # once capital → buy → build → sell
32
+ acu --tenant DEV diff config/ # prove zero drift (exit 2 on drift)
33
+ acu --tenant DEV snapshot # capture state/ trial-balance
34
+ # warm: capital once-skips; Owner Capital stays 50000 (not 100000)
35
+ acu --tenant DEV run scenario/
30
36
  ```
31
37
 
32
- **Distribution demo seed** (inventory, warehouse, items, vendors/customers, lifecycle scenarios):
33
-
34
- ```sh
35
- acu config init --flavor distribution --host erp.example.com my-dist
36
- cd my-dist # edit .env; start from an empty tenant
37
- acu config check && acu bootstrap \
38
- && acu apply config/ && acu run scenario/ && acu diff config/
39
- # warm re-run: capital once-skips; Owner Capital stays 50000 (not 100000)
40
- acu run scenario/
41
- ```
42
-
43
- See [docs/distribution.md](docs/distribution.md) for the entity map, once-guard, and apply-order notes.
38
+ Bare `apply` / `diff` (no path args) also prefer `config/` when those trees exist.
39
+ See [docs/demo-seed.md](docs/demo-seed.md) for the entity map, once-guard, and apply-order notes.
44
40
 
45
41
  **Hosted Acumatica (no SSH):** the tenant already exists; leave `ACU_SSH` blank.
46
42
 
@@ -49,8 +45,8 @@ acu config init --host customer.acumatica.com my-erp
49
45
  cd my-erp # edit .env: ACU_TENANT, ACU_PASSWORD; ACU_SSH=
50
46
  acu config check # REST preflight; ssh probe is skipped
51
47
  acu --tenant DEV bootstrap # publish AcuBootstrap via REST only
52
- acu --tenant DEV apply
53
- acu --tenant DEV diff
48
+ acu --tenant DEV apply config/
49
+ acu --tenant DEV diff config/
54
50
  # offline UI fallback when REST publish is blocked:
55
51
  acu bootstrap --export AcuBootstrap.zip # import + publish on SM204505
56
52
  ```
@@ -78,8 +74,7 @@ acu [--tenant NAME] [--url URL] [--ssh USER@HOST] [--api-version V]
78
74
  ├── schema [--out DIR] dump the endpoint's OpenAPI schema (swagger.json)
79
75
  │
80
76
  └── config configuration ops
81
- ├── init [--host HOST] [--flavor distribution] [DIR]
82
- │ scaffold a data repo (.env, target.yaml, example YAML)
77
+ ├── init [--host HOST] [DIR] scaffold full data repo (config/, scenario/, target.yaml)
83
78
  ├── show print the resolved config as a complete .env
84
79
  └── check [--strict] preflight: discovery, secrets, target, REST, endpoints, SSH
85
80
  ```
@@ -94,21 +89,22 @@ Run `acu <command> --help` for details on any command.
94
89
  ## The data repo
95
90
 
96
91
  Your configuration lives in its own git repo.
97
- `acu config init` scaffolds finance-minimal seeds at the **root** (Bootstrap `project.xml` at `Bootstrap/1.0.0`, no `master/`, no scenario).
98
- `acu config init --flavor distribution` scaffolds the full demo under `config/` (same contract identity, expanded COA, masters) plus lifecycle `scenario/` and README.
92
+ `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/snapshot/`, and README. There is no `--flavor`.
99
93
 
100
94
  | Path | What it holds |
101
95
  | ---- | ------------- |
102
- | `bootstrap/` / `config/bootstrap/` | virgin-tenant config: features, company, credit terms, `project.xml` |
103
- | `baseline/` / `config/baseline/` | reference data: subaccounts, COA, ledger, UOMs |
104
- | `setup/` / `config/setup/` | one-time actions: financial year, master calendar, open periods |
105
- | `config/master/` | distribution masters (prefs, warehouse, items, parties); flavor only |
106
- | `scenario/` | lifecycle txns for `acu run`: once capital, then buy, build stub, sell |
96
+ | `config/bootstrap/` | virgin-tenant config: features, company, credit terms, `project.xml` |
97
+ | `config/baseline/` | reference data: subaccounts, COA, ledger, UOMs, packaging |
98
+ | `config/setup/` | one-time actions: financial year, master calendar, open periods |
99
+ | `config/master/` | inventory/distribution masters (prefs, warehouse, items, parties) |
100
+ | `scenario/` | lifecycle txns for `acu run`: once capital, then buy, build, sell |
107
101
  | `config/snapshot/` | observer views for `acu snapshot` (`inquire:` / `entity:` / `gi:`; not SEED_DIRS) |
108
102
  | `state/` | committed derived-state observations (evidence, not seed; money/qty fixed-point) |
109
103
  | `target.yaml` | committed verified matrix: `erp` + `default_api` (what, not where) |
110
104
  | `.env` | where to apply and who signs in, every key an `ACU_*` variable |
111
105
 
106
+ Legacy data repos may still keep root `bootstrap/`…`master/`; bare `apply`/`diff` prefer `config/` when present and never merge both trees.
107
+
112
108
  Files in each directory apply alphabetically; the numbered prefixes (`10-`, `20-`, and so on) encode dependency order.
113
109
  The scaffolded `.gitignore` keeps `.env` out of git — store it encrypted (for example as `.env.gpg`) and decrypt once per clone.
114
110
  Commit `target.yaml` with the seeds so every clone knows the verified ERP line and Default API generation.
@@ -288,11 +284,13 @@ Configuration is one file: a decrypted `.env` at the repo root names the instanc
288
284
 
289
285
  The tier is self-contained.
290
286
  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.
291
- Scratch tenants (`E2E`, `E2EA`, `E2EB`) are created on the way in and always deleted on the way out, so nothing persists.
287
+ Scratch tenants (`E2E`, `E2EA`, `E2EB`, `E2ESCEN`) are created on the way in and always deleted on the way out, so nothing persists.
288
+ The packaged full `config init` seed (under `config/`) is the only scaffold.
292
289
 
293
290
  ```sh
294
291
  gmake e2e # whole tier, about 20 minutes
295
- gmake e2e FILE=test_provision_lifecycle # one file, by stem or path
292
+ gmake e2e FILE=test_provision_lifecycle # apply/diff focus
293
+ gmake e2e FILE=test_scenario_lifecycle # scenario + snapshot focus
296
294
  ```
297
295
 
298
296
  ## License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "acumatica-cli"
3
- version = "0.13.0"
3
+ version = "0.13.1"
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"
@@ -15,7 +15,6 @@ from click.shell_completion import get_completion_class
15
15
  from . import bootstrap, extract, firstlogin, output, run, seed, snapshot
16
16
  from .client import AcumaticaClient
17
17
  from .config import (
18
- INIT_FLAVORS,
19
18
  Instance,
20
19
  data_root,
21
20
  find_data_root,
@@ -411,45 +410,33 @@ def config_group() -> None:
411
410
  help="Hostname substituted into the scaffolded .env ACU_BASE_URL/ACU_SSH "
412
411
  "values (default: a placeholder)",
413
412
  )
414
- @click.option(
415
- "--flavor",
416
- type=click.Choice(sorted(INIT_FLAVORS), case_sensitive=True),
417
- default=None,
418
- help="Template set: omit for finance-minimal (default, offline e2e); "
419
- "distribution = full virgin-tenant demo seed (master/, scenario/, "
420
- "expanded COA/features)",
421
- )
422
413
  @click.argument(
423
414
  "directory", required=False, type=click.Path(file_okay=False, path_type=Path)
424
415
  )
425
- def config_init(host: str | None, flavor: str | None, directory: Path | None) -> None:
426
- """Scaffold a data repo: .env, .gitignore, bootstrap/, baseline/, setup/.
416
+ def config_init(host: str | None, directory: Path | None) -> None:
417
+ """Scaffold a data repo: .env, target.yaml, config/ seed, scenario/.
427
418
 
428
419
  Templates ship with the package; every value is a placeholder or a
429
- verified minimal example - no secrets. Both flavors scaffold the full
430
- company ``bootstrap/project.xml`` (Bootstrap/1.0.0). ``--flavor
431
- distribution`` scaffolds under ``config/`` + lifecycle ``scenario/``
432
- + README (V28/T87). Existing files are never overwritten (reported as
433
- skipped). DIRECTORY defaults to the current directory and is created
434
- if absent. No git init, no gpg.
420
+ verified example - no secrets. Single full seed under ``config/``
421
+ (bootstrap/baseline/setup/master) + lifecycle ``scenario/`` +
422
+ ``config/snapshot/`` + README; full company ``project.xml``
423
+ (Bootstrap/1.0.0). No ``--flavor`` (V28/T108). Existing files are
424
+ never overwritten (reported as skipped). DIRECTORY defaults to the
425
+ current directory and is created if absent. No git init, no gpg.
435
426
  """
436
427
  target = directory or Path.cwd()
437
- for action, path in scaffold(target, host=host, flavor=flavor):
428
+ for action, path in scaffold(target, host=host):
438
429
  suffix = " (exists)" if action == "skip" else ""
439
430
  output.data(f"{action} {path}{suffix}")
440
- # next-step cmds (issue #18/#19): operator rebuild order after scaffold
431
+ # next-step cmds: operator rebuild order after scaffold (V28)
441
432
  output.data("")
442
433
  output.data("next:")
443
434
  output.data(" 1. edit .env (set ACU_PASSWORD, ACU_TENANT; keep ACU_API_VERSION)")
444
435
  output.data(" 2. acu config check")
445
436
  output.data(" 3. acu bootstrap # or: acu tenant create ... (SSH)")
446
- if flavor == "distribution":
447
- output.data(" 4. acu apply config/")
448
- output.data(" 5. acu run scenario/")
449
- output.data(" 6. acu diff config/")
450
- else:
451
- output.data(" 4. acu apply")
452
- output.data(" 5. acu diff")
437
+ output.data(" 4. acu apply config/")
438
+ output.data(" 5. acu run scenario/")
439
+ output.data(" 6. acu diff config/")
453
440
 
454
441
 
455
442
  @config_group.command("show")
@@ -695,7 +682,7 @@ def default_seed_dirs() -> tuple[Path, ...]:
695
682
  When the data-repo ``config/`` has any SEED_DIRS child, only those
696
683
  ``config/<name>/`` paths are returned (dual layout never merges with
697
684
  root). Else root ``bootstrap/``…``master/`` for present names
698
- (finance-minimal). The data repo is the .env dir (V3 walk-up). None
685
+ (legacy root layout). The data repo is the .env dir (V3 walk-up). None
699
686
  existing is an error - an empty default would make a bare run a silent
700
687
  no-op. Paths come back relative to cwd so a bare run prints exactly
701
688
  what naming the dirs would.
@@ -32,156 +32,101 @@ DB_NAME = "AcumaticaDB"
32
32
  # Dotfiles are stored dotless (wheel tooling tends to drop dotfiles) and
33
33
  # mapped to their real names on write.
34
34
  #
35
- # Layout under templates/ (symmetric flavors):
36
- # finance/ — default finance-minimal package set (V28)
37
- # distribution/ — overlays + master/scenario extras
38
- # bootstrap/project.xml is not a templates/ file: scaffold copies the
39
- # packaged full company contract (bootstrap_project.xml) so both flavors
40
- # share Bootstrap/1.0.0 (T81/T82).
35
+ # Single full seed (V28/T108/T109): no --flavor. Package resources mirror
36
+ # dest layout under templates/ (derived from sibling acumatica-gitops seed
37
+ # trees; prune demo/, Makefile, live .env, state/, packaged project.xml).
38
+ # bootstrap project.xml is not a templates/ file: scaffold copies the
39
+ # packaged full company contract (bootstrap_project.xml) → Bootstrap/1.0.0.
41
40
  INIT_TEMPLATES = (
42
- ("finance/env", ".env"),
43
- ("finance/gitignore", ".gitignore"),
44
- ("finance/target", "target.yaml"),
45
- ("finance/baseline/10-subaccounts.yaml", "baseline/10-subaccounts.yaml"),
46
- ("finance/baseline/20-accounts.yaml", "baseline/20-accounts.yaml"),
47
- ("finance/baseline/40-ledger.yaml", "baseline/40-ledger.yaml"),
48
- ("finance/baseline/50-gl-preferences.yaml", "baseline/50-gl-preferences.yaml"),
49
- ("finance/baseline/60-ledger-company.yaml", "baseline/60-ledger-company.yaml"),
50
- ("finance/baseline/90-uoms.yaml", "baseline/90-uoms.yaml"),
51
- ("finance/bootstrap/company.yaml", "bootstrap/company.yaml"),
52
- ("finance/bootstrap/credit-terms.yaml", "bootstrap/credit-terms.yaml"),
53
- ("finance/bootstrap/features.yaml", "bootstrap/features.yaml"),
41
+ ("env", ".env"),
42
+ ("gitignore", ".gitignore"),
43
+ ("target", "target.yaml"),
44
+ ("README.md", "README.md"),
45
+ ("config/bootstrap/company.yaml", "config/bootstrap/company.yaml"),
46
+ ("config/bootstrap/credit-terms.yaml", "config/bootstrap/credit-terms.yaml"),
47
+ ("config/bootstrap/features.yaml", "config/bootstrap/features.yaml"),
54
48
  (
55
- "finance/bootstrap/project.xml",
56
- "bootstrap/project.xml",
49
+ "config/bootstrap/project.xml",
50
+ "config/bootstrap/project.xml",
57
51
  ), # sentinel; see scaffold
58
- ("finance/setup/10-financial-year.yaml", "setup/10-financial-year.yaml"),
59
- ("finance/setup/20-master-calendar.yaml", "setup/20-master-calendar.yaml"),
60
- ("finance/setup/30-open-periods.yaml", "setup/30-open-periods.yaml"),
61
- # V28/V32: observer views under config/snapshot/ (not SEED_DIRS; lone config/ ok)
52
+ ("config/baseline/10-subaccounts.yaml", "config/baseline/10-subaccounts.yaml"),
53
+ ("config/baseline/20-accounts.yaml", "config/baseline/20-accounts.yaml"),
54
+ ("config/baseline/40-ledger.yaml", "config/baseline/40-ledger.yaml"),
62
55
  (
63
- "finance/snapshot/10-trial-balance.yaml",
64
- "config/snapshot/10-trial-balance.yaml",
56
+ "config/baseline/50-gl-preferences.yaml",
57
+ "config/baseline/50-gl-preferences.yaml",
65
58
  ),
66
- )
67
-
68
- # Opt-in `--flavor distribution` overlays + extras (V28/V29/T87). Resource
69
- # paths live under templates/distribution/; dest paths rehome seed under
70
- # config/ (V30). Overlays replace rehomed finance-minimal files (company
71
- # LAB5, expanded COA, features, open-periods, uoms). Contract identity is
72
- # shared (T82) — no distribution-only project.xml. Lifecycle scenarios
73
- # replace monoscenario buy-sell (V28).
74
- _SEED_PREFIXES = ("bootstrap/", "baseline/", "setup/", "master/")
75
- _ROOT_META = frozenset({".env", ".gitignore", "target.yaml"})
76
-
77
- DISTRIBUTION_TEMPLATES = (
78
- ("distribution/bootstrap/company.yaml", "config/bootstrap/company.yaml"),
79
- ("distribution/bootstrap/features.yaml", "config/bootstrap/features.yaml"),
80
- ("distribution/baseline/20-accounts.yaml", "config/baseline/20-accounts.yaml"),
81
59
  (
82
- "distribution/baseline/60-ledger-company.yaml",
60
+ "config/baseline/60-ledger-company.yaml",
83
61
  "config/baseline/60-ledger-company.yaml",
84
62
  ),
85
- ("distribution/baseline/90-uoms.yaml", "config/baseline/90-uoms.yaml"),
86
- ("distribution/setup/30-open-periods.yaml", "config/setup/30-open-periods.yaml"),
87
- ("distribution/master/10-reason-codes.yaml", "config/master/10-reason-codes.yaml"),
63
+ ("config/baseline/90-uoms.yaml", "config/baseline/90-uoms.yaml"),
88
64
  (
89
- "distribution/master/20-in-preferences.yaml",
90
- "config/master/20-in-preferences.yaml",
65
+ "config/baseline/91-company-packaging.yaml",
66
+ "config/baseline/91-company-packaging.yaml",
91
67
  ),
68
+ ("config/setup/10-financial-year.yaml", "config/setup/10-financial-year.yaml"),
69
+ ("config/setup/20-master-calendar.yaml", "config/setup/20-master-calendar.yaml"),
70
+ ("config/setup/30-open-periods.yaml", "config/setup/30-open-periods.yaml"),
71
+ ("config/master/10-reason-codes.yaml", "config/master/10-reason-codes.yaml"),
72
+ ("config/master/20-in-preferences.yaml", "config/master/20-in-preferences.yaml"),
92
73
  (
93
- "distribution/master/30-availability-rules.yaml",
74
+ "config/master/30-availability-rules.yaml",
94
75
  "config/master/30-availability-rules.yaml",
95
76
  ),
77
+ ("config/master/40-posting-classes.yaml", "config/master/40-posting-classes.yaml"),
78
+ ("config/master/50-warehouse.yaml", "config/master/50-warehouse.yaml"),
96
79
  (
97
- "distribution/master/40-posting-classes.yaml",
98
- "config/master/40-posting-classes.yaml",
99
- ),
100
- ("distribution/master/50-warehouse.yaml", "config/master/50-warehouse.yaml"),
101
- (
102
- "distribution/master/51-warehouse-locations.yaml",
80
+ "config/master/51-warehouse-locations.yaml",
103
81
  "config/master/51-warehouse-locations.yaml",
104
82
  ),
105
83
  (
106
- "distribution/master/52-warehouse-defaults.yaml",
84
+ "config/master/52-warehouse-defaults.yaml",
107
85
  "config/master/52-warehouse-defaults.yaml",
108
86
  ),
87
+ ("config/master/53-tax-categories.yaml", "config/master/53-tax-categories.yaml"),
88
+ ("config/master/54-item-classes.yaml", "config/master/54-item-classes.yaml"),
89
+ ("config/master/56-so-preferences.yaml", "config/master/56-so-preferences.yaml"),
90
+ ("config/master/57-po-preferences.yaml", "config/master/57-po-preferences.yaml"),
91
+ ("config/master/58-order-types.yaml", "config/master/58-order-types.yaml"),
92
+ ("config/master/60-ar-preferences.yaml", "config/master/60-ar-preferences.yaml"),
93
+ ("config/master/61-ap-preferences.yaml", "config/master/61-ap-preferences.yaml"),
94
+ ("config/master/62-ca-preferences.yaml", "config/master/62-ca-preferences.yaml"),
95
+ ("config/master/63-cash-account.yaml", "config/master/63-cash-account.yaml"),
96
+ ("config/master/64-payment-methods.yaml", "config/master/64-payment-methods.yaml"),
109
97
  (
110
- "distribution/master/53-tax-categories.yaml",
111
- "config/master/53-tax-categories.yaml",
112
- ),
113
- ("distribution/master/54-item-classes.yaml", "config/master/54-item-classes.yaml"),
114
- (
115
- "distribution/master/56-so-preferences.yaml",
116
- "config/master/56-so-preferences.yaml",
117
- ),
118
- (
119
- "distribution/master/57-po-preferences.yaml",
120
- "config/master/57-po-preferences.yaml",
121
- ),
122
- ("distribution/master/58-order-types.yaml", "config/master/58-order-types.yaml"),
123
- (
124
- "distribution/master/60-ar-preferences.yaml",
125
- "config/master/60-ar-preferences.yaml",
126
- ),
127
- (
128
- "distribution/master/61-ap-preferences.yaml",
129
- "config/master/61-ap-preferences.yaml",
130
- ),
131
- (
132
- "distribution/master/62-ca-preferences.yaml",
133
- "config/master/62-ca-preferences.yaml",
134
- ),
135
- ("distribution/master/63-cash-account.yaml", "config/master/63-cash-account.yaml"),
136
- (
137
- "distribution/master/64-payment-methods.yaml",
138
- "config/master/64-payment-methods.yaml",
139
- ),
140
- (
141
- "distribution/master/65-statement-cycles.yaml",
98
+ "config/master/65-statement-cycles.yaml",
142
99
  "config/master/65-statement-cycles.yaml",
143
100
  ),
101
+ ("config/master/70-vendor-classes.yaml", "config/master/70-vendor-classes.yaml"),
144
102
  (
145
- "distribution/master/70-vendor-classes.yaml",
146
- "config/master/70-vendor-classes.yaml",
147
- ),
148
- (
149
- "distribution/master/71-customer-classes.yaml",
103
+ "config/master/71-customer-classes.yaml",
150
104
  "config/master/71-customer-classes.yaml",
151
105
  ),
152
- ("distribution/master/75-vendors.yaml", "config/master/75-vendors.yaml"),
153
- ("distribution/master/76-customers.yaml", "config/master/76-customers.yaml"),
106
+ ("config/master/75-vendors.yaml", "config/master/75-vendors.yaml"),
107
+ ("config/master/76-customers.yaml", "config/master/76-customers.yaml"),
154
108
  (
155
- "distribution/master/80-stock-items-parts.yaml",
109
+ "config/master/80-stock-items-parts.yaml",
156
110
  "config/master/80-stock-items-parts.yaml",
157
111
  ),
158
112
  (
159
- "distribution/master/82-stock-items-kits.yaml",
113
+ "config/master/82-stock-items-kits.yaml",
160
114
  "config/master/82-stock-items-kits.yaml",
161
115
  ),
162
116
  (
163
- "distribution/master/85-kit-specifications.yaml",
117
+ "config/master/85-kit-specifications.yaml",
164
118
  "config/master/85-kit-specifications.yaml",
165
119
  ),
166
120
  (
167
- "distribution/scenario/10-seed-capital.yaml",
168
- "scenario/10-seed-capital.yaml",
169
- ),
170
- (
171
- "distribution/scenario/20-buy-gateways.yaml",
172
- "scenario/20-buy-gateways.yaml",
173
- ),
174
- ("distribution/scenario/30-build.yaml", "scenario/30-build.yaml"),
175
- ("distribution/scenario/40-sell.yaml", "scenario/40-sell.yaml"),
176
- (
177
- "distribution/snapshot/10-trial-balance.yaml",
121
+ "config/snapshot/10-trial-balance.yaml",
178
122
  "config/snapshot/10-trial-balance.yaml",
179
123
  ),
180
- ("distribution/README.md", "README.md"),
124
+ ("scenario/10-seed-capital.yaml", "scenario/10-seed-capital.yaml"),
125
+ ("scenario/20-buy.yaml", "scenario/20-buy.yaml"),
126
+ ("scenario/30-build.yaml", "scenario/30-build.yaml"),
127
+ ("scenario/40-sell.yaml", "scenario/40-sell.yaml"),
181
128
  )
182
129
 
183
- INIT_FLAVORS = frozenset({"distribution"})
184
-
185
130
 
186
131
  class Instance(BaseSettings):
187
132
  """The resolved target: flags over ACU_* vars (.env or process) over defaults.
@@ -243,72 +188,26 @@ class Instance(BaseSettings):
243
188
  return v
244
189
 
245
190
 
246
- def templates_for(flavor: str | None) -> tuple[tuple[str, str], ...]:
247
- """Resolve (resource, dest) pairs for ``config init`` (V28/T87).
248
-
249
- Absent flavor → finance-minimal ``INIT_TEMPLATES`` at root (+ observer
250
- ``config/snapshot/``). ``distribution`` keeps root meta
251
- (``.env``/``.gitignore``/``target.yaml``), rehomes finance seed under
252
- ``config/``, then overlays ``DISTRIBUTION_TEMPLATES`` (config/ seeds +
253
- lifecycle ``scenario/`` + ``config/snapshot/`` + README). Never dual
254
- root+config seed trees. Paths already under ``config/`` (snapshot
255
- views) pass through unchanged.
256
- """
257
- if flavor is None:
258
- return INIT_TEMPLATES
259
- if flavor not in INIT_FLAVORS:
260
- raise SystemExit(
261
- f"unknown config init flavor {flavor!r}; "
262
- f"known: {', '.join(sorted(INIT_FLAVORS))}"
263
- )
264
- by_dest: dict[str, str] = {}
265
- order: list[str] = []
266
- for res, dest in INIT_TEMPLATES:
267
- if dest in _ROOT_META:
268
- by_dest[dest] = res
269
- order.append(dest)
270
- continue
271
- if dest.startswith("config/"):
272
- target = dest
273
- else:
274
- target = (
275
- f"config/{dest}"
276
- if any(dest.startswith(p) for p in _SEED_PREFIXES)
277
- else dest
278
- )
279
- if target not in by_dest:
280
- order.append(target)
281
- by_dest[target] = res
282
- for res, dest in DISTRIBUTION_TEMPLATES:
283
- if dest not in by_dest:
284
- order.append(dest)
285
- by_dest[dest] = res
286
- return tuple((by_dest[dest], dest) for dest in order)
287
-
288
-
289
- def scaffold(
290
- directory: Path, host: str | None = None, flavor: str | None = None
291
- ) -> Iterator[tuple[str, Path]]:
191
+ def scaffold(directory: Path, host: str | None = None) -> Iterator[tuple[str, Path]]:
292
192
  """Write the data-repo template set into ``directory``, never overwriting.
293
193
 
294
194
  Yields ("write" | "skip", path) per template file. ``host`` replaces the
295
195
  placeholder host inside the scaffolded .env ``ACU_BASE_URL``/``ACU_SSH``
296
- values; secrets stay placeholders (V2). ``flavor`` selects the template
297
- set (V28; default finance-minimal). The directory is created if absent.
298
- No git init, no gpg - version control and secret encryption stay the
299
- operator's call.
196
+ values; secrets stay placeholders (V2). Single full seed under
197
+ ``config/`` + lifecycle ``scenario/`` (V28/T108; no flavor). The
198
+ directory is created if absent. No git init, no gpg - version control
199
+ and secret encryption stay the operator's call.
300
200
  """
301
201
  pkg = resources.files("acumatica_cli") / "templates"
302
202
  directory.mkdir(parents=True, exist_ok=True)
303
- for resource, dest in templates_for(flavor):
203
+ for resource, dest in INIT_TEMPLATES:
304
204
  target = directory / dest
305
205
  if target.exists():
306
206
  yield "skip", target
307
207
  continue
308
208
  # Single full contract (T81/T82): scaffold from the packaged
309
209
  # bootstrap_project.xml so init cannot diverge from the fallback.
310
- # Finance-minimal root path; distribution rehomes under config/ (T87).
311
- if dest in ("bootstrap/project.xml", "config/bootstrap/project.xml"):
210
+ if dest == "config/bootstrap/project.xml":
312
211
  content = (
313
212
  resources.files("acumatica_cli") / "bootstrap_project.xml"
314
213
  ).read_text(encoding="utf-8")