/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ import FormModal, { FormSection, FormField } from '@/components/ui/FormModal'; import { Calendar, FileText, Plus, Save, Trash2 } from "lucide-react"; import { useAppDispatch, useAppSelector } from "@/store/hooks"; import ClientAutocomplete from "../molecules/ClientAutocomplete"; import { useEffect, useMemo, useState } from "react"; import { Client } from "@/types/clientType"; import ArticleAutocomplete from "../molecules/ArticleAutocomplete"; import { Article } from "@/types/articleType"; import { Alert, CircularProgress, Divider } from "@mui/material"; import Button from '@mui/material/Button'; import { useNavigate } from "react-router-dom"; import { toast } from "../ui/use-toast"; import { Facture, FactureRequest } from '@/types/factureType'; import { factureStatus } from '@/store/features/factures/selectors'; import { createFacture, getFacture, updateFacture } from '@/store/features/factures/thunk'; import { selectfacture } from '@/store/features/factures/slice'; import { ModalLoading } from "./ModalLoading"; import { formatForDateInput } from '@/lib/utils'; import { Input } from '../ui/ui'; // ✅ Interface corrigée avec les bons noms de champs interface LigneForm { article_code: string; quantite: number; prix_unitaire_ht: number; articles: Article | null; } export function ModalFacture({ open, onClose, title, editing, client }: { open: boolean; onClose: () => void; title?: string; editing?: Facture | null; client?: Client | null; }) { const navigate = useNavigate(); const dispatch = useAppDispatch(); const statusFacture = useAppSelector(factureStatus); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); const [dateFacture, setDateFacture] = useState(() => { const d = new Date(); d.setDate(d.getDate() + 1); return d.toISOString().split("T")[0]; }); const [dateEcheance, setDateEcheance] = useState(''); const [referenceClient, setReferenceClient] = useState(''); const [clientSelectionne, setClientSelectionne] = useState(client ?? null); // du le const [dateLivraison, setDateLivraison] = useState(() => { const d = new Date() d.setDate(d.getDate() + 8); return d.toISOString().split("T")[0]; }); // ✅ Ligne par défaut avec articles: null const [lignes, setLignes] = useState([ { article_code: '', quantite: 1, prix_unitaire_ht: 0, articles: null } ]); const isEditing = !!editing; // Calcul date d'échéance par défaut (30 jours) useEffect(() => { if (dateFacture && !dateEcheance) { const date = new Date(dateFacture); date.setDate(date.getDate() + 30); setDateEcheance(date.toISOString().split('T')[0]); } }, [dateFacture]); useEffect(() => { if (!open) return; if (editing) { setClientSelectionne({ numero: editing.client_code, intitule: editing.client_intitule, compte_collectif: "", adresse: "", code_postal: "", ville: "", email: "", telephone: "", } as Client); const lignesInitiales: LigneForm[] = editing.lignes?.map(ligne => ({ article_code: ligne.article_code, quantite: ligne.quantite, prix_unitaire_ht: ligne.prix_unitaire_ht ?? 0, articles: { reference: ligne.article_code, designation: ligne.designation ?? '', prix_vente: ligne.prix_unitaire_ht ?? 0, stock_reel: 0, } as Article, })) ?? []; setLignes(lignesInitiales.length > 0 ? lignesInitiales : [ { article_code: '', quantite: 1, prix_unitaire_ht: 0, articles: null } ]); if (editing.date) setDateFacture(editing.date); if(editing.date_livraison) setDateLivraison(editing.date_livraison); setReferenceClient(editing.reference || ''); } else { if (!client) { setClientSelectionne(null); } else { setClientSelectionne(client); } setLignes([ { article_code: '', quantite: 1, prix_unitaire_ht: 0, articles: null } ]); setDateFacture(new Date().toISOString().split('T')[0]); setDateLivraison(() => { const d = new Date() d.setDate(d.getDate() + 8); return d.toISOString().split("T")[0]; }); setReferenceClient(''); setDateEcheance(''); setReferenceClient(''); } setError(null); setSuccess(false); }, [open, editing, client]); const appliquerBareme = async (index: number, articleRef: string) => { if (!clientSelectionne) return; try { // Logique d'application du barème } catch (err) { console.error('Erreur application barème:', err); } }; const ajouterLigne = () => { setLignes([ ...lignes, { article_code: '', quantite: 1, prix_unitaire_ht: 0, articles: null }, ]); }; const supprimerLigne = (index: number) => { if (lignes.length > 1) { setLignes(lignes.filter((_, i) => i !== index)); } }; // ✅ Fonction updateLigne corrigée const updateLigne = (index: number, field: keyof LigneForm, value: any) => { const nouvelles = [...lignes]; if (field === 'articles' && value) { const article = value as Article; nouvelles[index] = { ...nouvelles[index], articles: article, article_code: article.reference, prix_unitaire_ht: article.prix_vente, }; if (clientSelectionne) { appliquerBareme(index, article.reference); } } else if (field === 'articles' && !value) { nouvelles[index] = { ...nouvelles[index], articles: null, article_code: '', prix_unitaire_ht: 0, }; } else { (nouvelles[index] as any)[field] = value; } setLignes(nouvelles); }; const calculerTotalLigne = (ligne: LigneForm) => { if (!ligne.articles) return 0; const prix = ligne.prix_unitaire_ht || ligne.articles.prix_vente; return prix * ligne.quantite; }; const calculerTotal = () => { return lignes.reduce((acc, ligne) => acc + calculerTotalLigne(ligne), 0); }; const canSave = useMemo(() => { if (!clientSelectionne) return false; const lignesValides = lignes.filter((l) => l.article_code); if (lignesValides.length === 0) return false; return true; }, [clientSelectionne, lignes]); const onSave = async () => { if (!clientSelectionne) { setError('Veuillez sélectionner un client'); return; } const lignesValides = lignes.filter((l) => l.article_code); if (lignesValides.length === 0) { setError('Veuillez ajouter au moins un article'); return; } try { setLoading(true); setError(null); if (isEditing) { const payloadUpdate = { client_id: clientSelectionne.numero, date_facture: (() => { const d = new Date(dateFacture); d.setDate(d.getDate() + 1); return d.toISOString().split("T")[0]; })(), reference: referenceClient, date_livraison: (() => { const d = new Date(dateLivraison); d.setDate(d.getDate() + 1); return d.toISOString().split("T")[0]; })(), lignes: lignesValides.map((l) => ({ article_code: l.article_code, quantite: l.quantite })), }; const result = await dispatch(updateFacture({ numero: editing!.numero, data: payloadUpdate })).unwrap() as any; const numero = result.facture.numero as any; setSuccess(true); toast({ title: "Facture mise à jour !", description: `La facture a été mise à jour avec succès.`, className: "bg-green-500 text-white border-green-600" }); await new Promise(resolve => setTimeout(resolve, 1500)); const itemCreated = await dispatch(getFacture(numero)).unwrap() as any; const res = itemCreated as Facture; dispatch(selectfacture(res)); setLoading(false); onClose(); navigate(`/home/factures/${numero}`); } else { const payloadCreate: FactureRequest = { client_id: clientSelectionne.numero, date_facture: (() => { const d = new Date(dateFacture); d.setDate(d.getDate() + 1); return d.toISOString().split("T")[0]; })(), reference: referenceClient, date_livraison: (() => { const d = new Date(dateLivraison); d.setDate(d.getDate() + 1); return d.toISOString().split("T")[0]; })(), lignes: lignesValides.map((l) => ({ article_code: l.article_code, quantite: l.quantite })), }; const result = await dispatch(createFacture(payloadCreate)).unwrap(); const data = result.data; setSuccess(true); toast({ title: "Facture créée !", description: `Une nouvelle facture ${data.numero_facture} a été créée avec succès.`, className: "bg-green-500 text-white border-green-600" }); await new Promise(resolve => setTimeout(resolve, 1500)); const itemCreated = await dispatch(getFacture(data.numero_facture)).unwrap() as any; const res = itemCreated as Facture; dispatch(selectfacture(res)); setLoading(false); onClose(); navigate(`/home/factures/${data.numero_facture}`); } } catch (err: any) { setError("Cet article n'est pas disponible pour ce client."); setLoading(false); } }; const totalHT = calculerTotal(); const totalTVA = totalHT * 0.20; const totalTTC = totalHT + totalTVA; const lignesValides = lignes.filter((l) => l.article_code).length; if (!open) return null; return ( {/* Section Client */}
setDateFacture(e.target.value)} /> setDateLivraison(e.target.value)} /> setReferenceClient(e.target.value)} />
{/* Section Lignes */}
{lignes.map((ligne, index) => ( ))}
Article / Description Qté P.U. HT Total HT
{/* ✅ Correction: passer articles et onChange sur 'articles' */} updateLigne(index, 'articles', article)} required /> updateLigne(index, 'quantite', parseFloat(e.target.value) || 0)} min={0} step={1} className="w-full text-center px-2 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-[#941403] focus:border-transparent" /> updateLigne(index, 'prix_unitaire_ht', parseFloat(e.target.value) || 0)} min={0} step={0.01} className="w-full text-right px-2 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-[#941403] focus:border-transparent" /> {calculerTotalLigne(ligne).toFixed(2)} €
{/* Récapitulatif */}
Client {clientSelectionne?.intitule || '-'}
Lignes {lignesValides}
Total HT {totalHT.toFixed(2)} €
Taux TVA 20%
Total TTC {totalTTC.toFixed(2)} €
{/* Erreurs */} {error && ( {error} )} {loading && }
); }