The write-capable companion to the read-only dolibarr* skills, scoped to the erp-sandbox. Lets an AI agent rehearse bookkeeping writes against a copy of prod (ADR-0003) before a human promotes the reviewed change to prod. - scripts/dol-write.sh: write wrapper that REFUSES any host that is not erp-sandbox.arcodange.lab (the structural prod-safety guarantee) using the ai_agent_sandbox key from a gitignored .env. - scripts/thirdparty-create.sh: create client/supplier fiches; codes auto-assign via the elephant mask (code="-1"). - scripts/invoice-create.sh: customer (/invoices) or supplier (/supplierinvoices) invoices with product/service lines + ref_supplier, optional validate. - scripts/payment-record.sh: record a règlement (VIR/CB/CHQ/LIQ); customer pays full + marks paid, supplier needs an amount. - SKILL.md (safety model + workflows + the human-gated promote flow), .env.example, example input. Proven end-to-end live against the sandbox: client -> invoice (service+product lines, HT 1100 / TTC 1320) -> validate -> payment (paid); supplier -> supplier invoice (ref_supplier carried) -> validate. Host guard verified to refuse a prod URL before sending. Avoirs (credit notes) and bin/arcodange CLI wiring are planned follow-ups. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
83 lines
3.1 KiB
Bash
Executable File
83 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Host-guarded WRITE wrapper for the Arcodange Dolibarr SANDBOX API.
|
|
#
|
|
# THE GUARANTEE (ADR-0003): this wrapper REFUSES to run against anything that is
|
|
# not the erp-sandbox host. It is the structural reason an AI agent driving this
|
|
# skill can never mutate production — prod is a different host, and the guard
|
|
# below rejects it before any request is sent.
|
|
#
|
|
# Usage:
|
|
# dol-write.sh <METHOD> <path> [json-body|@file]
|
|
# dol-write.sh POST /thirdparties '{"name":"Acme","client":"1"}'
|
|
# dol-write.sh POST /invoices @invoice.json
|
|
# dol-write.sh PUT /thirdparties/42 '{"phone":"+33..."}'
|
|
# dol-write.sh GET /thirdparties?limit=5 # reads are allowed too
|
|
#
|
|
# Reads DOLIBARR_SANDBOX_URL + DOLIBARR_SANDBOX_API_KEY from the sibling .env
|
|
# (.claude/skills/dolibarr-sandbox-write/.env), mode 600, gitignored.
|
|
# Prints the response body to stdout; exits non-zero on HTTP >= 400.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ENV_FILE="${SCRIPT_DIR}/../.env"
|
|
|
|
if [[ ! -f "${ENV_FILE}" ]]; then
|
|
echo "dol-write.sh: missing ${ENV_FILE}" >&2
|
|
echo " Create it with DOLIBARR_SANDBOX_URL + DOLIBARR_SANDBOX_API_KEY. See README.md." >&2
|
|
exit 2
|
|
fi
|
|
# shellcheck disable=SC1090
|
|
set -a; source "${ENV_FILE}"; set +a
|
|
|
|
: "${DOLIBARR_SANDBOX_URL:?dol-write.sh: DOLIBARR_SANDBOX_URL not set in .env}"
|
|
: "${DOLIBARR_SANDBOX_API_KEY:?dol-write.sh: DOLIBARR_SANDBOX_API_KEY not set in .env}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# HOST GUARD — the structural safety invariant. Only the sandbox host passes.
|
|
# Override the allowed pattern only via DOLIBARR_SANDBOX_HOST_RE in .env if the
|
|
# sandbox FQDN ever changes; never widen it to include a prod host.
|
|
# ---------------------------------------------------------------------------
|
|
ALLOWED_RE="${DOLIBARR_SANDBOX_HOST_RE:-^https://erp-sandbox\.arcodange\.lab(/|$)}"
|
|
if [[ ! "${DOLIBARR_SANDBOX_URL}" =~ ${ALLOWED_RE} ]]; then
|
|
echo "dol-write.sh: REFUSING to write — DOLIBARR_SANDBOX_URL='${DOLIBARR_SANDBOX_URL}'" >&2
|
|
echo " is not the erp-sandbox host (allowed: ${ALLOWED_RE})." >&2
|
|
echo " This skill only writes the sandbox. Promotion to prod is a separate, human-gated step." >&2
|
|
exit 3
|
|
fi
|
|
|
|
if [[ $# -lt 2 ]]; then
|
|
echo "dol-write.sh: usage: dol-write.sh <METHOD> <path> [json-body|@file]" >&2
|
|
exit 2
|
|
fi
|
|
METHOD="$1"; API_PATH="$2"; BODY="${3-}"
|
|
case "${METHOD}" in
|
|
GET|POST|PUT|DELETE|PATCH) ;;
|
|
*) echo "dol-write.sh: unsupported method '${METHOD}'" >&2; exit 2 ;;
|
|
esac
|
|
|
|
CURL_ARGS=(
|
|
-sS -X "${METHOD}"
|
|
-H "DOLAPIKEY: ${DOLIBARR_SANDBOX_API_KEY}"
|
|
-H "Accept: application/json"
|
|
--max-time 30
|
|
)
|
|
if [[ -n "${BODY}" ]]; then
|
|
# curl --data supports '@file' to read a JSON body from a file.
|
|
CURL_ARGS+=( -H "Content-Type: application/json" --data "${BODY}" )
|
|
fi
|
|
|
|
BODY_FILE="$(mktemp -t dolwrite.XXXXXX)"
|
|
trap 'rm -f "${BODY_FILE}"' EXIT
|
|
|
|
HTTP_CODE=$(curl "${CURL_ARGS[@]}" \
|
|
-o "${BODY_FILE}" -w "%{http_code}" \
|
|
"${DOLIBARR_SANDBOX_URL}/api/index.php${API_PATH}")
|
|
|
|
cat "${BODY_FILE}"
|
|
if [[ "${HTTP_CODE}" -ge 400 ]]; then
|
|
echo "" >&2
|
|
echo "dol-write.sh: HTTP ${HTTP_CODE} on ${METHOD} ${API_PATH}" >&2
|
|
exit 1
|
|
fi
|