acumatica-cli 0.18.0__tar.gz → 0.18.2__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 (71) hide show
  1. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/PKG-INFO +2 -2
  2. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/README.md +1 -1
  3. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/pyproject.toml +1 -1
  4. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/pyproject.toml.orig +1 -1
  5. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/cli.py +17 -1
  6. acumatica_cli-0.18.2/src/acumatica_cli/templates/gitignore +5 -0
  7. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/tenant.py +48 -2
  8. acumatica_cli-0.18.0/src/acumatica_cli/templates/gitignore +0 -2
  9. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/__init__.py +0 -0
  10. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/bootstrap.py +0 -0
  11. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/bootstrap_plugin.cs +0 -0
  12. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/bootstrap_project.xml +0 -0
  13. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/client.py +0 -0
  14. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/config.py +0 -0
  15. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/extract.py +0 -0
  16. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/firstlogin.py +0 -0
  17. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/inventory.py +0 -0
  18. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/models.py +0 -0
  19. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/output.py +0 -0
  20. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/reconcile.py +0 -0
  21. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/run.py +0 -0
  22. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/seed.py +0 -0
  23. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/seed_catalog.yaml +0 -0
  24. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/snapshot_map.yaml +0 -0
  25. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/state.py +0 -0
  26. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/target.py +0 -0
  27. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/README.md +0 -0
  28. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/baseline/10-subaccounts.yaml +0 -0
  29. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/baseline/20-accounts.yaml +0 -0
  30. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/baseline/40-ledger.yaml +0 -0
  31. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/baseline/50-gl-preferences.yaml +0 -0
  32. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/baseline/60-ledger-company.yaml +0 -0
  33. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/baseline/90-uoms.yaml +0 -0
  34. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/bootstrap/company.yaml +0 -0
  35. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/bootstrap/credit-terms.yaml +0 -0
  36. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/bootstrap/features.yaml +0 -0
  37. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/10-reason-codes.yaml +0 -0
  38. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/20-in-preferences.yaml +0 -0
  39. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/30-availability-rules.yaml +0 -0
  40. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/40-posting-classes.yaml +0 -0
  41. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/50-warehouse.yaml +0 -0
  42. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/51-warehouse-locations.yaml +0 -0
  43. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/52-warehouse-defaults.yaml +0 -0
  44. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/53-tax-categories.yaml +0 -0
  45. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/54-item-classes.yaml +0 -0
  46. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/56-so-preferences.yaml +0 -0
  47. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/57-po-preferences.yaml +0 -0
  48. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/58-order-types.yaml +0 -0
  49. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/60-ar-preferences.yaml +0 -0
  50. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/61-ap-preferences.yaml +0 -0
  51. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/62-ca-preferences.yaml +0 -0
  52. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/63-cash-account.yaml +0 -0
  53. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/64-payment-methods.yaml +0 -0
  54. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/65-statement-cycles.yaml +0 -0
  55. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/70-vendor-classes.yaml +0 -0
  56. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/71-customer-classes.yaml +0 -0
  57. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/75-vendors.yaml +0 -0
  58. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/76-customers.yaml +0 -0
  59. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/80-stock-items-parts.yaml +0 -0
  60. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/82-stock-items-kits.yaml +0 -0
  61. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/master/85-kit-specifications.yaml +0 -0
  62. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/setup/10-financial-year.yaml +0 -0
  63. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/setup/20-master-calendar.yaml +0 -0
  64. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/setup/30-open-periods.yaml +0 -0
  65. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/config/views/10-trial-balance.yaml +0 -0
  66. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/env +0 -0
  67. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/scenario/10-seed-capital.yaml +0 -0
  68. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/scenario/20-buy.yaml +0 -0
  69. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/scenario/30-build.yaml +0 -0
  70. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/scenario/40-sell.yaml +0 -0
  71. {acumatica_cli-0.18.0 → acumatica_cli-0.18.2}/src/acumatica_cli/templates/target +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: acumatica-cli
3
- Version: 0.18.0
3
+ Version: 0.18.2
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>
@@ -107,7 +107,7 @@ A path like `config/` expands nested seed dirs in that fixed order.
107
107
  `extract` always writes under `config/{bootstrap,baseline,setup,master}/` (catalog-driven; never root SEED_DIRS).
