feat(promote): resolve pre-existing entities by business key (#entity:field=value)
Closes the last promote gap: a manifest can now reference records it does NOT create. A value like "#thirdparty:name=KissMetrics" (or :code=CL0007) is looked up on the TARGET at apply time and resolved to that target's id — so the same manifest is portable (sandbox id on --target sandbox, prod id on --target prod). promote-apply.sh: resolve() gains a "#" branch + a lookup() helper that queries the target via the GET wrapper with sqlfilters. Supports thirdparty (name/code/supplier_code) and invoice/supplierinvoice (ref/ref_supplier). A lookup matching nothing OR more than one record ABORTS the run — it never guesses, so it cannot write to the wrong entity. Proven live: "#thirdparty:name=ACME Conseil" resolved to the existing client and invoiced it; a not-found code and an ambiguous (2-match) name both aborted with exit 1. Combined with @refs, arbitrary self-contained-or-referential change-sets now replay cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -141,10 +141,20 @@ dependent ops wire up on the target. `--target sandbox` writes via `dol-write.sh
|
||||
`ARCO_PROMOTE_CONFIRM` is set exactly. Pair it with `dolibarr-data-snapshot` (prod
|
||||
before/after) to confirm only the intended records changed.
|
||||
|
||||
Limitation: a manifest references entities it **creates** (via `@ref`). Referencing
|
||||
a *pre-existing* prod entity by a sandbox id won't match — resolve those by business
|
||||
key (name/code/ref) first. Self-contained change-sets (new thirdparty + its invoice
|
||||
+ payment) replay cleanly today.
|
||||
A manifest value can reference another entity two ways, both resolved against the
|
||||
**target** so the same file is portable sandbox↔prod:
|
||||
|
||||
- **`@ref`** — an entity **created earlier in this manifest** (resolves to the id
|
||||
just created on the target). For new records.
|
||||
- **`#entity:field=value`** — a **pre-existing** entity, looked up on the target by
|
||||
business key, e.g. `#thirdparty:name=KissMetrics` or `#thirdparty:code=CL0007`.
|
||||
Supports `thirdparty` (name/code/supplier_code) and `invoice`/`supplierinvoice`
|
||||
(ref/ref_supplier). A lookup matching **nothing or more than one** record **aborts
|
||||
the run** — it never guesses, so it can't write to the wrong entity.
|
||||
|
||||
So a real change like "invoice the existing client KissMetrics and record its
|
||||
payment" is `{"socid":"#thirdparty:name=KissMetrics", ...}` — it resolves to the
|
||||
sandbox KissMetrics on `--target sandbox` and the prod one on `--target prod`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
|
||||
Reference in New Issue
Block a user