/* Histórico do cliente — modal compartilhado Mostra todas as transações de um distribuidor ou picolezeiro num único cadastro. - Distribuidor: lista vendas com itens, hora, forma de pagamento, status - Picolezeiro: lista saídas/retornos com itens, praias, faturado, comissão Cada linha pode ser expandida e impressa individualmente. */ function ClientHistoryModal({ ctx, kind, client, onClose }) { // kind: "distributor" | "picolezeiro" const [expanded, setExpanded] = useState(null); const [printPick, setPrintPick] = useState(null); // {kind, payload} const [period, setPeriod] = useState({ kind: "all" }); const isDist = kind === "distributor"; // pega registros const records = useMemo(() => { if (isDist) { return ctx.state.sales .filter(s => s.clientId === client.id) .filter(s => inPeriod(s.date, period, DEFAULT_REF_DATE)) .sort((a,b) => (b.date + " " + (b.time||"")).localeCompare(a.date + " " + (a.time||""))); } else { return ctx.state.trips .filter(t => t.picolezeiroId === client.id) .filter(t => inPeriod(t.date, period, DEFAULT_REF_DATE)) .sort((a,b) => b.date.localeCompare(a.date)); } }, [ctx.state.sales, ctx.state.trips, client.id, isDist, period]); const totalFat = isDist ? records.reduce((s, r) => s + r.total, 0) : records.filter(t => t.status === "fechado").reduce((s, t) => s + (t.faturado || 0), 0); const totalUnidades = isDist ? records.reduce((s, r) => s + r.items.reduce((a, i) => a + i.units, 0), 0) : records.reduce((s, t) => s + t.items.reduce((a, i) => a + i.qty, 0), 0); const aReceber = isDist ? records.filter(r => r.status === "a_receber").reduce((s, r) => s + r.total, 0) : 0; const comissaoTotal = !isDist ? records.filter(t => t.status === "fechado").reduce((s, t) => s + (t.comissao || 0), 0) : 0; /* Impressão de uma transação específica */ const reprintSale = (sale, fmt) => { const distClient = ctx.state.distributors.find(d => d.id === sale.clientId) || client; printSaleReceipt({ company: ctx.state.company, sale: { id: sale.id, date: sale.date, client: distClient, payment: sale.payment, subtotal: sale.subtotal, discount: sale.discount, total: sale.total, perBox: sale.perBox }, items: sale.items.map(({productId, boxes}) => ({ productId, boxes })), products: ctx.state.products, fmt, }); }; const reprintTrip = (trip, fmt) => { const pz = client; if (trip.status === "fechado") { printRetornoReceipt({ company: ctx.state.company, trip, picolezeiro: pz, beaches: ctx.state.beaches, products: ctx.state.products, fmt, }); } else { printSaidaReceipt({ company: ctx.state.company, trip, picolezeiro: pz, beaches: ctx.state.beaches, products: ctx.state.products, fmt, }); } }; /* Impressão do histórico inteiro do cliente */ const printFullHistory = () => { const logo = ctx.state.company.logo; const logoIsImage = logo && !/^data:application\/pdf/i.test(logo); const headerHtml = `
${logoIsImage ? `` : `
P
`}
Histórico de ${isDist ? "compras" : "consignação"}
${ctx.state.company.name} · Gerado em ${new Date().toLocaleString("pt-BR")}
${client.name}
${isDist ? (client.doc||"") + " · " + (client.city||"") : (client.cpf||"") + " · " + (client.phone||"")}
${records.length}${isDist ? "compras" : "saídas/retornos"}
${totalUnidades}unidades
${fmtBRL(totalFat)}${isDist ? "faturado" : "faturado (fechados)"}
${isDist ? `
${fmtBRL(aReceber)}a receber
` : `
${fmtBRL(comissaoTotal)}comissão paga
`}
`; const rows = records.map(r => { if (isDist) { const itemsTxt = r.items.map(it => { const p = ctx.state.products.find(x => x.id === it.productId); return `${p?.flavor||"?"} ${it.boxes}cx`; }).join(" · "); return ` ${fmtDate(r.date)}
${r.time||""} ${itemsTxt} ${r.payment||"—"} ${fmtBRL(r.total)} ${r.status === "confirmado" ? "✓ Pago" : "⏳ A receber"} `; } else { const itemsTxt = r.items.map(it => { const p = ctx.state.products.find(x => x.id === it.productId); return `${p?.flavor||"?"} ${it.qty}un`; }).join(" · "); const sold = (r.sold||[]).reduce((s,x)=>s+x.qty,0); return ` ${fmtDate(r.date)} ${itemsTxt} ${r.status === "fechado" ? `${sold} un vendidas` : "—"} ${r.status === "fechado" ? fmtBRL(r.faturado||0) : "—"} ${r.status === "fechado" ? "✓ Fechado" : "⏳ Em rota"} `; } }).join(""); const headers = isDist ? `Data / HoraItensPagamentoTotalStatus` : `DataItens retiradosResultadoFaturadoStatus`; const body = `
${headerHtml} ${headers}${rows}
${ctx.state.company.name} · Documento sem valor fiscal
`; const css = `@page { size: A4; margin: 12mm 10mm; } * { box-sizing: border-box; } html, body { margin:0; padding:0; } body { font-family: Helvetica, Arial, sans-serif; color:#222; font-size:11px; line-height:1.5; } table { width:100%; border-collapse:collapse; table-layout:auto; word-wrap:break-word; } table th { background:transparent; font-weight:700; font-size:9.5px; text-transform:uppercase; letter-spacing:.08em; padding:7px 6px; border-bottom:1.5px solid #333; text-align:left; color:#555; } table td { padding:6px; border-bottom:1px solid #eee; font-size:10.5px; vertical-align:top; } tbody tr:last-child td { border-bottom:none; }`; const win = window.open("", "_blank", "width=1000,height=900"); win.document.open(); win.document.write(`Histórico — ${client.name}

Histórico — ${client.name}

${body}
`); win.document.close(); }; const initial = client.name.split(" ").map(w => w[0]).slice(0,2).join(""); const subtitle = isDist ? `${client.doc || ""} · ${client.city || ""}` : `${client.cpf || ""} · ${client.phone || ""} · ${client.commission || 0}% comissão`; return ( } > {/* Sumário */}
{initial}
{isDist ? : }
{records.length === 0 ? (

Nenhuma {isDist ? "compra" : "saída"} registrada

Quando este cliente realizar uma {isDist ? "compra" : "saída"}, ela aparece aqui.

) : (
{records.map((r, idx) => ( setExpanded(expanded === (r.id || idx) ? null : (r.id || idx))} onPrint={() => setPrintPick({ record: r })} isLast={idx === records.length - 1} /> ))}
)} {printPick && ( setPrintPick(null)} onPrint={(fmt) => { if (isDist) reprintSale(printPick.record, fmt); else reprintTrip(printPick.record, fmt); setPrintPick(null); }} /> )}
); } function SumPill({ label, value, color }) { return (
{label} {value}
); } function ClientHistoryRow({ kind, record, products, beaches, expanded, onToggle, onPrint, isLast }) { const isDist = kind === "distributor"; const isFechado = !isDist && record.status === "fechado"; const totalUnits = isDist ? record.items.reduce((s, i) => s + i.units, 0) : record.items.reduce((s, i) => s + i.qty, 0); const headLeft = isDist ? ( <>
Venda · {record.id?.slice(-6).toUpperCase()}
{fmtDate(record.date)}{record.time ? " · " + record.time : ""} · {record.payment} · {record.items.length} sabor{record.items.length===1?"":"es"} · {totalUnits} un
) : ( <>
{isFechado ? "Retorno fechado" : "Saída em rota"} · {record.id?.slice(-6).toUpperCase()}
{fmtDate(record.date)} · {(record.beachesActual || record.beachesPlanned || []).map(bid => beaches.find(b=>b.id===bid)?.name).filter(Boolean).join(", ") || "—"} · {totalUnits} un retiradas
); const headRight = isDist ? (
{fmtBRL(record.total)}
{record.status === "confirmado" ? pago : a receber}
) : (
{isFechado ? fmtBRL(record.faturado||0) : "—"}
{isFechado ? fechado : em rota}
); return (
{headLeft}
{headRight}
{expanded && (
{isDist ? ( {record.items.map(it => { const p = products.find(x => x.id === it.productId); return ( ); })} {record.discount > 0 && ( )}
Produto Caixas Unidades Preço un. Subtotal
{p?.flavor||"?"} {it.boxes} {it.units} {fmtBRL(it.unitPrice)} {fmtBRL(it.subtotal)}
Total da venda {fmtBRL(record.total)}
(desconto aplicado: {fmtBRL(record.discount)})
) : ( {record.items.map(it => { const p = products.find(x => x.id === it.productId); const sold = (record.sold||[]).find(x => x.productId === it.productId)?.qty || 0; const ret = (record.returned||[]).find(x => x.productId === it.productId)?.qty; const sobra = ret !== undefined ? ret : (isFechado ? it.qty - sold : null); return ( ); })} {isFechado && ( <> )}
Produto Retirado Vendido Sobra Faturado
{p?.flavor||"?"} {it.qty} {isFechado ? sold : "—"} {isFechado ? sobra : "—"} {isFechado ? fmtBRL(sold * (p?.price||0)) : "—"}
Faturado {fmtBRL(record.faturado||0)}
Comissão − {fmtBRL(record.comissao||0)}
Líquido para empresa {fmtBRL((record.faturado||0) - (record.comissao||0))}
)}
)}
); } Object.assign(window, { ClientHistoryModal });