108
108
  `inventory` is offline (no REST/SSH/password): SM203520 Settings XML ZIP or `ac.exe export xml` folder writes to `inventory/`.
109
109
  `reconcile` is offline: compare `inventory/` to optional `config/` and write `findings/` only (never writes seed).
110
- Optional `snapshot_map.yaml` (data-repo root or package defaults) maps DAC tables to catalog entities and normalizes join (pad-trim, key/field aliases, Account/Sub FK CD resolve, enum labelcode).
110
+ Optional `snapshot_map.yaml` (data-repo root or package defaults) maps DAC tables to catalog entities and normalizes join (pad-trim, key/field aliases, Account/Sub FK CD resolve, enum label to code).
111
111
  See [docs/demo-seed.md](docs/demo-seed.md).
112
112
  `acu --completion` emits a completion script for bash, zsh, or fish — source it from your shell profile.
113
113
  Run `acu <command> --help` for details on any command.
@@ -89,7 +89,7 @@ A path like `config/` expands nested seed dirs in that fixed order.
89
89
  `extract` always writes under `config/{bootstrap,baseline,setup,master}/` (catalog-driven; never root SEED_DIRS).
90
90
  `inventory` is offline (no REST/SSH/password): SM203520 Settings XML ZIP or `ac.exe export xml` folder writes to `inventory/`.
91
91
  `reconcile` is offline: compare `inventory/` to optional `config/` and write `findings/` only (never writes seed).
92
- Optional `snapshot_map.yaml` (data-repo root or package defaults) maps DAC tables to catalog entities and normalizes join (pad-trim, key/field aliases, Account/Sub FK CD resolve, enum labelcode).
92
+ Optional `snapshot_map.yaml` (data-repo root or package defaults) maps DAC tables to catalog entities and normalizes join (pad-trim, key/field aliases, Account/Sub FK CD resolve, enum label to code).
93
93
  See [docs/demo-seed.md](docs/demo-seed.md).
94
94
  `acu --completion` emits a completion script for bash, zsh, or fish — source it from your shell profile.
95
95
  Run `acu <command> --help` for details on any command.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "acumatica-cli"
3
- version = "0.18.0"
3
+ version = "0.18.2"
4
4
  description = "Acumatica ERP Config-as-Code: tenant provisioning, baseline config, and reference data"
5
5
  license = "PolyForm-Noncommercial-1.0.0"
6
6
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "acumatica-cli"
3
- version = "0.18.0"
3
+ version = "0.18.2"
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"
@@ -7,6 +7,7 @@ from collections.abc import Callable
7
7
  from importlib.metadata import distribution
8
8
  from pathlib import Path
9
9
  from typing import Concatenate
10
+ from urllib.parse import urlparse
10
11
 
11
12
  import click
12
13
  import httpx
@@ -239,8 +240,10 @@ def tenant_group() -> None:
239
240
  def tenant_list(inst: Instance) -> None:
240
241
  """List tenants: CompanyID, sign-in name, internal CD, type."""
241
242
  tenants = TenantManager(inst).list()
