The human-gated path that carries a reviewed sandbox change to prod.
- promote-plan.sh: render a manifest (JSON array of write ops with symbolic @refs
instead of ids — portable sandbox->prod) as a human-readable change-set.
- promote-apply.sh <manifest> --target sandbox|prod: replay it, resolving each
@ref to the id actually created during the run (dependent ops wire up). sandbox
rehearses via dol-write.sh; prod via dol-prod-write.sh.
- dol-prod-write.sh: the ONLY prod-write path. Prod key read from the ENVIRONMENT
only (DOLIBARR_PROD_WRITE_KEY, never a stored .env); every write refused unless
ARCO_PROMOTE_CONFIRM=I-UNDERSTAND-THIS-WRITES-PROD.
- create scripts take a DOL_WRITE override so promote-apply reuses them per target.
- bin/arcodange: `promote {plan|apply}` group + example manifest.
- payment-record.sh: fixed supplier payments (payment_mode_id + closepaidinvoices).
Proven live: plan renders; apply --target sandbox replays a 3-op chain with refs
resolved (@tp1->id, invoice socid=@tp1, payment invoice=@inv1); --target prod
without the confirm flag is REFUSED before sending. Supplier payment now works
end-to-end via the script.
Limitation (documented): manifests reference entities they create (@ref);
pre-existing prod entities need business-key resolution (follow-up).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
2.0 KiB
Bash
Executable File
51 lines
2.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Create a client and/or supplier thirdparty (fiche tiers) in the SANDBOX.
|
|
#
|
|
# Input: a JSON object on stdin (or a file path in $1). Fields:
|
|
# name (required)
|
|
# role "client" | "supplier" | "both" (default "client")
|
|
# country_id numeric, default 1 (France)
|
|
# client_code / supplier_code default "-1" = auto-generate via the code mask
|
|
# siret, tva_intra, address, zip, town, email, phone, idprof1 (optional)
|
|
#
|
|
# Emits the new thirdparty id on stdout. All writes go through dol-write.sh,
|
|
# which refuses any host that is not the sandbox.
|
|
#
|
|
# Examples:
|
|
# echo '{"name":"KissMetrics","role":"client","tva_intra":"US.."}' | thirdparty-create.sh
|
|
# thirdparty-create.sh fournisseur.json
|
|
set -euo pipefail
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
W="${DOL_WRITE:-${SCRIPT_DIR}/dol-write.sh}"
|
|
|
|
SRC="${1:-}"
|
|
if [[ -n "${SRC}" && "${SRC}" != "-" ]]; then INPUT="$(cat "${SRC}")"; else INPUT="$(cat)"; fi
|
|
|
|
PYF="$(mktemp -t dolpy.XXXXXX)"; trap 'rm -f "${PYF}"' EXIT
|
|
cat > "${PYF}" <<'PY'
|
|
import json, sys
|
|
d = json.loads(sys.stdin.read())
|
|
if not d.get("name"):
|
|
sys.exit("thirdparty-create.sh: 'name' is required")
|
|
role = d.get("role", "client").lower()
|
|
is_client = role in ("client", "both")
|
|
is_supp = role in ("supplier", "fournisseur", "both")
|
|
body = {
|
|
"name": d["name"],
|
|
"client": "1" if is_client else "0",
|
|
"fournisseur": "1" if is_supp else "0",
|
|
"country_id": str(d.get("country_id", 1)),
|
|
# "-1" => Dolibarr auto-assigns the next code from the mask
|
|
# (COMPANY_ELEPHANT_MASK_CUSTOMER / _SUPPLIER); "0" when that role is off.
|
|
"code_client": (d.get("client_code", "-1") if is_client else "0"),
|
|
"code_fournisseur": (d.get("supplier_code", "-1") if is_supp else "0"),
|
|
}
|
|
for k in ("siret", "tva_intra", "address", "zip", "town", "email", "phone", "idprof1"):
|
|
if d.get(k):
|
|
body[k] = d[k]
|
|
print(json.dumps(body))
|
|
PY
|
|
|
|
BODY="$(printf '%s' "${INPUT}" | python3 "${PYF}")"
|
|
"${W}" POST /thirdparties "${BODY}"
|