From f2b89d391af4408df796d2e444ec8c2925b01f14 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Mon, 24 Aug 2026 10:47:41 +0200 Subject: [PATCH] =?UTF-8?q?feat(erp):=20corriger=20l'adresse=20=C3=A9lectr?= =?UTF-8?q?onique=20de=20la=20fiche=20soci=C3=A9t=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'en-tête de chaque facture portait `arcodange@gmail.com` alors que `static/config/company.json` porte `gabrielradureau@arcodange.fr`. La divergence ne se voit nulle part dans l'ERP : elle ne s'est découverte qu'en relisant un PDF destiné au client. Arbitrage de l'opérateur : l'adresse au nom de domaine de la société est la bonne. `GET /setup/company` répond 403 à tout scope agent (« open to admin users only ») et aucune route REST n'écrit les constantes MAIN_INFO_SOCIETE_*. La fiche n'est donc modifiable que par l'interface. Deux pièges consignés dans le script. Le champ s'appelle simplement `mail` — pas `MAIN_INFO_SOCIETE_MAIL`, malgré le nom de la constante qu'il alimente et malgré la convention que suivent tous ses voisins du même formulaire. Et la page de retour réaffiche la valeur soumise qu'elle ait été enregistrée ou non : le script RECHARGE la page avant de vérifier. Répété en bac à sable, puis appliqué en production. FAC009 et FAC010 régénérées. FAC005, FAC007 et FAC008 gardent l'ancienne adresse, délibérément : elles ont été émises sous celle-ci, et on ne réécrit pas un document daté pour le faire coller à un état postérieur. Co-Authored-By: Claude Opus 5 (1M context) --- test/setCompanyEmail.ts | 94 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 test/setCompanyEmail.ts diff --git a/test/setCompanyEmail.ts b/test/setCompanyEmail.ts new file mode 100644 index 0000000..79d8929 --- /dev/null +++ b/test/setCompanyEmail.ts @@ -0,0 +1,94 @@ +/* + Corrige l'adresse électronique portée par la fiche société. + + POURQUOI CE CHEMIN EXISTE. `GET /setup/company` répond 403 à tout scope agent + (« open to admin users only »), et aucune route REST n'écrit les constantes + MAIN_INFO_SOCIETE_*. La fiche société n'est donc modifiable que par + l'interface — celle que l'opérateur emprunterait lui-même. + + POURQUOI ÇA COMPTE. Cette adresse s'imprime en en-tête de CHAQUE facture. Une + divergence entre la fiche et `static/config/company.json` ne se voit nulle part + dans l'ERP : elle ne se découvre qu'en lisant un PDF déjà parti chez le client. + C'est ainsi qu'elle a été trouvée le 2026-08-24. + + Un piège : la page de retour réaffiche la valeur soumise, qu'elle ait été + enregistrée ou non. On RECHARGE la page avant de vérifier — jamais la page de + retour. + + Usage : + deno run -A test/setCompanyEmail.ts --email contact@exemple.fr [--dry-run] +*/ +import "load_dotenv"; +import { chromium } from "playwright"; +import login from "./scripts/login.ts"; +import { assertSandbox } from "./scripts/guard.ts"; + +const argv = Deno.args; +const pick = (f: string, d = "") => (argv.includes(f) ? argv[argv.indexOf(f) + 1] : d); + +const email = pick("--email"); +const dryRun = argv.includes("--dry-run"); +// Le champ s'appelle simplement `mail` — PAS `MAIN_INFO_SOCIETE_MAIL`, malgré +// le nom de la constante qu'il alimente et malgré la convention suivie par +// tous ses voisins du même formulaire (ADDRESS, ZIP, TOWN…). Relevé sur la +// page le 2026-08-24 ; ne pas le « corriger » de mémoire. +const FIELD = 'input[name="mail"]'; + +if (!email || !email.includes("@")) { + console.error("--email est requis, et doit ressembler à une adresse"); + Deno.exit(2); +} + +const dolibarrAddress = assertSandbox(); +console.log(`cible : ${dolibarrAddress}`); +console.log(`email : ${email}`); + +const browser = await chromium.launch({ headless: true }); +const context = await browser.newContext({ locale: "fr-FR" }); +const page = await context.newPage(); + +try { + await login.doAdminLogin({ + page, + dolibarrAddress, + adminCredentials: { + username: Deno.env.get("DOLI_ADMIN_LOGIN") || "undefined", + password: Deno.env.get("DOLI_ADMIN_PASSWORD") || "undefined", + }, + }); + + const edit = `${dolibarrAddress}/admin/company.php?action=edit`; + await page.goto(edit); + const field = page.locator(FIELD); + if (await field.count() === 0) { + console.error("champ `mail` absent de /admin/company.php — droits insuffisants ?"); + Deno.exit(1); + } + const before = await field.inputValue(); + console.log(`avant : ${before || "(vide)"}`); + if (before === email) { + console.log("déjà à cette valeur, rien à faire."); + Deno.exit(0); + } + if (dryRun) { + console.log("\n--dry-run : rien n'est soumis."); + Deno.exit(0); + } + + await field.fill(email); + await page.locator('input[type="submit"][name="save"], input[type="submit"][value*="Enregistrer"]') + .first().click(); + await page.waitForLoadState("networkidle"); + + // On ne croit pas la page de retour : on recharge et on relit le champ. + await page.goto(edit, { waitUntil: "networkidle" }); + const after = await page.locator(FIELD).inputValue(); + console.log(`après : ${after || "(vide)"}`); + if (after !== email) { + console.error("ÉCHEC — la valeur relue ne correspond pas à celle soumise."); + Deno.exit(1); + } + console.log("ok — fiche société mise à jour et relue."); +} finally { + await browser.close(); +}