mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-19 21:18:16 +02:00
Qlik: Clear/SelectValues sur le CHAMP Periode, pas sur un objet de session
Le journal complet (enfin lisible) donne la cause, et elle est unique :
choix de « Periode » impossible :
{"code":-32601,"parameter":"Clear","message":"Method not found"}
extraction par date brute interrompue :
{"code":-32601,"parameter":"Clear","message":"Method not found"}
Clear et SelectValues sont des methodes de CHAMP. Je les appelais sur le
list object « Periode ». Consequence : la cartographie des periodes ET le
chemin d'extraction par date brute n'ont jamais tourne — d'ou la
couverture master annoncee a 0/12 alors que la master vit (total mesure :
11 591 263).
- Handle du champ « Periode » obtenu comme ceux d'Annee et de Date ; le
list object ne sert plus qu'a ENUMERER les valeurs.
- appliquerPeriode selectionne par texte et ne propage plus d'erreur :
une periode qu'on n'arrive pas a poser doit degrader le resultat, pas
interrompre l'extraction.
- Chaque candidat de la cartographie est isole : une valeur en echec est
notee et ignoree, elle n'emporte plus toute la carte.
Sonde fournisseur : « J009 » laissait 1 207 205 articles visibles, soit
tout le catalogue — la selection ne restreignait rien, et la
« couverture » de 97 % ne faisait que constater que le catalogue contient
nos codes. Une selection qui laisse plus de 3x les codes demandes est
desormais rejetee immediatement (elle coutait 3 x 24 s de lecture).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
This commit is contained in:
1 file changed
+80
-45
+80
-45
@@ -345,12 +345,27 @@ export async function fetchNetworkMetricsPlaywright(
|
|||||||
let dfh = -1;
|
let dfh = -1;
|
||||||
let fh = -1;
|
let fh = -1;
|
||||||
let yfh = -1;
|
let yfh = -1;
|
||||||
|
/**
|
||||||
|
* Handle du CHAMP « Période ».
|
||||||
|
*
|
||||||
|
* ⚠️ `Clear` et `SelectValues` sont des méthodes de **champ**. Les
|
||||||
|
* appeler sur un objet de session renvoie
|
||||||
|
* `{"code":-32601,"parameter":"Clear","message":"Method not found"}`,
|
||||||
|
* ce qui a fait avorter en production la cartographie des périodes ET
|
||||||
|
* le chemin d'extraction par date brute. Le list object ne sert donc
|
||||||
|
* plus qu'à ÉNUMÉRER les valeurs ; les sélections passent par ici.
|
||||||
|
*/
|
||||||
|
let pfh = -1;
|
||||||
const df = await rpc("GetField", { qFieldName: "Date" }, doc);
|
const df = await rpc("GetField", { qFieldName: "Date" }, doc);
|
||||||
dfh = (df.qReturn as { qHandle: number }).qHandle;
|
dfh = (df.qReturn as { qHandle: number }).qHandle;
|
||||||
try {
|
try {
|
||||||
const yf = await rpc("GetField", { qFieldName: "Année" }, doc);
|
const yf = await rpc("GetField", { qFieldName: "Année" }, doc);
|
||||||
yfh = (yf.qReturn as { qHandle: number }).qHandle;
|
yfh = (yf.qReturn as { qHandle: number }).qHandle;
|
||||||
} catch { yfh = -1; }
|
} catch { yfh = -1; }
|
||||||
|
try {
|
||||||
|
const pf = await rpc("GetField", { qFieldName: "Période" }, doc);
|
||||||
|
pfh = (pf.qReturn as { qHandle: number }).qHandle;
|
||||||
|
} catch { pfh = -1; }
|
||||||
if (codes.length) {
|
if (codes.length) {
|
||||||
const gf = await rpc("GetField", { qFieldName: "Article Code" }, doc);
|
const gf = await rpc("GetField", { qFieldName: "Article Code" }, doc);
|
||||||
fh = (gf.qReturn as { qHandle: number }).qHandle;
|
fh = (gf.qReturn as { qHandle: number }).qHandle;
|
||||||
@@ -898,6 +913,20 @@ export async function fetchNetworkMetricsPlaywright(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const visibles = await lireCodesVisibles();
|
const visibles = await lireCodesVisibles();
|
||||||
|
// Un vrai périmètre fournisseur est de l'ordre de grandeur des
|
||||||
|
// codes demandés. Mesuré en production : « J009 » laissait
|
||||||
|
// 1 207 205 articles visibles, soit tout le catalogue — la
|
||||||
|
// sélection n'avait rien restreint, et la « couverture » de 97 %
|
||||||
|
// ne faisait que constater que le catalogue contient nos codes.
|
||||||
|
if (visibles.size > attendus.size * 3) {
|
||||||
|
diag(
|
||||||
|
" champ « " + champ + " » = « " + fournisseurCode + " » → " + visibles.size +
|
||||||
|
" articles visibles pour " + attendus.size + " demandés : la sélection ne restreint rien, ignorée",
|
||||||
|
);
|
||||||
|
await rpc("Clear", {}, h);
|
||||||
|
await sleep(qlikSettleMs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let couverts = 0;
|
let couverts = 0;
|
||||||
for (const c of attendus) if (visibles.has(c)) couverts++;
|
for (const c of attendus) if (visibles.has(c)) couverts++;
|
||||||
const couverture = attendus.size > 0 ? couverts / attendus.size : 0;
|
const couverture = attendus.size > 0 ? couverts / attendus.size : 0;
|
||||||
@@ -917,36 +946,35 @@ export async function fetchNetworkMetricsPlaywright(
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Le list object « Période », gardé vivant : le détruire annulerait la
|
|
||||||
* sélection qu'il porte.
|
|
||||||
*/
|
|
||||||
let periodeLo: { qHandle: number } | null = null;
|
|
||||||
|
|
||||||
/** Valeur de `Période` à poser avant la passe d'une année donnée. */
|
/** Valeur de `Période` à poser avant la passe d'une année donnée. */
|
||||||
const periodeParAnnee = new Map<number, { elem: number | null; texte: string }>();
|
const periodeParAnnee = new Map<number, { texte: string | null }>();
|
||||||
|
|
||||||
/** Pose (ou efface, si `elem` est nul) la sélection `Période`. */
|
/**
|
||||||
const appliquerPeriode = async (elem: number | null): Promise<void> => {
|
* Pose (ou efface, si `texte` est nul) la sélection `Période`.
|
||||||
// Effacer doit rester possible même si le list object n'a jamais pu
|
*
|
||||||
// être créé : sinon la `Période` héritée de l'ouverture de l'app
|
* Passe par le HANDLE DU CHAMP, comme pour `Année` et `Date` : c'est la
|
||||||
// resterait posée sans que personne ne l'ait choisie.
|
* seule API qui expose `Clear` et `SelectValues`. Ne propage jamais
|
||||||
if (elem == null) {
|
* d'erreur — une `Période` qu'on n'arrive pas à poser doit dégrader le
|
||||||
await rpc("ClearField", { qFieldName: "Période" }, doc)
|
* résultat, pas interrompre l'extraction (leçon de production : un
|
||||||
.catch(() => (periodeLo ? rpc("Clear", {}, periodeLo.qHandle) : undefined));
|
* `Clear` en échec a fait avorter deux chemins entiers).
|
||||||
|
*/
|
||||||
|
const appliquerPeriode = async (texte: string | null): Promise<boolean> => {
|
||||||
|
if (pfh === -1) return texte == null;
|
||||||
|
try {
|
||||||
|
await rpc("Clear", {}, pfh);
|
||||||
await sleep(qlikSettleMs);
|
await sleep(qlikSettleMs);
|
||||||
return;
|
if (texte == null) return true;
|
||||||
}
|
const sel = await rpc("SelectValues", {
|
||||||
if (!periodeLo) return;
|
qFieldValues: [{ qText: texte }],
|
||||||
{
|
|
||||||
await rpc("SelectListObjectValues", {
|
|
||||||
qPath: "/qListObjectDef",
|
|
||||||
qValues: [elem],
|
|
||||||
qToggleMode: false,
|
qToggleMode: false,
|
||||||
qSoftLock: true,
|
qSoftLock: true,
|
||||||
}, periodeLo.qHandle);
|
}, pfh);
|
||||||
|
await sleep(qlikSettleMs);
|
||||||
|
return sel.qReturn !== false;
|
||||||
|
} catch (e) {
|
||||||
|
diag(" sélection Période « " + (texte ?? "(aucune)") + " » impossible : " + String((e as Error)?.message || e));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
await sleep(qlikSettleMs);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1053,10 +1081,6 @@ export async function fetchNetworkMetricsPlaywright(
|
|||||||
* sans année revient à juger « Année à date » sur la seule
|
* sans année revient à juger « Année à date » sur la seule
|
||||||
* année en cours (6/12) et à la rejeter à tort.
|
* année en cours (6/12) et à la rejeter à tort.
|
||||||
*/
|
*/
|
||||||
// Le list object doit survivre : le détruire annulerait la
|
|
||||||
// sélection qu'il porte. On le garde pour `appliquerPeriode`.
|
|
||||||
periodeLo = { qHandle: lo.qHandle };
|
|
||||||
|
|
||||||
// ─── Une `Période` PAR ANNÉE ─────────────────────────────
|
// ─── Une `Période` PAR ANNÉE ─────────────────────────────
|
||||||
//
|
//
|
||||||
// C'est le point que la version globale manquait : la bonne
|
// C'est le point que la version globale manquait : la bonne
|
||||||
@@ -1082,30 +1106,41 @@ export async function fetchNetworkMetricsPlaywright(
|
|||||||
await sleep(qlikSettleMs);
|
await sleep(qlikSettleMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
const candidats: Array<{ texte: string; elem: number | null }> = [
|
// `null` = état sans `Période`, candidat comme un autre.
|
||||||
{ texte: "(aucune)", elem: null },
|
const candidats: Array<string | null> = [null, ...valeurs.map((v) => v.texte)];
|
||||||
...valeurs.map((v) => ({ texte: v.texte, elem: v.elem as number | null })),
|
let meilleure: { texte: string | null; servis: string[] } | null = null;
|
||||||
];
|
|
||||||
let meilleure: { texte: string; elem: number | null; servis: string[] } | null = null;
|
|
||||||
for (const c of candidats) {
|
for (const c of candidats) {
|
||||||
await appliquerPeriode(c.elem);
|
const libelle = c ?? "(aucune)";
|
||||||
const mois = await mesurerCouverture();
|
// Un candidat qui échoue ne doit pas emporter toute la
|
||||||
const servis = mois.fenetre.filter((m) => attendusAnnee.includes(m));
|
// cartographie : on le note et on passe au suivant.
|
||||||
diag(
|
let servis: string[] = [];
|
||||||
" Période « " + c.texte + " » → sert " + servis.length + "/" + attendusAnnee.length +
|
try {
|
||||||
" mois attendus " + JSON.stringify(servis) + " ; master exposée : " + resume(mois.tous),
|
const pose = await appliquerPeriode(c);
|
||||||
);
|
if (!pose && c !== null) {
|
||||||
|
diag(" Période « " + libelle + " » → non sélectionnable, ignorée");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const mois = await mesurerCouverture();
|
||||||
|
servis = mois.fenetre.filter((m) => attendusAnnee.includes(m));
|
||||||
|
diag(
|
||||||
|
" Période « " + libelle + " » → sert " + servis.length + "/" + attendusAnnee.length +
|
||||||
|
" mois attendus " + JSON.stringify(servis) + " ; master exposée : " + resume(mois.tous),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
diag(" Période « " + libelle + " » → erreur " + String((e as Error)?.message || e));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!meilleure || servis.length > meilleure.servis.length) {
|
if (!meilleure || servis.length > meilleure.servis.length) {
|
||||||
meilleure = { texte: c.texte, elem: c.elem, servis };
|
meilleure = { texte: c, servis };
|
||||||
}
|
}
|
||||||
if (servis.length >= attendusAnnee.length) break; // année complète
|
if (servis.length >= attendusAnnee.length) break; // année complète
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meilleure) {
|
if (meilleure) {
|
||||||
periodeParAnnee.set(annee, { elem: meilleure.elem, texte: meilleure.texte });
|
periodeParAnnee.set(annee, { texte: meilleure.texte });
|
||||||
for (const m of meilleure.servis) couvertsGlobal.add(m);
|
for (const m of meilleure.servis) couvertsGlobal.add(m);
|
||||||
diag(
|
diag(
|
||||||
" → année " + annee + " sera extraite sous Période « " + meilleure.texte + " » (" +
|
" → année " + annee + " sera extraite sous Période « " + (meilleure.texte ?? "(aucune)") + " » (" +
|
||||||
meilleure.servis.length + "/" + attendusAnnee.length + " mois)",
|
meilleure.servis.length + "/" + attendusAnnee.length + " mois)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1762,8 +1797,8 @@ export async function fetchNetworkMetricsPlaywright(
|
|||||||
// une année révolue (cf. choisirPeriode).
|
// une année révolue (cf. choisirPeriode).
|
||||||
const periodeAnnee = periodeParAnnee.get(selectYear);
|
const periodeAnnee = periodeParAnnee.get(selectYear);
|
||||||
if (periodeAnnee) {
|
if (periodeAnnee) {
|
||||||
console.log("[qlik-pw] (mois) Période « " + periodeAnnee.texte + " » pour l'année " + selectYear);
|
console.log("[qlik-pw] (mois) Période « " + (periodeAnnee.texte ?? "(aucune)") + " » pour l'année " + selectYear);
|
||||||
await appliquerPeriode(periodeAnnee.elem);
|
await appliquerPeriode(periodeAnnee.texte);
|
||||||
}
|
}
|
||||||
|
|
||||||
await rpc("Clear", {}, yfh);
|
await rpc("Clear", {}, yfh);
|
||||||
|
|||||||
Reference in new issue
Block a user