V2 in the dolibarr-* family. Three workflows:
- km-payment-state.sh: per-invoice reconciliation (TTC vs sum of
payments) with OK / PARTIAL / UNPAID / OVERPAID classification.
More honest than the `paye` boolean for deferred-cycle agreements.
- km-payment-timeline.sh: all KM payments sorted by date with
cumulative balance — the foundation for cohort-review deferred
9-month-cycle tracking (actual cash receipts vs contractual schedule).
- payments-by-month.sh: monthly aggregation, KM-scoped by default
or --all-clients for accounting basis.
Also updates dolibarr/SKILL.md endpoint catalogue with
/invoices/{id}/payments (note the date-as-string vs unix-epoch quirk)
and /bankaccounts, plus captures the corresponding examples.
V1 baseline of live data: KM is fully reconciled across 5 invoices
(1 avoir + 4 regular), 8160 € total cash receipts spread Feb/Mar/Apr 2026,
all on WISE EURO (BE).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.8 KiB
paye=1 flag alone); (2) full payment timeline sorted by date with cumulative balance, suitable for cohort-review deferred-cycle tracking (compare actual cash receipts against the contractual schedule); (3) monthly cash-receipt aggregation, scoped to KissMetrics by default or --all-clients for global accounting. Surfaces credit notes (AVOIRs as negative payments) and the bank-account catalogue (QONTO, WISE EURO, G.RADUREAU CCA). Use when the user asks "état des paiements KM", "is invoice X paid", "qui doit combien", "suivi des paiements deferred", "deferred 9-month cycle", "cash receipts par mois", "réconciliation factures vs paiements", or any cohort-review billing-cycle check. Depends on the dolibarr skill for connection. SKIP for the legal-mention audit (handled by dolibarr-invoice-audit), for write operations (the API is read-only — payments are recorded by the Dolibarr UI), and for full bank-statement reconciliation (which would require parsing bank exports outside Dolibarr — out of scope here, V3 candidate).
requires:
bins: ["curl", "jq", "python3"]
auth: true
dolibarr-payments-state — KissMetrics cash-receipt tracking
This skill answers "who owes Arcodange what, and when did they pay?" against the live Dolibarr. It's the natural follow-up to dolibarr-invoice-audit: that one validates that a given invoice was emitted correctly; this one validates the money landed.
Depends on the dolibarr base skill for the connection, the .env, and the voir_tous permission flags. The existing Factures → Voir toutes flag from V1 is sufficient — payments are nested under invoices (/invoices/{id}/payments), and there's no /payments list-all endpoint (501 Not Implemented).
Why a dedicated skill?
The paye flag on /invoices/{id} is a single boolean. The cohort review needs the actual reconciliation: how much was paid, when, by what means, on which bank account. Three things the flag can't tell you:
- Partial payments. An invoice with one of three deferred installments paid still has
paye=0butsum(payments) > 0. - Credit notes. An AVOIR shows up in the payments list as a negative amount tied to the canceled invoice's pair — the timeline is the easy way to see the chain.
- Cash-receipt timing. For deferred-cycle agreements, the date a payment hit the bank matters more than whether the invoice is fully paid.
Prerequisites
dolibarr/.envpopulated, mode 600.ai_agentpermissions from dolibarr/README.md. The existing Facturesvoir_touscovers payments.
Workflow 1 — Per-invoice payment state (reconciliation)
./scripts/km-payment-state.sh # all KM invoices
./scripts/km-payment-state.sh --since 2026-02-01 # window
echo "exit: $?" # 0 if every invoice reconciles, 1 otherwise
Live output (captured at examples/km-payment-state.txt):
id ref date HT paid balance state payments
----------------------------------------------------------------------------------------------------------------------------------
12 FAC002-CL0001002 2026-02-24 5100.00 5100.00 0.00 OK 5100.00@2026-03-12(VIR)
13 FAC003-CL0001003 2026-02-24 2550.00 2550.00 0.00 OK 2550.00@2026-04-20(VIR)
2 FAC001-CL00001 2026-01-30 510.00 510.00 0.00 OK 510.00@2026-02-05(VIR)
11 FAC001-CL0001001 2026-01-30 510.00 510.00 0.00 OK 510.00@2026-02-05(VIR)
10 AVC001-CL0001001 2026-01-30 -510.00 -510.00 0.00 OK -510.00@2026-02-05(VIR)
----------------------------------------------------------------------------------------------------------------------------------
TOTAL 8160.00 8160.00 0.00
# 5 invoice(s), 0 not fully reconciled
state column legend:
OK—|balance| < 0.005(reconciled to the cent).UNPAID— no payments recorded at all.PARTIAL— payments < TTC.OVERPAID— payments > TTC. Worth investigating (overpayment, duplicate posting, currency drift).
Reconciles against TTC (the contractual payable), not HT, because that's what the customer actually sends. For KM under 259-1° CGI (TVA=0), HT == TTC, so the distinction doesn't matter; for any future French customer with TVA > 0, it would.
Workflow 2 — Payment timeline (cohort deferred-cycle tracking)
./scripts/km-payment-timeline.sh # all-time
./scripts/km-payment-timeline.sh --year 2026 # one year
./scripts/km-payment-timeline.sh --since 2026-02-01 --until 2026-04-30
Live output (captured at examples/km-payment-timeline.txt):
date invoice amount type ref bank_line running
--------------------------------------------------------------------------------------------------------------
2026-02-05 AVC001-CL0001001 -510.00 VIR REC003-CL00001 bl=7 -510.00
2026-02-05 FAC001-CL0001001 510.00 VIR REC002-####002 bl=6 0.00
2026-02-05 FAC001-CL00001 510.00 VIR REC001-####001 bl=5 510.00
2026-03-12 FAC002-CL0001002 5100.00 VIR REC003-CL00002 bl=19 5610.00
2026-04-20 FAC003-CL0001003 2550.00 VIR REC001-CL0001001 bl=24 8160.00
--------------------------------------------------------------------------------------------------------------
# 5 payment(s), net cash receipts: 8160.00
# Bank accounts known to this Dolibarr (label / IBAN-leading):
# id=1 ref=QON1 label=QONTO country=FR iban=FR761695800001...
# id=2 ref=WIS2 label=WISE EURO country=BE iban=BE58967543094979
# id=3 ref=CCA1 label=G.RADUREAU Compte Courant Asso country=FR iban=-
bank_line note. The fk_bank_line value on a payment is the row id in the bank-line ledger, not the bank-account id. Dolibarr's API doesn't expose /bankaccounts/{id}/lines to ai_agent in V1, so we surface the bank-account catalogue at the bottom of the timeline and leave the per-payment lookup as a manual / V3 task. For KissMetrics specifically, the IBAN on the FAC002 PDF (BE58 9675 4309 4979) matches WIS2 (WISE EURO) — all KM receipts land there.
Deferred-cycle interpretation (cohort review context):
- The contract's deferred-payment schedule (9 months for KM) defines an expected cash-receipt timeline.
- This workflow gives the actual one.
diffbetween them surfaces overdue cycles or unexpected early payments.
Workflow 3 — Monthly cash receipts
./scripts/payments-by-month.sh # KM only (default)
./scripts/payments-by-month.sh --year 2026
./scripts/payments-by-month.sh --all-clients # global, for accounting
Live output (captured at examples/payments-by-month.txt):
# Monthly cash receipts — scope: KissMetrics (socid=1)
month count amount cumulative
--------------------------------------------------
2026-02 3 510.00 510.00
2026-03 1 5100.00 5610.00
2026-04 1 2550.00 8160.00
--------------------------------------------------
TOTAL 5 8160.00
The 2026-02 count of 3 (for a net 510 €) is the AVOIR + reissue cycle around FAC001-CL00001 — three movements, one net economic event. This is normal and reflects the legal correctness of the credit-note + new-invoice approach Arcodange uses for corrections.
Use --all-clients once Arcodange has more than KM on the books, for monthly accounting / TVA basis (next skill).
Captured baselines
examples/ holds the live outputs of each script as of the V1 baseline (2026-05-28). To detect drift:
./scripts/km-payment-state.sh > /tmp/new.txt 2>&1
diff examples/km-payment-state.txt /tmp/new.txt && echo "unchanged"
When the actual data legitimately changes (new invoice, new payment), re-capture the baselines in the same commit.
Adding a new check / column
The scripts share a common shape: pull invoices → pull per-invoice payments → reconcile in Python (heredoc, argv-passing tmpfile pattern — the pipe + heredoc-stdin collision is real on macOS bash 3.2, see V1 commit history for the trap). Patterns to follow:
- Sort by TTC reconciliation, not
payeflag (workflow 1 demonstrates this). - The payment
dateis aYYYY-MM-DD HH:MM:SSstring, not a unix epoch — different shape from/invoices/{id}.date. Don'tint()it. - AVOIRs have negative amounts; treat them as first-class. Don't filter them out — that hides the chain.
Out of scope
- Bank-statement reconciliation. Matching Dolibarr payments against actual bank exports (Qonto / Wise CSV) is a separate concern; would live in a V3
arcodange-bank-recoskill. - TVA basis calculation per month. Needs the HT × TVA rate per period — next skill:
dolibarr-tva-reconciliation. - Writes. Recording a payment is done in the Dolibarr UI (or via a script with a write-scoped API key — not in scope here).
- Multi-currency. Everything is EUR today. If a future contract is USD or other, the cross-multicurrency fields (
multicurrency_total_ttc) need to be added to the reconciliation.