import React from 'react'; import { format } from 'date-fns'; import type { StockItem, StockMovement } from '../../services/stockService'; import '../../styles/reports.css'; interface StockInventoryReportProps { items: StockItem[]; movements: Map; // Key: stockItemId logoUrl?: string; reportType?: 'PAINT' | 'THINNER'; } export const StockInventoryReport: React.FC = ({ items, movements, logoUrl, reportType = 'PAINT' }) => { // Agrupamento por Produto + Cor const groups = React.useMemo(() => { const map = new Map(); items.forEach(item => { const productName = typeof item.dataSheetId === 'object' ? item.dataSheetId.name : 'Desconhecido'; const manufacturer = typeof item.dataSheetId === 'object' ? item.dataSheetId.manufacturer : ''; // Se for diluente, agrupar apenas pelo ID do produto, ignorando cor. // Se for tinta, agrupar por produto + cor. const key = reportType === 'THINNER' ? `${item.dataSheetId._id || item.dataSheetId}` : `${item.dataSheetId._id || item.dataSheetId}-${item.color}`; if (!map.has(key)) { map.set(key, { key, productName, manufacturer, color: reportType === 'THINNER' ? '-' : (item.color || '-'), minStock: item.minStock || 0, totalQty: 0, unit: item.unit, items: [], isLowStock: false }); } const group = map.get(key)!; group.items.push(item); group.totalQty += item.quantity; if (item.minStock && item.minStock > group.minStock) { group.minStock = item.minStock; } }); // Avaliar status de estoque baixo por grupo for (const group of map.values()) { if (group.minStock > 0 && group.totalQty < group.minStock) { group.isLowStock = true; } } return Array.from(map.values()); }, [items, reportType]); // Cálculos globais const totalItems = items.length; const totalQuantity = items.reduce((acc, item) => acc + item.quantity, 0); const expiredItems = items.filter(item => item.expirationDate && new Date(item.expirationDate) < new Date() ).length; const lowStockGroupsCount = groups.filter(g => g.isLowStock).length; const formatMovementType = (type: string) => { switch (type) { case 'ENTRY': return 'Entrada'; case 'ADJUSTMENT': return 'Ajuste'; case 'CONSUMPTION': return 'Consumo'; default: return type; } }; return (
{logoUrl ? ( Logo ) : (
)}
INVENTÁRIO DE ESTOQUE - {reportType === 'THINNER' ? 'DILUENTES' : 'TINTAS'}
Controle de {reportType === 'THINNER' ? 'Diluentes' : 'Tintas'} – Agrupado por Produto
Data: {format(new Date(), 'dd/MM/yyyy')}
Grupos Críticos: 0 ? 'red' : 'inherit' }}>{lowStockGroupsCount}
Total de Lotes
{totalItems}
Entradas ativas
Volume Total
{totalQuantity.toFixed(1)}
Litros em estoque
Alertas de Estoque
0 ? '#ef4444' : '#10b981' }}>{lowStockGroupsCount}
Produtos abaixo do mínimo
Vencidos
0 ? '#ef4444' : 'inherit' }}>{expiredItems}
Lotes expirados

DETALHAMENTO DO ESTOQUE

{reportType === 'PAINT' && } {groups.map((group) => ( {/* Group Header Row */} {reportType === 'PAINT' && ( )} {/* Individual Items */} {group.items.map((item) => { const isExpired = item.expirationDate && new Date(item.expirationDate) < new Date(); const itemMovements = movements.get(item._id!) || []; return ( {reportType === 'PAINT' && ( )} {/* Movimentações inline */} {itemMovements.length > 0 && ( )} ); })} ))}
Produto / RR Lote / Validade QuantidadeCorNota Fiscal
{group.productName.toUpperCase()}
{group.manufacturer}
{group.isLowStock && } {group.totalQty.toFixed(1)} {group.unit}
{group.minStock > 0 && (
Mín: {group.minStock}
)}
{group.color} {group.items.length} lote(s)
RR: {item.rrNumber}
Lote: {item.batchNumber}
{reportType === 'PAINT' && ( <> Val:{' '} {item.expirationDate ? format(new Date(item.expirationDate), 'dd/MM/yyyy') : '-'} )}
{item.quantity.toFixed(1)} {item.unit}
{/* Cor is already in group header, repeated here only if needed or keep empty/dash */}
-
{item.invoiceNumber || '-'}
{item.receivedBy && (
Rec: {item.receivedBy}
)}
{itemMovements.slice(0, 5).map((mov, idx) => ( {format(new Date(mov.date), 'dd/MM/yy')} -{' '} {formatMovementType(mov.type)}:{' '} {mov.type === 'CONSUMPTION' ? '-' : '+'}{Math.abs(mov.quantity)}L {mov.responsible && ` (${mov.responsible})`} {idx < Math.min(itemMovements.length, 5) - 1 && ' | '} ))} {itemMovements.length > 5 && ( ... +{itemMovements.length - 5} movimentações )}
); };