V6 — the first cross-system skill (under arcodange-* not dolibarr-*).
Closes the loop between what Dolibarr says (ERP-internal) and what the
bank actually saw.
What ships:
- arcodange-bank-reco/scripts/bank-curl.sh unified read-only wrapper for Qonto + Wise
- arcodange-bank-reco/scripts/bank-probe.sh auth + discovery (org slug, profile id, balances)
- arcodange-bank-reco/scripts/qonto-transactions Qonto txn lister with pagination + filters
- arcodange-bank-reco/scripts/wise-transactions Wise activity lister with --enrich for wire refs
- arcodange-bank-reco/scripts/bank-match.sh 3-bucket reconciliation (matched/bank-only/dol-only)
with internal Wise↔Qonto consolidation detection
- arcodange-bank-reco/scripts/bank-balance.sh live balances + Dolibarr cumulative-by-fk_account
The headline bank-curl.sh is SCA-aware (Wise RSA dance) even though we
don't end up using it: the EU statement endpoint is region-blocked
("Funding transfers and retrieving balance statements via API are not
supported except for accounts based in the US, Canada, Australia, New
Zealand, Singapore, and Malaysia" per Wise docs). The wrapper supports
SCA so when/if Wise opens it, we're ready.
The pivot that unblocked Wise incoming: /v1/profiles/{pid}/activities
(documented at https://docs.wise.com/api-reference/activity/activitylist.md)
returns ALL movements in a unified HTML-tagged feed, no SCA required.
Parsing strips the HTML and recovers structured amount/sign/currency.
CLI integration:
- bin/arcodange bank {probe,qonto-transactions,wise-transactions,match,balance,curl}
- dolibarr/SKILL.md catalogue + Pointers updated
- dolibarr/README.md env schema extended with QONTO_*, WISE_*
Live baseline findings to raise with the cohort review (captured in
examples/bank-match-2026-01-to-05.txt):
- Wise 2026-05-29 +2147 EUR Kissmetrics NOT YET in Dolibarr
- Qonto bank-only: MISTRAL.AI 172.68, CLAUDE.AI 180, URSSAF 493, FOUREZ +1000
- 6 movements matched cleanly across Jan-May 2026
- Wise→Qonto 5000 EUR consolidation on 2026-03-13 auto-detected as internal
- Live balance: Qonto 4191.54 + Wise 5308.25 = 9499.79 EUR
V7 candidates noted in SKILL.md out-of-scope: reference-based matching
via the Wise --enrich wire refs (FOR INVOICE FAC***), multi-row Dolibarr
sub-payment aggregation, smarter avoir cycle handling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
268 lines
11 KiB
Bash
Executable File
268 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# arcodange — read-only operational CLI for the Arcodange Dolibarr ERP.
|
||
#
|
||
# 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] 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
|
||
|
||
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
|
||
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
|
||
;;
|
||
|
||
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
|