Files
erp/test
arcodangeandClaude Opus 5 62fdd2325b feat(erp): les deux comptes bancaires tombent au centime — reprise FAC004/FAC006, réparation FAC009, cashbacks
Suite de la réconciliation ouverte par la question de l'opérateur. Après ce
passage, l'ERP et la banque disent le même chiffre sur les deux comptes :
Qonto 2 013,30 EUR et Wise 11 863,85 EUR, écart 0,00.

CE QUI A DÉBLOQUÉ LE RESTE. L'opérateur a établi que KissMetrics n'a JAMAIS reçu
la moindre facture — le client payait le forfait de 2 500 USD sans pièce. Rien
ne s'opposait donc à corriger des factures qu'on croyait entre ses mains.

FAC004 et FAC006 portaient toutes deux 2 145,92 EUR, le même montant repris tel
quel d'un cycle à l'autre, alors que la banque a reçu 2 147,00 EUR le 29/05 et
2 195,97 EUR le 25/06 : 2 500 USD à chaque fois, au taux du jour du paiement.
51,13 EUR d'encaissement manquaient aux livres. Leurs notes qualifiaient déjà
l'écart d'« écart de change » — défendable tant qu'on ne pouvait pas toucher aux
factures ; plus vrai maintenant qu'on le peut. Une facture, un règlement, une
ligne bancaire, le même chiffre.

RÉPARATION DE FAC009. `PUT /invoices/{id}/lines/{lid}` n'est PAS un PATCH : tout
champ absent du corps est remis à zéro. La correction appliquée ce matin ne
passait que subprice, pu_ht, qty et desc — `product_type` est donc retombé de 1
(service) à 0 (produit), faisant de FAC009 la seule ligne du registre typée
« produit » dans une société qui ne vend que des prestations et facture hors UE
sous l'art. 259-1° du CGI. Rétabli.

C'est le juge pré-gate qui l'a vu, sur la répétition de FAC004 et FAC006 — où le
même appel avait en plus effacé `desc` ENTIÈREMENT. Premier finding de la
journée qui apporte quelque chose de réel, et il aurait envoyé au client deux
factures sans désignation. Les charges de ligne sont désormais complètes.

