--- name: dolibarr-invoice-audit description: Audit Arcodange invoices and the KissMetrics thirdparty record against contractual and French legal expectations. Three workflows — (1) list KissMetrics invoices with payment state and flag credit notes (AVC refs or negative HT); (2) audit one invoice end-to-end — JSON side (socid / dates / HT / TVA reverse charge / paye flag) AND PDF side (structural presence of mandatory mentions: forme juridique, SIRET, numéro TVA intracom, RCS Évry, NAF-APE, capital social, TVA 259-1° CGI, L.441-10 penalties at BCE+10 or 12,15 %, 40 € indemnité forfaitaire, L.123-22 / R.123-237); (3) audit the KissMetrics thirdparty record for address completeness and EIN presence (today idprof1..6 are empty — known gap to surface). Use when the user asks "is the M1 invoice emitted", "is invoice X paid", "vérifier les mentions obligatoires", "audit mentions légales", "état des paiements KissMetrics", "vérifier la fiche KissMetrics", or any cohort-review billing check on the Arcodange Dolibarr. Depends on the `dolibarr` skill for connection + permissions. SKIP for non-Arcodange ERPs, for non-KissMetrics clients (future skill), for write/correction tasks (the API is read-only — use the Dolibarr UI), and for Helm / Ansible deployment work. requires: bins: ["curl", "jq", "python3", "pdftotext"] auth: true --- # dolibarr-invoice-audit — KissMetrics billing audit This skill answers the questions the Arcodange × KissMetrics cohort review keeps asking: - Has the M-N invoice been emitted? Is it paid? - Does the PDF carry the mandatory French legal mentions? - Is the KissMetrics thirdparty record complete enough (EIN, full address)? It depends on the [dolibarr](../dolibarr/SKILL.md) base skill for the connection, the `.env` credentials, and the `voir_tous` permission setup. **If `list-km-invoices.sh` returns nothing, your first stop is the permission gotcha in the base skill — not this one.** ## Prerequisites - [`dolibarr/.env`](../dolibarr/.env) populated with a valid `DOLIBARR_API_KEY`, mode 600. - `ai_agent` in Dolibarr has the four `voir_tous` flags ticked (see [dolibarr/README.md](../dolibarr/README.md) step 2). - `pdftotext` installed locally: `brew install poppler`. All scripts source the base skill's `.env` via the relative path `../../dolibarr/scripts/dol-curl.sh`. Run them from anywhere. ## Workflow 1 — List KissMetrics invoices ```bash ./scripts/list-km-invoices.sh # all KM invoices, newest first ./scripts/list-km-invoices.sh --since 2026-04-01 # filter by invoice date ``` Output (live as of 2026-05-28, captured at [examples/list-km-invoices.txt](examples/list-km-invoices.txt)): ``` id ref date HT TVA TTC paid AVC pdf ---------------------------------------------------------------------------------------------------------------- 12 FAC002-CL0001002 2026-02-24 5100.00 0.00 5100.00 yes facture/FAC002-CL0001002/FAC002-CL0001002.pdf 13 FAC003-CL0001003 2026-02-24 2550.00 0.00 2550.00 yes facture/FAC003-CL0001003/FAC003-CL0001003.pdf 2 FAC001-CL00001 2026-01-30 510.00 0.00 510.00 yes facture/FAC001-CL00001/FAC001-CL00001.pdf 11 FAC001-CL0001001 2026-01-30 510.00 0.00 510.00 yes facture/FAC001-CL0001001/FAC001-CL0001001.pdf 10 AVC001-CL0001001 2026-01-30 -510.00 0.00 -510.00 yes AVOIR facture/AVC001-CL0001001/AVC001-CL0001001.pdf ``` Read the columns: - **paid** — Dolibarr's `paye=1` flag. `yes` means fully paid (not just "validated"). - **AVC** — `AVOIR` is shown when the ref starts with `AVC` *or* the HT is negative. The two should match; a discrepancy is a Dolibarr data issue. - **pdf** — `last_main_doc`. Pass to `audit-invoice.sh` indirectly via the invoice id. KissMetrics is the thirdparty with `socid=1` in this Dolibarr. If that ever changes, update `KM_SOCID` in the script. The TOTAL line sums HT/TTC across all visible rows (including AVOIRs, which subtract — handy for net revenue checks). ## Workflow 2 — Audit one invoice (JSON facts + PDF mandatory mentions) ```bash ./scripts/audit-invoice.sh 12 # FAC002-CL0001002 (current M1 candidate) echo "exit: $?" # 0 if every mention check passes, 1 otherwise ``` The script does two passes against `/invoices/{id}` and the attached PDF. ### JSON side Prints the structured facts the cohort review needs to spot-check against the contract: ``` Thirdparty : KissMetrics (socid=1) Invoice date : 2026-02-24 Due date : 2026-03-31 (cond_reglement_code=10DENDMONTH) Total HT : 5100.00000000 Total TVA : 0.00000000 (TVA=0 → reverse-charge expected; the 259-1° CGI mention MUST be on the PDF) Total TTC : 5100.00000000 Payment state : PAID PDF : facture/FAC002-CL0001002/FAC002-CL0001002.pdf ``` Cross-check against the cohort spec: - `socid` matches `KissMetrics` (id=1) - `date` is in the expected billing month - `total_ht` matches the contract line (10 days × 510 €/d = 5 100 € for FAC002, etc.) - `total_tva == 0` confirms the reverse-charge posture — the 259-1° CGI mention is then **mandatory** on the PDF - `paye == 1` if the invoice should be paid by now - `cond_reglement_code` tells you the payment-term shape (`10DENDMONTH` = "10 jours fin de mois") ### PDF side — mandatory-mention audit Downloads the PDF via `/documents/download`, base64-decodes, runs `pdftotext -layout`, then greps for **structural presence** of each mandatory mention. We deliberately do NOT match against the placeholder values in `static/config/company.json` — that file is a template; the real legal identifiers (SIRET `99965745500013`, TVA `FR00999657455`, forme **SARL**) live in Dolibarr's own setup and are what gets rendered into the PDF. The checklist: | Mention | Why it must be there | |--------------------------------------|----------------------------------------------| | Forme juridique (SARL / SAS / EURL / SA / SCI / SASU) | L.123-12 du Code de commerce (forme + capital) | | SIRET (14 digits) | Décret 2003-1196 | | Numéro TVA intracom (FR + 11) | Article 242 nonies A annexe II du CGI | | RCS / R.C.S. Évry | L.123-12 (identification au registre) | | NAF-APE code | Décret 2003-1196 | | Capital social | L.123-12 du Code de commerce | | TVA 259-1° CGI (reverse charge) | Article 259-1° CGI for services to non-EU B2B | | L.441-10 — BCE+10 or 12,15 % | Code de commerce L.441-10 (late-payment penalties) | | 40 € indemnité forfaitaire | Décret 2012-1115 (recouvrement) | | L.123-22 / R.123-237 | Identification supplementaire des actes commerciaux | Live result on invoice 12 (captured at [examples/audit-invoice-12.txt](examples/audit-invoice-12.txt) — exit code 1): ``` [OK] Forme juridique (SARL/SAS/EURL/SA/SCI/SASU) [OK] SIRET (14 digits) [OK] Numéro TVA intracom (FR + 11 chars) [OK] RCS / R.C.S. Évry [OK] NAF-APE code [XX] Capital social [OK] TVA 259-1° CGI / autoliquidation [XX] L.441-10 — BCE+10 or 12,15 % [XX] 40 € indemnité forfaitaire [XX] L.123-22 / R.123-237 Real identifiers extracted from the PDF (informational): SARL SIRET: 99965745500013 TVA: FR00999657455 APE: 6201Z 6 pass / 4 fail ``` This is a **real cohort-review finding**: the four `[XX]` lines are mandatory mentions absent from the live Dolibarr PDF template. To fix them, edit the Dolibarr invoice template (Setup → PDF model → Edit) and re-issue an avoir + new invoice. The skill itself doesn't touch the template — it just measures. Also note: `static/config/company.json` claims forme juridique = SAS but the real Dolibarr says **SARL**. The PDF is the source of truth; the `static/config/company.json` file appears to hold placeholders. ## Workflow 3 — Audit the KissMetrics thirdparty record ```bash ./scripts/audit-km-thirdparty.sh echo "exit: $?" # 0 if complete, 1 otherwise ``` Live result (captured at [examples/audit-km-thirdparty.txt](examples/audit-km-thirdparty.txt) — exit code 1): ``` [OK] name = 'KissMetrics' [OK] client flag = 'yes' [OK] country_code = 'US' [OK] state_id = '1167' [OK] address = '2850 34th Street North, 307' [OK] zip = '33713' [OK] town = 'St. Petersburg' [OK] email = 'evan@kissmetrics.io' [XX] phone = None [XX] url = None idprof1..idprof6 (EIN slot for non-EU customers): idprof1 = '' ... idprof6 = '' [XX] EIN present (any idprof populated) ``` Open follow-ups for the cohort review: - **EIN missing** — all `idprof1..6` empty. KissMetrics is a US entity; the EIN should land in `idprof1` or `idprof2` per Dolibarr convention. Either fill it in the Dolibarr UI or document why it's intentionally absent. - **Phone / URL missing** — minor; not legally required for B2B invoicing but useful for the customer file. ## Captured baselines [`examples/`](examples/) holds the live outputs of each script as of the V1 baseline (2026-05-28). To detect drift, re-run a script and `diff` against the captured file: ```bash ./scripts/audit-invoice.sh 12 > /tmp/new.txt 2>&1 diff examples/audit-invoice-12.txt /tmp/new.txt && echo "unchanged" ``` When you intentionally update the audit (new mention, fixed regex, etc.), re-capture the baselines as part of the same change. ## Adding a new mandatory mention In [`scripts/audit-invoice.sh`](scripts/audit-invoice.sh) find the block under `# Mandatory-mention audit — structural presence on the PDF:` and add: ```bash check "Your new mention label" "your-extended-regex-pattern" ``` Use `[[:space:]]`, `[Cc]apital`-style character classes, and prefer matching presence of the law reference (`L\\.?441-10`) over matching the verbatim French text — the wording in the PDF template can drift. If the new check should source a value from somewhere (e.g. company data), pull it via `jq` from a stable file (don't re-parse `static/config/company.json` — its values are placeholders). For Dolibarr-derived expectations, fetch them via `dol-curl.sh` at script start. ## Out of scope - **Writes / corrections.** The API key is read-only. To fix a missing mention or a wrong identifier, edit the Dolibarr invoice template / company setup in the UI and re-issue. - **Audits for clients other than KissMetrics.** The scripts hard-code `KM_SOCID=1`. A future `dolibarr-invoice-audit-` (or a parametrized rewrite) will handle other thirdparties. - **Recurring-template inspection** (the `Kiss Metrics Invoice` template that backs FAC002 / FAC003). Different endpoint shape (`/invoices/templates`), V2 work. - **Payment-state cross-reference with bank statements.** Future skill (likely `dolibarr-payments-state`).