import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { useQuery } from "@tanstack/react-query"; import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useState, useEffect, useRef } from "react"; import { Loader2 } from "lucide-react"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Checkbox } from "@/components/ui/checkbox"; import { insertCustomerOrderFrontendSchema, type CustomerOrderWithRelations, type Group, type Supplier } from "@shared/schema"; import { useStore } from "@/contexts/StoreContext"; const customerOrderFormSchema = z.object({ orderTaker: z.string().min(1, "Qui a pris la commande est requis"), customerName: z.string().min(1, "Nom du client est requis"), contactNumber: z.string().optional(), productName: z.string().min(1, "Désignation du produit est requise"), productReference: z.string().optional(), gencode: z.string().optional(), quantity: z.coerce.number().int().positive().default(1), supplierId: z.coerce.number().int().positive().default(1), groupId: z.coerce.number().int().positive(), deposit: z.coerce.number().default(0), isPromotionalPrice: z.boolean().default(false), customerNotified: z.boolean().default(false), notes: z.string().optional(), customerEmail: z.string().optional(), }); type CustomerOrderFormData = z.infer; interface CustomerOrderFormProps { order?: CustomerOrderWithRelations; onSubmit: (data: CustomerOrderFormData) => void; onCancel: () => void; isLoading?: boolean; } const STATUS_OPTIONS = [ "En attente de Commande", "Commande en Cours", "Disponible", "Retiré", "Annulé" ]; export function CustomerOrderForm({ order, onSubmit, onCancel, isLoading = false }: CustomerOrderFormProps) { const { user } = useAuthUnified(); const { selectedStoreId } = useStore(); // Fetch groups for store selection const { data: groups = [] } = useQuery({ queryKey: ['/api/groups'], }); // Fetch suppliers for supplier selection const { data: suppliers = [] } = useQuery({ queryKey: ['/api/suppliers'], }); // Get user's assigned group automatically - no selection needed const getUserAssignedGroupId = () => { // Priority 1: User's first assigned group (employees have exactly one group) if (user?.userGroups?.[0]?.groupId) { return user.userGroups[0].groupId; } // Priority 2: Admin can use selected store if (user?.role === 'admin' && selectedStoreId) { return selectedStoreId; } // Priority 3: Admin gets first available group if no store selected if (user?.role === 'admin' && groups.length > 0) { return groups[0].id; } return null; }; const form = useForm({ resolver: zodResolver(customerOrderFormSchema), defaultValues: { orderTaker: order?.orderTaker || user?.name || "", customerName: order?.customerName || "", contactNumber: order?.customerPhone || "", productName: order?.productDesignation || "", productReference: order?.productReference || "", gencode: order?.gencode || "", quantity: order?.quantity || 1, supplierId: order?.supplierId || 1, status: "En attente de Commande", // Statut fixe deposit: order?.deposit || 0, isPromotionalPrice: order?.isPromotionalPrice || false, customerNotified: order?.customerNotified || false, groupId: order?.groupId || getUserAssignedGroupId() || 1, }, }); const handleSubmit = (data: CustomerOrderFormData) => { if (!data.customerName || data.customerName.trim() === '') return; const groupId = getUserAssignedGroupId(); if (!groupId) { alert("ERREUR: Votre utilisateur n'a pas de magasin assigné. Contactez l'administrateur."); return; } const submitData = { orderTaker: data.orderTaker || user?.firstName + ' ' + user?.lastName || user?.username || "Inconnu", customerName: data.customerName.trim(), customerPhone: data.contactNumber || '', customerEmail: data.customerEmail || '', productDesignation: data.productName || '', productReference: data.productReference || '', gencode: data.gencode || '', quantity: data.quantity, supplierId: data.supplierId || 1, status: data.status || "En attente de Commande", deposit: data.deposit || 0, isPromotionalPrice: data.isPromotionalPrice || false, customerNotified: data.customerNotified || false, notes: data.notes || '', groupId: groupId, }; onSubmit(submitData); }; // Auto-ensure groupId is always set to user's assigned group const currentGroupId = form.getValues('groupId'); const userGroupId = getUserAssignedGroupId(); if (!currentGroupId && userGroupId) { form.setValue('groupId', userGroupId); } // Lookup API ffnancy par gencode ou référence const [articleLookupLoading, setArticleLookupLoading] = useState(false); const [articleNotFound, setArticleNotFound] = useState(false); // Flag pour éviter que le remplissage auto de productReference ne redéclenche une recherche const autoFilledRef = useRef(false); const lookupDebounceRef = useRef | null>(null); const gencodeValue = form.watch("gencode"); const referenceValue = form.watch("productReference"); const fetchAndFillArticle = async (params: URLSearchParams) => { setArticleLookupLoading(true); setArticleNotFound(false); try { const res = await fetch(`/api/ffnancy/articles?${params}&limit=1`, { credentials: 'include' }); if (!res.ok) { setArticleNotFound(true); return; } const data = await res.json(); const article = data.articles?.[0]; if (!article) { setArticleNotFound(true); return; } setArticleNotFound(false); autoFilledRef.current = true; // bloquer le watcher référence form.setValue("productName", article.libelle1, { shouldValidate: true }); if (article.gtin) form.setValue("gencode", article.gtin, { shouldValidate: true }); if (article.ref_fou_principale) form.setValue("productReference", article.ref_fou_principale, { shouldValidate: true }); // Fournisseur : dernière entrée en stock en priorité 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 codefou_principal */ } if (codefouToMatch) { const matched = (suppliers as any[]).find((s: any) => 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()) ); if (matched) form.setValue("supplierId", matched.id, { shouldValidate: true }); } } catch { setArticleNotFound(true); } finally { setArticleLookupLoading(false); } }; useEffect(() => { if (!gencodeValue || gencodeValue.length < 8) { setArticleNotFound(false); return; } if (lookupDebounceRef.current) clearTimeout(lookupDebounceRef.current); lookupDebounceRef.current = setTimeout(() => { fetchAndFillArticle(new URLSearchParams({ ean: gencodeValue })); }, 600); return () => { if (lookupDebounceRef.current) clearTimeout(lookupDebounceRef.current); }; }, [gencodeValue]); useEffect(() => { // Si la valeur a été remplie automatiquement, on ignore ce cycle if (autoFilledRef.current) { autoFilledRef.current = false; return; } if (!referenceValue || referenceValue.length < 3) { setArticleNotFound(false); return; } if (lookupDebounceRef.current) clearTimeout(lookupDebounceRef.current); lookupDebounceRef.current = setTimeout(() => { fetchAndFillArticle(new URLSearchParams({ codein: referenceValue })); }, 600); return () => { if (lookupDebounceRef.current) clearTimeout(lookupDebounceRef.current); }; }, [referenceValue]); return (
{/* Informations de commande */}

Informations de commande

( Qui a pris la commande )} /> ( Nom du client )} /> ( N° de téléphone )} /> ( Fournisseur )} /> {/* Magasin automatiquement assigné selon l'utilisateur */}

Informations produit

( Désignation du produit