mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-21 22:18:08 +02:00
Merge branch 'claude/exciting-goldberg-wILkj' into main
Corrections Hit Parade / Gestion de stock (ventes nettes par magasin, tri stock négatif) et Grille (colonne INIT des gammes rechargée à chaque chargement, y compris sur cache; colonne Gamme conserve le snapshot).
This commit is contained in:
2 files changed
+31
-8
No files matched your search
@@ -47,10 +47,33 @@ export function invalidateGridRowsCache(codeFournisseur?: string) {
|
|||||||
gridRowsPending.clear();
|
gridRowsPending.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rafraîchit UNIQUEMENT la colonne INIT (codeGammeInit = gamme serveur courante)
|
||||||
|
* sur des lignes servies depuis le cache. La colonne Gamme (codeGamme) n'est pas
|
||||||
|
* touchée : elle conserve la valeur issue du snapshot. Ainsi l'état serveur (INIT)
|
||||||
|
* est toujours à jour à chaque chargement, même sur un hit du cache 10 min — et le
|
||||||
|
* marqueur "modifié" (Gamme ≠ INIT) se résorbe dès que le serveur rattrape la modif.
|
||||||
|
*/
|
||||||
|
async function refreshGammeInit(rows: ProductRow[], codeFournisseur: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const gammeMap = await pgGetGammesByFournisseur(codeFournisseur);
|
||||||
|
if (gammeMap.size === 0) return;
|
||||||
|
for (const row of rows) {
|
||||||
|
const live = gammeMap.get(row.codein);
|
||||||
|
if (live !== undefined) row.codeGammeInit = live as GammeCode;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[getProductRows] refreshGammeInit error:", (e as Error).message?.slice(0, 200));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function getProductRows(input: GetProductRowsInput): Promise<ProductRow[]> {
|
export async function getProductRows(input: GetProductRowsInput): Promise<ProductRow[]> {
|
||||||
const cacheKey = gridRowsCacheKey(input);
|
const cacheKey = gridRowsCacheKey(input);
|
||||||
const cached = gridRowsCache.get(cacheKey);
|
const cached = gridRowsCache.get(cacheKey);
|
||||||
if (!input.forceRefresh && cached && Date.now() - cached.createdAt < GRID_ROWS_CACHE_TTL_MS) {
|
if (!input.forceRefresh && cached && Date.now() - cached.createdAt < GRID_ROWS_CACHE_TTL_MS) {
|
||||||
|
// La colonne INIT doit refléter l'état serveur à CHAQUE chargement,
|
||||||
|
// y compris sur un hit de cache (les données lourdes restent, elles, cachées).
|
||||||
|
await refreshGammeInit(cached.rows, input.codeFournisseur);
|
||||||
return cached.rows;
|
return cached.rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,6 +337,9 @@ async function buildProductRows(input: GetProductRowsInput): Promise<ProductRow[
|
|||||||
await enrichWithNetworkMetrics(productMap);
|
await enrichWithNetworkMetrics(productMap);
|
||||||
|
|
||||||
// ─── Phase 9 : Restaurer gammes depuis dernier snapshot ──────────────
|
// ─── Phase 9 : Restaurer gammes depuis dernier snapshot ──────────────
|
||||||
|
// La colonne Gamme (codeGamme) conserve la valeur du snapshot telle quelle.
|
||||||
|
// La colonne INIT (codeGammeInit) reste, elle, l'état LIVE du serveur
|
||||||
|
// rechargé en Phase 6 — jamais écrasée ici.
|
||||||
try {
|
try {
|
||||||
const snaps = await db
|
const snaps = await db
|
||||||
.select()
|
.select()
|
||||||
@@ -327,7 +353,7 @@ async function buildProductRows(input: GetProductRowsInput): Promise<ProductRow[
|
|||||||
for (const [codein, change] of Object.entries(changes)) {
|
for (const [codein, change] of Object.entries(changes)) {
|
||||||
const product = productMap.get(codein);
|
const product = productMap.get(codein);
|
||||||
if (product && change.after) {
|
if (product && change.after) {
|
||||||
// codeGammeInit reste figé — seulement codeGamme est overridé
|
// codeGammeInit reste figé (état serveur) — seul codeGamme est overridé
|
||||||
product.codeGamme = change.after as GammeCode;
|
product.codeGamme = change.after as GammeCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -770,10 +770,9 @@ export interface HitParadeRow {
|
|||||||
* mouvements et fausser les sommes, et le LATERAL fournisseur ne s'exécute que
|
* mouvements et fausser les sommes, et le LATERAL fournisseur ne s'exécute que
|
||||||
* par article vendu (et non par ligne de mouvement).
|
* par article vendu (et non par ligne de mouvement).
|
||||||
*
|
*
|
||||||
* Filtre mntmvtttc <> 0 : mvtart contient des lignes genremvt=3 de quantité
|
* Cas vérifié (codein 487673, site 579, jan→juil 2026) : 31 ventes brutes,
|
||||||
* sans montant (corrections/régularisations, ex: codein 487673 site 579 —
|
* 7 retours clients → 24 net. L'ancien SUM(ABS(qtemvt)) affichait 38 (retours
|
||||||
* 38 affiché au lieu de 24 réels, CA exact). Une vente réelle a toujours un
|
* additionnés) ; SUM(-qtemvt) donne 24, identique à l'API mensuel officielle.
|
||||||
* montant TTC non nul ; les endpoints officiels de l'API les excluent aussi.
|
|
||||||
*/
|
*/
|
||||||
export async function pgGetHitParade(dateDebut: string, dateFin: string): Promise<HitParadeRow[]> {
|
export async function pgGetHitParade(dateDebut: string, dateFin: string): Promise<HitParadeRow[]> {
|
||||||
const result = await pgNoParallel(sql`
|
const result = await pgNoParallel(sql`
|
||||||
@@ -789,8 +788,6 @@ export async function pgGetHitParade(dateDebut: string, dateFin: string): Promis
|
|||||||
WHERE m.datmvt BETWEEN ${dateDebut}::date AND ${dateFin}::date
|
WHERE m.datmvt BETWEEN ${dateDebut}::date AND ${dateFin}::date
|
||||||
AND m.site IN ('292', '579')
|
AND m.site IN ('292', '579')
|
||||||
AND m.genremvt = 3
|
AND m.genremvt = 3
|
||||||
AND m.mntmvtttc IS NOT NULL
|
|
||||||
AND m.mntmvtttc <> 0
|
|
||||||
AND a.codein IS NOT NULL
|
AND a.codein IS NOT NULL
|
||||||
GROUP BY TRIM(a.codein::text), m.site
|
GROUP BY TRIM(a.codein::text), m.site
|
||||||
),
|
),
|
||||||
@@ -979,7 +976,7 @@ export async function pgGetStockNegatif(site?: string): Promise<PgStockNegatifRo
|
|||||||
WHERE cs.qte < 0
|
WHERE cs.qte < 0
|
||||||
AND cs.site IN ('292', '579')
|
AND cs.site IN ('292', '579')
|
||||||
${siteFilter}
|
${siteFilter}
|
||||||
ORDER BY cs.stockdispo ASC
|
ORDER BY cs.qte ASC
|
||||||
`);
|
`);
|
||||||
return result.rows as PgStockNegatifRow[];
|
return result.rows as PgStockNegatifRow[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user