- 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>
338 lines
15 KiB
Bash
Executable File
338 lines
15 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 (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
|