diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index 76dc341..a10a40a 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -1,13 +1,16 @@ -"use client"; +'use client'; -import { LoaderPinwheel, ChevronDown, ChevronRight, Filter, Maximize2, Minimize2, Download } from "lucide-react"; -import React, { useEffect, useState, useCallback, memo } from "react"; -import AnaliticoComponent from "./analitico"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Checkbox } from "@/components/ui/checkbox"; -import * as XLSX from "xlsx"; +import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; import { Sheet, SheetContent, @@ -16,14 +19,19 @@ import { SheetHeader, SheetTitle, SheetTrigger, -} from "@/components/ui/sheet"; +} from '@/components/ui/sheet'; import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; + ChevronDown, + ChevronRight, + Download, + Filter, + LoaderPinwheel, + Maximize2, + Minimize2, +} from 'lucide-react'; +import React, { memo, useCallback, useEffect, useState } from 'react'; +import * as XLSX from 'xlsx'; +import AnaliticoComponent from './analitico'; interface DREItem { codfilial: string; @@ -40,7 +48,7 @@ interface DREItem { } interface HierarchicalRow { - type: "grupo" | "conta" | "calculado"; + type: 'grupo' | 'conta' | 'calculado'; level: number; grupo?: string; codigo_grupo?: string; @@ -57,216 +65,259 @@ interface HierarchicalRow { } // Componente memoizado para linhas da tabela -const TableRow = memo(({ - row, - index, - handleRowClick, - getRowStyle, - getIndentStyle, - renderCellContent, - mesesDisponiveis, - opcoesFiliais, - filiaisSelecionadas, - filtrosAplicados, - formatCurrency, - formatCurrencyWithColor, - getFixedCellBackground -}: { - row: HierarchicalRow; - index: number; - handleRowClick: (row: HierarchicalRow, mes?: string, filial?: string) => void; - getRowStyle: (row: HierarchicalRow) => string; - getIndentStyle: (level: number) => React.CSSProperties; - renderCellContent: (row: HierarchicalRow) => React.ReactNode; - mesesDisponiveis: string[]; - opcoesFiliais: string[]; - filiaisSelecionadas: string[]; - filtrosAplicados: boolean; - formatCurrency: (value: number) => string; - formatCurrencyWithColor: (value: number) => { formatted: string; isNegative: boolean }; - getFixedCellBackground: (row: HierarchicalRow) => string; -}) => { - return ( - - handleRowClick(row)} +const TableRow = memo( + ({ + row, + index, + handleRowClick, + getRowStyle, + getIndentStyle, + renderCellContent, + mesesDisponiveis, + opcoesFiliais, + filiaisSelecionadas, + filtrosAplicados, + formatCurrency, + formatCurrencyWithColor, + getFixedCellBackground, + }: { + row: HierarchicalRow; + index: number; + handleRowClick: ( + row: HierarchicalRow, + mes?: string, + filial?: string + ) => void; + getRowStyle: (row: HierarchicalRow) => string; + getIndentStyle: (level: number) => React.CSSProperties; + renderCellContent: (row: HierarchicalRow) => React.ReactNode; + mesesDisponiveis: string[]; + opcoesFiliais: string[]; + filiaisSelecionadas: string[]; + filtrosAplicados: boolean; + formatCurrency: (value: number) => string; + formatCurrencyWithColor: (value: number) => { + formatted: string; + isNegative: boolean; + }; + getFixedCellBackground: (row: HierarchicalRow) => string; + }) => { + return ( + -
- {renderCellContent(row)} -
- - - {/* Colunas de valores por mês e por filial - cada filial tem suas próprias colunas */} - {mesesDisponiveis.map((mes) => { - const filiaisParaMes = (filtrosAplicados && filiaisSelecionadas.length > 0) - ? filiaisSelecionadas - : (opcoesFiliais.length > 0 ? opcoesFiliais : ['']); - - return ( - - {/* Colunas de filiais para este mês */} - {filiaisParaMes.map((filial: string) => { - // Só exibir se a filial estiver selecionada ou se não houver filtros aplicados - if (filtrosAplicados && filiaisSelecionadas.length > 0 && !filiaisSelecionadas.includes(filial)) { - return null; - } - return ( - - handleRowClick(row, mes, filial)} - title={ - filial && row.valoresPorMesPorFilial?.[mes]?.[filial] !== undefined - ? formatCurrency(row.valoresPorMesPorFilial[mes][filial]) - : row.valoresPorMes?.[mes] !== undefined - ? formatCurrency(row.valoresPorMes[mes]) - : "-" - } - > - {filial && row.valoresPorMesPorFilial?.[mes]?.[filial] !== undefined && row.valoresPorMesPorFilial[mes][filial] !== 0 ? ( - (() => { - const valor = row.valoresPorMesPorFilial[mes][filial]; - const { formatted, isNegative } = formatCurrencyWithColor(valor); - return ( - - {formatted} - - ); - })() - ) : !filial && row.valoresPorMes?.[mes] !== undefined ? ( - (() => { - const { formatted, isNegative } = formatCurrencyWithColor(row.valoresPorMes[mes]); - return ( - - {formatted} - - ); - })() - ) : ( - - - )} - - handleRowClick(row, mes, filial)} - title={ - filial && row.percentuaisPorMesPorFilial?.[mes]?.[filial] !== undefined - ? `${row.percentuaisPorMesPorFilial[mes][filial].toFixed(1)}%` - : row.percentuaisPorMes?.[mes] !== undefined - ? `${row.percentuaisPorMes[mes].toFixed(1)}%` - : "-" - } - > - {filial && row.percentuaisPorMesPorFilial?.[mes]?.[filial] !== undefined && row.percentuaisPorMesPorFilial[mes][filial] !== 0 ? ( - `${row.percentuaisPorMesPorFilial[mes][filial].toFixed(1)}%` - ) : !filial && row.percentuaisPorMes?.[mes] !== undefined ? ( - `${row.percentuaisPorMes[mes].toFixed(1)}%` - ) : ( - - - )} - - - ); - })} - - {/* Colunas de totalizador para este mês */} - handleRowClick(row, mes)} - title={row.valoresPorMes?.[mes] !== undefined ? formatCurrency(row.valoresPorMes[mes]) : "-"} - > - {row.valoresPorMes?.[mes] !== undefined ? ( - (() => { - const { formatted, isNegative } = formatCurrencyWithColor(row.valoresPorMes[mes]); - return ( - handleRowClick(row)} + > +
{renderCellContent(row)}
+ + + {/* Colunas de valores por mês e por filial - cada filial tem suas próprias colunas */} + {mesesDisponiveis.map((mes) => { + const filiaisParaMes = + filtrosAplicados && filiaisSelecionadas.length > 0 + ? filiaisSelecionadas + : opcoesFiliais.length > 0 + ? opcoesFiliais + : ['']; + + return ( + + {/* Colunas de filiais para este mês */} + {filiaisParaMes.map((filial: string) => { + // Só exibir se a filial estiver selecionada ou se não houver filtros aplicados + if ( + filtrosAplicados && + filiaisSelecionadas.length > 0 && + !filiaisSelecionadas.includes(filial) + ) { + return null; + } + return ( + + handleRowClick(row, mes, filial)} + title={ + filial && + row.valoresPorMesPorFilial?.[mes]?.[filial] !== + undefined + ? formatCurrency( + row.valoresPorMesPorFilial[mes][filial] + ) + : row.valoresPorMes?.[mes] !== undefined + ? formatCurrency(row.valoresPorMes[mes]) + : '-' } > - {formatted} -
- ); - })() - ) : ( - - - )} - - handleRowClick(row, mes)} - title={ - row.percentuaisPorMes?.[mes] !== undefined - ? `${row.percentuaisPorMes[mes].toFixed(1)}%` - : "-" - } - > - {row.percentuaisPorMes?.[mes] !== undefined - ? `${row.percentuaisPorMes[mes].toFixed(1)}%` - : -} - -
- ); - })} - - {/* Coluna Total */} - handleRowClick(row)} - title={row.total ? formatCurrency(row.total) : "-"} - > - {(() => { - const { formatted, isNegative } = formatCurrencyWithColor( - row.total! + {filial && + row.valoresPorMesPorFilial?.[mes]?.[filial] !== + undefined && + row.valoresPorMesPorFilial[mes][filial] !== 0 ? ( + (() => { + const valor = row.valoresPorMesPorFilial[mes][filial]; + const { formatted, isNegative } = + formatCurrencyWithColor(valor); + return ( + + {formatted} + + ); + })() + ) : !filial && row.valoresPorMes?.[mes] !== undefined ? ( + (() => { + const { formatted, isNegative } = + formatCurrencyWithColor(row.valoresPorMes[mes]); + return ( + + {formatted} + + ); + })() + ) : ( + - + )} + + handleRowClick(row, mes, filial)} + title={ + filial && + row.percentuaisPorMesPorFilial?.[mes]?.[filial] !== + undefined + ? `${row.percentuaisPorMesPorFilial[mes][ + filial + ].toFixed(1)}%` + : row.percentuaisPorMes?.[mes] !== undefined + ? `${row.percentuaisPorMes[mes].toFixed(1)}%` + : '-' + } + > + {filial && + row.percentuaisPorMesPorFilial?.[mes]?.[filial] !== + undefined && + row.percentuaisPorMesPorFilial[mes][filial] !== 0 ? ( + `${row.percentuaisPorMesPorFilial[mes][filial].toFixed( + 1 + )}%` + ) : !filial && + row.percentuaisPorMes?.[mes] !== undefined ? ( + `${row.percentuaisPorMes[mes].toFixed(1)}%` + ) : ( + - + )} + + + ); + })} + + {/* Colunas de totalizador para este mês */} + handleRowClick(row, mes)} + title={ + row.valoresPorMes?.[mes] !== undefined + ? formatCurrency(row.valoresPorMes[mes]) + : '-' + } + > + {row.valoresPorMes?.[mes] !== undefined ? ( + (() => { + const { formatted, isNegative } = formatCurrencyWithColor( + row.valoresPorMes[mes] + ); + return ( + + {formatted} + + ); + })() + ) : ( + - + )} + + handleRowClick(row, mes)} + title={ + row.percentuaisPorMes?.[mes] !== undefined + ? `${row.percentuaisPorMes[mes].toFixed(1)}%` + : '-' + } + > + {row.percentuaisPorMes?.[mes] !== undefined ? ( + `${row.percentuaisPorMes[mes].toFixed(1)}%` + ) : ( + - + )} + + ); - return ( - - {formatted} - - ); - })()} - - - {/* Coluna Percentual Total */} - handleRowClick(row)} - title={ - row.percentualTotal !== undefined + })} + + {/* Coluna Total */} + handleRowClick(row)} + title={row.total ? formatCurrency(row.total) : '-'} + > + {(() => { + const { formatted, isNegative } = formatCurrencyWithColor( + row.total! + ); + return ( + + {formatted} + + ); + })()} + + + {/* Coluna Percentual Total */} + handleRowClick(row)} + title={ + row.percentualTotal !== undefined + ? `${row.percentualTotal.toFixed(1)}%` + : '-' + } + > + {row.percentualTotal !== undefined ? `${row.percentualTotal.toFixed(1)}%` - : "-" - } - > - {row.percentualTotal !== undefined - ? `${row.percentualTotal.toFixed(1)}%` - : "-"} - - - ); -}); + : '-'} + + + ); + } +); TableRow.displayName = 'TableRow'; @@ -279,38 +330,38 @@ export default function Teste() { // Estados para filtros const [filtros, setFiltros] = useState({ - periodoDe: "", - periodoAte: "", - filial: "Todas", + periodoDe: '', + periodoAte: '', + filial: 'Todas', }); - + // Estados para multi-seleção const [filiaisSelecionadas, setFiliaisSelecionadas] = useState([]); const [isFilterOpen, setIsFilterOpen] = useState(false); const [dadosFiltrados, setDadosFiltrados] = useState([]); const [filtrosAplicados, setFiltrosAplicados] = useState(false); - + // Estados para opções dos filtros const [opcoesGrupos, setOpcoesGrupos] = useState([]); const [opcoesFiliais, setOpcoesFiliais] = useState([]); - + // Estados para filtros de busca nos campos de seleção - const [filtroFilial, setFiltroFilial] = useState(""); + const [filtroFilial, setFiltroFilial] = useState(''); // Estados para analítico const [analiticoFiltros, setAnaliticoFiltros] = useState({ - dataInicio: "", - dataFim: "", - centroCusto: "", - codigoGrupo: "", - codigoSubgrupo: "", - codigoConta: "", - codFilial: "", - linhaSelecionada: "", - excluirCentroCusto: "", - excluirCodigoConta: "", - codigosCentrosCustoSelecionados: "", - codigosContasSelecionadas: "", + dataInicio: '', + dataFim: '', + centroCusto: '', + codigoGrupo: '', + codigoSubgrupo: '', + codigoConta: '', + codFilial: '', + linhaSelecionada: '', + excluirCentroCusto: '', + excluirCodigoConta: '', + codigosCentrosCustoSelecionados: '', + codigosContasSelecionadas: '', }); const [linhaSelecionada, setLinhaSelecionada] = useState(null); const [isAllExpanded, setIsAllExpanded] = useState(false); @@ -322,49 +373,64 @@ export default function Teste() { const carregarPeriodosDisponiveis = async () => { try { - const response = await fetch("/api/dre-filial-oracle"); + const response = await fetch('/api/dre-filial-oracle'); if (!response.ok) { throw new Error(`Erro HTTP: ${response.status}`); } - + const dadosCompletos = await response.json(); - + // Extrair períodos únicos dos dados - const periodosUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.data_competencia))].sort() as string[]; + const periodosUnicos = [ + ...new Set( + dadosCompletos.map((item: DREItem) => item.data_competencia) + ), + ].sort() as string[]; setMesesDisponiveis(periodosUnicos); - + // Extrair grupos únicos - const gruposUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.grupo))].sort() as string[]; + const gruposUnicos = [ + ...new Set(dadosCompletos.map((item: DREItem) => item.grupo)), + ].sort() as string[]; setOpcoesGrupos(gruposUnicos); - + // Extrair filiais únicas - const filiaisUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.filial || item.codfilial).filter(Boolean))].sort() as string[]; + const filiaisUnicas = [ + ...new Set( + dadosCompletos + .map((item: DREItem) => item.filial || item.codfilial) + .filter(Boolean) + ), + ].sort() as string[]; setOpcoesFiliais(filiaisUnicas); - + // Inicializar com todas as filiais selecionadas setFiliaisSelecionadas(filiaisUnicas); - + // Inicializar filtros de período com o ano corrente const agora = new Date(); const anoAtual = agora.getFullYear(); const mesAtual = String(agora.getMonth() + 1).padStart(2, '0'); const periodoAtual = `${anoAtual}-${mesAtual}`; const primeiroMesAno = `${anoAtual}-01`; - + // Verificar se os períodos existem nos dados disponíveis - const periodoDeValido = periodosUnicos.includes(primeiroMesAno) ? primeiroMesAno : (periodosUnicos[0] || primeiroMesAno); - const periodoAteValido = periodosUnicos.includes(periodoAtual) ? periodoAtual : (periodosUnicos[periodosUnicos.length - 1] || periodoAtual); - - setFiltros(prev => ({ + const periodoDeValido = periodosUnicos.includes(primeiroMesAno) + ? primeiroMesAno + : periodosUnicos[0] || primeiroMesAno; + const periodoAteValido = periodosUnicos.includes(periodoAtual) + ? periodoAtual + : periodosUnicos[periodosUnicos.length - 1] || periodoAtual; + + setFiltros((prev) => ({ ...prev, periodoDe: periodoDeValido, - periodoAte: periodoAteValido + periodoAte: periodoAteValido, })); - + // NÃO inicializar filtros do analítico - só serão definidos após clique em célula - } catch (error) { - console.error("Erro ao carregar períodos:", error); + console.error('Erro ao carregar períodos:', error); } }; @@ -372,7 +438,7 @@ export default function Teste() { try { setLoading(true); setError(null); - const response = await fetch("/api/dre-filial-oracle"); + const response = await fetch('/api/dre-filial-oracle'); if (!response.ok) { throw new Error(`Erro ao carregar dados: ${response.status}`); @@ -392,166 +458,190 @@ export default function Teste() { setMesesDisponiveis(meses); } catch (err) { - setError(err instanceof Error ? err.message : "Erro desconhecido"); + setError(err instanceof Error ? err.message : 'Erro desconhecido'); } finally { setLoading(false); } }; const formatCurrency = React.useCallback((value: string | number) => { - const numValue = typeof value === "string" ? parseFloat(value) : value; - return numValue.toLocaleString("pt-BR", { - style: "currency", - currency: "BRL", + const numValue = typeof value === 'string' ? parseFloat(value) : value; + return numValue.toLocaleString('pt-BR', { + style: 'currency', + currency: 'BRL', }); }, []); - const formatCurrencyWithColor = React.useCallback((value: string | number) => { - const numValue = typeof value === "string" ? parseFloat(value) : value; - const formatted = formatCurrency(value); - const isNegative = numValue < 0; - return { formatted, isNegative }; - }, [formatCurrency]); + const formatCurrencyWithColor = React.useCallback( + (value: string | number) => { + const numValue = typeof value === 'string' ? parseFloat(value) : value; + const formatted = formatCurrency(value); + const isNegative = numValue < 0; + return { formatted, isNegative }; + }, + [formatCurrency] + ); // Função para lidar com clique nas linhas - const handleRowClick = React.useCallback((row: HierarchicalRow, mesSelecionado?: string, filialSelecionada?: string) => { - console.log('🖱️ Clique na linha:', row); - console.log('📅 Mês selecionado:', mesSelecionado); - console.log('🏢 Filial selecionada:', filialSelecionada); - - // Linhas calculadas não devem abrir o componente analítico - if (row.type === "calculado") { - console.log('⚠️ Linha calculada - não abre componente analítico'); - return; - } - - if (!data.length) { - console.log('⚠️ Sem dados disponíveis'); - return; - } + const handleRowClick = React.useCallback( + ( + row: HierarchicalRow, + mesSelecionado?: string, + filialSelecionada?: string + ) => { + console.log('🖱️ Clique na linha:', row); + console.log('📅 Mês selecionado:', mesSelecionado); + console.log('🏢 Filial selecionada:', filialSelecionada); - // Pegar todas as datas disponíveis para definir o período - const datas = data.map((item) => item.data_competencia); - const dataInicio = Math.min(...datas.map((d) => new Date(d).getTime())); - const dataFim = Math.max(...datas.map((d) => new Date(d).getTime())); + // Linhas calculadas não devem abrir o componente analítico + if (row.type === 'calculado') { + console.log('⚠️ Linha calculada - não abre componente analítico'); + return; + } - const dataInicioStr = new Date(dataInicio).toISOString().substring(0, 7); // YYYY-MM - const dataFimStr = new Date(dataFim).toISOString().substring(0, 7); // YYYY-MM + if (!data.length) { + console.log('⚠️ Sem dados disponíveis'); + return; + } - // Se um mês específico foi selecionado, usar apenas esse mês - const dataInicioFiltro = mesSelecionado || dataInicioStr; - const dataFimFiltro = mesSelecionado || dataFimStr; + // Pegar todas as datas disponíveis para definir o período + const datas = data.map((item) => item.data_competencia); + const dataInicio = Math.min(...datas.map((d) => new Date(d).getTime())); + const dataFim = Math.max(...datas.map((d) => new Date(d).getTime())); - // Determinar filtros baseado na hierarquia [grupo, conta] - let codigoGrupoFiltro = ""; - let codigoContaFiltro = ""; - - if (row.type === "grupo" || row.type === "conta") { - // Buscar o CODGRUPO dos dados originais que correspondem a esta linha - const itemsCorrespondentes = data.filter((item: DREItem) => { - // Filtrar por período se um mês específico foi selecionado - if (mesSelecionado && item.data_competencia !== mesSelecionado) { - return false; - } - - // Se é um totalizador (mesSelecionado presente mas filialSelecionada não), não filtrar por filial - // Caso contrário, filtrar por filial se especificada - if (filialSelecionada) { - const itemFilial = item.filial || item.codfilial || ""; - if (itemFilial !== filialSelecionada) { + const dataInicioStr = new Date(dataInicio).toISOString().substring(0, 7); // YYYY-MM + const dataFimStr = new Date(dataFim).toISOString().substring(0, 7); // YYYY-MM + + // Se um mês específico foi selecionado, usar apenas esse mês + const dataInicioFiltro = mesSelecionado || dataInicioStr; + const dataFimFiltro = mesSelecionado || dataFimStr; + + // Determinar filtros baseado na hierarquia [grupo, conta] + let codigoGrupoFiltro = ''; + let codigoContaFiltro = ''; + + if (row.type === 'grupo' || row.type === 'conta') { + // Buscar o CODGRUPO dos dados originais que correspondem a esta linha + const itemsCorrespondentes = data.filter((item: DREItem) => { + // Filtrar por período se um mês específico foi selecionado + if (mesSelecionado && item.data_competencia !== mesSelecionado) { return false; } - } - - if (row.type === "grupo") { - return item.codigo_grupo === row.codigo_grupo || item.codgrupo === row.codigo_grupo; - } else if (row.type === "conta") { - return (item.codigo_grupo === row.codigo_grupo || item.codgrupo === row.codigo_grupo) && - item.codigo_conta === row.codigo_conta; - } - return false; - }); - - // Pegar o CODGRUPO do primeiro item encontrado - if (itemsCorrespondentes.length > 0) { - const primeiroItem = itemsCorrespondentes[0]; - codigoGrupoFiltro = primeiroItem.codgrupo || primeiroItem.codigo_grupo || ""; - } - } - - // Filtrar por conta se for nível conta - if (row.type === "conta") { - codigoContaFiltro = row.codigo_conta?.toString() || ""; - } - // Determinar CODFILIAL baseado na filial selecionada - // Se é um totalizador (mesSelecionado presente mas filialSelecionada não), incluir todas as filiais selecionadas - let codFilialFiltro = ""; - if (filialSelecionada) { - // Se a filial selecionada já é um código numérico, usar diretamente - // Caso contrário, buscar o CODFILIAL correspondente ao nome da filial nos dados - if (/^\d+$/.test(filialSelecionada)) { - codFilialFiltro = filialSelecionada; - } else { - // Buscar o CODFILIAL correspondente ao nome da filial nos dados - const itemComFilial = data.find((item: DREItem) => { - const itemFilial = item.filial || item.codfilial || ""; - return itemFilial === filialSelecionada; + // Se é um totalizador (mesSelecionado presente mas filialSelecionada não), não filtrar por filial + // Caso contrário, filtrar por filial se especificada + if (filialSelecionada) { + const itemFilial = item.filial || item.codfilial || ''; + if (itemFilial !== filialSelecionada) { + return false; + } + } + + if (row.type === 'grupo') { + return ( + item.codigo_grupo === row.codigo_grupo || + item.codgrupo === row.codigo_grupo + ); + } else if (row.type === 'conta') { + return ( + (item.codigo_grupo === row.codigo_grupo || + item.codgrupo === row.codigo_grupo) && + item.codigo_conta === row.codigo_conta + ); + } + return false; }); - if (itemComFilial) { - codFilialFiltro = itemComFilial.codfilial || filialSelecionada; - } else { - // Se não encontrar, tentar usar o próprio valor como código - codFilialFiltro = filialSelecionada; + + // Pegar o CODGRUPO do primeiro item encontrado + if (itemsCorrespondentes.length > 0) { + const primeiroItem = itemsCorrespondentes[0]; + codigoGrupoFiltro = + primeiroItem.codgrupo || primeiroItem.codigo_grupo || ''; } } - } else if (mesSelecionado) { - // Se mesSelecionado está presente mas filialSelecionada não, é um totalizador - // Incluir todas as filiais selecionadas (ou todas disponíveis se nenhuma estiver selecionada) - const filiaisParaTotalizador = filiaisSelecionadas.length > 0 - ? filiaisSelecionadas - : opcoesFiliais; - - // Converter nomes de filiais para códigos CODFILIAL - const codigosFiliais = filiaisParaTotalizador.map(filial => { - if (/^\d+$/.test(filial)) { - return filial; - } else { - const itemComFilial = data.find((item: DREItem) => { - const itemFilial = item.filial || item.codfilial || ""; - return itemFilial === filial; - }); - return itemComFilial?.codfilial || filial; - } - }).filter(Boolean); - - // Passar como string separada por vírgula para a API processar com IN - codFilialFiltro = codigosFiliais.join(','); - console.log('📊 Totalizador: incluindo todas as filiais selecionadas:', codigosFiliais); - } - const novosFiltros = { - dataInicio: dataInicioFiltro, - dataFim: dataFimFiltro, - centroCusto: "", // Não aplicável na hierarquia filial - codigoGrupo: codigoGrupoFiltro, - codigoSubgrupo: "", // Não aplicável na hierarquia filial - codigoConta: codigoContaFiltro, - codFilial: codFilialFiltro, // Vazio para totalizador = todas as filiais do mês - linhaSelecionada: row.grupo || row.conta || "", - excluirCentroCusto: "", - excluirCodigoConta: "", - codigosCentrosCustoSelecionados: "", - codigosContasSelecionadas: "", - }; - - console.log('🎯 Novos filtros para analítico:', novosFiltros); - console.log('📊 É totalizador?', mesSelecionado && !filialSelecionada ? 'SIM' : 'NÃO'); - setAnaliticoFiltros(novosFiltros); - }, [data, filiaisSelecionadas, opcoesFiliais]); + // Filtrar por conta se for nível conta + if (row.type === 'conta') { + codigoContaFiltro = row.codigo_conta?.toString() || ''; + } + + // Determinar CODFILIAL baseado na filial selecionada + // Se é um totalizador (mesSelecionado presente mas filialSelecionada não), incluir todas as filiais selecionadas + let codFilialFiltro = ''; + if (filialSelecionada) { + // Se a filial selecionada já é um código numérico, usar diretamente + // Caso contrário, buscar o CODFILIAL correspondente ao nome da filial nos dados + if (/^\d+$/.test(filialSelecionada)) { + codFilialFiltro = filialSelecionada; + } else { + // Buscar o CODFILIAL correspondente ao nome da filial nos dados + const itemComFilial = data.find((item: DREItem) => { + const itemFilial = item.filial || item.codfilial || ''; + return itemFilial === filialSelecionada; + }); + if (itemComFilial) { + codFilialFiltro = itemComFilial.codfilial || filialSelecionada; + } else { + // Se não encontrar, tentar usar o próprio valor como código + codFilialFiltro = filialSelecionada; + } + } + } else if (mesSelecionado) { + // Se mesSelecionado está presente mas filialSelecionada não, é um totalizador + // Incluir todas as filiais selecionadas (ou todas disponíveis se nenhuma estiver selecionada) + const filiaisParaTotalizador = + filiaisSelecionadas.length > 0 ? filiaisSelecionadas : opcoesFiliais; + + // Converter nomes de filiais para códigos CODFILIAL + const codigosFiliais = filiaisParaTotalizador + .map((filial) => { + if (/^\d+$/.test(filial)) { + return filial; + } else { + const itemComFilial = data.find((item: DREItem) => { + const itemFilial = item.filial || item.codfilial || ''; + return itemFilial === filial; + }); + return itemComFilial?.codfilial || filial; + } + }) + .filter(Boolean); + + // Passar como string separada por vírgula para a API processar com IN + codFilialFiltro = codigosFiliais.join(','); + console.log( + '📊 Totalizador: incluindo todas as filiais selecionadas:', + codigosFiliais + ); + } + + const novosFiltros = { + dataInicio: dataInicioFiltro, + dataFim: dataFimFiltro, + centroCusto: '', // Não aplicável na hierarquia filial + codigoGrupo: codigoGrupoFiltro, + codigoSubgrupo: '', // Não aplicável na hierarquia filial + codigoConta: codigoContaFiltro, + codFilial: codFilialFiltro, // Vazio para totalizador = todas as filiais do mês + linhaSelecionada: row.grupo || row.conta || '', + excluirCentroCusto: '', + excluirCodigoConta: '', + codigosCentrosCustoSelecionados: '', + codigosContasSelecionadas: '', + }; + + console.log('🎯 Novos filtros para analítico:', novosFiltros); + console.log( + '📊 É totalizador?', + mesSelecionado && !filialSelecionada ? 'SIM' : 'NÃO' + ); + setAnaliticoFiltros(novosFiltros); + }, + [data, filiaisSelecionadas, opcoesFiliais] + ); const toggleGrupo = useCallback((codigoGrupo: string) => { - setExpandedGrupos(prev => { + setExpandedGrupos((prev) => { const newExpanded = new Set(prev); if (newExpanded.has(codigoGrupo)) { newExpanded.delete(codigoGrupo); @@ -563,9 +653,9 @@ export default function Teste() { }, []); const toggleFilial = (filial: string) => { - setFiliaisSelecionadas(prev => { + setFiliaisSelecionadas((prev) => { if (prev.includes(filial)) { - return prev.filter(f => f !== filial); + return prev.filter((f) => f !== filial); } else { return [...prev, filial]; } @@ -581,59 +671,69 @@ export default function Teste() { }; // Função auxiliar para calcular valores por mês - const calcularValoresPorMes = React.useCallback((items: DREItem[]): Record => { - const valoresPorMes: Record = {}; + const calcularValoresPorMes = React.useCallback( + (items: DREItem[]): Record => { + const valoresPorMes: Record = {}; - mesesDisponiveis.forEach(mes => { - valoresPorMes[mes] = 0; - }); + mesesDisponiveis.forEach((mes) => { + valoresPorMes[mes] = 0; + }); - items.forEach((item) => { - const anoMes = item.data_competencia; - if (anoMes && valoresPorMes[anoMes] !== undefined) { - valoresPorMes[anoMes] += parseFloat(item.valor); - } - }); + items.forEach((item) => { + const anoMes = item.data_competencia; + if (anoMes && valoresPorMes[anoMes] !== undefined) { + valoresPorMes[anoMes] += parseFloat(item.valor); + } + }); - return valoresPorMes; - }, [mesesDisponiveis]); + return valoresPorMes; + }, + [mesesDisponiveis] + ); // Função auxiliar para calcular valores por mês e por filial - const calcularValoresPorMesPorFilial = React.useCallback((items: DREItem[]): Record> => { - const valoresPorMesPorFilial: Record> = {}; + const calcularValoresPorMesPorFilial = React.useCallback( + (items: DREItem[]): Record> => { + const valoresPorMesPorFilial: Record> = {}; - // Extrair filiais únicas dos próprios items para evitar dependência de arrays externos - const filiaisDisponiveis = [...new Set(items.map(item => item.filial || item.codfilial).filter(Boolean))] as string[]; + // Extrair filiais únicas dos próprios items para evitar dependência de arrays externos + const filiaisDisponiveis = [ + ...new Set( + items.map((item) => item.filial || item.codfilial).filter(Boolean) + ), + ] as string[]; - mesesDisponiveis.forEach(mes => { - valoresPorMesPorFilial[mes] = {}; - filiaisDisponiveis.forEach(filial => { - valoresPorMesPorFilial[mes][filial] = 0; + mesesDisponiveis.forEach((mes) => { + valoresPorMesPorFilial[mes] = {}; + filiaisDisponiveis.forEach((filial) => { + valoresPorMesPorFilial[mes][filial] = 0; + }); }); - }); - items.forEach((item) => { - const anoMes = item.data_competencia; - const filial = item.filial || item.codfilial || ""; - if (anoMes && valoresPorMesPorFilial[anoMes] && filial) { - if (!valoresPorMesPorFilial[anoMes][filial]) { - valoresPorMesPorFilial[anoMes][filial] = 0; + items.forEach((item) => { + const anoMes = item.data_competencia; + const filial = item.filial || item.codfilial || ''; + if (anoMes && valoresPorMesPorFilial[anoMes] && filial) { + if (!valoresPorMesPorFilial[anoMes][filial]) { + valoresPorMesPorFilial[anoMes][filial] = 0; + } + valoresPorMesPorFilial[anoMes][filial] += parseFloat(item.valor); } - valoresPorMesPorFilial[anoMes][filial] += parseFloat(item.valor); - } - }); + }); - return valoresPorMesPorFilial; - }, [mesesDisponiveis]); + return valoresPorMesPorFilial; + }, + [mesesDisponiveis] + ); // Memoizar valores do grupo 01 por mês para evitar recálculos repetidos const valoresGrupo01PorMesMemo = React.useMemo(() => { const valores: Record = {}; - mesesDisponiveis.forEach(mes => { + mesesDisponiveis.forEach((mes) => { valores[mes] = data - .filter(item => { - const codgrupo = item.codgrupo || item.codigo_grupo || ""; - return codgrupo === "01" && item.data_competencia === mes; + .filter((item) => { + const codgrupo = item.codgrupo || item.codigo_grupo || ''; + return codgrupo === '01' && item.data_competencia === mes; }) .reduce((sum, item) => sum + parseFloat(item.valor), 0); }); @@ -644,16 +744,24 @@ export default function Teste() { const valoresGrupo01PorMesPorFilialMemo = React.useMemo(() => { const valores: Record> = {}; // Extrair filiais únicas dos próprios dados para evitar dependência de arrays externos - const filiaisDisponiveis = [...new Set(data.map(item => item.filial || item.codfilial).filter(Boolean))] as string[]; - - mesesDisponiveis.forEach(mes => { + const filiaisDisponiveis = [ + ...new Set( + data.map((item) => item.filial || item.codfilial).filter(Boolean) + ), + ] as string[]; + + mesesDisponiveis.forEach((mes) => { valores[mes] = {}; - filiaisDisponiveis.forEach(filial => { + filiaisDisponiveis.forEach((filial) => { valores[mes][filial] = data - .filter(item => { - const codgrupo = item.codgrupo || item.codigo_grupo || ""; - const itemFilial = item.filial || item.codfilial || ""; - return codgrupo === "01" && item.data_competencia === mes && itemFilial === filial; + .filter((item) => { + const codgrupo = item.codgrupo || item.codigo_grupo || ''; + const itemFilial = item.filial || item.codfilial || ''; + return ( + codgrupo === '01' && + item.data_competencia === mes && + itemFilial === filial + ); }) .reduce((sum, item) => sum + parseFloat(item.valor), 0); }); @@ -662,99 +770,115 @@ export default function Teste() { }, [data, mesesDisponiveis]); // Função para calcular percentuais baseado no CODGRUPO 01 (FATURAMENTO LÍQUIDO) - const calcularPercentuaisPorMes = React.useCallback(( - valoresPorMes: Record, - codigoGrupo?: string - ): Record => { - const percentuais: Record = {}; - - // Se for CODGRUPO 01, sempre retornar 100% - if (codigoGrupo === "01") { - mesesDisponiveis.forEach(mes => { - percentuais[mes] = 100; - }); - return percentuais; - } + const calcularPercentuaisPorMes = React.useCallback( + ( + valoresPorMes: Record, + codigoGrupo?: string + ): Record => { + const percentuais: Record = {}; - // Usar valores memoizados do grupo 01 - Object.keys(valoresPorMes).forEach((mes) => { - const valorAtual = valoresPorMes[mes]; - const valorGrupo01 = valoresGrupo01PorMesMemo[mes] || 0; - - if (valorGrupo01 !== 0) { - percentuais[mes] = (valorAtual / valorGrupo01) * 100; - } else { - percentuais[mes] = 0; - } - }); - - return percentuais; - }, [mesesDisponiveis, valoresGrupo01PorMesMemo]); - - // Função para calcular percentuais por mês e por filial baseado no CODGRUPO 01 - const calcularPercentuaisPorMesPorFilial = React.useCallback(( - valoresPorMesPorFilial: Record>, - codigoGrupo?: string - ): Record> => { - const percentuaisPorMesPorFilial: Record> = {}; - - // Extrair filiais únicas dos próprios valoresPorMesPorFilial para evitar dependência de arrays externos - const primeiroMes = mesesDisponiveis[0] || ""; - const filiaisDisponiveis = Object.keys(valoresPorMesPorFilial[primeiroMes] || {}); - - // Se for CODGRUPO 01, sempre retornar 100% para todas as filiais - if (codigoGrupo === "01") { - mesesDisponiveis.forEach(mes => { - percentuaisPorMesPorFilial[mes] = {}; - filiaisDisponiveis.forEach(filial => { - percentuaisPorMesPorFilial[mes][filial] = 100; + // Se for CODGRUPO 01, sempre retornar 100% + if (codigoGrupo === '01') { + mesesDisponiveis.forEach((mes) => { + percentuais[mes] = 100; }); - }); - return percentuaisPorMesPorFilial; - } + return percentuais; + } + + // Usar valores memoizados do grupo 01 + Object.keys(valoresPorMes).forEach((mes) => { + const valorAtual = valoresPorMes[mes]; + const valorGrupo01 = valoresGrupo01PorMesMemo[mes] || 0; - // Usar valores memoizados do grupo 01 - mesesDisponiveis.forEach(mes => { - percentuaisPorMesPorFilial[mes] = {}; - filiaisDisponiveis.forEach(filial => { - const valorAtual = valoresPorMesPorFilial[mes]?.[filial] || 0; - const valorGrupo01 = valoresGrupo01PorMesPorFilialMemo[mes]?.[filial] || 0; - if (valorGrupo01 !== 0) { - percentuaisPorMesPorFilial[mes][filial] = (valorAtual / valorGrupo01) * 100; + percentuais[mes] = (valorAtual / valorGrupo01) * 100; } else { - percentuaisPorMesPorFilial[mes][filial] = 0; + percentuais[mes] = 0; } }); - }); - return percentuaisPorMesPorFilial; - }, [mesesDisponiveis, valoresGrupo01PorMesPorFilialMemo]); + return percentuais; + }, + [mesesDisponiveis, valoresGrupo01PorMesMemo] + ); + + // Função para calcular percentuais por mês e por filial baseado no CODGRUPO 01 + const calcularPercentuaisPorMesPorFilial = React.useCallback( + ( + valoresPorMesPorFilial: Record>, + codigoGrupo?: string + ): Record> => { + const percentuaisPorMesPorFilial: Record< + string, + Record + > = {}; + + // Extrair filiais únicas dos próprios valoresPorMesPorFilial para evitar dependência de arrays externos + const primeiroMes = mesesDisponiveis[0] || ''; + const filiaisDisponiveis = Object.keys( + valoresPorMesPorFilial[primeiroMes] || {} + ); + + // Se for CODGRUPO 01, sempre retornar 100% para todas as filiais + if (codigoGrupo === '01') { + mesesDisponiveis.forEach((mes) => { + percentuaisPorMesPorFilial[mes] = {}; + filiaisDisponiveis.forEach((filial) => { + percentuaisPorMesPorFilial[mes][filial] = 100; + }); + }); + return percentuaisPorMesPorFilial; + } + + // Usar valores memoizados do grupo 01 + mesesDisponiveis.forEach((mes) => { + percentuaisPorMesPorFilial[mes] = {}; + filiaisDisponiveis.forEach((filial) => { + const valorAtual = valoresPorMesPorFilial[mes]?.[filial] || 0; + const valorGrupo01 = + valoresGrupo01PorMesPorFilialMemo[mes]?.[filial] || 0; + + if (valorGrupo01 !== 0) { + percentuaisPorMesPorFilial[mes][filial] = + (valorAtual / valorGrupo01) * 100; + } else { + percentuaisPorMesPorFilial[mes][filial] = 0; + } + }); + }); + + return percentuaisPorMesPorFilial; + }, + [mesesDisponiveis, valoresGrupo01PorMesPorFilialMemo] + ); // Memoizar total do grupo 01 const totalGrupo01Memo = React.useMemo(() => { return data - .filter(item => { - const codgrupo = item.codgrupo || item.codigo_grupo || ""; - return codgrupo === "01"; + .filter((item) => { + const codgrupo = item.codgrupo || item.codigo_grupo || ''; + return codgrupo === '01'; }) .reduce((sum, item) => sum + parseFloat(item.valor), 0); }, [data]); // Função para calcular percentual do total baseado no CODGRUPO 01 - const calcularPercentualTotal = React.useCallback((total: number, codigoGrupo?: string): number => { - // Se for CODGRUPO 01, sempre retornar 100% - if (codigoGrupo === "01") { - return 100; - } - - // Usar total memoizado do grupo 01 - if (totalGrupo01Memo !== 0) { - return (total / totalGrupo01Memo) * 100; - } else { - return 0; - } - }, [totalGrupo01Memo]); + const calcularPercentualTotal = React.useCallback( + (total: number, codigoGrupo?: string): number => { + // Se for CODGRUPO 01, sempre retornar 100% + if (codigoGrupo === '01') { + return 100; + } + + // Usar total memoizado do grupo 01 + if (totalGrupo01Memo !== 0) { + return (total / totalGrupo01Memo) * 100; + } else { + return 0; + } + }, + [totalGrupo01Memo] + ); const buildHierarchicalData = React.useCallback((): HierarchicalRow[] => { const rows: HierarchicalRow[] = []; @@ -762,7 +886,7 @@ export default function Teste() { // Hierarquia simplificada: [grupo, conta] // Agrupar por CODGRUPO const gruposPorCodigo = data.reduce((acc, item) => { - const codgrupo = item.codgrupo || item.codigo_grupo || ""; + const codgrupo = item.codgrupo || item.codigo_grupo || ''; if (!codgrupo) return acc; if (!acc[codgrupo]) { acc[codgrupo] = []; @@ -773,19 +897,23 @@ export default function Teste() { // Calcular valores por grupo para linhas calculadas const valoresPorGrupo: Record> = {}; - Object.keys(gruposPorCodigo).forEach(codgrupo => { - valoresPorGrupo[codgrupo] = calcularValoresPorMes(gruposPorCodigo[codgrupo]); + 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; + 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); } - return codA.localeCompare(codB); - }); + ); sortedGrupos.forEach(([codgrupo, items], index) => { // Calcular total do grupo @@ -798,7 +926,7 @@ export default function Teste() { // Linha do grupo (Level 0) rows.push({ - type: "grupo", + type: 'grupo', level: 0, grupo: items[0]?.grupo || codgrupo, codigo_grupo: codgrupo, @@ -806,15 +934,21 @@ export default function Teste() { isExpanded: expandedGrupos.has(codgrupo), valoresPorMes: valoresGrupoPorMes, valoresPorMesPorFilial: valoresGrupoPorMesPorFilial, - percentuaisPorMes: calcularPercentuaisPorMes(valoresGrupoPorMes, codgrupo), - percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial(valoresGrupoPorMesPorFilial, codgrupo), + percentuaisPorMes: calcularPercentuaisPorMes( + valoresGrupoPorMes, + codgrupo + ), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( + valoresGrupoPorMesPorFilial, + codgrupo + ), percentualTotal: calcularPercentualTotal(totalGrupo, codgrupo), }); if (expandedGrupos.has(codgrupo)) { // Agrupar por conta dentro do grupo const contas = items.reduce((acc, item) => { - const conta = item.conta || ""; + const conta = item.conta || ''; if (!conta) return acc; if (!acc[conta]) { acc[conta] = []; @@ -824,16 +958,18 @@ export default function Teste() { }, {} 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; + 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); } - - return contaA.localeCompare(contaB); - }); + ); sortedContas.forEach(([conta, contaItems]) => { const totalConta = contaItems.reduce( @@ -841,11 +977,12 @@ export default function Teste() { 0 ); const valoresContaPorMes = calcularValoresPorMes(contaItems); - const valoresContaPorMesPorFilial = calcularValoresPorMesPorFilial(contaItems); + const valoresContaPorMesPorFilial = + calcularValoresPorMesPorFilial(contaItems); // Linha da conta (Level 1) rows.push({ - type: "conta", + type: 'conta', level: 1, grupo: items[0]?.grupo || codgrupo, codigo_grupo: codgrupo, @@ -855,8 +992,14 @@ export default function Teste() { isExpanded: false, valoresPorMes: valoresContaPorMes, valoresPorMesPorFilial: valoresContaPorMesPorFilial, - percentuaisPorMes: calcularPercentuaisPorMes(valoresContaPorMes, codgrupo), - percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial(valoresContaPorMesPorFilial, codgrupo), + percentuaisPorMes: calcularPercentuaisPorMes( + valoresContaPorMes, + codgrupo + ), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( + valoresContaPorMesPorFilial, + codgrupo + ), percentualTotal: calcularPercentualTotal(totalConta, codgrupo), }); }); @@ -866,198 +1009,243 @@ export default function Teste() { // 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)) { + + if ( + codgrupo === '02' || + (parseInt(codgrupo) === 2 && proximoNumero > 2) + ) { // Calcular MARGEM DE LOJA = CODGRUPO 01 - CODGRUPO 02 - const valoresGrupo01 = valoresPorGrupo["01"] || {}; - const valoresGrupo02 = valoresPorGrupo["02"] || {}; - + const valoresGrupo01 = valoresPorGrupo['01'] || {}; + const valoresGrupo02 = valoresPorGrupo['02'] || {}; + // Calcular valores por mês para MARGEM DE LOJA const valoresMargemPorMes: Record = {}; - mesesDisponiveis.forEach(mes => { + mesesDisponiveis.forEach((mes) => { const valor01 = valoresGrupo01[mes] || 0; const valor02 = valoresGrupo02[mes] || 0; - valoresMargemPorMes[mes] = valor01 - valor02; + valoresMargemPorMes[mes] = valor01 + valor02; }); // Calcular valores por mês e por filial para MARGEM DE LOJA - const valoresMargemPorMesPorFilial: Record> = {}; - const valoresGrupo01PorFilial = gruposPorCodigo["01"] ? calcularValoresPorMesPorFilial(gruposPorCodigo["01"]) : {}; - const valoresGrupo02PorFilial = gruposPorCodigo["02"] ? calcularValoresPorMesPorFilial(gruposPorCodigo["02"]) : {}; - + 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 => { + const primeiroMes = mesesDisponiveis[0] || ''; + const filiaisDisponiveis = [ + ...new Set([ + ...Object.keys(valoresGrupo01PorFilial[primeiroMes] || {}), + ...Object.keys(valoresGrupo02PorFilial[primeiroMes] || {}), + ]), + ]; + + mesesDisponiveis.forEach((mes) => { valoresMargemPorMesPorFilial[mes] = {}; - filiaisDisponiveis.forEach(filial => { + filiaisDisponiveis.forEach((filial) => { const valor01 = valoresGrupo01PorFilial[mes]?.[filial] || 0; const valor02 = valoresGrupo02PorFilial[mes]?.[filial] || 0; - valoresMargemPorMesPorFilial[mes][filial] = valor01 - valor02; + valoresMargemPorMesPorFilial[mes][filial] = valor01 + valor02; }); }); // Calcular total - const totalMargem = Object.values(valoresMargemPorMes).reduce((sum, val) => sum + val, 0); + const totalMargem = Object.values(valoresMargemPorMes).reduce( + (sum, val) => sum + val, + 0 + ); // Adicionar linha calculada rows.push({ - type: "calculado", + type: 'calculado', level: 0, - grupo: "MARGEM DE LOJA", - codigo_grupo: "MARGEM", + 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"), + percentuaisPorMes: calcularPercentuaisPorMes( + valoresMargemPorMes, + 'MARGEM' + ), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( + valoresMargemPorMesPorFilial, + 'MARGEM' + ), + percentualTotal: calcularPercentualTotal(totalMargem, 'MARGEM'), isCalculado: true, }); } }); return rows; - }, [data, mesesDisponiveis, expandedGrupos, calcularValoresPorMes, calcularValoresPorMesPorFilial, calcularPercentuaisPorMes, calcularPercentuaisPorMesPorFilial, calcularPercentualTotal]); + }, [ + data, + mesesDisponiveis, + expandedGrupos, + calcularValoresPorMes, + calcularValoresPorMesPorFilial, + calcularPercentuaisPorMes, + calcularPercentuaisPorMesPorFilial, + calcularPercentualTotal, + ]); - const getRowStyle = React.useCallback((row: HierarchicalRow) => { - const baseStyle = - "transition-all duration-200 hover:bg-gradient-to-r hover:from-blue-50/30 hover:to-indigo-50/30"; + const getRowStyle = React.useCallback( + (row: HierarchicalRow) => { + const baseStyle = + 'transition-all duration-200 hover:bg-gradient-to-r hover:from-blue-50/30 hover:to-indigo-50/30'; - const linhaId = `${row.type}-${row.codigo_grupo || ""}-${row.codigo_conta || ""}`; - const isSelected = linhaSelecionada === linhaId; + const linhaId = `${row.type}-${row.codigo_grupo || ''}-${ + row.codigo_conta || '' + }`; + const isSelected = linhaSelecionada === linhaId; - let style = baseStyle; + let style = baseStyle; - if (isSelected) { - style += - " bg-gradient-to-r from-green-100 to-emerald-100 border-l-4 border-green-500 shadow-lg"; - } + if (isSelected) { + style += + ' bg-gradient-to-r from-green-100 to-emerald-100 border-l-4 border-green-500 shadow-lg'; + } - switch (row.type) { - case "grupo": - return `${style} bg-gradient-to-r from-blue-50/20 to-indigo-50/20 font-bold text-gray-900 border-b-2 border-blue-200`; - case "calculado": - return `${style} bg-gradient-to-r from-purple-50/30 to-pink-50/30 font-bold text-purple-900 border-b-2 border-purple-300 italic`; - case "conta": - return `${style} bg-white font-normal text-gray-600`; - default: - return style; - } - }, [linhaSelecionada]); + switch (row.type) { + case 'grupo': + return `${style} bg-gradient-to-r from-blue-50/20 to-indigo-50/20 font-bold text-gray-900 border-b-2 border-blue-200`; + case 'calculado': + return `${style} bg-gradient-to-r from-purple-50/30 to-pink-50/30 font-bold text-purple-900 border-b-2 border-purple-300 italic`; + case 'conta': + return `${style} bg-white font-normal text-gray-600`; + default: + return style; + } + }, + [linhaSelecionada] + ); - const getFixedCellBackground = React.useCallback((row: HierarchicalRow): string => { - const linhaId = `${row.type}-${row.codigo_grupo || ""}-${row.codigo_conta || ""}`; - const isSelected = linhaSelecionada === linhaId; + const getFixedCellBackground = React.useCallback( + (row: HierarchicalRow): string => { + const linhaId = `${row.type}-${row.codigo_grupo || ''}-${ + row.codigo_conta || '' + }`; + const isSelected = linhaSelecionada === linhaId; - if (isSelected) { - return "bg-gradient-to-r from-green-100 to-emerald-100"; - } + if (isSelected) { + return 'bg-gradient-to-r from-green-100 to-emerald-100'; + } - switch (row.type) { - case "grupo": - return "bg-gradient-to-r from-blue-50 to-indigo-50"; - case "calculado": - return "bg-gradient-to-r from-purple-50 to-pink-50"; - case "conta": - return "bg-white"; - default: - return "bg-white"; - } - }, [linhaSelecionada]); + switch (row.type) { + case 'grupo': + return 'bg-gradient-to-r from-blue-50 to-indigo-50'; + case 'calculado': + return 'bg-gradient-to-r from-purple-50 to-pink-50'; + case 'conta': + return 'bg-white'; + default: + return 'bg-white'; + } + }, + [linhaSelecionada] + ); const getIndentStyle = React.useCallback((level: number) => { return { paddingLeft: `${level * 20}px` }; }, []); - const renderCellContent = React.useCallback((row: HierarchicalRow) => { - switch (row.type) { - case "grupo": - return ( -
- + +
+ ); + case 'calculado': + return ( +
+
+ {/* = */} - )} - - -
- ); - case "calculado": - return ( -
-
- {/* = */} - -
-
-
- - {row.grupo} - -
-
-
- ); - case "conta": - return ( -
-
- -
- -
- ); - default: - return null; - } - }, [toggleGrupo, handleRowClick]); +
+ ); + case 'conta': + return ( +
+
+ +
+ +
+ ); + default: + return null; + } + }, + [toggleGrupo, handleRowClick] + ); const toggleExpandAll = () => { if (isAllExpanded) { setExpandedGrupos(new Set()); setIsAllExpanded(false); } else { - const todosGrupos = [...new Set(data.map(item => item.codgrupo || item.codigo_grupo).filter(Boolean))]; + const todosGrupos = [ + ...new Set( + data.map((item) => item.codgrupo || item.codigo_grupo).filter(Boolean) + ), + ]; setExpandedGrupos(new Set(todosGrupos)); setIsAllExpanded(true); } @@ -1065,58 +1253,60 @@ export default function Teste() { const aplicarFiltros = async () => { setIsFilterOpen(false); - + setTimeout(async () => { try { setLoading(true); setError(null); - - const response = await fetch("/api/dre-filial-oracle"); + + const response = await fetch('/api/dre-filial-oracle'); if (!response.ok) { throw new Error(`Erro HTTP: ${response.status}`); } - + const dadosCompletos = await response.json(); - + // Aplicar filtros nos dados let dadosFiltrados = dadosCompletos; - + // Filtro por período if (filtros.periodoDe && filtros.periodoAte) { dadosFiltrados = dadosFiltrados.filter((item: DREItem) => { const dataItem = item.data_competencia; - return dataItem >= filtros.periodoDe && dataItem <= filtros.periodoAte; + return ( + dataItem >= filtros.periodoDe && dataItem <= filtros.periodoAte + ); }); } - - // Filtro por filial (multi-seleção) + + // Filtro por filial (multi-seleção) if (filiaisSelecionadas.length > 0) { dadosFiltrados = dadosFiltrados.filter((item: DREItem) => { - const filialItem = item.filial || item.codfilial || ""; + const filialItem = item.filial || item.codfilial || ''; return filiaisSelecionadas.includes(filialItem); }); } - + setDadosFiltrados(dadosFiltrados); setData(dadosFiltrados); setFiltrosAplicados(true); - + // Limpar filtros do analítico ao aplicar novos filtros na tabela setAnaliticoFiltros({ - dataInicio: "", - dataFim: "", - centroCusto: "", - codigoGrupo: "", - codigoSubgrupo: "", - codigoConta: "", - codFilial: "", - linhaSelecionada: "", - excluirCentroCusto: "", - excluirCodigoConta: "", - codigosCentrosCustoSelecionados: "", - codigosContasSelecionadas: "", + dataInicio: '', + dataFim: '', + centroCusto: '', + codigoGrupo: '', + codigoSubgrupo: '', + codigoConta: '', + codFilial: '', + linhaSelecionada: '', + excluirCentroCusto: '', + excluirCodigoConta: '', + codigosCentrosCustoSelecionados: '', + codigosContasSelecionadas: '', }); - + // Extrair meses únicos dos dados filtrados const meses = [ ...new Set( @@ -1124,9 +1314,8 @@ export default function Teste() { ), ].sort() as string[]; setMesesDisponiveis(meses); - } catch (err) { - setError(err instanceof Error ? err.message : "Erro desconhecido"); + setError(err instanceof Error ? err.message : 'Erro desconhecido'); } finally { setLoading(false); } @@ -1138,13 +1327,13 @@ export default function Teste() { const anoAtual = agora.getFullYear(); const mesAtual = String(agora.getMonth() + 1).padStart(2, '0'); const periodoAtual = `${anoAtual}-${mesAtual}`; - - setFiltros({ - periodoDe: `${anoAtual}-01`, - periodoAte: periodoAtual, - filial: "Todas", - }); - + + setFiltros({ + periodoDe: `${anoAtual}-01`, + periodoAte: periodoAtual, + filial: 'Todas', + }); + setFiliaisSelecionadas([]); setData([]); setDadosFiltrados([]); @@ -1152,23 +1341,23 @@ export default function Teste() { setMesesDisponiveis([]); setIsAllExpanded(false); setIsFilterOpen(false); - + // Limpar filtros do analítico também setAnaliticoFiltros({ - dataInicio: "", - dataFim: "", - centroCusto: "", - codigoGrupo: "", - codigoSubgrupo: "", - codigoConta: "", - codFilial: "", - linhaSelecionada: "", - excluirCentroCusto: "", - excluirCodigoConta: "", - codigosCentrosCustoSelecionados: "", - codigosContasSelecionadas: "", + dataInicio: '', + dataFim: '', + centroCusto: '', + codigoGrupo: '', + codigoSubgrupo: '', + codigoConta: '', + codFilial: '', + linhaSelecionada: '', + excluirCentroCusto: '', + excluirCodigoConta: '', + codigosCentrosCustoSelecionados: '', + codigosContasSelecionadas: '', }); - + carregarPeriodosDisponiveis(); }; @@ -1179,20 +1368,20 @@ export default function Teste() { } const dadosCompletosExpandidos = buildHierarchicalData(); - + const dadosExportacao = dadosCompletosExpandidos.map((row, index) => { const linha: any = { - 'Linha': index + 1, - 'Tipo': row.type, - 'Nível': row.level, - 'Grupo': row.grupo || '', + Linha: index + 1, + Tipo: row.type, + Nível: row.level, + Grupo: row.grupo || '', 'Código Grupo': row.codigo_grupo || '', - 'Conta': row.conta || '', + Conta: row.conta || '', 'Código Conta': row.codigo_conta || '', - 'Total': row.total || 0, + Total: row.total || 0, }; - mesesDisponiveis.forEach(mes => { + mesesDisponiveis.forEach((mes) => { const valor = row.valoresPorMes?.[mes] || 0; const percentual = row.percentuaisPorMes?.[mes] || 0; linha[`Valor ${mes}`] = valor; @@ -1204,43 +1393,52 @@ export default function Teste() { const wb = XLSX.utils.book_new(); const ws = XLSX.utils.json_to_sheet(dadosExportacao); - + const colWidths = [ - { wch: 8 }, // Linha - { wch: 15 }, // Tipo - { wch: 8 }, // Nível - { wch: 25 }, // Grupo - { wch: 15 }, // Código Grupo - { wch: 35 }, // Conta - { wch: 12 }, // Código Conta - { wch: 15 }, // Total + { wch: 8 }, // Linha + { wch: 15 }, // Tipo + { wch: 8 }, // Nível + { wch: 25 }, // Grupo + { wch: 15 }, // Código Grupo + { wch: 35 }, // Conta + { wch: 12 }, // Código Conta + { wch: 15 }, // Total ]; - + mesesDisponiveis.forEach(() => { colWidths.push({ wch: 15 }); // Valor colWidths.push({ wch: 10 }); // % }); - + ws['!cols'] = colWidths; - + XLSX.utils.book_append_sheet(wb, ws, 'DRE Filial Completo'); - - const resumoData = [ - { 'Informação': 'Período', 'Valor': `${filtros.periodoDe} a ${filtros.periodoAte}` }, - { 'Informação': 'Filial', 'Valor': filtros.filial }, - { 'Informação': 'Total de Registros', 'Valor': dadosCompletosExpandidos.length }, - { 'Informação': 'Data de Exportação', 'Valor': new Date().toLocaleString('pt-BR') }, + + const resumoData = [ + { + Informação: 'Período', + Valor: `${filtros.periodoDe} a ${filtros.periodoAte}`, + }, + { Informação: 'Filial', Valor: filtros.filial }, + { + Informação: 'Total de Registros', + Valor: dadosCompletosExpandidos.length, + }, + { + Informação: 'Data de Exportação', + Valor: new Date().toLocaleString('pt-BR'), + }, ]; - + const wsResumo = XLSX.utils.json_to_sheet(resumoData); wsResumo['!cols'] = [{ wch: 20 }, { wch: 30 }]; XLSX.utils.book_append_sheet(wb, wsResumo, 'Resumo'); - + const dataAtual = new Date().toISOString().split('T')[0]; const nomeArquivo = `DRE_Filial_Completo_${dataAtual}.xlsx`; - + XLSX.writeFile(wb, nomeArquivo); - + console.log('✅ Arquivo XLSX completo exportado:', nomeArquivo); }; @@ -1259,12 +1457,14 @@ export default function Teste() {
-

Despesa Filial

+

+ Despesa Filial +

Demonstração do Resultado do Exercício

+
-
{/* Controles */}
@@ -1297,7 +1497,7 @@ export default function Teste() { Expandir Tudo )} - + @@ -1314,10 +1514,11 @@ export default function Teste() { Filtros - Ajuste os critérios e clique em Pesquisar para atualizar a visão. + Ajuste os critérios e clique em Pesquisar para atualizar a + visão. - +
{/* Período */} @@ -1325,27 +1526,57 @@ export default function Teste() {
- - + setFiltros((prev) => ({ + ...prev, + periodoDe: value, + })) + } + > - {mesesDisponiveis.map(mes => ( - {mes} + {mesesDisponiveis.map((mes) => ( + + {mes} + ))}
- - + setFiltros((prev) => ({ + ...prev, + periodoAte: value, + })) + } + > - {mesesDisponiveis.map(mes => ( - {mes} + {mesesDisponiveis.map((mes) => ( + + {mes} + ))} @@ -1353,74 +1584,85 @@ export default function Teste() {
- {/* Filial */} -
-
- -
- - + {/* Filial */} +
+
+ +
+ + +
-
- setFiltroFilial(e.target.value)} - className="h-8 text-sm" - /> -
- {opcoesFiliais - .filter(filial => { - if (!filtroFilial) return true; - const termo = filtroFilial.toLowerCase(); - return filial.toLowerCase().includes(termo); - }) - .map(filial => ( -
- toggleFilial(filial)} - /> -
- )} -
+ {filiaisSelecionadas.length > 0 && ( +
+ {filiaisSelecionadas.length} filial(is) selecionada(s) +
+ )} +
- -
-
+
+ {/* Loading quando aplicando filtros */} @@ -1457,8 +1699,18 @@ export default function Teste() {
- - + +
@@ -1466,7 +1718,10 @@ export default function Teste() { Erro ao carregar dados

{error}

-
@@ -1486,7 +1741,8 @@ export default function Teste() { Nenhum dado exibido

- Clique no botão "Filtros" para definir os critérios de busca e visualizar os dados do DRE. + Clique no botão "Filtros" para definir os critérios de busca e + visualizar os dados do DRE.

@@ -1511,34 +1767,56 @@ export default function Teste() { Descrição {mesesDisponiveis.map((mes) => { - const filiaisParaMes = (filtrosAplicados && filiaisSelecionadas.length > 0) - ? filiaisSelecionadas - : (opcoesFiliais.length > 0 ? opcoesFiliais : ['']); - + const filiaisParaMes = + filtrosAplicados && filiaisSelecionadas.length > 0 + ? filiaisSelecionadas + : opcoesFiliais.length > 0 + ? opcoesFiliais + : ['']; + return ( {/* Cabeçalhos de filiais para este mês */} {filiaisParaMes.map((filial: string) => ( - {mes}{filial && <>
- Filial - {filial}} + {mes} + {filial && ( + <> +
+ + Filial - {filial} + + + )} - %{filial && <>
- Filial - {filial}} + % + {filial && ( + <> +
+ + Filial - {filial} + + + )}
))} - + {/* Cabeçalhos de totalizador para este mês */} - {mes}
- Total + {mes} +
+ + Total + - %
- Total + %
+ + Total +
); @@ -1555,7 +1833,9 @@ export default function Teste() { {/* Table Body */} {hierarchicalData.map((row, index) => { - const linhaId = `${row.type}-${row.codigo_grupo || ""}-${row.codigo_conta || ""}`; + const linhaId = `${row.type}-${row.codigo_grupo || ''}-${ + row.codigo_conta || '' + }`; const isSelected = linhaSelecionada === linhaId; return ( )} - {/* Componente Analítico - Sempre visível, mas só carrega dados após clique */} -
- -
+ {/* Componente Analítico - Sempre visível, mas só carrega dados após clique */} +
+ +
); } -