acumatica-cli 0.10.3__tar.gz → 0.12.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.10.3 → acumatica_cli-0.12.0}/PKG-INFO +47 -18
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/README.md +46 -17
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/pyproject.toml +1 -1
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/bootstrap.py +33 -22
- acumatica_cli-0.12.0/src/acumatica_cli/bootstrap_project.xml +789 -0
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/cli.py +80 -28
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/client.py +21 -2
- acumatica_cli-0.12.0/src/acumatica_cli/config.py +370 -0
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/extract.py +4 -4
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/extract_manifest.yaml +1 -1
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/run.py +164 -30
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/seed.py +5 -4
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/README.md +59 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/baseline/20-accounts.yaml +238 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/baseline/60-ledger-company.yaml +27 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/baseline/90-uoms.yaml +17 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/bootstrap/company.yaml +15 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/bootstrap/features.yaml +26 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/10-reason-codes.yaml +49 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/20-in-preferences.yaml +34 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/30-availability-rules.yaml +16 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/40-posting-classes.yaml +44 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/50-warehouse.yaml +23 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/51-warehouse-locations.yaml +26 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/52-warehouse-defaults.yaml +13 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/53-tax-categories.yaml +14 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/54-item-classes.yaml +43 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/56-so-preferences.yaml +21 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/57-po-preferences.yaml +21 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/58-order-types.yaml +21 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/60-ar-preferences.yaml +15 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/61-ap-preferences.yaml +14 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/62-ca-preferences.yaml +18 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/63-cash-account.yaml +23 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/64-payment-methods.yaml +27 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/65-statement-cycles.yaml +23 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/70-vendor-classes.yaml +38 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/71-customer-classes.yaml +28 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/75-vendors.yaml +73 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/76-customers.yaml +54 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/80-stock-items-parts.yaml +63 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/82-stock-items-kits.yaml +37 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/master/85-kit-specifications.yaml +94 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/scenario/10-seed-capital.yaml +46 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/scenario/20-buy-gateways.yaml +112 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/scenario/30-build.yaml +5 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/scenario/40-sell.yaml +264 -0
- acumatica_cli-0.12.0/src/acumatica_cli/templates/distribution/setup/30-open-periods.yaml +33 -0
- acumatica_cli-0.10.3/src/acumatica_cli/bootstrap_project.xml +0 -238
- acumatica_cli-0.10.3/src/acumatica_cli/config.py +0 -198
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/__init__.py +0 -0
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/bootstrap_plugin.cs +0 -0
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/firstlogin.py +0 -0
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/models.py +0 -0
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/output.py +0 -0
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/src/acumatica_cli/target.py +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/baseline/10-subaccounts.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/baseline/20-accounts.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/baseline/40-ledger.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/baseline/50-gl-preferences.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/baseline/60-ledger-company.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/baseline/90-uoms.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/bootstrap/company.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/bootstrap/credit-terms.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/bootstrap/features.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/env +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/gitignore +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/setup/10-financial-year.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/setup/20-master-calendar.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/setup/30-open-periods.yaml +0 -0
- {acumatica_cli-0.10.3/src/acumatica_cli/templates → acumatica_cli-0.12.0/src/acumatica_cli/templates/finance}/target +0 -0
- {acumatica_cli-0.10.3 → acumatica_cli-0.12.0}/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.
|
|
3
|
+
Version: 0.12.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>
|
|
@@ -16,7 +16,7 @@ Project-URL: Homepage, https://lab5.ca
|
|
|
16
16
|
Project-URL: Repository, https://github.com/kborovik/acumatica-cli
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
|
|
19
|
-
# Acumatica GitOps
|
|
19
|
+
# Acumatica ERP - GitOps CLI
|
|
20
20
|
|
|
21
21
|
**`acu`** configures Acumatica ERP from YAML files in a git repo (GitOps).
|
|
22
22
|
|
|
@@ -43,10 +43,23 @@ cd my-erp # edit .env: set ACU_PASSWORD
|
|
|
43
43
|
|
|
44
44
|
acu config check # read-only preflight (incl. target.yaml)
|
|
45
45
|
acu tenant create --id 3 --login DEV # create the tenant + bootstrap it (needs SSH)
|
|
46
|
-
acu --tenant DEV apply # seed bootstrap/, baseline/, setup/
|
|
46
|
+
acu --tenant DEV apply # seed bootstrap/, baseline/, setup/ (, master/)
|
|
47
47
|
acu --tenant DEV diff # prove zero drift (exit 2 on drift)
|
|
48
48
|
```
|
|
49
49
|
|
|
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.
|
|
62
|
+
|
|
50
63
|
**Hosted Acumatica (no SSH):** the tenant already exists; leave `ACU_SSH` blank.
|
|
51
64
|
|
|
52
65
|
```sh
|
|
@@ -81,36 +94,42 @@ acu [--tenant NAME] [--url URL] [--ssh USER@HOST] [--api-version V]
|
|
|
81
94
|
├── schema [--out DIR] dump the endpoint's OpenAPI schema (swagger.json)
|
|
82
95
|
│
|
|
83
96
|
└── config configuration ops
|
|
84
|
-
├── init [--host HOST] [DIR]
|
|
97
|
+
├── init [--host HOST] [--flavor distribution] [DIR]
|
|
98
|
+
│ scaffold a data repo (.env, target.yaml, example YAML)
|
|
85
99
|
├── show print the resolved config as a complete .env
|
|
86
100
|
└── check [--strict] preflight: discovery, secrets, target, REST, endpoints, SSH
|
|
87
101
|
```
|
|
88
102
|
|
|
89
|
-
`apply` and `diff`
|
|
90
|
-
`
|
|
103
|
+
`apply` and `diff` without FILES prefer `config/<name>/` when any seed child exists under `config/`; otherwise root `bootstrap/`, `baseline/`, `setup/`, then `master/` when present.
|
|
104
|
+
A path like `config/` expands nested seed dirs in that fixed order.
|
|
105
|
+
`run` without FILES defaults to `scenario/`.
|
|
91
106
|
`acu --completion` emits a completion script for bash, zsh, or fish — source it from your shell profile.
|
|
92
107
|
Run `acu <command> --help` for details on any command.
|
|
93
108
|
|
|
94
109
|
## The data repo
|
|
95
110
|
|
|
96
|
-
Your configuration lives in its own git repo.
|
|
111
|
+
Your configuration lives in its own git repo.
|
|
112
|
+
`acu config init` scaffolds finance-minimal seeds at the **root** (Bootstrap `project.xml` at `Bootstrap/1.0.0`, no `master/`, no scenario).
|
|
113
|
+
`acu config init --flavor distribution` scaffolds the full demo under `config/` (same contract identity, expanded COA, masters) plus lifecycle `scenario/` and README.
|
|
97
114
|
|
|
98
|
-
| Path
|
|
99
|
-
|
|
|
100
|
-
| `bootstrap/`
|
|
101
|
-
| `baseline/`
|
|
102
|
-
| `setup/`
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
|
|
|
115
|
+
| Path | What it holds |
|
|
116
|
+
| ---- | ------------- |
|
|
117
|
+
| `bootstrap/` / `config/bootstrap/` | virgin-tenant config: features, company, credit terms, `project.xml` |
|
|
118
|
+
| `baseline/` / `config/baseline/` | reference data: subaccounts, COA, ledger, UOMs |
|
|
119
|
+
| `setup/` / `config/setup/` | one-time actions: financial year, master calendar, open periods |
|
|
120
|
+
| `config/master/` | distribution masters (prefs, warehouse, items, parties); flavor only |
|
|
121
|
+
| `scenario/` | lifecycle txns for `acu run`: once capital, then buy, build stub, sell |
|
|
122
|
+
| `target.yaml` | committed verified matrix: `erp` + `default_api` (what, not where) |
|
|
123
|
+
| `.env` | where to apply and who signs in, every key an `ACU_*` variable |
|
|
106
124
|
|
|
107
125
|
Files in each directory apply alphabetically; the numbered prefixes (`10-`, `20-`, and so on) encode dependency order.
|
|
108
126
|
The scaffolded `.gitignore` keeps `.env` out of git — store it encrypted (for example as `.env.gpg`) and decrypt once per clone.
|
|
109
127
|
Commit `target.yaml` with the seeds so every clone knows the verified ERP line and Default API generation.
|
|
110
128
|
|
|
111
|
-
Seed YAML
|
|
129
|
+
Seed YAML is state: `apply` upserts it, `diff` proves it.
|
|
112
130
|
Scenario YAML is different — it describes transactions that flow forward.
|
|
113
|
-
`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
|
|
131
|
+
`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.
|
|
132
|
+
`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).
|
|
114
133
|
|
|
115
134
|
### Seed `endpoint:` symbols
|
|
116
135
|
|
|
@@ -121,7 +140,7 @@ Dual-served entities (on both Bootstrap and Default) need an explicit `endpoint:
|
|
|
121
140
|
| omitted | `Default/<ACU_API_VERSION>` for Default-only entities |
|
|
122
141
|
| `bootstrap` | active `Bootstrap/<ver>` from `bootstrap/project.xml` or the packaged contract |
|
|
123
142
|
| `default` | `Default/<ACU_API_VERSION>` — tracks the operator API version |
|
|
124
|
-
| `Bootstrap/1.
|
|
143
|
+
| `Bootstrap/1.0.0` or `Default/25.200.001` | literal pin (ignores `ACU_API_VERSION` for Default) |
|
|
125
144
|
|
|
126
145
|
Prefer symbolic `default` over a pinned `Default/25.200.001` so the seed tree travels with the configured API generation.
|
|
127
146
|
|
|
@@ -258,6 +277,16 @@ gmake check # offline gate: ruff, basedpyright strict, pytest
|
|
|
258
277
|
The default test suite is fully offline.
|
|
259
278
|
REST is faked with `httpx.MockTransport`, SSH with a monkeypatched `subprocess.run` — no live instance is needed.
|
|
260
279
|
`gmake check` must pass before every commit.
|
|
280
|
+
GitHub Actions runs the same gate on every push and pull request to `main`.
|
|
281
|
+
|
|
282
|
+
### Release
|
|
283
|
+
|
|
284
|
+
```sh
|
|
285
|
+
gmake release patch # or minor | major
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Local release runs `gmake check`, bumps the version, commits, tags `v<version>`, and pushes.
|
|
289
|
+
GitHub Actions re-runs the check on the tag, then publishes the GitHub release and PyPI package only if that check passes.
|
|
261
290
|
|
|
262
291
|
### Live end-to-end tier
|
|
263
292
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Acumatica GitOps
|
|
1
|
+
# Acumatica ERP - GitOps CLI
|
|
2
2
|
|
|
3
3
|
**`acu`** configures Acumatica ERP from YAML files in a git repo (GitOps).
|
|
4
4
|
|
|
@@ -25,10 +25,23 @@ cd my-erp # edit .env: set ACU_PASSWORD
|
|
|
25
25
|
|
|
26
26
|
acu config check # read-only preflight (incl. target.yaml)
|
|
27
27
|
acu tenant create --id 3 --login DEV # create the tenant + bootstrap it (needs SSH)
|
|
28
|
-
acu --tenant DEV apply # seed bootstrap/, baseline/, setup/
|
|
28
|
+
acu --tenant DEV apply # seed bootstrap/, baseline/, setup/ (, master/)
|
|
29
29
|
acu --tenant DEV diff # prove zero drift (exit 2 on drift)
|
|
30
30
|
```
|
|
31
31
|
|
|
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.
|
|
44
|
+
|
|
32
45
|
**Hosted Acumatica (no SSH):** the tenant already exists; leave `ACU_SSH` blank.
|
|
33
46
|
|
|
34
47
|
```sh
|
|
@@ -63,36 +76,42 @@ acu [--tenant NAME] [--url URL] [--ssh USER@HOST] [--api-version V]
|
|
|
63
76
|
├── schema [--out DIR] dump the endpoint's OpenAPI schema (swagger.json)
|
|
64
77
|
│
|
|
65
78
|
└── config configuration ops
|
|
66
|
-
├── init [--host HOST] [DIR]
|
|
79
|
+
├── init [--host HOST] [--flavor distribution] [DIR]
|
|
80
|
+
│ scaffold a data repo (.env, target.yaml, example YAML)
|
|
67
81
|
├── show print the resolved config as a complete .env
|
|
68
82
|
└── check [--strict] preflight: discovery, secrets, target, REST, endpoints, SSH
|
|
69
83
|
```
|
|
70
84
|
|
|
71
|
-
`apply` and `diff`
|
|
72
|
-
`
|
|
85
|
+
`apply` and `diff` without FILES prefer `config/<name>/` when any seed child exists under `config/`; otherwise root `bootstrap/`, `baseline/`, `setup/`, then `master/` when present.
|
|
86
|
+
A path like `config/` expands nested seed dirs in that fixed order.
|
|
87
|
+
`run` without FILES defaults to `scenario/`.
|
|
73
88
|
`acu --completion` emits a completion script for bash, zsh, or fish — source it from your shell profile.
|
|
74
89
|
Run `acu <command> --help` for details on any command.
|
|
75
90
|
|
|
76
91
|
## The data repo
|
|
77
92
|
|
|
78
|
-
Your configuration lives in its own git repo.
|
|
93
|
+
Your configuration lives in its own git repo.
|
|
94
|
+
`acu config init` scaffolds finance-minimal seeds at the **root** (Bootstrap `project.xml` at `Bootstrap/1.0.0`, no `master/`, no scenario).
|
|
95
|
+
`acu config init --flavor distribution` scaffolds the full demo under `config/` (same contract identity, expanded COA, masters) plus lifecycle `scenario/` and README.
|
|
79
96
|
|
|
80
|
-
| Path
|
|
81
|
-
|
|
|
82
|
-
| `bootstrap/`
|
|
83
|
-
| `baseline/`
|
|
84
|
-
| `setup/`
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
|
|
|
97
|
+
| Path | What it holds |
|
|
98
|
+
| ---- | ------------- |
|
|
99
|
+
| `bootstrap/` / `config/bootstrap/` | virgin-tenant config: features, company, credit terms, `project.xml` |
|
|
100
|
+
| `baseline/` / `config/baseline/` | reference data: subaccounts, COA, ledger, UOMs |
|
|
101
|
+
| `setup/` / `config/setup/` | one-time actions: financial year, master calendar, open periods |
|
|
102
|
+
| `config/master/` | distribution masters (prefs, warehouse, items, parties); flavor only |
|
|
103
|
+
| `scenario/` | lifecycle txns for `acu run`: once capital, then buy, build stub, sell |
|
|
104
|
+
| `target.yaml` | committed verified matrix: `erp` + `default_api` (what, not where) |
|
|
105
|
+
| `.env` | where to apply and who signs in, every key an `ACU_*` variable |
|
|
88
106
|
|
|
89
107
|
Files in each directory apply alphabetically; the numbered prefixes (`10-`, `20-`, and so on) encode dependency order.
|
|
90
108
|
The scaffolded `.gitignore` keeps `.env` out of git — store it encrypted (for example as `.env.gpg`) and decrypt once per clone.
|
|
91
109
|
Commit `target.yaml` with the seeds so every clone knows the verified ERP line and Default API generation.
|
|
92
110
|
|
|
93
|
-
Seed YAML
|
|
111
|
+
Seed YAML is state: `apply` upserts it, `diff` proves it.
|
|
94
112
|
Scenario YAML is different — it describes transactions that flow forward.
|
|
95
|
-
`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
|
|
113
|
+
`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.
|
|
114
|
+
`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).
|
|
96
115
|
|
|
97
116
|
### Seed `endpoint:` symbols
|
|
98
117
|
|
|
@@ -103,7 +122,7 @@ Dual-served entities (on both Bootstrap and Default) need an explicit `endpoint:
|
|
|
103
122
|
| omitted | `Default/<ACU_API_VERSION>` for Default-only entities |
|
|
104
123
|
| `bootstrap` | active `Bootstrap/<ver>` from `bootstrap/project.xml` or the packaged contract |
|
|
105
124
|
| `default` | `Default/<ACU_API_VERSION>` — tracks the operator API version |
|
|
106
|
-
| `Bootstrap/1.
|
|
125
|
+
| `Bootstrap/1.0.0` or `Default/25.200.001` | literal pin (ignores `ACU_API_VERSION` for Default) |
|
|
107
126
|
|
|
108
127
|
Prefer symbolic `default` over a pinned `Default/25.200.001` so the seed tree travels with the configured API generation.
|
|
109
128
|
|
|
@@ -240,6 +259,16 @@ gmake check # offline gate: ruff, basedpyright strict, pytest
|
|
|
240
259
|
The default test suite is fully offline.
|
|
241
260
|
REST is faked with `httpx.MockTransport`, SSH with a monkeypatched `subprocess.run` — no live instance is needed.
|
|
242
261
|
`gmake check` must pass before every commit.
|
|
262
|
+
GitHub Actions runs the same gate on every push and pull request to `main`.
|
|
263
|
+
|
|
264
|
+
### Release
|
|
265
|
+
|
|
266
|
+
```sh
|
|
267
|
+
gmake release patch # or minor | major
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Local release runs `gmake check`, bumps the version, commits, tags `v<version>`, and pushes.
|
|
271
|
+
GitHub Actions re-runs the check on the tag, then publishes the GitHub release and PyPI package only if that check passes.
|
|
243
272
|
|
|
244
273
|
### Live end-to-end tier
|
|
245
274
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "acumatica-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.12.0"
|
|
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"
|
|
@@ -8,10 +8,11 @@ virgin tenant, so bootstrap = publish a package whose CustomizationPlugin
|
|
|
8
8
|
cannot write CS100000 at all (T3 verdict) — and whose Bootstrap contract
|
|
9
9
|
endpoint exposes the seeding surface (serialization verified T12).
|
|
10
10
|
|
|
11
|
-
Contract ownership is hybrid (T69/V2): the data repo's
|
|
12
|
-
``bootstrap/project.xml`` is preferred
|
|
13
|
-
else the packaged
|
|
14
|
-
|
|
11
|
+
Contract ownership is hybrid (T69/V2/T81/T85): the data repo's
|
|
12
|
+
``config/bootstrap/project.xml`` is preferred, then root
|
|
13
|
+
``bootstrap/project.xml``; else the packaged full company
|
|
14
|
+
``bootstrap_project.xml`` (``Bootstrap/1.0.0``) so PyPI-only and offline
|
|
15
|
+
virgin paths still bootstrap the full surface.
|
|
15
16
|
|
|
16
17
|
Customization publishes are tenant-scoped, so the package must be published
|
|
17
18
|
per tenant; publish() is idempotent on content — the skip gate compares the
|
|
@@ -19,8 +20,9 @@ digest embedded in the published package's description against the package
|
|
|
19
20
|
built now, and the plugin's UpdateDatabase is a keyed update on re-run.
|
|
20
21
|
|
|
21
22
|
The feature set the plugin enables is data, not code (V2): load_features()
|
|
22
|
-
reads
|
|
23
|
-
and package_zip() splices it into the plugin
|
|
23
|
+
reads config/bootstrap/features.yaml then root bootstrap/features.yaml
|
|
24
|
+
(absent -> the built-in six) and package_zip() splices it into the plugin
|
|
25
|
+
source at build time.
|
|
24
26
|
"""
|
|
25
27
|
|
|
26
28
|
import hashlib
|
|
@@ -43,11 +45,10 @@ PACKAGE_NAME = "AcuBootstrap"
|
|
|
43
45
|
PLUGIN_CLASS = "AcuBootstrapPlugin"
|
|
44
46
|
_ENDPOINT_NS = "{http://www.acumatica.com/entity/maintenance/5.31}"
|
|
45
47
|
|
|
46
|
-
# Code default when the data repo carries no
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
# source — B6).
|
|
48
|
+
# Code default when the data repo carries no features.yaml (SPEC I.data)
|
|
49
|
+
# — the minimum for company/branch + baseline seeding. The set is injected
|
|
50
|
+
# into the plugin source at package build; it never lives in
|
|
51
|
+
# bootstrap_plugin.cs (V2: feature flags are config, not tool source — B6).
|
|
51
52
|
DEFAULT_FEATURES = (
|
|
52
53
|
"FinancialModule",
|
|
53
54
|
"FinancialStandard",
|
|
@@ -59,8 +60,16 @@ DEFAULT_FEATURES = (
|
|
|
59
60
|
FEATURES_SENTINEL = "/*ACU_FEATURES*/"
|
|
60
61
|
|
|
61
62
|
|
|
63
|
+
def _bootstrap_file(root: Path, name: str) -> Path | None:
|
|
64
|
+
"""Resolve a bootstrap package input: config/bootstrap then root (V30)."""
|
|
65
|
+
for path in (root / "config" / "bootstrap" / name, root / "bootstrap" / name):
|
|
66
|
+
if path.is_file():
|
|
67
|
+
return path
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
62
71
|
def packaged_contract_xml() -> bytes:
|
|
63
|
-
"""The CLI-shipped
|
|
72
|
+
"""The CLI-shipped full company Bootstrap contract (``Bootstrap/1.0.0``)."""
|
|
64
73
|
return (resources.files("acumatica_cli") / "bootstrap_project.xml").read_bytes()
|
|
65
74
|
|
|
66
75
|
|
|
@@ -68,12 +77,13 @@ def load_contract_xml(root: Path | None = None) -> bytes:
|
|
|
68
77
|
"""Active contract bytes: data-repo override when present, else packaged.
|
|
69
78
|
|
|
70
79
|
``root`` is the data-repo discovery root (the dir holding ``.env``).
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
Prefer ``config/bootstrap/project.xml``, then root ``bootstrap/project.xml``
|
|
81
|
+
(V30 dual resolve). Absent root or file falls back to the packaged full
|
|
82
|
+
company contract (V2/T81, same absence pattern as features.yaml).
|
|
73
83
|
"""
|
|
74
84
|
if root is not None:
|
|
75
|
-
path = root
|
|
76
|
-
if path
|
|
85
|
+
path = _bootstrap_file(root, "project.xml")
|
|
86
|
+
if path is not None:
|
|
77
87
|
return path.read_bytes()
|
|
78
88
|
return packaged_contract_xml()
|
|
79
89
|
|
|
@@ -97,15 +107,16 @@ PACKAGE_DESCRIPTION = ET.fromstring(packaged_contract_xml()).get("description",
|
|
|
97
107
|
|
|
98
108
|
|
|
99
109
|
def load_features(root: Path) -> list[str]:
|
|
100
|
-
"""
|
|
110
|
+
"""FeaturesSet property names from config/ then root bootstrap (V30).
|
|
101
111
|
|
|
112
|
+
Prefer ``config/bootstrap/features.yaml``, then ``bootstrap/features.yaml``.
|
|
102
113
|
Absent file -> the built-in six (SPEC I.data). Names are validated as
|
|
103
114
|
plausible property names here (they are spliced into C# string literals);
|
|
104
115
|
whether each matches a real FeaturesSet property only the plugin can
|
|
105
116
|
tell — it logs the strays at publish time (the silent-typo guard).
|
|
106
117
|
"""
|
|
107
|
-
path = root
|
|
108
|
-
if
|
|
118
|
+
path = _bootstrap_file(root, "features.yaml")
|
|
119
|
+
if path is None:
|
|
109
120
|
return list(DEFAULT_FEATURES)
|
|
110
121
|
with open(path) as f:
|
|
111
122
|
data = yaml.safe_load(f)
|
|
@@ -136,9 +147,9 @@ def package_zip(
|
|
|
136
147
|
``Enabled`` set at the ACU_FEATURES sentinel — the one point where the
|
|
137
148
|
data repo's feature list enters the package (V2).
|
|
138
149
|
|
|
139
|
-
Contract XML (V2/T69): ``contract`` when given; else
|
|
140
|
-
``bootstrap/project.xml``
|
|
141
|
-
packaged
|
|
150
|
+
Contract XML (V2/T69/T81/T85): ``contract`` when given; else
|
|
151
|
+
``config/bootstrap/project.xml`` then ``bootstrap/project.xml`` under
|
|
152
|
+
``root`` when present; else the packaged full company template.
|
|
142
153
|
"""
|
|
143
154
|
pkg = resources.files("acumatica_cli")
|
|
144
155
|
if contract is None:
|