import type { FC } from 'react'; import { useEffect, useMemo, useState } from 'react'; import { Plus, Trash2 } from 'lucide-react'; import { Modal, Button, Input, TextArea, Select, toast } from '../ui'; import { odooApi, type Compra, type InventoryItem, type MonedaCompra, type Proveedor } from '../../services/odoo'; import { MONEDA_OPTIONS, fmtMoney } from './shared'; interface LineaForm { item_id: string; cantidad_pedida: string; costo_unitario: string; descuento: string; lote: string; fecha_caducidad: string; } const emptyLinea: LineaForm = { item_id: '', cantidad_pedida: '1', costo_unitario: '0', descuento: '0', lote: '', fecha_caducidad: '', }; interface Props { isOpen: boolean; onClose: () => void; compra: Compra | null; onDone: (compraId?: number) => void; } const hoy = () => new Date().toISOString().slice(0, 10); const CompraFormModal: FC = ({ isOpen, onClose, compra, onDone }) => { const [proveedores, setProveedores] = useState([]); const [items, setItems] = useState([]); const [partnerId, setPartnerId] = useState(''); const [fecha, setFecha] = useState(hoy()); const [entrega, setEntrega] = useState(''); const [moneda, setMoneda] = useState('MXN'); const [descuento, setDescuento] = useState('0'); const [referencia, setReferencia] = useState(''); const [notas, setNotas] = useState(''); const [lineas, setLineas] = useState([{ ...emptyLinea }]); const [saving, setSaving] = useState(false); useEffect(() => { if (!isOpen) return; odooApi.getProveedores({ page_size: 500 }) .then((res) => { if (res.status === 'success') setProveedores(res.proveedores || []); }) .catch(() => {}); odooApi.getInventory() .then((res) => setItems(res.items || [])) .catch(() => {}); }, [isOpen]); useEffect(() => { if (!isOpen) return; if (compra) { setPartnerId(String(compra.partner_id)); setFecha(compra.fecha || hoy()); setEntrega(compra.fecha_entrega_esperada || ''); setMoneda(compra.moneda); setDescuento(String(compra.descuento || 0)); setReferencia(compra.referencia_factura || ''); setNotas(compra.notas || ''); setLineas((compra.lineas || []).map((l) => ({ item_id: String(l.item_id), cantidad_pedida: String(l.cantidad_pedida), costo_unitario: String(l.costo_unitario), descuento: String(l.descuento || 0), lote: l.lote || '', fecha_caducidad: l.fecha_caducidad || '', }))); } else { setPartnerId(''); setFecha(hoy()); setEntrega(''); setMoneda('MXN'); setDescuento('0'); setReferencia(''); setNotas(''); setLineas([{ ...emptyLinea }]); } }, [isOpen, compra]); const itemMap = useMemo(() => new Map(items.map((i) => [i.id, i])), [items]); const setLinea = (idx: number, patch: Partial) => setLineas((prev) => prev.map((l, i) => (i === idx ? { ...l, ...patch } : l))); const onPickItem = (idx: number, itemId: string) => { const it = itemMap.get(Number(itemId)); const costo = it ? (it.ultimo_costo || it.costo_promedio || it.cost || 0) : 0; setLinea(idx, { item_id: itemId, costo_unitario: String(costo) }); }; const subtotalLinea = (l: LineaForm) => Math.max(0, (parseFloat(l.cantidad_pedida) || 0) * (parseFloat(l.costo_unitario) || 0) - (parseFloat(l.descuento) || 0)); const subtotal = lineas.reduce((s, l) => s + subtotalLinea(l), 0); const desc = parseFloat(descuento) || 0; const guardar = async () => { if (!partnerId) { toast.error('Selecciona un proveedor'); return; } const payload = lineas .filter((l) => l.item_id && (parseFloat(l.cantidad_pedida) || 0) > 0) .map((l) => ({ item_id: Number(l.item_id), cantidad_pedida: parseFloat(l.cantidad_pedida) || 0, costo_unitario: parseFloat(l.costo_unitario) || 0, descuento: parseFloat(l.descuento) || 0, lote: l.lote, fecha_caducidad: l.fecha_caducidad || null, })); if (payload.length === 0) { toast.error('Agrega al menos una línea con artículo y cantidad'); return; } setSaving(true); try { const body = { partner_id: Number(partnerId), fecha, fecha_entrega_esperada: entrega || null, moneda, descuento: desc, notas, referencia_factura: referencia, lineas: payload, }; if (compra) { await odooApi.updateCompra(compra.id, body); } else { await odooApi.crearOrdenCompra(body); } toast.success(compra ? 'Compra actualizada' : 'Compra creada'); onDone(compra?.id); onClose(); } catch (err) { const msg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message; toast.error(msg || 'No se pudo guardar la compra'); } finally { setSaving(false); } }; return ( Subtotal {fmtMoney(subtotal, moneda)} {desc > 0 && <> · Descuento {fmtMoney(desc, moneda)}}
)} >
setFecha(e.target.value)} /> setEntrega(e.target.value)} /> setDescuento(e.target.value)} /> setReferencia(e.target.value)} />
{lineas.map((l, idx) => ( ))}
Artículo Cantidad Costo unit. Desc. Lote Caducidad Subtotal
setLinea(idx, { cantidad_pedida: e.target.value })} /> setLinea(idx, { costo_unitario: e.target.value })} /> setLinea(idx, { descuento: e.target.value })} /> setLinea(idx, { lote: e.target.value })} /> setLinea(idx, { fecha_caducidad: e.target.value })} /> {fmtMoney(subtotalLinea(l), moneda)}