/* global React */ const { useState, useEffect, useCallback } = React; const { brl, intFmt } = window; const _fmtDate = (iso) => { if (!iso) return '—'; const d = new Date(iso); return d.toLocaleDateString('pt-BR') + ' · ' + d.toLocaleTimeString('pt-BR', { hour:'2-digit', minute:'2-digit' }); }; // ===================================================================== // MateriasPrimasView — aba principal // ===================================================================== function MateriasPrimasView({ params, onShowToast, askConfirm, setLoading, disabled, onItemsChange }) { const [items, setItems] = useState([]); const [loaded, setLoaded] = useState(false); const [editing, setEditing] = useState(null); // {modo: 'novo'|'editar', dados: {...}} const [xmlOpen, setXmlOpen] = useState(false); const [xmlParsed, setXmlParsed] = useState(null); // Notifica o pai sempre que a lista mudar useEffect(() => { if (loaded && onItemsChange) onItemsChange(items); }, [items, loaded]); const recarregar = useCallback(async () => { if (disabled) return; setLoading?.({ text: 'Carregando matérias-primas…' }); try { const data = await window.cloud.listMP(); setItems(data || []); setLoaded(true); onShowToast(`${(data || []).length} matéria(s)-prima(s) carregada(s)`, 'ok'); } catch (e) { onShowToast('Falha ao carregar: ' + e.message, 'err'); } finally { setLoading?.(null); } }, [disabled, onShowToast, setLoading]); const novo = () => { setEditing({ modo: 'novo', dados: { nome: '', ncm: '', fornecedor: '', cnpj_fornecedor: '', nf_valor: 0, nf_kg: 0, nf_icms: 18, nf_ipi: 0, nf_simples: false, nf_frete: 0, nf_seguro: 0, nf_outras: 0, nf_desconto: 0, nf_xml_info: null, notas: '', }, }); }; const abrirParaEdicao = async (mpId) => { setLoading?.({ text: 'Carregando…' }); try { const mp = await window.cloud.getMP(mpId); const N = (x) => { const n = parseFloat(x); return isNaN(n) ? 0 : n; }; setEditing({ modo: 'editar', dados: { ...mp, // garantir tipos numéricos (Postgres retorna strings para numeric) nf_valor: N(mp.nf_valor), nf_kg: N(mp.nf_kg), nf_icms: N(mp.nf_icms), nf_ipi: N(mp.nf_ipi), nf_frete: N(mp.nf_frete), nf_seguro: N(mp.nf_seguro), nf_outras: N(mp.nf_outras), nf_desconto: N(mp.nf_desconto), custo_kg: N(mp.custo_kg), nf_simples: !!mp.nf_simples, }, }); } catch (e) { onShowToast('Falha: ' + e.message, 'err'); } finally { setLoading?.(null); } }; const salvar = async () => { const d = editing.dados; if (!d.nome || !d.nome.trim()) { onShowToast('Informe o nome da matéria-prima', 'err'); return; } if (!d.nf_kg || d.nf_kg <= 0) { onShowToast('Informe a quantidade em kg', 'err'); return; } setLoading?.({ text: 'Salvando…' }); try { const custoKg = window.cloud.calcularCustoKgMP(d, params); const payload = { ...d, custo_kg: custoKg, ultima_compra_em: new Date().toISOString(), }; if (editing.modo === 'novo') { const saved = await window.cloud.createMP(payload); onShowToast(`MP #${String(saved.seq).padStart(4,'0')} criada · custo R$ ${custoKg.toFixed(4)}/kg`, 'ok'); } else { await window.cloud.updateMP(d.id, payload); onShowToast(`MP atualizada · novo custo R$ ${custoKg.toFixed(4)}/kg`, 'ok'); } setEditing(null); await recarregar(); } catch (e) { onShowToast('Falha: ' + e.message, 'err'); } finally { setLoading?.(null); } }; const excluir = async (mp) => { const ok = await askConfirm({ title: 'Excluir matéria-prima', message: `Excluir "${mp.nome}" (MP #${String(mp.seq).padStart(4,'0')})? Produtos vinculados perdem o link mas continuam funcionando com a NF embutida atual.`, okLabel: 'Excluir', danger: true, }); if (!ok) return; setLoading?.({ text: 'Excluindo…' }); try { await window.cloud.deleteMP(mp.id); setItems(prev => prev.filter(i => i.id !== mp.id)); onShowToast('Matéria-prima excluída', 'ok'); } catch (e) { onShowToast('Falha: ' + e.message, 'err'); } finally { setLoading?.(null); } }; // Importar XML cria nova MP (ou abre seleção de itens se múltiplos) const [xmlText, setXmlText] = useState(''); const processarXmlMP = () => { const texto = (xmlText || '').trim(); if (!texto) { onShowToast('Cole o XML da NF-e', 'err'); return; } const parsed = window.parseXmlNFe(texto); if (!parsed.ok) { onShowToast(parsed.erro || 'Falha', 'err'); return; } const grupos = window.agruparItensXml(parsed.itens); if (grupos.length === 1) { aplicarXmlNovaMP(window.calcularSelecaoXml(grupos, parsed), parsed); } else { setXmlParsed(parsed); setXmlOpen(true); } }; const aplicarXmlNovaMP = (selecao, parsed) => { // Cria editor preenchido a partir do XML setXmlText(''); setXmlOpen(false); setXmlParsed(null); setEditing({ modo: 'novo', dados: { nome: selecao.descricoes?.[0] || parsed.fornecedor || 'Nova matéria-prima', ncm: selecao.ncms?.[0] || '', fornecedor: parsed.fornecedor || '', cnpj_fornecedor: parsed.cnpj || '', descricao_xml: selecao.descricoes?.join(' / ') || '', nf_valor: +selecao.valor.toFixed(2), nf_kg: selecao.isKg ? +selecao.qtd.toFixed(3) : 0, nf_icms: +selecao.aliqICMS.toFixed(2), nf_ipi: +selecao.aliqIPI.toFixed(2), nf_simples: parsed.isSimples, nf_frete: +(selecao.frete || 0).toFixed(2), nf_seguro: +(selecao.seguro || 0).toFixed(2), nf_outras: +(selecao.outras || 0).toFixed(2), nf_desconto: +(selecao.desconto || 0).toFixed(2), nf_xml_info: { fornecedor: parsed.fornecedor, cnpj: parsed.cnpj, descricoes: selecao.descricoes, ncms: selecao.ncms, qtdItens: selecao.qtdItens, unidade: selecao.unidade, isKg: selecao.isKg, qtdOriginal: selecao.qtd, totalItensNF: parsed.itens.length, importadoEm: new Date().toISOString(), }, notas: !selecao.isKg ? `⚠ Unidade do XML era "${selecao.unidade}", preencha kg manualmente.` : '', }, }); let msg = `XML lido: ${selecao.qtdItens} item(s). Revise e salve.`; if (selecao.hasST) msg += ' · ⚠ contém ICMS-ST'; if (!selecao.isKg) msg += ` · ⚠ unidade ${selecao.unidade} — informe kg`; onShowToast(msg, selecao.hasST || !selecao.isKg ? 'warn' : 'ok'); }; // Renderiza editor (modal) se houver if (editing) { return setEditing(null)} />; } return ( <>

Matérias-primas

{loaded ? `${items.length} cadastradas` : 'Não carregado'}
XML Importar matéria-prima a partir de NF-e