test/deleteInvoicePayment.ts. Corriger une facture encaissée suppose de refaire
son règlement, et l'API REST ne sait pas le supprimer : `PUT
/invoices/{id}/payments` ne met à jour que son NUMÉRO. Deux verrous dans
l'interface, consignés dans le fichier : tant que la facture est marquée payée
le lien de suppression est simplement ABSENT — pas d'erreur, rien — il faut
d'abord la rouvrir ; et la confirmation est une boîte MODALE jQuery dont les
boutons n'ont ni name ni value, si bien qu'un sélecteur sur input[type=submit]
ne trouve rien et que la suppression n'a pas lieu, silencieusement. Sans
--payment, le script retrouve le règlement sur la fiche : coder en dur un
identifiant relevé en bac à sable est une façon commode de supprimer le mauvais.

ABONNEMENT ANTHROPIC D'AOÛT. 90,00 EUR du 19/08, reçu 2330-6710-2536 arrivé dans
books@ le 24/08. Second abonnement à usage interne, distinct du budget IA client.

CASHBACKS WISE — compte 768. Wise parle de « remise », mais le montant suit le
SOLDE et non les frais : 0,19 EUR sur ~510 EUR en mars, 4,97 EUR sur ~9 700 EUR
en août, soit ~0,5-0,6 % l'an, stable. C'est la rémunération d'un solde, donc un
produit financier. Le seul frais Wise de l'exercice est le forfait de 50 EUR du
26/01, sans rapport. Ni 763, qui vise les revenus de créances commerciales, ni
764, réservé aux valeurs mobilières de placement : 768.

RISTOURNE QONTO — compte 627 au crédit. L'opération est typée `qonto_fee` par la
banque elle-même, côté crédit : une remise sur un service reçu vient en
diminution de la charge, elle ne crée pas un produit.

PIÈGE CATALOGUÉ. bank-match.sh continuera d'afficher ces sept écritures en
BANK-ONLY : il ne rapproche que les RÈGLEMENTS de factures et ignore les
paiements divers. Les voir listées ne veut donc PAS dire qu'elles manquent —
c'est écrit dans known-patterns.json, sans quoi le prochain agent les
ressaisirait.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-24 13:12:49 +02:00
..
2025-08-08 17:57:56 +02:00

test — Dolibarr UI automation (Deno + Playwright)

A small Deno + Playwright POC that drives the Dolibarr admin UI in the fr-FR locale. Playwright fills the same forms a human admin would, so the automation works even where the REST API can't (e.g. generating an API key, which is encrypted with the instance's own DOLI_INSTANCE_UNIQUE_ID).

Layout

  • main.ts — original entrypoint (first install, company/display/module setup).
  • provisionSandbox.ts — entrypoint that provisions the erp-sandbox instance for the AI agent (enable REST API, create a write-scoped user, generate its API key).
  • scripts/login.ts — admin login / logout / whoami helpers.
  • scripts/forms.tsfillForm, toggleOnOff, CKEditor/ACE helpers.
  • scripts/admin/moduleSetup.tsconfigureModule, enableApiModule.
  • scripts/admin/userSetup.tscreateUser, assignRights, generateApiKey.

Configure

Copy .env.example to .env and fill it in. .env, *.key, and .ai_agent_sandbox.key are gitignored — never commit secrets.

cp .env.example .env

Lock the installer (after a fresh install via main.ts)

Dolibarr keeps its web installer reachable until an install.lock file exists. After a fresh install (the main.ts flow), create it in the target pod — for the sandbox:

kubectl -n erp-sandbox exec \
  "$(kubectl get pod -n erp-sandbox -l app.kubernetes.io/instance=erp-sandbox -o name)" -- \
  /bin/sh -c 'touch /var/www/documents/install.lock && chown www-data:www-data /var/www/documents/install.lock'

The path is the Dolibarr data root (/var/www/documents, a PVC) — that's where Dolibarr checks, and being on the PVC the lock persists across pod restarts. For prod, swap to -n erp -l app.kubernetes.io/instance=erp. A sandbox seeded from prod still needs this: the seed (see ../ops/sandbox/) copies the DB + documents/mycompany, not install.lock.

Provision the sandbox

Provisions erp-sandbox.arcodange.lab: enables the REST API module, creates the write-scoped ai_agent_sandbox user, grants it its write rights, and has Dolibarr generate the user's API key. The key is written to test/.ai_agent_sandbox.key (gitignored) — it is never printed.

cd test
deno run --allow-all provisionSandbox.ts

Populate .env from the erp-sandbox namespace secrets first. secretkv carries the app env (including DOLI_ADMIN_PASSWORD); vso-db-credentials carries the database password:

# Admin password (key DOLI_ADMIN_PASSWORD inside the secretkv secret)
kubectl get secret secretkv -n erp-sandbox \
  -o jsonpath='{.data.DOLI_ADMIN_PASSWORD}' | base64 -d

# Database password (key `password` inside vso-db-credentials)
kubectl get secret vso-db-credentials -n erp-sandbox \
  -o jsonpath='{.data.password}' | base64 -d

Set in .env:

DOLIBARR_ADDRESS=https://erp-sandbox.arcodange.lab
DOLI_ADMIN_LOGIN=admin
DOLI_ADMIN_PASSWORD="<from secretkv above>"
DOLI_DB_PASSWORD="<from vso-db-credentials above>"
# Optional — otherwise a random password is generated and only the API key emitted:
# AI_AGENT_SANDBOX_PASSWORD="<choose one>"

After it runs

The generated API key lands in test/.ai_agent_sandbox.key. Next step (not automated by this POC): load it into the dolibarr skill's sandbox config / Vault at kvv2/erp-sandbox/ai_agent.

Important

The sandbox Dolibarr is not installed/provisioned yet (empty DB, fresh install wizard). Until the install wizard has been completed against the sandbox, provisionSandbox.ts will not have a UI to drive, and the selectors in moduleSetup.ts / userSetup.ts are best-effort (Dolibarr 22 conventions, not verified live). Confirm them on the first real run.

Write rights granted

The ai_agent_sandbox user is created non-admin and granted (the authoritative list is WRITE_IDS in provisionSandbox.ts):

Module rights ids
facture lire=11, creer=12
societe lire=121, creer=122, client voir=262
societe contact lire=281, creer=282
fournisseur lire=1181, facture lire=1231, facture creer=1232
produit lire=31, creer=32
banque lire=111
user lire=251 — requis par le probe armé GET /users/info (checkpoint status/relink-env)