diff --git a/src/app/dre-filial/analitico.tsx b/src/app/dre-filial/analitico.tsx index 3ca8fa1..5cdab95 100644 --- a/src/app/dre-filial/analitico.tsx +++ b/src/app/dre-filial/analitico.tsx @@ -1,50 +1,56 @@ -"use client"; +'use client'; -import * as React from "react"; -import { DataGridPremium, GridToolbar, GridColDef, GridFilterModel } from "@mui/x-data-grid-premium"; +import { Button } from '@/components/ui/button'; +import { Card, CardContent } from '@/components/ui/card'; +import { Checkbox } from '@/components/ui/checkbox'; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from '@/components/ui/drawer'; +import { Input } from '@/components/ui/input'; +import { + DataGridPremium, + GridColDef, + GridToolbar, +} from '@mui/x-data-grid-premium'; import { LicenseInfo } from '@mui/x-license-pro'; +import { + ArrowDown, + ArrowUp, + ArrowUpDown, + Download, + Maximize2, + Minimize2, + Search, + X, +} from 'lucide-react'; +import * as React from 'react'; +import * as XLSX from 'xlsx'; // Garantir que a licença seja aplicada no componente atualização de licença if (typeof window !== 'undefined') { try { - const PERPETUAL_LICENSE_KEY = 'e0d9bb8070ce0054c9d9ecb6e82cb58fTz0wLEU9MzI0NzIxNDQwMDAwMDAsUz1wcmVtaXVtLExNPXBlcnBldHVhbCxLVj0y'; + const PERPETUAL_LICENSE_KEY = + 'e0d9bb8070ce0054c9d9ecb6e82cb58fTz0wLEU9MzI0NzIxNDQwMDAwMDAsUz1wcmVtaXVtLExNPXBlcnBldHVhbCxLVj0y'; LicenseInfo.setLicenseKey(PERPETUAL_LICENSE_KEY); console.log('✅ Licença MUI X aplicada no componente Analítico'); } catch (error) { console.warn('⚠️ Erro ao aplicar licença no componente:', error); } } -import { Card, CardContent } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogFooter, - DialogTrigger, -} from "@/components/ui/dialog"; -import { - Select, - SelectTrigger, - SelectValue, - SelectContent, - SelectItem, -} from "@/components/ui/select"; -import { Checkbox } from "@/components/ui/checkbox"; -import { - Drawer, - DrawerClose, - DrawerContent, - DrawerDescription, - DrawerFooter, - DrawerHeader, - DrawerTitle, - DrawerTrigger, -} from "@/components/ui/drawer"; -import { Download, Filter, X, Search, ArrowUpDown, ArrowUp, ArrowDown, Maximize2, Minimize2 } from "lucide-react"; -import * as XLSX from "xlsx"; interface AnaliticoItem { codigo_grupo: string; @@ -123,8 +129,9 @@ const ExcelFilter: React.FC = ({ currentSort = null, }) => { const [isOpen, setIsOpen] = React.useState(false); - const [searchTerm, setSearchTerm] = React.useState(""); - const [selectedValues, setSelectedValues] = React.useState(currentFilter); + const [searchTerm, setSearchTerm] = React.useState(''); + const [selectedValues, setSelectedValues] = + React.useState(currentFilter); const [selectAll, setSelectAll] = React.useState(false); // Sincronizar selectedValues com currentFilter quando ele mudar @@ -137,10 +144,12 @@ const ExcelFilter: React.FC = ({ const values = filteredData .map((row) => { const value = row[column.field]; - if (value === null || value === undefined) return ""; + if (value === null || value === undefined) return ''; return String(value); }) - .filter((value, index, self) => self.indexOf(value) === index && value !== "") + .filter( + (value, index, self) => self.indexOf(value) === index && value !== '' + ) .sort(); return values; @@ -172,7 +181,9 @@ const ExcelFilter: React.FC = ({ newValues = selectedValues.filter((v) => v !== value); } setSelectedValues(newValues); - setSelectAll(newValues.length === filteredValues.length && filteredValues.length > 0); + setSelectAll( + newValues.length === filteredValues.length && filteredValues.length > 0 + ); }; const handleApply = () => { @@ -210,7 +221,7 @@ const ExcelFilter: React.FC = ({ Filtrar por "{column.headerName}" - +
{/* Opções de ordenação */}
@@ -222,8 +233,7 @@ const ExcelFilter: React.FC = ({ className="h-8 text-xs" onClick={() => handleSort('asc')} > - - A a Z + A a Z
@@ -273,13 +282,15 @@ const ExcelFilter: React.FC = ({ (Selecionar Tudo) - + {filteredValues.map((value) => (
handleValueToggle(value, checked)} + onCheckedChange={(checked: boolean) => + handleValueToggle(value, checked) + } />
- - + - - - {/* Conteúdo Principal - Versão Normal */} - {renderAnaliticoContent(false)} + {/* Conteúdo Principal - Versão Normal */} + {renderAnaliticoContent(false)} ); } - diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index c90a5e9..5424875 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -322,6 +322,33 @@ const TableRow = memo( TableRow.displayName = 'TableRow'; export default function Teste() { + // Função para ordenar filiais numericamente (1, 2, 3...) ao invés de alfabeticamente (1, 10, 11...) + const ordenarFiliaisNumericamente = (filiais: string[]): string[] => { + return [...filiais].sort((a, b) => { + // Tentar converter para número + const numA = parseInt(a, 10); + const numB = parseInt(b, 10); + + // Se ambos são números válidos, ordenar numericamente + if (!isNaN(numA) && !isNaN(numB)) { + return numA - numB; + } + + // Se apenas A é número, vem primeiro + if (!isNaN(numA)) { + return -1; + } + + // Se apenas B é número, vem primeiro + if (!isNaN(numB)) { + return 1; + } + + // Se nenhum é número, ordenar alfabeticamente + return a.localeCompare(b); + }); + }; + const [data, setData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -395,13 +422,13 @@ export default function Teste() { setOpcoesGrupos(gruposUnicos); // Extrair filiais únicas - const filiaisUnicas = [ + const filiaisUnicas = ordenarFiliaisNumericamente([ ...new Set( dadosCompletos .map((item: DREItem) => item.filial || item.codfilial) .filter(Boolean) ), - ].sort() as string[]; + ] as string[]); setOpcoesFiliais(filiaisUnicas); // Inicializar com todas as filiais selecionadas @@ -1459,13 +1486,344 @@ export default function Teste() { carregarPeriodosDisponiveis(); }; + // Função para construir dados hierárquicos completamente expandidos (para exportação) + const buildHierarchicalDataCompleta = + React.useCallback((): HierarchicalRow[] => { + const rows: HierarchicalRow[] = []; + + // Hierarquia simplificada: [grupo, conta] + // Agrupar por CODGRUPO + const gruposPorCodigo = data.reduce((acc, item) => { + const codgrupo = item.codgrupo || item.codigo_grupo || ''; + if (!codgrupo) return acc; + if (!acc[codgrupo]) { + acc[codgrupo] = []; + } + acc[codgrupo].push(item); + return acc; + }, {} as Record); + + // Calcular valores por grupo para linhas calculadas + const valoresPorGrupo: Record> = {}; + Object.keys(gruposPorCodigo).forEach((codgrupo) => { + valoresPorGrupo[codgrupo] = calcularValoresPorMes( + gruposPorCodigo[codgrupo] + ); + }); + + // Ordenar por CODGRUPO (numericamente) + const sortedGrupos = Object.entries(gruposPorCodigo).sort( + ([codA], [codB]) => { + const numA = parseInt(codA) || 0; + const numB = parseInt(codB) || 0; + if (numA !== numB) { + return numA - numB; + } + return codA.localeCompare(codB); + } + ); + + // Variáveis para armazenar valores da MARGEM BRUTA (para uso no RESULTADO LOJA) + let valoresMargemBrutaPorMes: Record | null = null; + let valoresMargemBrutaPorMesPorFilial: Record< + string, + Record + > | null = null; + let totalMargemBruta: number | null = null; + + sortedGrupos.forEach(([codgrupo, items], index) => { + // Calcular total do grupo + const totalGrupo = items.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + const valoresGrupoPorMes = calcularValoresPorMes(items); + const valoresGrupoPorMesPorFilial = + calcularValoresPorMesPorFilial(items); + + // Linha do grupo (Level 0) + rows.push({ + type: 'grupo', + level: 0, + grupo: items[0]?.grupo || codgrupo, + codigo_grupo: codgrupo, + total: totalGrupo, + isExpanded: true, // Sempre expandido na exportação + valoresPorMes: valoresGrupoPorMes, + valoresPorMesPorFilial: valoresGrupoPorMesPorFilial, + percentuaisPorMes: calcularPercentuaisPorMes( + valoresGrupoPorMes, + codgrupo + ), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( + valoresGrupoPorMesPorFilial, + codgrupo + ), + percentualTotal: calcularPercentualTotal(totalGrupo, codgrupo), + }); + + // SEMPRE incluir todas as contas (não depende de expandedGrupos) + // Agrupar por conta dentro do grupo + const contas = items.reduce((acc, item) => { + const conta = item.conta || ''; + if (!conta) return acc; + if (!acc[conta]) { + acc[conta] = []; + } + acc[conta].push(item); + return acc; + }, {} as Record); + + // Ordenar contas por CODCONTA + const sortedContas = Object.entries(contas).sort( + ([contaA, itemsA], [contaB, itemsB]) => { + const codcontaA = itemsA[0]?.codigo_conta || 0; + const codcontaB = itemsB[0]?.codigo_conta || 0; + + if (codcontaA && codcontaB) { + return codcontaA - codcontaB; + } + + return contaA.localeCompare(contaB); + } + ); + + sortedContas.forEach(([conta, contaItems]) => { + const totalConta = contaItems.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + const valoresContaPorMes = calcularValoresPorMes(contaItems); + const valoresContaPorMesPorFilial = + calcularValoresPorMesPorFilial(contaItems); + + // Linha da conta (Level 1) + rows.push({ + type: 'conta', + level: 1, + grupo: items[0]?.grupo || codgrupo, + codigo_grupo: codgrupo, + conta, + codigo_conta: contaItems[0]?.codigo_conta, + total: totalConta, + isExpanded: false, + valoresPorMes: valoresContaPorMes, + valoresPorMesPorFilial: valoresContaPorMesPorFilial, + percentuaisPorMes: calcularPercentuaisPorMes( + valoresContaPorMes, + codgrupo + ), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( + valoresContaPorMesPorFilial, + codgrupo + ), + percentualTotal: calcularPercentualTotal(totalConta, codgrupo), + }); + }); + + // Adicionar linha calculada "MARGEM BRUTA" após o grupo 02 + // Verificar se é o último grupo ou se o próximo grupo é maior que 02 + const proximoCodigo = sortedGrupos[index + 1]?.[0]; + const proximoNumero = proximoCodigo ? parseInt(proximoCodigo) : 999; + + if ( + codgrupo === '02' || + (parseInt(codgrupo) === 2 && proximoNumero > 2) + ) { + // Calcular MARGEM BRUTA = CODGRUPO 01 + CODGRUPO 02 + const valoresGrupo01 = valoresPorGrupo['01'] || {}; + const valoresGrupo02 = valoresPorGrupo['02'] || {}; + + // Calcular valores por mês para MARGEM BRUTA + const valoresMargemPorMes: Record = {}; + mesesDisponiveis.forEach((mes) => { + const valor01 = valoresGrupo01[mes] || 0; + const valor02 = valoresGrupo02[mes] || 0; + valoresMargemPorMes[mes] = valor01 + valor02; + }); + + // Calcular valores por mês e por filial para MARGEM BRUTA + const valoresMargemPorMesPorFilial: Record< + string, + Record + > = {}; + const valoresGrupo01PorFilial = gruposPorCodigo['01'] + ? calcularValoresPorMesPorFilial(gruposPorCodigo['01']) + : {}; + const valoresGrupo02PorFilial = gruposPorCodigo['02'] + ? calcularValoresPorMesPorFilial(gruposPorCodigo['02']) + : {}; + + // Extrair filiais únicas dos valores calculados + const primeiroMes = mesesDisponiveis[0] || ''; + const filiaisDisponiveis = [ + ...new Set([ + ...Object.keys(valoresGrupo01PorFilial[primeiroMes] || {}), + ...Object.keys(valoresGrupo02PorFilial[primeiroMes] || {}), + ]), + ]; + + mesesDisponiveis.forEach((mes) => { + valoresMargemPorMesPorFilial[mes] = {}; + filiaisDisponiveis.forEach((filial) => { + const valor01 = valoresGrupo01PorFilial[mes]?.[filial] || 0; + const valor02 = valoresGrupo02PorFilial[mes]?.[filial] || 0; + valoresMargemPorMesPorFilial[mes][filial] = valor01 + valor02; + }); + }); + + // Calcular total + const totalMargem = Object.values(valoresMargemPorMes).reduce( + (sum, val) => sum + val, + 0 + ); + + // Armazenar valores da MARGEM BRUTA para uso posterior no RESULTADO LOJA + valoresMargemBrutaPorMes = valoresMargemPorMes; + valoresMargemBrutaPorMesPorFilial = valoresMargemPorMesPorFilial; + totalMargemBruta = totalMargem; + + // Adicionar linha calculada + rows.push({ + type: 'calculado', + level: 0, + grupo: 'MARGEM BRUTA', + codigo_grupo: 'MARGEM', + total: totalMargem, + isExpanded: false, + valoresPorMes: valoresMargemPorMes, + valoresPorMesPorFilial: valoresMargemPorMesPorFilial, + percentuaisPorMes: calcularPercentuaisPorMes( + valoresMargemPorMes, + 'MARGEM' + ), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( + valoresMargemPorMesPorFilial, + 'MARGEM' + ), + percentualTotal: calcularPercentualTotal(totalMargem, 'MARGEM'), + isCalculado: true, + }); + } + + // Adicionar linha calculada "RESULTADO LOJA" após o grupo 04 (DESPESAS) + // Verificar se é o último grupo ou se o próximo grupo é maior que 04 + const proximoCodigoDespesas = sortedGrupos[index + 1]?.[0]; + const proximoNumeroDespesas = proximoCodigoDespesas + ? parseInt(proximoCodigoDespesas) + : 999; + + if ( + (codgrupo === '04' || + (parseInt(codgrupo) === 4 && proximoNumeroDespesas > 4)) && + valoresMargemBrutaPorMes !== null && + valoresMargemBrutaPorMesPorFilial !== null && + totalMargemBruta !== null + ) { + // Calcular RESULTADO LOJA = MARGEM BRUTA + DESPESAS (grupo 04) + const valoresGrupo04 = valoresPorGrupo['04'] || {}; + + // Calcular valores por mês para RESULTADO LOJA + const valoresResultadoPorMes: Record = {}; + mesesDisponiveis.forEach((mes) => { + const valorMargem = valoresMargemBrutaPorMes![mes] || 0; + const valorDespesas = valoresGrupo04[mes] || 0; + valoresResultadoPorMes[mes] = valorMargem + valorDespesas; + }); + + // Calcular valores por mês e por filial para RESULTADO LOJA + const valoresResultadoPorMesPorFilial: Record< + string, + Record + > = {}; + const valoresGrupo04PorFilial = gruposPorCodigo['04'] + ? calcularValoresPorMesPorFilial(gruposPorCodigo['04']) + : {}; + + // Extrair filiais únicas dos valores calculados + const primeiroMes = mesesDisponiveis[0] || ''; + const filiaisDisponiveis = [ + ...new Set([ + ...Object.keys( + valoresMargemBrutaPorMesPorFilial![primeiroMes] || {} + ), + ...Object.keys(valoresGrupo04PorFilial[primeiroMes] || {}), + ]), + ]; + + mesesDisponiveis.forEach((mes) => { + valoresResultadoPorMesPorFilial[mes] = {}; + filiaisDisponiveis.forEach((filial) => { + const valorMargem = + valoresMargemBrutaPorMesPorFilial![mes]?.[filial] || 0; + const valorDespesas = valoresGrupo04PorFilial[mes]?.[filial] || 0; + valoresResultadoPorMesPorFilial[mes][filial] = + valorMargem + valorDespesas; + }); + }); + + // Calcular total + const totalResultado = Object.values(valoresResultadoPorMes).reduce( + (sum, val) => sum + val, + 0 + ); + + // Adicionar linha calculada + rows.push({ + type: 'calculado', + level: 0, + grupo: 'RESULTADO LOJA', + codigo_grupo: 'RESULTADO', + total: totalResultado, + isExpanded: false, + valoresPorMes: valoresResultadoPorMes, + valoresPorMesPorFilial: valoresResultadoPorMesPorFilial, + percentuaisPorMes: calcularPercentuaisPorMes( + valoresResultadoPorMes, + 'RESULTADO' + ), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( + valoresResultadoPorMesPorFilial, + 'RESULTADO' + ), + percentualTotal: calcularPercentualTotal( + totalResultado, + 'RESULTADO' + ), + isCalculado: true, + }); + } + }); + + return rows; + }, [ + data, + mesesDisponiveis, + calcularValoresPorMes, + calcularValoresPorMesPorFilial, + calcularPercentuaisPorMes, + calcularPercentuaisPorMesPorFilial, + calcularPercentualTotal, + ]); + const exportarXLSX = () => { if (!data.length) { console.log('⚠️ Nenhum dado para exportar'); return; } - const dadosCompletosExpandidos = buildHierarchicalData(); + console.log('📊 Exportando TODOS os dados expandidos para XLSX...'); + + const dadosCompletosExpandidos = buildHierarchicalDataCompleta(); + + // Determinar filiais para exportação (mesma lógica da tabela) + const filiaisParaExportacao = ordenarFiliaisNumericamente( + filtrosAplicados && filiaisSelecionadas.length > 0 + ? filiaisSelecionadas + : opcoesFiliais.length > 0 + ? opcoesFiliais + : [''] + ); const dadosExportacao = dadosCompletosExpandidos.map((row, index) => { const linha: any = { @@ -1479,11 +1837,37 @@ export default function Teste() { Total: row.total || 0, }; + // Para cada mês, adicionar colunas para cada filial e depois o totalizador mesesDisponiveis.forEach((mes) => { - const valor = row.valoresPorMes?.[mes] || 0; - const percentual = row.percentuaisPorMes?.[mes] || 0; - linha[`Valor ${mes}`] = valor; - linha[`% ${mes}`] = percentual; + // Colunas por filial + filiaisParaExportacao.forEach((filial: string) => { + // Só incluir se a filial estiver selecionada ou se não houver filtros aplicados + if ( + !filtrosAplicados || + filiaisSelecionadas.length === 0 || + filiaisSelecionadas.includes(filial) + ) { + const valorFilial = + filial && + row.valoresPorMesPorFilial?.[mes]?.[filial] !== undefined + ? row.valoresPorMesPorFilial[mes][filial] + : 0; + const percentualFilial = + filial && + row.percentuaisPorMesPorFilial?.[mes]?.[filial] !== undefined + ? row.percentuaisPorMesPorFilial[mes][filial] + : 0; + + linha[`${mes} - Filial ${filial} - Valor`] = valorFilial; + linha[`${mes} - Filial ${filial} - %`] = percentualFilial; + } + }); + + // Totalizador do mês + const valorTotal = row.valoresPorMes?.[mes] || 0; + const percentualTotal = row.percentuaisPorMes?.[mes] || 0; + linha[`${mes} - Total - Valor`] = valorTotal; + linha[`${mes} - Total - %`] = percentualTotal; }); return linha; @@ -1503,9 +1887,20 @@ export default function Teste() { { wch: 15 }, // Total ]; + // Larguras das colunas por mês (filiais + totalizador) mesesDisponiveis.forEach(() => { - colWidths.push({ wch: 15 }); // Valor - colWidths.push({ wch: 10 }); // % + filiaisParaExportacao.forEach((filial: string) => { + if ( + !filtrosAplicados || + filiaisSelecionadas.length === 0 || + filiaisSelecionadas.includes(filial) + ) { + colWidths.push({ wch: 18 }); // Valor Filial + colWidths.push({ wch: 12 }); // % Filial + } + }); + colWidths.push({ wch: 18 }); // Valor Total + colWidths.push({ wch: 12 }); // % Total }); ws['!cols'] = colWidths; @@ -1517,7 +1912,13 @@ export default function Teste() { Informação: 'Período', Valor: `${filtros.periodoDe} a ${filtros.periodoAte}`, }, - { Informação: 'Filial', Valor: filtros.filial }, + { + Informação: 'Filiais', + Valor: + filiaisSelecionadas.length > 0 + ? filiaisSelecionadas.join(', ') + : 'Todas', + }, { Informação: 'Total de Registros', Valor: dadosCompletosExpandidos.length,