Files
erp/.claude/skills/dolibarr/SKILL.md
Gabriel Radureau bd90266372 add arcodange-bank-reco — Qonto + Wise reconciliation against Dolibarr
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>
2026-05-31 13:57:21 +02:00

13 KiB

name, description, requires
name description requires
dolibarr Reference + connection layer for the Arcodange Dolibarr ERP REST API at https://erp.arcodange.lab/. Documents authentication via DOLAPIKEY, the read-only `ai_agent` account's permission requirements (the `voir_tous` ACL trap that returns empty arrays or 404s instead of 403s on listing endpoints), the credentials layout at `.claude/skills/dolibarr/.env`, the `scripts/dol-curl.sh` wrapper, the catalogue of useful read endpoints (invoices, thirdparties, documents/download, products, status, users/info), and the common gotchas (mode-as-int, paye-vs-status, empty-vs-404-vs-403, unix-epoch dates). Use when setting up the connection, debugging an API call, looking up an endpoint, decoding a permission error, or as a dependency referenced by any `dolibarr-*` workflow skill. SKIP for Dolibarr admin / Helm chart / Ansible config (covered by `chart/` + `ansible/`), for any ERP other than Arcodange's, and for specific business workflows that have their own `dolibarr-<topic>` skill.
bins auth
curl
jq
python3
true

dolibarr — Arcodange ERP API platform reference

This is the base skill for talking to the Arcodange Dolibarr instance from Claude Code. It documents the connection, the permissions, the endpoint catalogue, and the gotchas. Each business workflow lives in its own dolibarr-<topic> sibling skill that depends on this one — V1 ships dolibarr-invoice-audit for the KissMetrics audit; future siblings will follow the same pattern (e.g. dolibarr-payments-state, dolibarr-tva-reconciliation).

The API key for ai_agent is read-only by design. Never attempt writes from any dolibarr-* skill — use the Dolibarr UI directly for corrections.

Quick start

From the repo root:

bin/arcodange whoami | jq -r .login           # → ai_agent
bin/arcodange ping                            # → {"success":{"code":200,"dolibarr_version":"22.0.4",...}}
bin/arcodange curl /invoices/12               # raw read-only call

The bin/arcodange CLI is the human entry point; under the hood it delegates to the same per-skill scripts. Direct script access still works:

./.claude/skills/dolibarr/scripts/dol-curl.sh /users/info | jq -r .login

If you see ai_agent, auth works. If not, check .env (next section) and dol-curl.sh's error message.

For a liveness check that needs no auth:

curl -s https://erp.arcodange.lab/api/index.php/status | jq .
# → {"success":{"code":200,"dolibarr_version":"22.0.4",...}}

Credentials

Schema of .claude/skills/dolibarr/.env (mode 600, gitignored):

DOLIBARR_URL=https://erp.arcodange.lab
DOLIBARR_API_KEY=<rotatable api key for ai_agent>
DOLIBARR_USER=ai_agent
DOLIBARR_PASSWORD=<used only for browser/UI login, very rarely>

The DOLAPIKEY is the only secret needed for API calls. DOLIBARR_USER/DOLIBARR_PASSWORD are kept here only so the operator has them at hand if they need to log into the Dolibarr UI to fix permissions.

Rules:

  • chmod 600 .env. Verify with stat -f %Lp .env600.
  • Never commit. The root .gitignore already excludes .env at any depth; redundant patterns (*.credentials, .claude/skills/**/.env) belt-and-braces it.
  • Never paste the key into chat. If a key leaks, rotate it from the Dolibarr UI: Users → ai_agent → API key → Generate new.
  • Every sibling dolibarr-* skill sources this file via ../dolibarr/.env. There is exactly one copy.

Permission gotcha — read this first

