feat(erp): corriger l'adresse électronique de la fiche société
L'en-tête de chaque facture portait `[email protected]` alors que `static/config/company.json` porte `[email protected]`. 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) <[email protected]>
This commit is contained in:
@@ -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 [email protected] [--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();
|
||||
}
|
||||
Reference in New Issue
Block a user