Files
CollectFlow/src/features/grid/api/enregistrer-gammes.ts
T
Claude 9448e68509 API : affecter ou changer la gamme d'un produit
Deux endpoints d'écriture dans /api/v1 :
- PUT /products/{codein}/gamme { gamme, fournisseur? } pour un article ;
- POST /gammes { fournisseur, changes[] } pour plusieurs articles d'un
  fournisseur, en tout ou rien.

Ils passent par le même enregistrement que la Grille (extrait de
saveDraftChanges dans enregistrerGammes) : snapshot du fournisseur, cache
de la Grille et instantané grid_rows. Gamme validée contre la liste des
gammes (A, B, C, Y, Z) ; un article déjà dans la gamme demandée n'est pas
réécrit. Spec OpenAPI et page de connexion à l'API mises à jour ; la
légende des gammes de la spec, qui citait une gamme D inexistante, suit
désormais lib/gammes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VEXT61tGbsXHUDNBeghCXQ
2026-10-05 08:19:00 +00:00

81 lines
2.9 KiB
TypeScript

import "server-only";
import { db } from "@/db";
import { sessionSnapshots } from "@/db/schema";
import { eq, desc } from "drizzle-orm";
import { patchGridRowsCache } from "./get-product-rows";
import { updateGridRowsGamme } from "@/lib/grid-store";
export interface ChangementGamme {
codein: string;
/** Gamme avant la modification (état serveur), conservée pour l'historique. */
codeGammeBefore: string | null;
codeGamme: string;
}
export interface EnregistrementGammes {
codeFournisseur: string;
nomFournisseur?: string | null;
magasin: string;
changes: ChangementGamme[];
userId: number | null;
/** Libellé du snapshot, « Draft — date » par défaut. */
label?: string;
}
/**
* Enregistre des gammes pour un fournisseur — chemin unique de la Grille et de /api/v1.
*
* Les changements sont fusionnés par-dessus le dernier snapshot du fournisseur,
* puis reportés dans le cache mémoire de la Grille et dans l'instantané `grid_rows`.
* Lève une erreur si l'écriture du snapshot échoue.
*/
export async function enregistrerGammes(input: EnregistrementGammes): Promise<number> {
const { codeFournisseur, nomFournisseur, magasin, changes, userId } = input;
if (changes.length === 0) return 0;
// Charger le dernier snapshot existant pour merger les changements
const existing = await db
.select()
.from(sessionSnapshots)
.where(eq(sessionSnapshots.codeFournisseur, codeFournisseur))
.orderBy(desc(sessionSnapshots.createdAt))
.limit(1);
const prevChanges: Record<string, { before: string | null; after: string }> =
existing.length > 0
? (existing[0].changes as Record<string, { before: string | null; after: string }>)
: {};
// Merger les nouveaux changements par-dessus l'existant
const mergedChanges = { ...prevChanges };
for (const c of changes) {
mergedChanges[c.codein] = {
before: c.codeGammeBefore,
after: c.codeGamme,
};
}
// Upsert : insérer un nouveau snapshot "draft" pour ce fournisseur
await db.insert(sessionSnapshots).values({
userId,
codeFournisseur,
nomFournisseur: nomFournisseur ?? null,
magasin,
label: input.label ?? `Draft — ${new Date().toLocaleDateString("fr-FR")}`,
changes: mergedChanges,
summaryJson: null,
type: "snapshot",
});
// Les lignes en cache reçoivent les nouvelles gammes au lieu d'être jetées :
// l'invalidation imposait un recalcul complet (jusqu'à ~40 s) à la
// réouverture suivante. L'instantané lu par /api/v1 suit, sans bloquer.
patchGridRowsCache(codeFournisseur, changes);
void updateGridRowsGamme(codeFournisseur, changes).catch((e) =>
console.error("[enregistrerGammes] mise à jour grid_rows KO:", (e as Error).message?.slice(0, 200)),
);
return changes.length;
}