feat: implement DLC tracking page with EAN lookup and CRUD operations for product expiration management

This commit is contained in:
Michael committed 2026-04-02 09:46:59 +02:00
1 parent 44950d2239
commit a91027c10b
3 files changed
+53 -11

No files matched your search

+18 -4
View File
@@ -128,19 +128,33 @@ export default function DlcPage() {
eanDebounceRef.current = setTimeout(async () => {
setEanLookupLoading(true);
try {
// Étape 1 : récupérer l'article par EAN
const res = await fetch(`/api/ffnancy/articles?ean=${encodeURIComponent(gencodeValue)}&limit=1`, { credentials: 'include' });
if (!res.ok) return;
const data = await res.json();
const article = data.articles?.[0];
if (!article) return;
// Toujours mettre à jour le nom du produit
form.setValue("productName", article.libelle1, { shouldValidate: true });
// Matcher le fournisseur par codefou en priorité, sinon par nom
if (article.codefou_principal) {
// Étape 2 : chercher le fournisseur de la dernière entrée en stock
let codefouToMatch = article.codefou_principal;
try {
const mvtRes = await fetch(
`/api/ffnancy/mouvements/entrees?artNoId=${article.no_id}&limit=1&dateDebut=2000-01-01`,
{ credentials: 'include' }
);
if (mvtRes.ok) {
const mvtData = await mvtRes.json();
const lastEntree = mvtData.entrees?.[0];
if (lastEntree?.codefou) codefouToMatch = lastEntree.codefou;
}
} catch { /* fallback sur codefou_principal */ }
// Matching : codefou exact en priorité, fallback par nom
if (codefouToMatch) {
const matched = (suppliers as any[]).find((s: any) =>
s.codefou && s.codefou.trim().toLowerCase() === article.codefou_principal.trim().toLowerCase()
s.codefou && s.codefou.trim().toLowerCase() === codefouToMatch.trim().toLowerCase()
) || (suppliers as any[]).find((s: any) =>
s.name.toLowerCase().trim().includes(article.nom_fou_principal?.toLowerCase().trim() || '') ||
(article.nom_fou_principal?.toLowerCase().trim() || '').includes(s.name.toLowerCase().trim())
+18 -2
View File
@@ -153,6 +153,7 @@ export default function MobileDlcPage() {
eanDebounceRef.current = setTimeout(async () => {
setEanLookupLoading(true);
try {
// Étape 1 : récupérer l'article par EAN
const res = await fetch(`/api/ffnancy/articles?ean=${encodeURIComponent(gencodeValue)}&limit=1`, { credentials: 'include' });
if (!res.ok) return;
const data = await res.json();
@@ -161,9 +162,24 @@ export default function MobileDlcPage() {
form.setValue("productName", article.libelle1, { shouldValidate: true });
if (article.codefou_principal) {
// Étape 2 : chercher le fournisseur de la dernière entrée en stock
let codefouToMatch = article.codefou_principal;
try {
const mvtRes = await fetch(
`/api/ffnancy/mouvements/entrees?artNoId=${article.no_id}&limit=1&dateDebut=2000-01-01`,
{ credentials: 'include' }
);
if (mvtRes.ok) {
const mvtData = await mvtRes.json();
const lastEntree = mvtData.entrees?.[0];
if (lastEntree?.codefou) codefouToMatch = lastEntree.codefou;
}
} catch { /* fallback sur codefou_principal */ }
// Matching : codefou exact en priorité, fallback par nom
if (codefouToMatch) {
const matched = (suppliers as any[]).find((s: any) =>
s.codefou && s.codefou.trim().toLowerCase() === article.codefou_principal.trim().toLowerCase()
s.codefou && s.codefou.trim().toLowerCase() === codefouToMatch.trim().toLowerCase()
) || (suppliers as any[]).find((s: any) =>
s.name.toLowerCase().trim().includes(article.nom_fou_principal?.toLowerCase().trim() || '') ||
(article.nom_fou_principal?.toLowerCase().trim() || '').includes(s.name.toLowerCase().trim())
+17 -5
View File
@@ -6040,12 +6040,24 @@ RÉSUMÉ DU SCAN
if (req.query[key]) params.append(key, req.query[key] as string);
}
const response = await fetch(`https://api.ffnancy.fr/api/articles?${params}`);
if (!response.ok) {
return res.status(response.status).json({ error: 'API ffnancy error' });
if (!response.ok) return res.status(response.status).json({ error: 'API ffnancy error' });
res.json(await response.json());
} catch {
res.status(500).json({ error: 'Failed to reach API ffnancy' });
}
});
app.get('/api/ffnancy/mouvements/entrees', requireAuth, async (req: Request, res: Response) => {
try {
const params = new URLSearchParams();
const allowed = ['artNoId', 'dateDebut', 'dateFin', 'site', 'page', 'limit'];
for (const key of allowed) {
if (req.query[key]) params.append(key, req.query[key] as string);
}
const data = await response.json();
res.json(data);
} catch (error) {
const response = await fetch(`https://api.ffnancy.fr/api/mouvements/entrees?${params}`);
if (!response.ok) return res.status(response.status).json({ error: 'API ffnancy error' });
res.json(await response.json());
} catch {
res.status(500).json({ error: 'Failed to reach API ffnancy' });
}
});