mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
fix Qlik month dimension selection
This commit is contained in:
1 parent
4ef0e9fdeb
commit
cd2e77e909
2 files changed
+123
-7
No files matched your search
@@ -108,6 +108,14 @@ Un seul cube `[Article Code, Mois]` couvre les 12 mois : ni passe N-1 ni
|
||||
rattrapage — c'est exactement ce qu'ils compensaient. Les totaux deviennent la
|
||||
**somme des 12 mois de la fenêtre**.
|
||||
|
||||
Sur l'app de production, les champs `Date`, `Date calendrier` et `Date_Key`
|
||||
existent mais leur sélection ne réduit pas `Sum(quantite)` : ils restent à
|
||||
100 % du total historique. L'extracteur essaie d'abord ces champs puis utilise
|
||||
la dimension maître `Mois` comme chemin fiable. Il crée un list object, retrouve
|
||||
les 12 valeurs `YYYY-MM`, les sélectionne par `qElemNumber`, puis exécute le
|
||||
cube mensuel. Cette méthode ne dépend ni du champ sous-jacent ni de son format
|
||||
dual et conserve le chemin rapide (une sélection articles + un cube paginé).
|
||||
|
||||
Garde-fous : « Quantité N » est incluse dans le cube pour **calibrage**, et le
|
||||
log compare les deux sur les mois de l'année en cours, seul périmètre où la
|
||||
master measure est juste :
|
||||
@@ -174,7 +182,7 @@ n'a aucun effet. Candidats essayés dans l'ordre : `QLIK_DATE_FIELD` (si défini
|
||||
Si aucun ne filtre, l'extraction échoue explicitement plutôt que de produire une
|
||||
fenêtre fausse. Chaque sélection d'un candidat rejeté est effacée avant l'essai
|
||||
suivant ; autrement un premier candidat à zéro contaminait tous les contrôles
|
||||
suivants.
|
||||
suivants. Avant cet échec, la dimension maître `Mois` est testée elle aussi.
|
||||
|
||||
## La passe de rattrapage a été supprimée
|
||||
|
||||
|
||||
+114
-6
@@ -631,6 +631,101 @@ export async function fetchNetworkMetricsPlaywright(
|
||||
return h;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sélectionne les 12 mois via la dimension maître « Mois ».
|
||||
*
|
||||
* Dans l'app Magasins Vision Consolidée, les champs Date exposés
|
||||
* existent mais sont dissociés des faits de vente : leur sélection
|
||||
* laisse Sum(quantite) à 100 %. La dimension maître utilisée par le
|
||||
* cube, elle, porte les vraies valeurs associées. Un list object
|
||||
* permet de sélectionner ses qElemNumber sans connaître le nom du
|
||||
* champ sous-jacent ni le format dual exact.
|
||||
*/
|
||||
const selectionnerFenetreViaDimensionMois = async (
|
||||
moisAttendus: Set<string>,
|
||||
): Promise<{ total: number; trouves: string[] } | null> => {
|
||||
let objet: { handle: number; id: string } | null = null;
|
||||
try {
|
||||
const cree = await rpcWithRetry("createMonthList", "CreateSessionObject", {
|
||||
qProp: {
|
||||
qInfo: { qType: "cf-month-list" },
|
||||
qListObjectDef: {
|
||||
qLibraryId: moisDimId,
|
||||
qShowAlternatives: true,
|
||||
qInitialDataFetch: [{ qTop: 0, qLeft: 0, qWidth: 1, qHeight: 100 }],
|
||||
},
|
||||
},
|
||||
}, doc);
|
||||
const ret = cree.qReturn as { qHandle: number; qGenericId?: string; qId?: string };
|
||||
objet = {
|
||||
handle: ret.qHandle,
|
||||
id: String(ret.qGenericId ?? ret.qId ?? ""),
|
||||
};
|
||||
|
||||
const layout = await rpcWithRetry("layoutMonthList", "GetLayout", {}, objet.handle);
|
||||
const lo = (layout.qLayout as {
|
||||
qListObject?: {
|
||||
qSize?: { qcy?: number };
|
||||
qDataPages?: Array<{
|
||||
qArea?: { qTop?: number };
|
||||
qMatrix?: Array<Array<{ qText?: string; qElemNumber?: number }>>;
|
||||
}>;
|
||||
};
|
||||
}).qListObject;
|
||||
const size = Number(lo?.qSize?.qcy) || 0;
|
||||
const cellules: Array<{ qText?: string; qElemNumber?: number }> = [];
|
||||
for (const page of lo?.qDataPages ?? []) {
|
||||
for (const row of page.qMatrix ?? []) if (row[0]) cellules.push(row[0]);
|
||||
}
|
||||
for (let top = cellules.length; top < size; top += 500) {
|
||||
const data = await rpcWithRetry("monthListData", "GetListObjectData", {
|
||||
qPath: "/qListObjectDef",
|
||||
qPages: [{ qTop: top, qLeft: 0, qWidth: 1, qHeight: Math.min(500, size - top) }],
|
||||
}, objet.handle);
|
||||
const matrix = (data.qDataPages as Array<{
|
||||
qMatrix?: Array<Array<{ qText?: string; qElemNumber?: number }>>;
|
||||
}>)?.[0]?.qMatrix ?? [];
|
||||
for (const row of matrix) if (row[0]) cellules.push(row[0]);
|
||||
if (matrix.length === 0) break;
|
||||
}
|
||||
|
||||
const parMois = new Map<string, number>();
|
||||
for (const cellule of cellules) {
|
||||
const mois = normMois(String(cellule.qText ?? "").trim());
|
||||
const elem = Number(cellule.qElemNumber);
|
||||
if (moisAttendus.has(mois) && Number.isInteger(elem) && elem >= 0) {
|
||||
parMois.set(mois, elem);
|
||||
}
|
||||
}
|
||||
const trouves = [...parMois.keys()].sort();
|
||||
console.log(
|
||||
"[qlik-pw][expr] dimension Mois : " + trouves.length + "/" + moisAttendus.size +
|
||||
" valeurs trouvées " + JSON.stringify(trouves),
|
||||
);
|
||||
if (trouves.length !== moisAttendus.size) return null;
|
||||
|
||||
const selection = await rpcWithRetry("selectMonthList", "SelectListObjectValues", {
|
||||
qPath: "/qListObjectDef",
|
||||
qValues: [...parMois.values()],
|
||||
qToggleMode: false,
|
||||
qSoftLock: true,
|
||||
}, objet.handle);
|
||||
console.log("[qlik-pw][expr] sélection dimension Mois → " + JSON.stringify(selection.qReturn));
|
||||
await sleep(qlikSettleMs);
|
||||
const total = await totalDeControle();
|
||||
return { total, trouves };
|
||||
} catch (e) {
|
||||
console.log("[qlik-pw][expr] sélection dimension Mois impossible : " + String((e as Error)?.message || e));
|
||||
return null;
|
||||
} finally {
|
||||
if (objet?.id) {
|
||||
try {
|
||||
await rpc("DestroySessionObject", { qId: objet.id }, doc);
|
||||
} catch { /* objet limité à la session */ }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const moisParExpressions = async (): Promise<boolean> => {
|
||||
const timings: Record<string, number> = {};
|
||||
const marqueOut = out.length;
|
||||
@@ -642,11 +737,11 @@ export async function fetchNetworkMetricsPlaywright(
|
||||
if (codes.length && fh !== -1) {
|
||||
await timed("clearCode", "Clear", () => rpc("Clear", {}, fh), timings);
|
||||
await sleep(qlikSettleMs);
|
||||
await timed("selectCodeAll", "SelectValues", () => rpc("SelectValues", {
|
||||
await timed("selectCodeAll", "SelectValues", () => rpcWithRetry("selectCodeAll", "SelectValues", {
|
||||
qFieldValues: codes.map((c) => ({ qText: c })),
|
||||
qToggleMode: false,
|
||||
qSoftLock: true,
|
||||
}, fh), timings);
|
||||
}, fh, () => { aggCode15Retries++; }), timings);
|
||||
await sleep(qlikSettleMs);
|
||||
}
|
||||
|
||||
@@ -665,6 +760,7 @@ export async function fetchNetworkMetricsPlaywright(
|
||||
|
||||
const allSerials = monthlyPayload.flatMap((m) => m.serials);
|
||||
const allYmd = monthlyPayload.flatMap((m) => m.ymd);
|
||||
const moisFenetre = new Set(monthlyPayload.map((m) => m.label));
|
||||
let champDate: string | null = null;
|
||||
let totalFenetre = 0;
|
||||
for (const cand of champsDateCandidats) {
|
||||
@@ -690,11 +786,24 @@ export async function fetchNetworkMetricsPlaywright(
|
||||
await rpc("Clear", {}, handleCand);
|
||||
await sleep(qlikSettleMs);
|
||||
}
|
||||
if (!champDate) {
|
||||
const selectionMois = await selectionnerFenetreViaDimensionMois(moisFenetre);
|
||||
const ratio = selectionMois && sansFiltre > 0 ? selectionMois.total / sansFiltre : 0;
|
||||
if (selectionMois) {
|
||||
console.log(
|
||||
"[qlik-pw][expr] dimension « Mois » → " + Math.round(selectionMois.total) +
|
||||
" (" + Math.round(ratio * 100) + "% du total sans filtre)",
|
||||
);
|
||||
}
|
||||
if (selectionMois && selectionMois.total > 0 && ratio < 0.995) {
|
||||
champDate = "dimension Mois";
|
||||
totalFenetre = selectionMois.total;
|
||||
}
|
||||
}
|
||||
if (!champDate) {
|
||||
console.error(
|
||||
"[qlik-pw][expr] aucun champ date ne filtre les faits (essayés : " +
|
||||
JSON.stringify(champsDateCandidats) + ") → impossible de borner la fenêtre 12 mois. " +
|
||||
"Forcer le bon champ avec QLIK_DATE_FIELD.",
|
||||
"[qlik-pw][expr] ni les champs date ni la dimension Mois ne filtrent les faits " +
|
||||
"(champs essayés : " + JSON.stringify(champsDateCandidats) + ") → impossible de borner la fenêtre 12 mois.",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -702,7 +811,6 @@ export async function fetchNetworkMetricsPlaywright(
|
||||
|
||||
// Seuls les mois de la fenêtre nous intéressent ; la dimension
|
||||
// Mois peut en exposer d'autres si une sélection déborde.
|
||||
const moisFenetre = new Set(monthlyPayload.map((m) => m.label));
|
||||
let totalQte = 0;
|
||||
let calibrees = 0, calibrOk = 0;
|
||||
let horsFenetre = 0;
|
||||
|
||||
Reference in new issue
Block a user