mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-17 04:09:39 +02:00
Portage de tout ce qui, dans la Grille, ne dépend ni de la base FF ni de Qlik : - types.rs : ProductRow et types associés, JSON identique à JSON.stringify (camelCase, champs facultatifs omis, entiers sans décimale, NaN → null) et relecture sans perte des payloads grid_rows écrits par la version Node ; - store.rs : grid_rows (upsert par lots de 200 + purge, instantané, report des gammes, requêtes filtrées/triées/paginées via QueryBuilder et liste blanche de tri, fiche, fournisseurs, fraîcheur, nomenclatures) ; - rows_cache.rs : cache mémoire 10 min des lignes calculées (clé, LRU, patchGridRowsCache, invalidation, calculs en cours, compléments par magasin) ; - network_trend.rs, months.rs, store_patch.rs : logique pure testée ; - gammes_a_valider.rs, enregistrer.rs, snapshots.rs : actions getSnapshots, deleteSnapshot, saveSnapshot, saveDraftChanges, getGammesAValider, rejeterGammesAValider (validation zod reproduite, mêmes messages) ; - exports.rs : /api/export/excel et /api/export/modified-gammes (rust_xlsxwriter, mêmes feuilles, styles, largeurs et formats) ; - api_schemas.rs, zod.rs : schémas /api/v1, issues zod 4 à l'identique ; - api_v1.rs, openapi.rs : /api/v1/nomenclatures et /api/v1/openapi.json (document identique octet pour octet à celui de la version Node). Écart volontaire : listGridNomenclatures groupe par position (GROUP BY 1) ; la requête drizzle répétait left(code3, $n) avec un autre paramètre et échouait systématiquement sous PostgreSQL. Test d'intégration (grid_rows, snapshots, gammes à valider) activé par GRID_TEST_DATABASE_URL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LG2LLJuswrrTTPKJmunEVf
71 lines
2.4 KiB
Rust
71 lines
2.4 KiB
Rust
//! Les gammes d'assortiment : code, nom et couleurs — source unique
|
|
//! (port de `src/lib/gammes.ts`, utilisé par la validation `/api/v1` et le
|
|
//! document OpenAPI).
|
|
|
|
pub struct Gamme {
|
|
pub code: &'static str,
|
|
/// Nom court : « Cœur », « Sortie »…
|
|
pub nom: &'static str,
|
|
/// Phrase d'explication pour les infobulles.
|
|
pub description: &'static str,
|
|
/// Classes de pastille (fond, bordure, texte) lisibles en clair comme en sombre.
|
|
pub classes: &'static str,
|
|
}
|
|
|
|
pub const GAMMES: [Gamme; 5] = [
|
|
Gamme {
|
|
code: "A",
|
|
nom: "Cœur",
|
|
description: "Produit permanent, au cœur de l'assortiment.",
|
|
classes: "border-emerald-500/40 bg-emerald-500/10 text-emerald-800 dark:text-emerald-300",
|
|
},
|
|
Gamme {
|
|
code: "B",
|
|
nom: "Complémentaire",
|
|
description: "Produit qui complète l'offre du rayon.",
|
|
classes: "border-blue-500/40 bg-blue-500/10 text-blue-800 dark:text-blue-300",
|
|
},
|
|
Gamme {
|
|
code: "C",
|
|
nom: "Saisonnier",
|
|
description: "Produit présent selon la saison.",
|
|
classes: "border-amber-500/40 bg-amber-500/10 text-amber-800 dark:text-amber-300",
|
|
},
|
|
Gamme {
|
|
code: "Y",
|
|
nom: "En veille",
|
|
description: "Produit mis en attente : il n'est plus commandé pour l'instant.",
|
|
classes: "border-violet-500/40 bg-violet-500/10 text-violet-800 dark:text-violet-300",
|
|
},
|
|
Gamme {
|
|
code: "Z",
|
|
nom: "Sortie",
|
|
description: "Produit à retirer de l'assortiment.",
|
|
classes: "border-rose-500/40 bg-rose-500/10 text-rose-800 dark:text-rose-300",
|
|
},
|
|
];
|
|
|
|
/// Pastille d'un produit sans gamme.
|
|
pub const CLASSES_SANS_GAMME: &str = "border-slate-400/40 bg-slate-500/10 text-slate-700 dark:text-slate-300";
|
|
|
|
/// Codes de gamme, dans l'ordre de [`GAMMES`].
|
|
pub fn codes_gamme() -> Vec<&'static str> {
|
|
GAMMES.iter().map(|g| g.code).collect()
|
|
}
|
|
|
|
pub fn trouver_gamme(code: Option<&str>) -> Option<&'static Gamme> {
|
|
let code = code.filter(|c| !c.is_empty())?;
|
|
let c = super::js::js_trim(code);
|
|
GAMMES.iter().find(|g| g.code == c)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn coherent_avec_le_socle() {
|
|
assert_eq!(super::codes_gamme(), crate::domain::CODES_GAMME.to_vec());
|
|
assert_eq!(super::trouver_gamme(Some(" Z ")).unwrap().nom, "Sortie");
|
|
assert!(super::trouver_gamme(None).is_none());
|
|
}
|
|
}
|