Dolibarr enforces TWO layers of access:

  1. Per-API-endpoint permissions (does the user's role allow /invoices?).
  2. Per-record ACL (can this user see THIS invoice/thirdparty?).

Layer 2 is the trap. When ai_agent has the endpoint permission but not the voir_tous flag, listing endpoints silently filter rows out:

  • GET /invoices returns [] with HTTP 200 (looks like "no invoices exist").
  • GET /thirdparties returns HTTP 404 with "No third parties found" (looks like "no thirdparties exist").
  • GET /thirdparties/{id} returns HTTP 403 with "Access not allowed for login ai_agent on this thirdparty" (this is the only honest error).

Diagnostic recipe. If a listing endpoint returns empty/404 while you expect data:

./scripts/dol-curl.sh /thirdparties/1

If you get a 403 with Access not allowed, the ACL is the cause (saved as a reference at examples/acl_403_thirdparty.json). Do not retry the call blindly. Fix the permissions, then retest.

Fix. In the Dolibarr UI (https://erp.arcodange.lab/ → Setup → Users & Groups → ai_agent → Permissions), tick:

  • Tiers → Lire les tiers
  • Tiers → Voir tous les tiers (et pas seulement ceux liés à l'utilisateur courant)
  • Factures → Lire les factures
  • Factures → Voir toutes les factures (et pas seulement celles liées à l'utilisateur courant)

Add the equivalent flags for any new module a future dolibarr-* skill needs (Paiements, Produits, …). The ai_agent user has no UI access by default, so voir_tous is the right grant — there's no sales-rep relationship to honor.

Endpoint catalogue

Read-only endpoints we've validated against this instance. Live captures are under examples/.

Endpoint HTTP Purpose Key params / gotchas Example
GET /status 200 Liveness + Dolibarr version No auth needed status.json
GET /users/info 200 Current API user (auth sanity) Returns login, id, rights tree users_info.json
GET /invoices 200 List invoices limit, sortfield=t.datef, sortorder=DESC, status=draft|unpaid|paid, sqlfilters invoices_list.json
GET /invoices/{id} 200 Invoice detail paye=1 is the canonical "paid" flag invoice_detail.json
GET /invoices/{id}/payments 200 Payments applied to one invoice Returns array of {amount, type, date, num, ref, fk_bank_line}. date is a string YYYY-MM-DD HH:MM:SS, NOT unix epoch like elsewhere. Amounts negative for AVOIRs. invoices_12_payments.json
GET /payments 501 (list-all not implemented) Returns "API not found". To enumerate payments, iterate invoices.
GET /bankaccounts / /{id} 200 List bank accounts / detail Returns ref, label, iban, country_code. fk_bank_line on a payment doesn't directly map to the account id — see dolibarr-payments-state skill for the lookup. bankaccounts_list.json
GET /thirdparties 200 List thirdparties mode MUST be integer (mode=1 = customers). String fails 400. (use /{id} below)
GET /thirdparties/{id} 200 Thirdparty detail KissMetrics is id=1 in this instance thirdparty_km.json
GET /products / /products/{id} 200 Product catalogue KM-audit is id=1, KM-cloud-devops is id=2 products_list.json
GET /supplierinvoices / /{id} 200 Supplier (fournisseur) invoices Lines are included inline on the detail endpoint (same shape as /invoices/{id}). Sub-endpoint /lines returns HTTP 403 for ai_agent — not needed since inline lines work. supplierinvoices_list.json · supplierinvoice_detail.json
GET /invoices/templates/{id} 200 Recurring invoice template detail No list endpoint — probe ids 1..N. Empty ids return HTTP 200 with id=null (sentinel for "doesn't exist"). Fields: ref, socid, frequency, unit_frequency, nb_gen_done, date_when, date_last_gen, suspended, auto_validate, lines. invoice_template_km.json
GET /documents/download 200 Download a stored document as base64 modulepart=facture&original_file=<REF>/<REF>.pdf (URL-encode slashes). Returns {filename, content-type, filesize, content} with content base64. document_download_meta.json

Not available on this account (intentionally): /setup/modules (admin-only), /invoices/templates (requires a path arg — different route shape; V2).

The dol-curl.sh wrapper

scripts/dol-curl.sh is a thin wrapper used by this skill and every sibling. It:

  • Sources ../.env (relative to itself), so it works regardless of the caller's CWD.
  • Fails loudly if .env is missing or DOLIBARR_URL / DOLIBARR_API_KEY is unset.
  • Prepends DOLAPIKEY and Accept: application/json headers + a 30s timeout.
  • Prints the response body to stdout.
  • On HTTP ≥ 400, also prints dol-curl.sh: HTTP <code> on <path> to stderr and exits 1 — so set -e in caller scripts surfaces the failure.
# Examples
./scripts/dol-curl.sh /users/info | jq .login
./scripts/dol-curl.sh '/invoices?limit=5&sortfield=t.datef&sortorder=DESC' | jq '.[].ref'
./scripts/dol-curl.sh /invoices/12 | jq '{ref, paye, total_ht, last_main_doc}'
./scripts/dol-curl.sh '/documents/download?modulepart=facture&original_file=FAC002-CL0001002%2FFAC002-CL0001002.pdf' \
  | jq -r .content | base64 -d > /tmp/inv.pdf

Common gotchas

  • mode is an integer. ?mode=1 works; ?mode=customer → HTTP 400 Invalid value specified for mode.
  • Empty list vs 404 vs 403. /invoices returns [] (200) on empty/permission-filtered. /thirdparties returns 404 "No third parties found". /thirdparties/{id} returns 403 if the ACL hides the row. All three can mean "no permission" — the diagnostic is to hit /thirdparties/1 directly (see Permission gotcha above).
  • Dates are usually unix epoch (seconds) on most objects: .date, .datef, .date_lim_reglement, .tms are integers.
    python3 -c "import datetime,sys; print(datetime.datetime.fromtimestamp(int(sys.argv[1])))" 1771887600
    # → 2026-02-24 00:00:00
    
    Exception: /invoices/{id}/payments returns .date as the string "YYYY-MM-DD HH:MM:SS", not an int. Check the field type before parsing.
  • paye is the paid flag, status is the workflow state. status=2 means "validated", paye=1 means "fully paid". They're independent — a status-2 invoice can still be unpaid.
  • last_main_doc paths are relative to Dolibarr's document root. For /documents/download, pass modulepart=facture + URL-encoded original_file=<dir>/<file> (encode the /).
  • array_options=[] vs {}. Dolibarr returns [] when no extrafields are set and {} when there are some. Treat both as "no custom fields" in Python.

Pointers

  • Workflow skill for invoice + thirdparty audits: dolibarr-invoice-audit.
  • Workflow skill for payment-state and cash-receipt tracking: dolibarr-payments-state.
  • Workflow skill for monthly TVA basis (CA3 / CA12 preparation): dolibarr-tva-reconciliation.
  • Workflow skill for recurring invoice templates: dolibarr-recurring-templates.
  • Workflow skill for point-in-time state archival: dolibarr-data-snapshot.
  • Workflow skill for thirdparty completeness audit (any client / supplier): dolibarr-thirdparty-completeness.
  • Workflow skill for supplier-side TVA déductible (CA3 lignes 19 / 20 / 17+24): dolibarr-tva-deductible.
  • Workflow skill for composite CA3-ready TVA summary (collectée + déductible + net): dolibarr-tva-summary.
  • Bank-side reconciliation (Qonto + Wise ↔ Dolibarr matching): arcodange-bank-reco.
  • Future workflow skills follow the dolibarr-<topic> convention (ERP-internal) or arcodange-<topic> (cross-system, like bank reconciliation). Each one depends on this skill for connection + permissions + endpoint reference; each one keeps its triggers focused on its specific business workflow.

Out of scope

  • Writes. The ai_agent key is read-only by design. For corrections (edit a thirdparty, void an invoice, fix a mention), use the Dolibarr UI.
  • Admin endpoints. /setup/* is gated to admins or to logins listed in API_LOGINS_ALLOWED_FOR_GET_MODULES; not available to ai_agent.
  • Helm chart / Ansible / Kubernetes operations. Covered by chart/, ansible/, and the project root README.md.