feat: flag products whose last registered supplier differs from current view

Instead of unreliable preference=1, use artfou1.no_id DESC (most recently
added artfou1 entry) to identify the last supplier for each product.
Shows an amber warning icon in the Désignation column when the last
supplier ≠ current supplier view, with tooltip showing who to attribute
the sales to.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Sonnet 4.6 committed 2026-04-03 11:55:42 +02:00
1 parent 6ef23de317
commit 6b26676836
4 files changed
+61 -31

No files matched your search

@@ -70,6 +70,8 @@ export async function getProductRows(input: GetProductRowsInput): Promise<Produc
codein: art.codein,
codeFournisseur: art.codefou ?? codeFournisseur,
nomFournisseur: art.nomfou ?? "",
fournisseurPrincipalCode: art.codefou_principal ?? undefined,
fournisseurPrincipalNom: art.nomfou_principal ?? undefined,
libelle1: art.libelle1 ?? "",
gtin: art.gtin ?? "",
reference: art.reference ?? "",
+39 -27
View File
@@ -13,7 +13,7 @@ import {
RowSelectionState,
} from "@tanstack/react-table";
import { useVirtualizer } from "@tanstack/react-virtual";
import { ChevronUp, ChevronDown, ChevronsUpDown, Copy, Check, Store, SlidersHorizontal, ShoppingCart, PackageOpen, Warehouse } from "lucide-react";
import { ChevronUp, ChevronDown, ChevronsUpDown, Copy, Check, Store, SlidersHorizontal, ShoppingCart, PackageOpen, Warehouse, AlertTriangle } from "lucide-react";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
@@ -430,33 +430,45 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) {
accessorKey: "libelle1",
header: "Désignation",
size: 280,
cell: ({ row }) => (
<div className="flex items-center gap-2 overflow-hidden w-full pr-1">
<span className="text-[13px] font-bold truncate flex-1" title={row.original.libelle1} style={{ color: "var(--text-primary)" }}>
{row.original.libelle1}
</span>
<div className="flex gap-1.5 shrink-0">
{row.original.workingStores.map((magasin) => {
const config = getStoreConfig(magasin);
return (
<div
key={magasin}
title={`Travaillé par : ${SITE_LABELS[magasin]?.nom ?? magasin}`}
className="px-1.5 py-0.5 rounded-md flex items-center gap-1 text-[10px] font-black border shadow-sm transition-transform hover:scale-110"
style={{
background: config.bg,
borderColor: config.border,
color: config.text,
}}
>
<Store className="w-2.5 h-2.5" strokeWidth={3} />
<span>{config.label}</span>
</div>
);
})}
cell: ({ row }) => {
const r = row.original;
const isDifferentSupplier = r.fournisseurPrincipalCode && r.fournisseurPrincipalCode !== r.codeFournisseur;
const warnTitle = isDifferentSupplier
? `Dernier fournisseur enregistré : ${r.fournisseurPrincipalNom ?? r.fournisseurPrincipalCode} (${r.fournisseurPrincipalCode}) — les ventes sont à attribuer à ce fournisseur`
: undefined;
return (
<div className="flex items-center gap-2 overflow-hidden w-full pr-1">
{isDifferentSupplier && (
<span title={warnTitle} className="shrink-0 flex items-center">
<AlertTriangle className="w-3.5 h-3.5 text-amber-500" />
</span>
)}
<span className="text-[13px] font-bold truncate flex-1" title={r.libelle1} style={{ color: "var(--text-primary)" }}>
{r.libelle1}
</span>
<div className="flex gap-1.5 shrink-0">
{r.workingStores.map((magasin) => {
const config = getStoreConfig(magasin);
return (
<div
key={magasin}
title={`Travaillé par : ${SITE_LABELS[magasin]?.nom ?? magasin}`}
className="px-1.5 py-0.5 rounded-md flex items-center gap-1 text-[10px] font-black border shadow-sm transition-transform hover:scale-110"
style={{
background: config.bg,
borderColor: config.border,
color: config.text,
}}
>
<Store className="w-2.5 h-2.5" strokeWidth={3} />
<span>{config.label}</span>
</div>
);
})}
</div>
</div>
</div>
),
);
},
},
{
accessorKey: "libelle3",