/* 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(); }