The two V9 follow-ups, both proven live on the sandbox. - creditnote-create.sh: `kind:"supplier"` makes an avoir fournisseur on /supplierinvoices (type=2 + fk_facture_source, carries ref_supplier); default customer path unchanged. Proven: customer AVC002 (-240) + supplier AVF2026001 (-144, ref_supplier carried, linked to source, validated). - bank-accounts.sh + `arcodange sandbox accounts`: list bank accounts (id/label/ bank) so a payment can pick its account_id. Needs `banque lire` (rights 111), now added to the provisioner's WRITE_IDS so fresh runs include it; the existing ai_agent_sandbox user was granted it live. GET /bankaccounts now returns the 3 accounts (QONTO, WISE EURO, Compte Courant Asso). - SKILL.md: supplier-avoir example + accounts helper + updated banque-lire note. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
369 lines
16 KiB
Bash
Executable File
369 lines
16 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# arcodange — operational CLI for the Arcodange Dolibarr ERP. Read-only on prod;
|
||
# host-guarded WRITE ops on the sandbox via `arcodange sandbox ...`.
|
||
#
|
||
# Usage: arcodange <command> [subcommand] [args]
|
||
#
|
||
# Run `arcodange help` for the full command list.
|
||
#
|
||
# This is a thin dispatcher: every subcommand delegates to a script under
|
||
# .claude/skills/<skill>/scripts/. The skills (markdown SKILL.md files)
|
||
# remain the source of behaviour documentation; this CLI is the human-
|
||
# friendly entry point so you don't have to spell out 5-component paths.
|
||
|
||
set -euo pipefail
|
||
|
||
# --- Locate the project root ----------------------------------------------
|
||
# Strategy:
|
||
# 1. git rev-parse --show-toplevel (works when CWD is inside the repo).
|
||
# 2. Walk up from this script's directory to find a sibling .claude/skills.
|
||
# Either approach handles being run from a worktree without surprises.
|
||
|
||
if SROOT=$(git rev-parse --show-toplevel 2>/dev/null) && [[ -d "${SROOT}/.claude/skills" ]]; then
|
||
:
|
||
else
|
||
SROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
if [[ ! -d "${SROOT}/.claude/skills" ]]; then
|
||
echo "arcodange: cannot find project root (no .claude/skills/ found)" >&2
|
||
exit 2
|
||
fi
|
||
fi
|
||
|
||
SKILLS="${SROOT}/.claude/skills"
|
||
DOLC="${SKILLS}/dolibarr/scripts/dol-curl.sh"
|
||
|
||
# --- Help text -----------------------------------------------------------
|
||
|
||
usage() {
|
||
cat <<'EOF'
|
||
arcodange — read-only Arcodange Dolibarr CLI.
|
||
|
||
USAGE
|
||
arcodange <command> [subcommand] [args...]
|
||
|
||
COMMANDS
|
||
|
||
tva French TVA monthly preparation
|
||
collect [--year|--since|--until] TVA collectée by month × rate (CA3 A1/A4/E2)
|
||
collect-detail [--year|--since|--until] Per-line audit, customer side
|
||
deductible [--year|--since|--until] TVA déductible by month × rate (CA3 19/20/17+24)
|
||
deductible-detail [--year|--since|--until] Per-line audit, supplier side
|
||
summary [--year|--since|--until] Composite CA3-ready monthly summary
|
||
|
||
invoice Customer invoices (KissMetrics)
|
||
list [--since YYYY-MM-DD] Table of KM invoices with payment state
|
||
audit <invoice-id> JSON facts + PDF mandatory-mention audit
|
||
|
||
thirdparty Clients + suppliers completeness
|
||
audit <socid> Country-aware audit for one thirdparty
|
||
audit-all [--clients-only|--suppliers-only] Audit every visible thirdparty
|
||
|
||
payments Cash receipts (KissMetrics-side)
|
||
state [--since YYYY-MM-DD] Per-invoice TTC vs payments reconciliation
|
||
timeline [--year|--since|--until] Payment timeline with cumulative balance
|
||
by-month [--year|--all-clients] Monthly aggregation
|
||
|
||
templates Recurring invoice templates
|
||
list [--max-id N] Enumerate templates (probes ids)
|
||
inspect <template-id> Full template audit with health checks
|
||
|
||
snapshot [--out FILE|--print-only] Bundle full read-only state into one JSON
|
||
|
||
bank Bank-side data (Qonto + Wise) + Dolibarr reconciliation
|
||
probe Auth + discovery (org slug, profile id, balance ids)
|
||
qonto-transactions [--month|--since|--until] Qonto transactions table (incoming + outgoing)
|
||
wise-transactions [--month|--since|--until|--type|--enrich] Wise activities (incoming + outgoing)
|
||
match [--month|--since|--until|--window-days N|--enrich] Match bank ↔ Dolibarr (split buckets)
|
||
balance Live balances + Dolibarr cross-check per fk_account
|
||
curl <qonto|wise> <path> Raw read-only curl through bank-curl.sh
|
||
|
||
email Supplier-invoice emails from the Zoho mailbox
|
||
list [--folder|--limit|--candidates-only|--all-folders] List candidates
|
||
inspect <messageId> [--folder|--save-pdf|--json] Parse PDFs + draft Dolibarr entry
|
||
curl <path> Raw read-only curl through zoho-curl.sh
|
||
|
||
sandbox WRITE ops on erp-sandbox ONLY (host-guarded; JSON on stdin)
|
||
thirdparty Create a client/supplier fiche
|
||
invoice Customer/supplier invoice + product/service lines
|
||
payment Record a règlement on a validated invoice
|
||
creditnote Create an avoir — customer or supplier (kind)
|
||
accounts List bank accounts (id/label) to pick account_id
|
||
write <METHOD> <path> [body] Raw host-guarded write
|
||
|
||
promote Replay a reviewed change-set sandbox -> prod (ADR-0003)
|
||
plan <manifest.json> Human-readable review of the change-set
|
||
apply <manifest.json> [--target sandbox|prod] Replay it (prod is key+confirm gated)
|
||
|
||
whoami GET /users/info — confirm auth
|
||
ping GET /status — liveness + Dolibarr version
|
||
curl <path> Raw read-only curl through dol-curl.sh
|
||
|
||
help [command] Show this help (or subcommand help)
|
||
|
||
CREDENTIALS
|
||
Reads .claude/skills/dolibarr/.env (mode 600, gitignored). See
|
||
.claude/skills/dolibarr/README.md for one-time setup.
|
||
|
||
EXAMPLES
|
||
arcodange ping
|
||
arcodange whoami
|
||
arcodange invoice list
|
||
arcodange invoice audit 12
|
||
arcodange tva summary --year 2026
|
||
arcodange thirdparty audit-all
|
||
arcodange snapshot --out /tmp/erp.json
|
||
echo '{"name":"OVH","role":"supplier"}' | arcodange sandbox thirdparty
|
||
EOF
|
||
}
|
||
|
||
# --- Dispatch ------------------------------------------------------------
|
||
|
||
cmd="${1:-help}"
|
||
shift || true
|
||
|
||
case "${cmd}" in
|
||
|
||
tva)
|
||
sub="${1:-help}"; shift || true
|
||
case "${sub}" in
|
||
collect) exec "${SKILLS}/dolibarr-tva-reconciliation/scripts/tva-by-month.sh" "$@" ;;
|
||
collect-detail) exec "${SKILLS}/dolibarr-tva-reconciliation/scripts/tva-line-detail.sh" "$@" ;;
|
||
deductible) exec "${SKILLS}/dolibarr-tva-deductible/scripts/deductible-by-month.sh" "$@" ;;
|
||
deductible-detail) exec "${SKILLS}/dolibarr-tva-deductible/scripts/deductible-line-detail.sh" "$@" ;;
|
||
summary) exec "${SKILLS}/dolibarr-tva-summary/scripts/tva-summary.sh" "$@" ;;
|
||
help|-h|--help)
|
||
cat <<'EOF'
|
||
arcodange tva — French TVA monthly preparation.
|
||
|
||
collect TVA collectée by month × rate (CA3 A1/A4/E2)
|
||
collect-detail Per-line audit, customer side
|
||
deductible TVA déductible by month × rate (CA3 19/20/17+24)
|
||
deductible-detail Per-line audit, supplier side
|
||
summary CA3-ready monthly net summary (collectée − déductible)
|
||
|
||
All accept --year YYYY, --since YYYY-MM-DD, --until YYYY-MM-DD.
|
||
EOF
|
||
;;
|
||
*) echo "arcodange tva: unknown subcommand '${sub}' (try 'arcodange tva help')" >&2; exit 2 ;;
|
||
esac
|
||
;;
|
||
|
||
invoice)
|
||
sub="${1:-help}"; shift || true
|
||
case "${sub}" in
|
||
list) exec "${SKILLS}/dolibarr-invoice-audit/scripts/list-km-invoices.sh" "$@" ;;
|
||
audit) exec "${SKILLS}/dolibarr-invoice-audit/scripts/audit-invoice.sh" "$@" ;;
|
||
help|-h|--help)
|
||
cat <<'EOF'
|
||
arcodange invoice — customer (KissMetrics) invoice operations.
|
||
|
||
list [--since YYYY-MM-DD] Table of KM invoices with payment state
|
||
audit <invoice-id> JSON facts + PDF mandatory-mention audit
|
||
EOF
|
||
;;
|
||
*) echo "arcodange invoice: unknown subcommand '${sub}' (try 'arcodange invoice help')" >&2; exit 2 ;;
|
||
esac
|
||
;;
|
||
|
||
thirdparty)
|
||
sub="${1:-help}"; shift || true
|
||
case "${sub}" in
|
||
audit) exec "${SKILLS}/dolibarr-thirdparty-completeness/scripts/audit-thirdparty.sh" "$@" ;;
|
||
audit-all) exec "${SKILLS}/dolibarr-thirdparty-completeness/scripts/audit-all-thirdparties.sh" "$@" ;;
|
||
help|-h|--help)
|
||
cat <<'EOF'
|
||
arcodange thirdparty — clients + suppliers completeness.
|
||
|
||
audit <socid> Country-aware audit for one thirdparty
|
||
audit-all [--clients-only|--suppliers-only] Audit every visible thirdparty
|
||
EOF
|
||
;;
|
||
*) echo "arcodange thirdparty: unknown subcommand '${sub}'" >&2; exit 2 ;;
|
||
esac
|
||
;;
|
||
|
||
payments)
|
||
sub="${1:-help}"; shift || true
|
||
case "${sub}" in
|
||
state) exec "${SKILLS}/dolibarr-payments-state/scripts/km-payment-state.sh" "$@" ;;
|
||
timeline) exec "${SKILLS}/dolibarr-payments-state/scripts/km-payment-timeline.sh" "$@" ;;
|
||
by-month) exec "${SKILLS}/dolibarr-payments-state/scripts/payments-by-month.sh" "$@" ;;
|
||
help|-h|--help)
|
||
cat <<'EOF'
|
||
arcodange payments — KissMetrics cash-receipt tracking.
|
||
|
||
state [--since YYYY-MM-DD] Per-invoice TTC vs payments reconciliation
|
||
timeline [--year|--since|--until] Payment timeline with cumulative balance
|
||
by-month [--year|--all-clients] Monthly aggregation
|
||
EOF
|
||
;;
|
||
*) echo "arcodange payments: unknown subcommand '${sub}'" >&2; exit 2 ;;
|
||
esac
|
||
;;
|
||
|
||
templates)
|
||
sub="${1:-help}"; shift || true
|
||
case "${sub}" in
|
||
list) exec "${SKILLS}/dolibarr-recurring-templates/scripts/list-templates.sh" "$@" ;;
|
||
inspect) exec "${SKILLS}/dolibarr-recurring-templates/scripts/inspect-template.sh" "$@" ;;
|
||
help|-h|--help)
|
||
cat <<'EOF'
|
||
arcodange templates — recurring invoice templates.
|
||
|
||
list [--max-id N] Enumerate templates (probes ids)
|
||
inspect <template-id> Full audit with health checks
|
||
EOF
|
||
;;
|
||
*) echo "arcodange templates: unknown subcommand '${sub}'" >&2; exit 2 ;;
|
||
esac
|
||
;;
|
||
|
||
snapshot)
|
||
exec "${SKILLS}/dolibarr-data-snapshot/scripts/snapshot.sh" "$@"
|
||
;;
|
||
|
||
bank)
|
||
sub="${1:-help}"; shift || true
|
||
case "${sub}" in
|
||
probe) exec "${SKILLS}/arcodange-bank-reco/scripts/bank-probe.sh" "$@" ;;
|
||
qonto-transactions) exec "${SKILLS}/arcodange-bank-reco/scripts/qonto-transactions.sh" "$@" ;;
|
||
wise-transactions) exec "${SKILLS}/arcodange-bank-reco/scripts/wise-transactions.sh" "$@" ;;
|
||
match) exec "${SKILLS}/arcodange-bank-reco/scripts/bank-match.sh" "$@" ;;
|
||
balance) exec "${SKILLS}/arcodange-bank-reco/scripts/bank-balance.sh" "$@" ;;
|
||
curl)
|
||
if [[ $# -lt 2 ]]; then
|
||
echo "arcodange bank curl: usage: bank curl <qonto|wise> <path>" >&2; exit 2
|
||
fi
|
||
exec "${SKILLS}/arcodange-bank-reco/scripts/bank-curl.sh" "$@"
|
||
;;
|
||
help|-h|--help)
|
||
cat <<'EOF'
|
||
arcodange bank — bank-side data (Qonto + Wise) and Dolibarr reconciliation.
|
||
|
||
probe Auth + discovery (org slug, profile id, balance ids)
|
||
qonto-transactions [--month|--since|--until] Qonto transactions table
|
||
wise-transactions [--month|--since|--until|--type|--enrich] Wise activities (incoming + outgoing)
|
||
match [--month|--since|--until|--window-days N] Match bank ↔ Dolibarr (3 buckets)
|
||
balance Live balances + Dolibarr cross-check per fk_account
|
||
curl <qonto|wise> <path> Raw read-only curl through bank-curl.sh
|
||
|
||
Requires QONTO_LOGIN, QONTO_SECRET_KEY, QONTO_ORG_SLUG, WISE_API_TOKEN,
|
||
WISE_PROFILE_ID in .env. See arcodange-bank-reco/SKILL.md for setup.
|
||
EOF
|
||
;;
|
||
*) echo "arcodange bank: unknown subcommand '${sub}' (try 'arcodange bank help')" >&2; exit 2 ;;
|
||
esac
|
||
;;
|
||
|
||
email)
|
||
sub="${1:-help}"; shift || true
|
||
case "${sub}" in
|
||
list) exec "${SKILLS}/arcodange-email-ingest/scripts/email-list.sh" "$@" ;;
|
||
inspect) exec "${SKILLS}/arcodange-email-ingest/scripts/email-inspect.sh" "$@" ;;
|
||
curl) exec "${SKILLS}/arcodange-email-ingest/scripts/zoho-curl.sh" "$@" ;;
|
||
help|-h|--help)
|
||
cat <<'EOF'
|
||
arcodange email — supplier-invoice ingestion from the Zoho mailbox.
|
||
|
||
list [--folder PATH|--limit N|--candidates-only|--all-folders]
|
||
List messages (default: /Inbox/books)
|
||
inspect <messageId> [--folder PATH|--save-pdf DIR|--json]
|
||
Parse PDF attachments, propose Dolibarr supplier-invoice draft
|
||
curl <path> Raw read-only call through zoho-curl.sh
|
||
|
||
Requires ZOHO_CLIENT_ID, ZOHO_CLIENT_SECRET, ZOHO_REFRESH_TOKEN, ZOHO_DC in .env.
|
||
See arcodange-email-ingest/SKILL.md for OAuth setup.
|
||
EOF
|
||
;;
|
||
*) echo "arcodange email: unknown subcommand '${sub}' (try 'arcodange email help')" >&2; exit 2 ;;
|
||
esac
|
||
;;
|
||
|
||
sandbox)
|
||
sub="${1:-help}"; shift || true
|
||
case "${sub}" in
|
||
thirdparty) exec "${SKILLS}/dolibarr-sandbox-write/scripts/thirdparty-create.sh" "$@" ;;
|
||
invoice) exec "${SKILLS}/dolibarr-sandbox-write/scripts/invoice-create.sh" "$@" ;;
|
||
payment) exec "${SKILLS}/dolibarr-sandbox-write/scripts/payment-record.sh" "$@" ;;
|
||
creditnote) exec "${SKILLS}/dolibarr-sandbox-write/scripts/creditnote-create.sh" "$@" ;;
|
||
accounts) exec "${SKILLS}/dolibarr-sandbox-write/scripts/bank-accounts.sh" "$@" ;;
|
||
write) exec "${SKILLS}/dolibarr-sandbox-write/scripts/dol-write.sh" "$@" ;;
|
||
help|-h|--help)
|
||
cat <<'EOF'
|
||
arcodange sandbox — WRITE operations against erp-sandbox (rehearsal ONLY).
|
||
Every write goes through dol-write.sh, which REFUSES any non-sandbox host.
|
||
Each subcommand reads a JSON object on stdin (or a file path arg).
|
||
|
||
thirdparty client/supplier fiche
|
||
echo '{"name":"OVH","role":"supplier"}' | arcodange sandbox thirdparty
|
||
invoice customer/supplier invoice with product/service lines (+ ref_supplier)
|
||
echo '{"socid":42,"validate":true,"lines":[{"desc":"X","qty":1,"price_ht":100,"tva":20,"type":"service"}]}' | arcodange sandbox invoice
|
||
payment règlement on a validated invoice
|
||
echo '{"invoice_id":19,"mode":"VIR","account_id":1}' | arcodange sandbox payment
|
||
creditnote avoir (credit note) referencing a source invoice
|
||
echo '{"socid":42,"source_invoice":19,"validate":true,"lines":[...]}' | arcodange sandbox creditnote
|
||
write raw host-guarded write arcodange sandbox write POST /thirdparties '{"name":".."}'
|
||
|
||
Needs .claude/skills/dolibarr-sandbox-write/.env (DOLIBARR_SANDBOX_URL + _API_KEY).
|
||
See dolibarr-sandbox-write/SKILL.md.
|
||
EOF
|
||
;;
|
||
*) echo "arcodange sandbox: unknown subcommand '${sub}' (try 'arcodange sandbox help')" >&2; exit 2 ;;
|
||
esac
|
||
;;
|
||
|
||
promote)
|
||
sub="${1:-help}"; shift || true
|
||
case "${sub}" in
|
||
plan) exec "${SKILLS}/dolibarr-sandbox-write/scripts/promote-plan.sh" "$@" ;;
|
||
apply) exec "${SKILLS}/dolibarr-sandbox-write/scripts/promote-apply.sh" "$@" ;;
|
||
help|-h|--help)
|
||
cat <<'EOF'
|
||
arcodange promote — replay a reviewed sandbox change-set onto a target.
|
||
|
||
A manifest is a JSON array of write ops with symbolic refs (@name) instead of
|
||
ids, so the same file replays on sandbox or prod (an invoice refs @tp1, its
|
||
just-created thirdparty). See dolibarr-sandbox-write/examples/promote-manifest.json.
|
||
|
||
plan <manifest.json> Human-readable review of the change-set
|
||
apply <manifest.json> [--target sandbox|prod]
|
||
Replay it (sandbox = rehearse; prod = real)
|
||
|
||
PROD apply is gated: requires DOLIBARR_PROD_WRITE_KEY + ARCO_PROMOTE_CONFIRM=
|
||
I-UNDERSTAND-THIS-WRITES-PROD in the environment (the prod key is never stored).
|
||
EOF
|
||
;;
|
||
*) echo "arcodange promote: unknown subcommand '${sub}' (try 'arcodange promote help')" >&2; exit 2 ;;
|
||
esac
|
||
;;
|
||
|
||
whoami)
|
||
exec "${DOLC}" /users/info
|
||
;;
|
||
|
||
ping)
|
||
exec "${DOLC}" /status
|
||
;;
|
||
|
||
curl)
|
||
if [[ $# -lt 1 ]]; then
|
||
echo "arcodange curl: missing path. Example: arcodange curl /invoices/12" >&2
|
||
exit 2
|
||
fi
|
||
exec "${DOLC}" "$@"
|
||
;;
|
||
|
||
help|-h|--help|"")
|
||
if [[ $# -gt 0 ]]; then
|
||
exec "$0" "$1" help
|
||
fi
|
||
usage
|
||
;;
|
||
|
||
*)
|
||
echo "arcodange: unknown command '${cmd}'" >&2
|
||
echo " Try 'arcodange help' for the list." >&2
|
||
exit 2
|
||
;;
|
||
esac
|