Files
erp/bin/arcodange
Gabriel Radureau 79286650d7 feat(skills,cli): sandbox avoir (credit note) + arcodange sandbox CLI group
- dolibarr-sandbox-write/scripts/creditnote-create.sh: create a customer avoir
  (credit note) — a customer invoice type=2 referencing source_invoice
  (fk_facture_source); amounts negative, validates to an AVC… ref. Proven live.
- bin/arcodange: new `sandbox` command group wiring the write scripts —
  `arcodange sandbox {thirdparty|invoice|payment|creditnote|write}` (JSON on
  stdin). Header + usage updated to note the CLI now does host-guarded sandbox
  writes (still read-only on prod).
- SKILL.md: avoir workflow + CLI notes.

Verified end-to-end through the CLI: thirdparty -> invoice (FAC…) -> avoir
(AVC…, total_ttc -240, fk_facture_source set); host-guard intact via the CLI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-29 21:04:49 +02:00

338 lines
15 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 (credit note) of a source invoice
write <METHOD> <path> [body] Raw host-guarded write
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" "$@" ;;
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
;;
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