243
+ # V9/B27: control-plane identity is the host, never scheme+path
244
+ host = urlparse(inst.base_url).hostname or inst.base_url
242
245
  output.table(
243
- f"Tenants on {inst.base_url}",
246
+ f"Tenants on {host}",
244
247
  ("ID", "Login", "CD", "Type"),
245
248
  (
246
249
  (str(t.company_id), t.login_name, t.company_cd, t.company_type)
@@ -306,6 +309,11 @@ def tenant_create(
306
309
  create is skipped and the init + digest-gated publish chain still runs,
307
310
  so re-running create is the republish route for existing tenants. --id
308
311
  must match the existing CompanyID, else hard error naming both.
312
+
313
+ After create (or on exists-skip), CompanyCD is set equal to --login.
314
+ ac.exe only writes LoginName into CompanyKey and auto-generates CD
315
+ (Company2, Company3, …); the align step matches what operators do on
316
+ the Companies screen so ``acu tenant list`` shows Login = CD.
309
317
  """
310
318
  mgr = TenantManager(inst)
311
319
  existing = next((t for t in mgr.list() if t.login_name == login_name), None)
@@ -326,6 +334,14 @@ def tenant_create(
326
334
  company_id, login_name, parent_id, not hidden, company_type or ""
327
335
  )
328
336
  output.data(raw.splitlines()[-1] if raw.strip() else "created")
337
+ # ac.exe only sets CompanyKey from LoginName; CompanyCD stays auto-generated
338
+ # (Company2/Company3/…). Align CD → login so list shows matching columns.
339
+ # Idempotent on re-run / exists-skip (docs/ac-exe.md).
340
+ with output.step(f"aligning CompanyCD to login ({login_name})"):
341
+ if mgr.set_company_cd(company_id, login_name):
342
+ output.data(f"CompanyCD set to {login_name}")
343
+ else:
344
+ output.data(f"CompanyCD already {login_name}")
329
345
  if no_init:
330
346
  output.warn("skipping init: tenant is invisible until an app-pool recycle")
331
347
  return
@@ -0,0 +1,5 @@
1
+ .env
2
+ findings/
3
+ inventory/
4
+ schemas/
5
+ state/
@@ -14,8 +14,10 @@ class Tenant(Model):
14
14
  """One row of the instance's Company table.
15
15
 
16
16
  login_name is dbo.Company.CompanyKey — the name on the sign-in page and
17
- the REST login's tenant value. company_cd is the internal auto-generated
18
- code (ac.exe ignores LoginName for it; see docs/ac-exe.md).
17
+ the REST login's tenant value. company_cd is dbo.Company.CompanyCD; ac.exe
18
+ auto-generates it (Company2, Company3, …) and ignores LoginName for it
19
+ (docs/ac-exe.md). ``acu tenant create`` aligns CompanyCD to the login
20
+ after CompanyConfig so list Login and CD match.
19
21
  """
20
22
 
21
23
  company_id: int
@@ -131,6 +133,9 @@ class TenantManager:
131
133
  flags preset the tenant's admin to the instance credentials with no
132
134
  must-change flag, so the tenant is REST-loginable right after the
133
135
  app-pool recycle — no first-login dance (verified, docs/ac-exe.md).
136
+
137
+ Does not set CompanyCD (ac.exe auto-generates it). Call
138
+ :meth:`set_company_cd` after create to align CD with the login.
134
139
  """
135
140
  company = (
136
141
  f"CompanyID={company_id};ParentID={parent_id};"
@@ -142,6 +147,47 @@ class TenantManager:
142
147
  )
143
148
  return self._company_config(company, extra=admin)
144
149
 
150
+ def set_company_cd(self, company_id: int, company_cd: str) -> bool:
151
+ """Set ``dbo.Company.CompanyCD`` for a tenant (sqlcmd over SSH).
152
+
153
+ ac.exe CompanyConfig only writes LoginName into CompanyKey; CompanyCD
154
+ stays auto-generated (``Company2``, ``Company3``, …). Aligning CD to
155
+ the login makes ``acu tenant list`` show matching Login/CD columns and
156
+ matches the Companies-screen rename operators do by hand.
157
+
158
+ Returns True when an UPDATE ran, False when CD already matches.
159
+ Raises if the target id is missing or ``company_cd`` is already used
160
+ by a different CompanyID.
161
+ """
162
+ tenants = self.list()
163
+ target = next((t for t in tenants if t.company_id == company_id), None)
164
+ if target is None:
165
+ raise RuntimeError(f"no tenant with CompanyID={company_id}")
166
+ if target.company_cd == company_cd:
167
+ return False
168
+ conflict = next(
169
+ (
170
+ t
171
+ for t in tenants
172
+ if t.company_cd == company_cd and t.company_id != company_id
173
+ ),
174
+ None,
175
+ )
176
+ if conflict is not None:
177
+ raise RuntimeError(
178
+ f"CompanyCD {company_cd!r} already used by "
179
+ f"CompanyID={conflict.company_id}"
180
+ )
181
+ # company_id is int; company_cd is quoted with doubled single-quotes
182
+ escaped = company_cd.replace("'", "''")
183
+ query = (
184
+ f"UPDATE {DB_NAME}.dbo.Company "
185
+ f"SET CompanyCD = N'{escaped}' "
186
+ f"WHERE CompanyID = {company_id}"
187
+ )
188
+ self._ssh(f'sqlcmd -S "(local)" -E -C -Q "{query}"')
189
+ return True
190
+
145
191
  def delete(self, company_id: int) -> str:
146
192
  """Delete the tenant and all its data.
147
193
 
@@ -1,2 +0,0 @@
1
- .env
2
- schemas/