From c5090a29a6b083a58c349cb66e6188af54b33423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 11:29:32 -0300 Subject: [PATCH 01/12] =?UTF-8?q?fix:=20corre=C3=A7=C3=B5es=20de=20inicial?= =?UTF-8?q?iza=C3=A7=C3=A3o=20da=20rota=20/dre-filial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/analitico-filial-oracle/route.ts | 255 ++++ src/app/api/dre-filial-oracle/route.ts | 102 ++ src/app/dre-filial/analitico.tsx | 997 +++++++++++++++ src/app/dre-filial/page.tsx | 10 + src/app/dre-filial/teste.tsx | 1182 ++++++++++++++++++ 5 files changed, 2546 insertions(+) create mode 100644 src/app/api/analitico-filial-oracle/route.ts create mode 100644 src/app/api/dre-filial-oracle/route.ts create mode 100644 src/app/dre-filial/analitico.tsx create mode 100644 src/app/dre-filial/page.tsx create mode 100644 src/app/dre-filial/teste.tsx diff --git a/src/app/api/analitico-filial-oracle/route.ts b/src/app/api/analitico-filial-oracle/route.ts new file mode 100644 index 0000000..9891e47 --- /dev/null +++ b/src/app/api/analitico-filial-oracle/route.ts @@ -0,0 +1,255 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { executeOracleQuery } from '@/db/oracle'; + +export async function GET(request: NextRequest) { + try { + console.log('🔄 Buscando dados analíticos do Oracle (Filial)...'); + console.log('📋 URL completa:', request.url); + console.log('🔍 Query params:', request.nextUrl.searchParams.toString()); + console.log('📊 Headers:', Object.fromEntries(request.headers.entries())); + + // Extrair parâmetros de query + const searchParams = request.nextUrl.searchParams; + const dataInicio = searchParams.get('dataInicio'); + const dataFim = searchParams.get('dataFim'); + const centroCusto = searchParams.get('centroCusto'); + const codigoGrupo = searchParams.get('codigoGrupo'); + const codigoSubgrupo = searchParams.get('codigoSubgrupo'); + const codigoConta = searchParams.get('codigoConta'); + + // Parâmetros para exclusão de valores específicos + const excluirCentroCusto = searchParams.get('excluirCentroCusto'); + const excluirCodigoConta = searchParams.get('excluirCodigoConta'); + + // Novos parâmetros para códigos selecionados no filtro + const codigosCentrosCustoSelecionados = searchParams.get('codigosCentrosCustoSelecionados'); + const codigosContasSelecionadas = searchParams.get('codigosContasSelecionadas'); + + console.log('🎯 Filtros recebidos na API:', { + dataInicio, + dataFim, + centroCusto, + codigoGrupo, + codigoSubgrupo, + codigoConta, + excluirCentroCusto, + excluirCodigoConta, + codigosCentrosCustoSelecionados, + codigosContasSelecionadas + }); + console.log('🔍 Verificação específica de centroCusto:', { + centroCusto, + tipo: typeof centroCusto, + vazio: centroCusto === null || centroCusto === undefined || centroCusto === '', + codigosCentrosCustoSelecionados, + codigosCentrosCustoSelecionadosVazio: !codigosCentrosCustoSelecionados || codigosCentrosCustoSelecionados === '' + }); + + // Construir query SQL com filtros usando a view VB_DRE_FILIAL_DESPESA_ANALITICO + let sql = `SELECT * FROM VB_DRE_FILIAL_DESPESA_ANALITICO WHERE 1=1`; + const params: any[] = []; + let paramIndex = 1; + + // Filtro por período (usando ANOMESCOMP) + if (dataInicio && dataFim) { + sql += ` AND ANOMESCOMP >= :${paramIndex} AND ANOMESCOMP <= :${paramIndex + 1}`; + params.push(dataInicio, dataFim); + paramIndex += 2; + console.log('📅 Adicionando filtro de período:', dataInicio, 'a', dataFim); + } + + // Filtro por código do grupo + if (codigoGrupo) { + sql += ` AND CODGRUPO = :${paramIndex}`; + params.push(codigoGrupo); + paramIndex++; + console.log('📊 Adicionando filtro de grupo:', codigoGrupo); + } + + // Filtro por subgrupo (DIRETO, INDIRETO, SEM CC, etc.) + if (codigoSubgrupo) { + sql += ` AND SUBGRUPO = :${paramIndex}`; + params.push(codigoSubgrupo); + paramIndex++; + console.log('📊 Adicionando filtro de subgrupo:', codigoSubgrupo); + } + + // Filtro por código da conta + if (codigoConta) { + sql += ` AND CODCONTA = :${paramIndex}`; + params.push(codigoConta); + paramIndex++; + console.log('💰 Adicionando filtro de conta:', codigoConta); + } + + // Filtro por códigos de centros de custo selecionados no filtro + // Se houver codigosCentrosCustoSelecionados E centroCusto individual, incluir ambos + console.log('🔍 Antes de aplicar filtro de centro de custo:', { + codigosCentrosCustoSelecionados, + centroCusto, + codigosCentrosCustoSelecionadosVazio: !codigosCentrosCustoSelecionados || codigosCentrosCustoSelecionados.trim() === '', + centroCustoVazio: !centroCusto || centroCusto.trim() === '', + centroCustoLength: centroCusto?.length, + codigosCentrosCustoSelecionadosLength: codigosCentrosCustoSelecionados?.length + }); + + // IMPORTANTE: Quando centroCusto individual é fornecido (clique na célula), ele tem PRIORIDADE ABSOLUTA + // e deve filtrar APENAS por ele, ignorando codigosCentrosCustoSelecionados do filtro geral + if (centroCusto && centroCusto.trim() !== '') { + // Quando há centroCusto individual (clique na célula), usar APENAS ele + // Ignorar codigosCentrosCustoSelecionados do filtro geral para garantir filtro preciso + sql += ` AND CODIGOCENTROCUSTO = :${paramIndex}`; + params.push(centroCusto); + paramIndex++; + console.log('🏢 PRIORIDADE: Filtrando APENAS por centroCusto individual (clique na célula):', centroCusto); + console.log('⚠️ Ignorando codigosCentrosCustoSelecionados do filtro geral quando há centroCusto individual'); + console.log('📝 SQL após adicionar filtro =:', sql.substring(0, 200) + '...'); + } else if (codigosCentrosCustoSelecionados && codigosCentrosCustoSelecionados.trim() !== '') { + // Se só codigosCentrosCustoSelecionados existe (sem clique na célula), usar ele + const codigosArray = codigosCentrosCustoSelecionados.split(',').filter(c => c.trim() !== ''); + if (codigosArray.length > 0) { + const placeholders = codigosArray.map(() => `:${paramIndex++}`).join(','); + sql += ` AND CODIGOCENTROCUSTO IN (${placeholders})`; + params.push(...codigosArray); + console.log('🏢 Filtrando por códigos de centros de custo selecionados (filtro geral):', codigosArray); + console.log('📝 SQL após adicionar filtro IN:', sql.substring(0, 200) + '...'); + } + } else { + console.log('⚠️ Nenhum filtro de centro de custo aplicado - ambos estão vazios'); + } + + // Exclusão de centro de custo específico (quando desmarcado) + // Só aplicar se não houver codigosCentrosCustoSelecionados, para evitar conflito + if (excluirCentroCusto && !codigosCentrosCustoSelecionados) { + sql += ` AND CODIGOCENTROCUSTO != :${paramIndex}`; + params.push(excluirCentroCusto); + paramIndex++; + console.log('🚫 Excluindo centro de custo:', excluirCentroCusto); + } + + // Exclusão de código de conta específico (quando desmarcado) + if (excluirCodigoConta) { + sql += ` AND CODCONTA != :${paramIndex}`; + params.push(excluirCodigoConta); + paramIndex++; + console.log('🚫 Excluindo código de conta:', excluirCodigoConta); + } + + // Filtro por códigos de contas selecionadas no filtro + if (codigosContasSelecionadas) { + const codigosArray = codigosContasSelecionadas.split(','); + const placeholders = codigosArray.map(() => `:${paramIndex++}`).join(','); + sql += ` AND CODCONTA IN (${placeholders})`; + params.push(...codigosArray); + console.log('💰 Filtrando por códigos de contas:', codigosArray); + } + + sql += ` ORDER BY DTVENC, CODFORNEC, CODCONTA`; + + // Log detalhado da query SQL final + console.log('═══════════════════════════════════════════════════════════════'); + console.log('🗄️ QUERY SQL FINAL:'); + console.log('═══════════════════════════════════════════════════════════════'); + console.log(sql); + console.log('═══════════════════════════════════════════════════════════════'); + console.log('📋 PARÂMETROS FINAIS (na ordem dos placeholders :1, :2, :3, ...):'); + console.log('═══════════════════════════════════════════════════════════════'); + params.forEach((param, index) => { + console.log(` :${index + 1} = ${param} (${typeof param})`); + }); + console.log('═══════════════════════════════════════════════════════════════'); + console.log('📊 RESUMO DOS FILTROS APLICADOS:'); + console.log('═══════════════════════════════════════════════════════════════'); + console.log({ + temPeriodo: dataInicio && dataFim, + periodo: dataInicio && dataFim ? `${dataInicio} a ${dataFim}` : 'N/A', + temCodigoGrupo: !!codigoGrupo, + codigoGrupo: codigoGrupo || 'N/A', + temCodigoSubgrupo: !!codigoSubgrupo, + codigoSubgrupo: codigoSubgrupo || 'N/A', + temCentroCusto: !!centroCusto, + centroCusto: centroCusto || 'N/A', + temCodigosCentrosCustoSelecionados: !!codigosCentrosCustoSelecionados, + codigosCentrosCustoSelecionados: codigosCentrosCustoSelecionados || 'N/A', + temCodigoConta: !!codigoConta, + codigoConta: codigoConta || 'N/A', + temCodigosContasSelecionadas: !!codigosContasSelecionadas, + codigosContasSelecionadas: codigosContasSelecionadas || 'N/A', + temExcluirCentroCusto: !!excluirCentroCusto, + excluirCentroCusto: excluirCentroCusto || 'N/A', + temExcluirCodigoConta: !!excluirCodigoConta, + excluirCodigoConta: excluirCodigoConta || 'N/A' + }); + console.log('═══════════════════════════════════════════════════════════════'); + + // Se há centroCusto individual, destacar especialmente + if (centroCusto && centroCusto.trim() !== '') { + console.log('🎯 FILTRO INDIVIDUAL DE CENTRO DE CUSTO ATIVO (clique na célula)'); + console.log(` Centro de Custo: ${centroCusto}`); + console.log(' ⚠️ Este filtro tem PRIORIDADE sobre codigosCentrosCustoSelecionados'); + } + console.log('═══════════════════════════════════════════════════════════════'); + + const data = await executeOracleQuery(sql, params); + + console.log('✅ Query executada com sucesso:', data.length, 'registros encontrados'); + console.log('📝 Primeiros 3 registros:', data.slice(0, 3)); + + // Transformar os dados do Oracle para o formato esperado pelo componente + // Usando a view VB_DRE_FILIAL_DESPESA_ANALITICO + const transformedData = data.map((item: any) => { + return { + codigo_grupo: item.CODGRUPO || "", + codigo_subgrupo: item.SUBGRUPO || "", // SUBGRUPO existe na nova view + codigo_fornecedor: item.CODFORNEC || "", + nome_fornecedor: item.FORNECEDOR || "", + id: item.NUMLANC || 0, + codfilial: item.FILIAL || "001", // Usar FILIAL da view + recnum: item.NUMLANC || 0, + data_competencia: item.ANOMESCOMP || "", + data_vencimento: item.DTVENC ? new Date(item.DTVENC).toISOString().split('T')[0] : "", + data_pagamento: item.DTPAGTO ? new Date(item.DTPAGTO).toISOString().split('T')[0] : "", + data_caixa: item.DTCAIXA ? new Date(item.DTCAIXA).toISOString().split('T')[0] : "", + codigo_conta: item.CODCONTA || "", + conta: item.CONTA || "", + codigo_centrocusto: item.CODIGOCENTROCUSTO || "", + centro_custo: item.CENTROCUSTO || "", + valor: item.VLREALIZADO !== null && item.VLREALIZADO !== undefined ? Number(item.VLREALIZADO) : 0, + historico: item.HISTORICO || "", + historico2: item.HISTORICO2 || "", + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + // Campos adicionais do Oracle + entidade: item.ENTIDADE || "", + tipo_parceiro: item.TIPOPARCEIRO || "", + valor_previsto: item.VLPREVISTO !== null && item.VLPREVISTO !== undefined ? Number(item.VLPREVISTO) : 0, + valor_confirmado: item.VLCONFIRMADO !== null && item.VLCONFIRMADO !== undefined ? Number(item.VLCONFIRMADO) : 0, + valor_pago: item.VLPAGO !== null && item.VLPAGO !== undefined ? Number(item.VLPAGO) : 0, + numero_lancamento: item.NUMLANC || 0, + ano_mes_comp: item.ANOMESCOMP || "", + codgrupo: item.CODGRUPO || "", + // Novos campos + data_lancamento: item.DTLANC ? new Date(item.DTLANC).toISOString().split('T')[0] : "", + data_compensacao: item.DTCOMPENSACAO ? new Date(item.DTCOMPENSACAO).toISOString().split('T')[0] : "", + data_pagto: item.DTPAGTO ? new Date(item.DTPAGTO).toISOString().split('T')[0] : "" + }; + }); + + console.log('🔄 Dados transformados:', transformedData.length, 'registros'); + console.log('📝 Primeiros 3 transformados:', transformedData.slice(0, 3)); + + return NextResponse.json(transformedData); + + } catch (error) { + console.error('❌ Erro ao buscar dados analíticos do Oracle (Filial):', error); + + return NextResponse.json( + { + success: false, + error: error instanceof Error ? error.message : 'Erro desconhecido', + details: error instanceof Error ? error.stack : undefined + }, + { status: 500 } + ); + } +} diff --git a/src/app/api/dre-filial-oracle/route.ts b/src/app/api/dre-filial-oracle/route.ts new file mode 100644 index 0000000..c4ae29a --- /dev/null +++ b/src/app/api/dre-filial-oracle/route.ts @@ -0,0 +1,102 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { executeOracleQuery } from '@/db/oracle'; + +export async function GET(request: NextRequest) { + try { + console.log('🔄 Buscando dados DRE do Oracle (Filial) - Views VB_DRE_FILIAL_*...'); + + // Query para buscar dados das views VB_DRE_FILIAL_CMV, VB_DRE_FILIAL_DESPESA e VB_DRE_FILIAL_FATLIQ + const sql = `SELECT * +FROM ( + SELECT 'CMV' AS TIPO, + "DATA", + CODGRUPO, GRUPO, FILIAL, CODCONTA, CONTA, VALOR + FROM SEVEN.VB_DRE_FILIAL_CMV + UNION ALL + SELECT 'DESPESA' AS TIPO, + "DATA", + CODGRUPO, GRUPO, FILIAL, CODCONTA, CONTA, VALOR + FROM SEVEN.VB_DRE_FILIAL_DESPESA + UNION ALL + SELECT 'FATLIQ' AS TIPO, + "DATA", + CODGRUPO, GRUPO, FILIAL, CODCONTA, CONTA, VALOR + FROM SEVEN.VB_DRE_FILIAL_FATLIQ +) X +ORDER BY CODGRUPO, CODCONTA`; + + const data = await executeOracleQuery(sql); + + console.log('✅ Query executada com sucesso:', data.length, 'registros encontrados'); + + // Debug: Verificar estrutura dos dados + if (data.length > 0) { + console.log('🔍 Primeiro registro do Oracle:', Object.keys(data[0])); + console.log('🔍 Primeiro registro completo:', data[0]); + console.log('🔍 Valores únicos de CODGRUPO:', [...new Set(data.map((item: any) => item.CODGRUPO).filter(Boolean))]); + console.log('🔍 Valores únicos de FILIAL:', [...new Set(data.map((item: any) => item.FILIAL).filter(Boolean))]); + } + + // Transformar os dados do Oracle para o formato esperado pelo componente + const transformedData = data.map((item: any) => { + // Converter DATA para formato YYYY-MM se necessário + let dataCompetencia = item.DATA; + if (dataCompetencia) { + // Se DATA for uma string no formato YYYY-MM, usar diretamente + if (typeof dataCompetencia === 'string' && dataCompetencia.match(/^\d{4}-\d{2}$/)) { + // Já está no formato correto + } else if (dataCompetencia instanceof Date) { + // Se for Date, converter para YYYY-MM + const year = dataCompetencia.getFullYear(); + const month = String(dataCompetencia.getMonth() + 1).padStart(2, '0'); + dataCompetencia = `${year}-${month}`; + } else { + // Tentar converter string de data para YYYY-MM + try { + const date = new Date(dataCompetencia); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + dataCompetencia = `${year}-${month}`; + } catch (e) { + console.warn('⚠️ Erro ao converter DATA:', dataCompetencia); + dataCompetencia = "2023-03"; // Valor padrão + } + } + } else { + dataCompetencia = "2023-03"; // Valor padrão + } + + return { + codfilial: item.FILIAL || "001", + data_competencia: dataCompetencia, + data_cai: dataCompetencia, + grupo: item.GRUPO || "", // GRUPO (nome) + codigo_grupo: item.CODGRUPO || "", // CODGRUPO (código) + codigo_conta: parseInt(item.CODCONTA) || 0, // Converter CODCONTA para número + conta: item.CONTA || "", // CONTA + valor: item.VALOR?.toString() || "0", // Converter VALOR para string + codgrupo: item.CODGRUPO || "", // CODGRUPO + tipo: item.TIPO || "", // TIPO (CMV, DESPESA, FATLIQ) + filial: item.FILIAL || "", // FILIAL + }; + }); + + console.log('✅ Dados transformados:', transformedData.length, 'registros'); + console.log('🔍 Exemplo de registro transformado:', transformedData[0]); + + return NextResponse.json(transformedData); + + } catch (error) { + console.error('❌ Erro ao buscar dados DRE do Oracle:', error); + + return NextResponse.json( + { + success: false, + error: error instanceof Error ? error.message : 'Erro desconhecido', + details: error instanceof Error ? error.stack : undefined + }, + { status: 500 } + ); + } +} + diff --git a/src/app/dre-filial/analitico.tsx b/src/app/dre-filial/analitico.tsx new file mode 100644 index 0000000..02c04c3 --- /dev/null +++ b/src/app/dre-filial/analitico.tsx @@ -0,0 +1,997 @@ +"use client"; + +import * as React from "react"; +import { DataGridPremium, GridToolbar, GridColDef, GridFilterModel } from "@mui/x-data-grid-premium"; +import { LicenseInfo } from '@mui/x-license-pro'; + +// Garantir que a licença seja aplicada no componente atualização de licença +if (typeof window !== 'undefined') { + try { + 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; + codigo_subgrupo: string; + codigo_fornecedor: string; + nome_fornecedor: string; + id: number; + codfilial: string; + recnum: number; + data_competencia: string; + data_vencimento: string; + data_pagamento: string; + data_caixa: string; + codigo_conta: string; + conta: string; + codigo_centrocusto: string; + centro_custo?: string; + valor: number; + historico: string; + historico2: string; + created_at: string; + updated_at: string; + // Campos adicionais do Oracle + entidade?: string; + tipo_parceiro?: string; + valor_previsto?: number; + valor_confirmado?: number; + valor_pago?: number; + numero_lancamento?: number; + ano_mes_comp?: string; + codgrupo?: string; + grupo?: string; + filial?: string; + tipo?: string; + // Novos campos + data_lancamento?: string; + data_compensacao?: string; + data_pagto?: string; +} + +interface AnaliticoProps { + filtros: { + dataInicio: string; + dataFim: string; + codigoGrupo?: string; + codigoConta?: string; + linhaSelecionada?: string; + excluirCodigoConta?: string; + codigosContasSelecionadas?: string; + filial?: string; + }; +} + +// Componente de filtro customizado estilo Excel +interface ExcelFilterProps { + column: GridColDef; + data: any[]; + filteredData: any[]; // Dados filtrados para mostrar apenas valores disponíveis + onFilterChange: (field: string, values: string[]) => void; + onSortChange: (field: string, direction: 'asc' | 'desc' | null) => void; + currentFilter?: string[]; + currentSort?: 'asc' | 'desc' | null; +} + +const ExcelFilter: React.FC = ({ + column, + data, + filteredData, + onFilterChange, + onSortChange, + currentFilter = [], + currentSort = null, +}) => { + const [isOpen, setIsOpen] = React.useState(false); + const [searchTerm, setSearchTerm] = React.useState(""); + const [selectedValues, setSelectedValues] = React.useState(currentFilter); + const [selectAll, setSelectAll] = React.useState(false); + + // Sincronizar selectedValues com currentFilter quando ele mudar + React.useEffect(() => { + setSelectedValues(currentFilter); + }, [currentFilter]); + + // Obter valores únicos da coluna baseado nos dados filtrados + const uniqueValues = React.useMemo(() => { + const values = filteredData + .map((row) => { + const value = row[column.field]; + if (value === null || value === undefined) return ""; + return String(value); + }) + .filter((value, index, self) => self.indexOf(value) === index && value !== "") + .sort(); + + return values; + }, [filteredData, column.field]); + + // Filtrar valores baseado na busca + const filteredValues = React.useMemo(() => { + if (!searchTerm) return uniqueValues; + return uniqueValues.filter((value) => + value.toLowerCase().includes(searchTerm.toLowerCase()) + ); + }, [uniqueValues, searchTerm]); + + const handleSelectAll = (checked: boolean) => { + if (checked) { + setSelectedValues(filteredValues); + setSelectAll(true); + } else { + setSelectedValues([]); + setSelectAll(false); + } + }; + + const handleValueToggle = (value: string, checked: boolean) => { + let newValues: string[]; + if (checked) { + newValues = [...selectedValues, value]; + } else { + newValues = selectedValues.filter((v) => v !== value); + } + setSelectedValues(newValues); + setSelectAll(newValues.length === filteredValues.length && filteredValues.length > 0); + }; + + const handleApply = () => { + onFilterChange(column.field, selectedValues); + setIsOpen(false); + }; + + const handleClear = () => { + setSelectedValues([]); + setSelectAll(false); + onFilterChange(column.field, []); + setIsOpen(false); + }; + + const handleSort = (direction: 'asc' | 'desc') => { + onSortChange(column.field, direction); + setIsOpen(false); + }; + + return ( + + + + + + + + Filtrar por "{column.headerName}" + + + +
+ {/* Opções de ordenação */} +
+
Ordenar
+
+ + +
+
+ +
+ +
+ + {/* Barra de pesquisa */} +
+ + setSearchTerm(e.target.value)} + className="pl-8 h-8 text-sm" + /> +
+ + {/* Lista de valores com checkboxes */} +
+
+
+ + +
+ + {filteredValues.map((value) => ( +
+ handleValueToggle(value, checked)} + /> + +
+ ))} +
+
+ + {/* Botões de ação */} + + + + +
+
+
+ ); +}; + +export default function AnaliticoComponent({ filtros }: AnaliticoProps) { + const [data, setData] = React.useState([]); + const [loading, setLoading] = React.useState(false); + const [globalFilter, setGlobalFilter] = React.useState(""); + const [open, setOpen] = React.useState(false); + const [drawerOpen, setDrawerOpen] = React.useState(false); + const [columnFilters, setColumnFilters] = React.useState>({}); + const [columnSorts, setColumnSorts] = React.useState>({}); + const [conditions, setConditions] = React.useState([ + { column: "", operator: "contains", value: "" }, + ]); + + // Estados para o card de agregação customizado (simplificado) + const [aggregationCardRef, setAggregationCardRef] = React.useState(null); + + // Estado para armazenar filtros externos (vindos do teste.tsx) + const [filtrosExternos, setFiltrosExternos] = React.useState(filtros); + + // Funções para gerenciar filtros customizados + const handleColumnFilterChange = React.useCallback((field: string, values: string[]) => { + setColumnFilters(prev => ({ + ...prev, + [field]: values + })); + }, []); + + const handleColumnSortChange = React.useCallback((field: string, direction: 'asc' | 'desc' | null) => { + setColumnSorts(prev => ({ + ...prev, + [field]: direction + })); + }, []); + + // Função para contar filtros aplicados (apenas filtros internos do modal customizado) + const getFilterCount = React.useCallback(() => { + let count = 0; + + // Contar filtros de coluna (filtros do modal customizado) + count += Object.keys(columnFilters).length; + + // Contar filtro global (se aplicável) + if (globalFilter && globalFilter.trim() !== "") { + count += 1; + } + + return count; + }, [columnFilters, globalFilter]); + + // Função para limpar todos os filtros internos (mantém filtros externos) + const clearAllFilters = React.useCallback(() => { + setColumnFilters({}); + setColumnSorts({}); + setGlobalFilter(""); + }, []); + + // Atualizar filtros externos quando os props mudarem + React.useEffect(() => { + console.log('🔄 Analítico - useEffect dos filtros chamado'); + console.log('📋 Filtros recebidos via props:', filtros); + setFiltrosExternos(filtros); + }, [filtros]); + + const fetchData = React.useCallback(async () => { + console.log('🔄 Analítico - fetchData chamado'); + console.log('📋 Filtros externos recebidos:', filtrosExternos); + + if (!filtrosExternos.dataInicio || !filtrosExternos.dataFim) { + console.log('⚠️ Sem dataInicio ou dataFim, limpando dados'); + setData([]); + return; + } + + setLoading(true); + try { + const params = new URLSearchParams(); + + if (filtrosExternos.dataInicio) { + params.append('dataInicio', filtrosExternos.dataInicio); + } + if (filtrosExternos.dataFim) { + params.append('dataFim', filtrosExternos.dataFim); + } + if (filtrosExternos.codigoGrupo) { + params.append('codigoGrupo', filtrosExternos.codigoGrupo); + } + if (filtrosExternos.codigoConta) { + params.append('codigoConta', filtrosExternos.codigoConta); + } + if (filtrosExternos.filial) { + params.append('filial', filtrosExternos.filial); + } + if (filtrosExternos.excluirCodigoConta) { + params.append('excluirCodigoConta', filtrosExternos.excluirCodigoConta); + } + if (filtrosExternos.codigosContasSelecionadas) { + params.append('codigosContasSelecionadas', filtrosExternos.codigosContasSelecionadas); + } + + const url = `/api/analitico-filial-oracle?${params.toString()}`; + console.log('🌐 Fazendo requisição para:', url); + console.log('📋 Parâmetros enviados:', { + dataInicio: filtrosExternos.dataInicio, + dataFim: filtrosExternos.dataFim, + codigoGrupo: filtrosExternos.codigoGrupo, + codigoConta: filtrosExternos.codigoConta, + filial: filtrosExternos.filial, + codigosContasSelecionadas: filtrosExternos.codigosContasSelecionadas + }); + + const response = await fetch(url); + if (response.ok) { + const result = await response.json(); + console.log('✅ Resposta da API recebida:', result.length, 'registros'); + console.log('📝 Primeiros 2 registros:', result.slice(0, 2)); + setData(result as AnaliticoItem[]); + } else { + console.error("❌ Erro ao buscar dados:", await response.text()); + } + } catch (error) { + console.error("❌ Erro ao buscar dados:", error); + } finally { + setLoading(false); + } + }, [filtrosExternos]); + + React.useEffect(() => { + fetchData(); + }, [fetchData]); + + // Filtrar dados baseado nos filtros de coluna + const filteredData = React.useMemo(() => { + if (!data || data.length === 0) return data; + + return data.filter((row) => { + return Object.entries(columnFilters).every(([field, filterValues]) => { + if (!filterValues || filterValues.length === 0) return true; + + const cellValue = (row as any)[field]; + const stringValue = cellValue === null || cellValue === undefined ? "" : String(cellValue); + + return filterValues.includes(stringValue); + }); + }).map((row, index) => ({ + ...row, + id: `filtered-${row.id || row.recnum || index}` // Garantir ID único e estável + })); + }, [data, columnFilters]); + + // Função para renderizar header com filtro Excel + const renderHeaderWithFilter = React.useCallback((column: GridColDef) => { + return (params: any) => ( +
+ {column.headerName} +
+ +
+
+ ); + }, [data, filteredData, columnFilters, columnSorts, handleColumnFilterChange, handleColumnSortChange]); + + // Definir colunas do DataGridPro na ordem solicitada + const columns = React.useMemo(() => { + const dateCellRenderer = (params: any) => { + if (!params.value) return "-"; + try { + return new Date(params.value).toLocaleDateString("pt-BR"); + } catch (error) { + return params.value; + } + }; + + const currencyCellRenderer = (params: any, showZero: boolean = false) => { + const value = params.value; + if (value === null || value === undefined || value === "") return "-"; + if (!showZero && value === 0) return "-"; + const numValue = typeof value === "string" ? parseFloat(value) : Number(value); + if (isNaN(numValue)) return "-"; + const formatted = new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(numValue); + return ( + + {formatted} + + ); + }; + + const baseColumns = [ + { + field: "ano_mes_comp", + headerName: "Ano/Mês Comp", + width: 110, + sortable: true, + resizable: true, + renderCell: (params: any) => params.value || "-", + }, + { + field: "filial", + headerName: "Filial", + width: 90, + sortable: true, + resizable: true, + renderCell: (params: any) => params.value || "-", + }, + { + field: "codigo_grupo", + headerName: "Cod.Grupo", + width: 100, + sortable: true, + resizable: true, + }, + { + field: "grupo", + headerName: "Grupo", + width: 200, + sortable: true, + resizable: true, + }, + { + field: "codigo_conta", + headerName: "Cod.Conta", + width: 100, + sortable: true, + resizable: true, + }, + { + field: "conta", + headerName: "Conta", + width: 200, + sortable: true, + resizable: true, + }, + { + field: "valor", + headerName: "VI.Realizado", + type: "number" as const, + width: 120, + sortable: true, + resizable: true, + renderCell: (params: any) => currencyCellRenderer(params, true), + }, + { + field: "tipo", + headerName: "Tipo", + width: 95, + sortable: true, + resizable: true, + renderCell: (params: any) => params.value || "-", + }, + ]; + + // Adicionar renderHeader com filtro Excel para todas as colunas + return baseColumns.map((col) => ({ + ...col, + renderHeader: renderHeaderWithFilter(col), + })); + }, [renderHeaderWithFilter]); + + // Ordenar dados baseado na ordenação de coluna + const sortedAndFilteredData = React.useMemo(() => { + if (!filteredData || filteredData.length === 0) return filteredData; + + const sortField = Object.keys(columnSorts).find(field => columnSorts[field] !== null); + if (!sortField || !columnSorts[sortField]) return filteredData; + + return [...filteredData].sort((a, b) => { + const aValue = (a as any)[sortField]; + const bValue = (b as any)[sortField]; + + // Converter para string para comparação + const aString = aValue === null || aValue === undefined ? "" : String(aValue); + const bString = bValue === null || bValue === undefined ? "" : String(bValue); + + if (columnSorts[sortField] === 'asc') { + return aString.localeCompare(bString); + } else { + return bString.localeCompare(aString); + } + }); + }, [filteredData, columnSorts]); + + // Calcular valor total dos dados filtrados + const valorTotal = React.useMemo(() => { + return sortedAndFilteredData.reduce((sum, item) => sum + (Number(item.valor) || 0), 0); + }, [sortedAndFilteredData]); + + // Limpar filtros de colunas que não têm mais valores disponíveis + React.useEffect(() => { + const updatedFilters = { ...columnFilters }; + let hasChanges = false; + + Object.keys(columnFilters).forEach(field => { + const currentFilterValues = columnFilters[field] || []; + if (currentFilterValues.length === 0) return; + + // Obter valores únicos disponíveis para esta coluna nos dados filtrados + const availableValues = filteredData + .map(row => { + const value = (row as any)[field]; + return value === null || value === undefined ? "" : String(value); + }) + .filter((value, index, self) => self.indexOf(value) === index && value !== ""); + + // Filtrar apenas os valores que ainda estão disponíveis + const validFilterValues = currentFilterValues.filter(value => + availableValues.includes(value) + ); + + if (validFilterValues.length !== currentFilterValues.length) { + if (validFilterValues.length === 0) { + delete updatedFilters[field]; + } else { + updatedFilters[field] = validFilterValues; + } + hasChanges = true; + } + }); + + if (hasChanges) { + setColumnFilters(updatedFilters); + } + }, [filteredData, columnFilters]); + + // Exportação XLSX - Exporta exatamente as colunas e valores da grid + const exportToExcel = () => { + if (sortedAndFilteredData.length === 0) return; + + // Funções auxiliares para formatar valores exatamente como na grid + const formatCurrencyValue = (value: any, showZero: boolean = false): string | number => { + if (value === null || value === undefined || value === "") return "-"; + const numValue = typeof value === "string" ? parseFloat(value) : Number(value); + if (isNaN(numValue)) return "-"; + if (!showZero && numValue === 0) return "-"; + // Para Excel, retornar o número formatado como string (mantém o formato de moeda) + return new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(numValue); + }; + + const formatCellValue = (column: GridColDef, item: any): any => { + const value = item[column.field]; + + // Se a coluna tem renderCell, aplicar a mesma lógica + if (column.renderCell) { + // Para valores monetários + if (column.field === "valor") { + return formatCurrencyValue(value, true); + } + + // Para campos que retornam "-" se vazios + if (column.field === "filial" || column.field === "ano_mes_comp" || + column.field === "tipo") { + return value || "-"; + } + } + + // Valor padrão + return value ?? ""; + }; + + // Criar dados de exportação usando as colunas da grid na ordem exata + const exportData = sortedAndFilteredData.map((item) => { + const row: Record = {}; + + // Iterar sobre as colunas na ordem da grid + columns.forEach((column) => { + const headerName = column.headerName || column.field; + row[headerName] = formatCellValue(column, item); + }); + + return row; + }); + + const wb = XLSX.utils.book_new(); + const ws = XLSX.utils.json_to_sheet(exportData); + + const resumoData = [ + { Métrica: "Total de Registros", Valor: sortedAndFilteredData.length }, + { Métrica: "Valor Total", Valor: valorTotal }, + { Métrica: "Filtros Aplicados", Valor: Object.keys(columnFilters).length > 0 ? "Sim" : "Não" }, + ]; + const wsResumo = XLSX.utils.json_to_sheet(resumoData); + + XLSX.utils.book_append_sheet(wb, ws, "Dados Analíticos"); + XLSX.utils.book_append_sheet(wb, wsResumo, "Resumo"); + + const now = new Date(); + const timestamp = now.toISOString().slice(0, 19).replace(/:/g, "-"); + const fileName = `analitico_filial_${timestamp}.xlsx`; + + XLSX.writeFile(wb, fileName); + }; + + // Aplicar filtros avançados + const applyFilters = () => { + // Implementar lógica de filtros avançados se necessário + setOpen(false); + }; + + const clearFilters = () => { + setConditions([{ column: "", operator: "contains", value: "" }]); + setGlobalFilter(""); + }; + + // Função para renderizar o conteúdo principal do componente (reutilizável) + const renderAnaliticoContent = (isMaximized: boolean = false) => { + return ( + <> + {/* Filtros Externos Ativos - Apenas quando maximizado */} + {isMaximized && (filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoConta) && ( +
+
+ Filtros aplicados pela tabela DRE Filial: +
+ {filtrosExternos.dataInicio && filtrosExternos.dataFim && ( + + Período: {filtrosExternos.dataInicio} a {filtrosExternos.dataFim} + + )} + {filtrosExternos.codigoGrupo && ( + + Grupo: {filtrosExternos.codigoGrupo} + + )} + {filtrosExternos.codigoConta && ( + + Conta: {filtrosExternos.codigoConta} + + )} +
+
+ )} + + {/* Controls - Apenas quando maximizado */} + {isMaximized && ( +
+ {data.length > 0 && ( + + )} + +
+ )} + + {/* DataGridPro */} + + +
+
+ Total de Registros:{" "} + {sortedAndFilteredData.length} +
+
+ Valor Total:{" "} + + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(valorTotal)} + +
+
+ +
+ row.id || `row-${row.recnum || Math.random()}`} + sx={{ + height: "100%", + width: "100%", + "& .MuiDataGrid-root": { + border: "none", + }, + "& .MuiDataGrid-columnHeaders": { + backgroundColor: "#f9fafb", + borderBottom: "1px solid #e5e7eb", + }, + "& .MuiDataGrid-columnHeader": { + backgroundColor: "#f9fafb !important", + fontWeight: 600, + fontSize: "0.875rem", + }, + "& .MuiDataGrid-cell": { + borderBottom: "1px solid #f0f0f0", + fontSize: "0.875rem", + }, + "& .MuiDataGrid-virtualScroller": { + scrollbarWidth: "thin", + "&::-webkit-scrollbar": { + width: "8px", + height: "8px", + }, + "&::-webkit-scrollbar-track": { + background: "#f1f1f1", + }, + "&::-webkit-scrollbar-thumb": { + background: "#888", + borderRadius: "4px", + }, + "&::-webkit-scrollbar-thumb:hover": { + background: "#555", + }, + }, + "& .MuiDataGrid-toolbarContainer": { + backgroundColor: "#f8fafc", + borderBottom: "1px solid #e5e7eb", + padding: "8px 16px", + }, + "& .MuiDataGrid-columnHeaderMenuContainer": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderMenuButton": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderSortIcon": { + display: "none !important", + }, + "& .MuiDataGrid-footerContainer": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderTitleContainer": { + width: "100%", + display: "flex", + alignItems: "center", + justifyContent: "space-between", + }, + }} + /> +
+
+
+ + ); + }; + + return ( +
+ {/* Header Section */} +
+
+
+

+ Análise Analítica{filtros.linhaSelecionada ? ` - ${filtros.linhaSelecionada}` : ""} +

+

+ Relatório detalhado de transações +

+
+ + {/* Filtros Externos Ativos - Centralizado */} + {(filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoConta) && ( +
+
+ Filtros aplicados pela tabela DRE Filial: +
+ {filtrosExternos.dataInicio && filtrosExternos.dataFim && ( + + Período: {filtrosExternos.dataInicio} a {filtrosExternos.dataFim} + + )} + {filtrosExternos.codigoGrupo && ( + + Grupo: {filtrosExternos.codigoGrupo} + + )} + {filtrosExternos.codigoConta && ( + + Conta: {filtrosExternos.codigoConta} + + )} +
+
+ )} + + {/* Controls */} +
+
+ {data.length > 0 && ( + + )} + + + + + + + +
+
+ + Análise Analítica{filtros.linhaSelecionada ? ` - ${filtros.linhaSelecionada}` : ""} + + + Relatório detalhado de transações - Versão Maximizada + +
+ + + +
+
+
+ {renderAnaliticoContent(true)} +
+
+
+
+
+
+ +
+ + {/* Conteúdo Principal - Versão Normal */} + {renderAnaliticoContent(false)} +
+ ); +} + diff --git a/src/app/dre-filial/page.tsx b/src/app/dre-filial/page.tsx new file mode 100644 index 0000000..4d789d4 --- /dev/null +++ b/src/app/dre-filial/page.tsx @@ -0,0 +1,10 @@ +import Teste from './teste'; + +export default function DreFilialPage() { + return ( +
+ +
+ ); +} + diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx new file mode 100644 index 0000000..dfc2ebc --- /dev/null +++ b/src/app/dre-filial/teste.tsx @@ -0,0 +1,1182 @@ +"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 { + Sheet, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, + SheetTrigger, +} from "@/components/ui/sheet"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +interface DREItem { + codfilial: string; + data_competencia: string; + data_cai: string; + grupo: string; + codigo_grupo: string; + codigo_conta: number; + conta: string; + valor: string; + codgrupo?: string; + tipo?: string; + filial?: string; +} + +interface HierarchicalRow { + type: "grupo" | "conta"; + level: number; + grupo?: string; + codigo_grupo?: string; + conta?: string; + codigo_conta?: number; + total?: number; + isExpanded?: boolean; + valoresPorMes?: Record; + percentuaisPorMes?: Record; + percentualTotal?: number; +} + +// Componente memoizado para linhas da tabela +const TableRow = memo(({ + row, + index, + handleRowClick, + getRowStyle, + getIndentStyle, + renderCellContent, + mesesDisponiveis, + formatCurrency, + formatCurrencyWithColor, + getFixedCellBackground +}: { + row: HierarchicalRow; + index: number; + handleRowClick: (row: HierarchicalRow, mes?: string) => void; + getRowStyle: (row: HierarchicalRow) => string; + getIndentStyle: (level: number) => React.CSSProperties; + renderCellContent: (row: HierarchicalRow) => React.ReactNode; + mesesDisponiveis: string[]; + formatCurrency: (value: number) => string; + formatCurrencyWithColor: (value: number) => { formatted: string; isNegative: boolean }; + getFixedCellBackground: (row: HierarchicalRow) => string; +}) => { + return ( + + handleRowClick(row)} + > +
+ {renderCellContent(row)} +
+ + + {/* Colunas de valores por mês */} + {mesesDisponiveis.map((mes) => ( + + handleRowClick(row, mes)} + title={ + row.valoresPorMes && row.valoresPorMes[mes] + ? formatCurrency(row.valoresPorMes[mes]) + : "-" + } + > + {row.valoresPorMes && row.valoresPorMes[mes] + ? (() => { + const { formatted, isNegative } = + formatCurrencyWithColor(row.valoresPorMes[mes]); + return ( + + {formatted} + + ); + })() + : "-"} + + handleRowClick(row, mes)} + title={ + row.percentuaisPorMes && + row.percentuaisPorMes[mes] !== undefined + ? `${row.percentuaisPorMes[mes].toFixed(1)}%` + : "-" + } + > + {row.percentuaisPorMes && + 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! + ); + return ( + + {formatted} + + ); + })()} + + + {/* Coluna Percentual Total */} + handleRowClick(row)} + title={ + row.percentualTotal !== undefined + ? `${row.percentualTotal.toFixed(1)}%` + : "-" + } + > + {row.percentualTotal !== undefined + ? `${row.percentualTotal.toFixed(1)}%` + : "-"} + + + ); +}); + +TableRow.displayName = 'TableRow'; + +export default function Teste() { + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [expandedGrupos, setExpandedGrupos] = useState>(new Set()); + const [mesesDisponiveis, setMesesDisponiveis] = useState([]); + + // Estados para filtros + const [filtros, setFiltros] = useState({ + periodoDe: "", + periodoAte: "", + grupo: "Todos", + conta: "Todas", + }); + + // Estados para multi-seleção + const [contasSelecionadas, setContasSelecionadas] = useState([]); + const [codigosContas, setCodigosContas] = 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 [opcoesContas, setOpcoesContas] = useState([]); + + // Estados para filtros de busca nos campos de seleção + const [filtroConta, setFiltroConta] = useState(""); + + // Estados para analítico + const [analiticoFiltros, setAnaliticoFiltros] = useState({ + dataInicio: "", + dataFim: "", + centroCusto: "", + codigoGrupo: "", + codigoSubgrupo: "", + codigoConta: "", + linhaSelecionada: "", + excluirCentroCusto: "", + excluirCodigoConta: "", + codigosCentrosCustoSelecionados: "", + codigosContasSelecionadas: "", + }); + const [linhaSelecionada, setLinhaSelecionada] = useState(null); + const [isAllExpanded, setIsAllExpanded] = useState(false); + + useEffect(() => { + // Carregar períodos disponíveis da API + carregarPeriodosDisponiveis(); + }, []); + + const carregarPeriodosDisponiveis = async () => { + try { + 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[]; + setMesesDisponiveis(periodosUnicos); + + // Extrair grupos únicos + const gruposUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.grupo))].sort() as string[]; + setOpcoesGrupos(gruposUnicos); + + // Extrair contas únicas + const contasUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.conta))].sort() as string[]; + setOpcoesContas(contasUnicas); + + // Criar objeto de códigos das contas + const codigosContasObj: Record = {}; + dadosCompletos.forEach((item: DREItem) => { + if (item.conta && item.codigo_conta) { + codigosContasObj[item.conta] = item.codigo_conta.toString(); + } + }); + setCodigosContas(codigosContasObj); + + // Inicializar com todas as contas selecionadas + setContasSelecionadas(contasUnicas); + + } catch (error) { + console.error("Erro ao carregar períodos:", error); + } + }; + + const fetchData = async () => { + try { + setLoading(true); + setError(null); + const response = await fetch("/api/dre-filial-oracle"); + + if (!response.ok) { + throw new Error(`Erro ao carregar dados: ${response.status}`); + } + + const result = await response.json(); + setData(result); + + // Extrair meses únicos dos dados + const meses = [ + ...new Set( + result.map((item: DREItem) => { + return item.data_competencia; + }) + ), + ].sort() as string[]; + + setMesesDisponiveis(meses); + } catch (err) { + setError(err instanceof Error ? err.message : "Erro desconhecido"); + } finally { + setLoading(false); + } + }; + + const formatCurrency = (value: string | number) => { + const numValue = typeof value === "string" ? parseFloat(value) : value; + return numValue.toLocaleString("pt-BR", { + style: "currency", + currency: "BRL", + }); + }; + + const formatCurrencyWithColor = (value: string | number) => { + const numValue = typeof value === "string" ? parseFloat(value) : value; + const formatted = formatCurrency(value); + const isNegative = numValue < 0; + return { formatted, isNegative }; + }; + + // Função para lidar com clique nas linhas + const handleRowClick = (row: HierarchicalRow, mesSelecionado?: string) => { + console.log('🖱️ Clique na linha:', row); + console.log('📅 Mês selecionado:', mesSelecionado); + + if (!data.length) { + console.log('⚠️ Sem dados disponíveis'); + return; + } + + // 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())); + + 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() || ""; + } + + // Obter códigos das contas selecionadas no filtro + const codigosContasSelecionadas = contasSelecionadas + .map(conta => { + const item = data.find((d: DREItem) => d.conta === conta); + return item?.codigo_conta?.toString(); + }) + .filter(codigo => codigo) + .join(','); + + 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, + linhaSelecionada: row.grupo || row.conta || "", + excluirCentroCusto: "", + excluirCodigoConta: "", + codigosCentrosCustoSelecionados: "", + codigosContasSelecionadas, + }; + + console.log('🎯 Novos filtros para analítico:', novosFiltros); + setAnaliticoFiltros(novosFiltros); + }; + + const toggleGrupo = useCallback((codigoGrupo: string) => { + setExpandedGrupos(prev => { + const newExpanded = new Set(prev); + if (newExpanded.has(codigoGrupo)) { + newExpanded.delete(codigoGrupo); + } else { + newExpanded.add(codigoGrupo); + } + return newExpanded; + }); + }, []); + + const toggleConta = (conta: string) => { + setContasSelecionadas(prev => { + if (prev.includes(conta)) { + return prev.filter(c => c !== conta); + } else { + return [...prev, conta]; + } + }); + }; + + const selecionarTodasContas = () => { + setContasSelecionadas(opcoesContas); + }; + + const limparContas = () => { + setContasSelecionadas([]); + }; + + // Função auxiliar para calcular valores por mês + const calcularValoresPorMes = (items: DREItem[]): Record => { + const valoresPorMes: Record = {}; + + mesesDisponiveis.forEach(mes => { + valoresPorMes[mes] = 0; + }); + + items.forEach((item) => { + const anoMes = item.data_competencia; + if (anoMes && valoresPorMes[anoMes] !== undefined) { + valoresPorMes[anoMes] += parseFloat(item.valor); + } + }); + + return valoresPorMes; + }; + + // Função para calcular percentuais (simplificada - sem faturamento líquido) + const calcularPercentuaisPorMes = ( + valoresPorMes: Record + ): Record => { + const percentuais: Record = {}; + + // Calcular total geral por mês + const totaisPorMes: Record = {}; + mesesDisponiveis.forEach(mes => { + totaisPorMes[mes] = data + .filter(item => item.data_competencia === mes) + .reduce((sum, item) => sum + parseFloat(item.valor), 0); + }); + + Object.keys(valoresPorMes).forEach((mes) => { + const valorAtual = valoresPorMes[mes]; + const totalMes = totaisPorMes[mes] || 0; + + if (totalMes !== 0) { + percentuais[mes] = (valorAtual / totalMes) * 100; + } else { + percentuais[mes] = 0; + } + }); + + return percentuais; + }; + + // Função para calcular percentual do total + const calcularPercentualTotal = (total: number): number => { + const totalGeral = data.reduce((sum, item) => sum + parseFloat(item.valor), 0); + + if (totalGeral !== 0) { + return (total / totalGeral) * 100; + } else { + return 0; + } + }; + + const buildHierarchicalData = (): 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); + + // 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); + }); + + sortedGrupos.forEach(([codgrupo, items]) => { + // Calcular total do grupo + const totalGrupo = items.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + const valoresGrupoPorMes = calcularValoresPorMes(items); + + // Linha do grupo (Level 0) + rows.push({ + type: "grupo", + level: 0, + grupo: items[0]?.grupo || codgrupo, + codigo_grupo: codgrupo, + total: totalGrupo, + isExpanded: expandedGrupos.has(codgrupo), + valoresPorMes: valoresGrupoPorMes, + percentuaisPorMes: calcularPercentuaisPorMes(valoresGrupoPorMes), + percentualTotal: calcularPercentualTotal(totalGrupo), + }); + + if (expandedGrupos.has(codgrupo)) { + // 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); + + // 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, + percentuaisPorMes: calcularPercentuaisPorMes(valoresContaPorMes), + percentualTotal: calcularPercentualTotal(totalConta), + }); + }); + } + }); + + return rows; + }; + + const getRowStyle = (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; + + let style = baseStyle; + + 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 "conta": + return `${style} bg-white font-normal text-gray-600`; + default: + return style; + } + }; + + const getFixedCellBackground = (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"; + } + + switch (row.type) { + case "grupo": + return "bg-gradient-to-r from-blue-50 to-indigo-50"; + case "conta": + return "bg-white"; + default: + return "bg-white"; + } + }; + + const getIndentStyle = (level: number) => { + return { paddingLeft: `${level * 20}px` }; + }; + + const renderCellContent = (row: HierarchicalRow) => { + switch (row.type) { + case "grupo": + return ( +
+ + +
+ ); + case "conta": + return ( +
+
+ +
+ +
+ ); + default: + return null; + } + }; + + const toggleExpandAll = () => { + if (isAllExpanded) { + setExpandedGrupos(new Set()); + setIsAllExpanded(false); + } else { + const todosGrupos = [...new Set(data.map(item => item.codgrupo || item.codigo_grupo).filter(Boolean))]; + setExpandedGrupos(new Set(todosGrupos)); + setIsAllExpanded(true); + } + }; + + const aplicarFiltros = async () => { + setIsFilterOpen(false); + + setTimeout(async () => { + try { + setLoading(true); + setError(null); + + 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; + }); + } + + // Filtro por grupo + if (filtros.grupo !== "Todos") { + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => + item.grupo === filtros.grupo + ); + } + + // Filtro por conta (multi-seleção) + if (contasSelecionadas.length > 0) { + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => { + return contasSelecionadas.includes(item.conta); + }); + } + + setDadosFiltrados(dadosFiltrados); + setData(dadosFiltrados); + setFiltrosAplicados(true); + + // Extrair meses únicos dos dados filtrados + const meses = [ + ...new Set( + dadosFiltrados.map((item: DREItem) => item.data_competencia) + ), + ].sort() as string[]; + setMesesDisponiveis(meses); + + } catch (err) { + setError(err instanceof Error ? err.message : "Erro desconhecido"); + } finally { + setLoading(false); + } + }, 100); + }; + + const limparFiltros = () => { + const agora = new Date(); + const anoAtual = agora.getFullYear(); + const mesAtual = String(agora.getMonth() + 1).padStart(2, '0'); + const periodoAtual = `${anoAtual}-${mesAtual}`; + + setFiltros({ + periodoDe: `${anoAtual}-01`, + periodoAte: periodoAtual, + grupo: "Todos", + conta: "Todas", + }); + + setContasSelecionadas([]); + setData([]); + setDadosFiltrados([]); + setFiltrosAplicados(false); + setMesesDisponiveis([]); + setIsAllExpanded(false); + setIsFilterOpen(false); + + carregarPeriodosDisponiveis(); + }; + + const exportarXLSX = () => { + if (!data.length) { + console.log('⚠️ Nenhum dado para exportar'); + return; + } + + 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 || '', + 'Código Grupo': row.codigo_grupo || '', + 'Conta': row.conta || '', + 'Código Conta': row.codigo_conta || '', + 'Total': row.total || 0, + }; + + mesesDisponiveis.forEach(mes => { + const valor = row.valoresPorMes?.[mes] || 0; + const percentual = row.percentuaisPorMes?.[mes] || 0; + linha[`Valor ${mes}`] = valor; + linha[`% ${mes}`] = percentual; + }); + + return linha; + }); + + 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 + ]; + + 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': 'Grupo', 'Valor': filtros.grupo }, + { 'Informação': 'Conta', 'Valor': filtros.conta }, + { '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); + }; + + const hierarchicalData = buildHierarchicalData(); + + return ( +
+ {/* Header Section */} +
+
+
+
+

Despesa Filial

+

+ Demonstração do Resultado do Exercício +

+
+
+ + {/* Controles */} +
+ + + + + + + + + + + Filtros + + Configure os filtros para visualizar os dados do DRE Filial + + + +
+ {/* Período */} +
+ +
+
+ + setFiltros(prev => ({ ...prev, periodoDe: e.target.value }))} + className="h-9" + /> +
+
+ + setFiltros(prev => ({ ...prev, periodoAte: e.target.value }))} + className="h-9" + /> +
+
+
+ + {/* Grupo */} +
+ + +
+ + {/* Conta */} +
+
+ +
+ + +
+
+ setFiltroConta(e.target.value)} + className="h-8 text-sm" + /> +
+ {opcoesContas + .filter(conta => { + if (!filtroConta) return true; + const termo = filtroConta.toLowerCase(); + const nomeCompleto = `${conta}${codigosContas[conta] ? ` - ${codigosContas[conta]}` : ''}`; + return nomeCompleto.toLowerCase().includes(termo); + }) + .map(conta => ( +
+ toggleConta(conta)} + /> + +
+ ))} +
+ {contasSelecionadas.length > 0 && ( +
+ {contasSelecionadas.length} conta(s) selecionada(s) +
+ )} +
+
+ + + + + + +
+
+
+
+
+ + {/* Loading quando aplicando filtros */} + {loading && ( +
+
+
+ +
+
+

+ Aplicando filtros... +

+

+ Aguarde enquanto processamos os dados. +

+
+
+
+ )} + + {/* Erro */} + {error && !loading && ( +
+
+
+ + + +
+
+

+ Erro ao carregar dados +

+

{error}

+ +
+
+
+ )} + + {/* Mensagem quando não há dados */} + {!filtrosAplicados && !loading && !error && ( +
+
+
+ +
+
+

+ Nenhum dado exibido +

+

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

+
+
+
+ )} + + {/* Table Container */} + {filtrosAplicados && !loading && !error && ( +
+
+ + {/* Table Header */} + + + + {mesesDisponiveis.map((mes) => ( + + + + + ))} + + + + + + {/* Table Body */} + + {hierarchicalData.map((row, index) => { + const linhaId = `${row.type}-${row.codigo_grupo || ""}-${row.codigo_conta || ""}`; + const isSelected = linhaSelecionada === linhaId; + return ( + + ); + })} + +
+ Descrição + + {mes} + + % + + Total + + % +
+
+
+ )} + + {/* Componente Analítico - Sempre visível */} +
+ +
+
+ ); +} + From 95d8279f9c6d677089a52d8f818d9d5b07ef8fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 11:41:39 -0300 Subject: [PATCH 02/12] fix: ajuste no filtro --- src/app/dre-entidade/teste.tsx | 2 +- src/app/dre-filial/teste.tsx | 68 +++++++++++++++++++--------------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/app/dre-entidade/teste.tsx b/src/app/dre-entidade/teste.tsx index 1bc9c2b..6310086 100644 --- a/src/app/dre-entidade/teste.tsx +++ b/src/app/dre-entidade/teste.tsx @@ -2368,7 +2368,7 @@ export default function Teste() { ))} - +
setFiltros(prev => ({ ...prev, periodoDe: e.target.value }))} - className="h-9" - /> -
-
- - setFiltros(prev => ({ ...prev, periodoAte: e.target.value }))} - className="h-9" - /> -
- - + +
+
+ {/* Período */} +
+ +
+
+ + +
+
+ + +
+
+
{/* Grupo */}
@@ -1030,6 +1037,7 @@ export default function Teste() {
)}
+
From bca20e5a571f94a8bf69a2dcd9c428a40d0d7e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 11:43:12 -0300 Subject: [PATCH 03/12] fix: ajuste no filtro --- src/app/dre-filial/teste.tsx | 44 ++++++++---------------------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index 9a849e1..ad7a577 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -193,7 +193,6 @@ export default function Teste() { const [filtros, setFiltros] = useState({ periodoDe: "", periodoAte: "", - grupo: "Todos", conta: "Todas", }); @@ -726,14 +725,7 @@ export default function Teste() { }); } - // Filtro por grupo - if (filtros.grupo !== "Todos") { - dadosFiltrados = dadosFiltrados.filter((item: DREItem) => - item.grupo === filtros.grupo - ); - } - - // Filtro por conta (multi-seleção) + // Filtro por conta (multi-seleção) if (contasSelecionadas.length > 0) { dadosFiltrados = dadosFiltrados.filter((item: DREItem) => { return contasSelecionadas.includes(item.conta); @@ -766,12 +758,11 @@ export default function Teste() { const mesAtual = String(agora.getMonth() + 1).padStart(2, '0'); const periodoAtual = `${anoAtual}-${mesAtual}`; - setFiltros({ - periodoDe: `${anoAtual}-01`, - periodoAte: periodoAtual, - grupo: "Todos", - conta: "Todas", - }); + setFiltros({ + periodoDe: `${anoAtual}-01`, + periodoAte: periodoAtual, + conta: "Todas", + }); setContasSelecionadas([]); setData([]); @@ -837,10 +828,9 @@ export default function Teste() { XLSX.utils.book_append_sheet(wb, ws, 'DRE Filial Completo'); - const resumoData = [ - { 'Informação': 'Período', 'Valor': `${filtros.periodoDe} a ${filtros.periodoAte}` }, - { 'Informação': 'Grupo', 'Valor': filtros.grupo }, - { 'Informação': 'Conta', 'Valor': filtros.conta }, + const resumoData = [ + { 'Informação': 'Período', 'Valor': `${filtros.periodoDe} a ${filtros.periodoAte}` }, + { 'Informação': 'Conta', 'Valor': filtros.conta }, { 'Informação': 'Total de Registros', 'Valor': dadosCompletosExpandidos.length }, { 'Informação': 'Data de Exportação', 'Valor': new Date().toLocaleString('pt-BR') }, ]; @@ -960,22 +950,6 @@ export default function Teste() { - {/* Grupo */} -
- - -
- {/* Conta */}
From c4130155427bee272b26c9a71a7392d9f51c60ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 11:46:20 -0300 Subject: [PATCH 04/12] =?UTF-8?q?fix:=20ajuste=20no=20filtro=20de=20per?= =?UTF-8?q?=C3=ADodo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/dre-filial/teste.tsx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index ad7a577..5632a3d 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -265,6 +265,30 @@ export default function Teste() { // Inicializar com todas as contas selecionadas setContasSelecionadas(contasUnicas); + // 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 => ({ + ...prev, + periodoDe: periodoDeValido, + periodoAte: periodoAteValido + })); + + // Inicializar filtros do analítico também + setAnaliticoFiltros(prev => ({ + ...prev, + dataInicio: periodoDeValido, + dataFim: periodoAteValido + })); + } catch (error) { console.error("Erro ao carregar períodos:", error); } From 977918957b6ae977695d279c77f1693af433e690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 11:59:23 -0300 Subject: [PATCH 05/12] fix: ajuste no carregamento do componente analitico de /dre-filial --- src/app/dre-filial/analitico.tsx | 211 +++++++++++++++++++++++++++---- 1 file changed, 187 insertions(+), 24 deletions(-) diff --git a/src/app/dre-filial/analitico.tsx b/src/app/dre-filial/analitico.tsx index 02c04c3..6eee0f2 100644 --- a/src/app/dre-filial/analitico.tsx +++ b/src/app/dre-filial/analitico.tsx @@ -89,12 +89,15 @@ interface AnaliticoProps { filtros: { dataInicio: string; dataFim: string; + centroCusto?: string; codigoGrupo?: string; + codigoSubgrupo?: string; codigoConta?: string; linhaSelecionada?: string; + excluirCentroCusto?: string; excluirCodigoConta?: string; + codigosCentrosCustoSelecionados?: string; codigosContasSelecionadas?: string; - filial?: string; }; } @@ -382,18 +385,27 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { if (filtrosExternos.dataFim) { params.append('dataFim', filtrosExternos.dataFim); } + if (filtrosExternos.centroCusto) { + params.append('centroCusto', filtrosExternos.centroCusto); + } if (filtrosExternos.codigoGrupo) { params.append('codigoGrupo', filtrosExternos.codigoGrupo); } + if (filtrosExternos.codigoSubgrupo) { + params.append('codigoSubgrupo', filtrosExternos.codigoSubgrupo); + } if (filtrosExternos.codigoConta) { params.append('codigoConta', filtrosExternos.codigoConta); } - if (filtrosExternos.filial) { - params.append('filial', filtrosExternos.filial); + if (filtrosExternos.excluirCentroCusto) { + params.append('excluirCentroCusto', filtrosExternos.excluirCentroCusto); } if (filtrosExternos.excluirCodigoConta) { params.append('excluirCodigoConta', filtrosExternos.excluirCodigoConta); } + if (filtrosExternos.codigosCentrosCustoSelecionados) { + params.append('codigosCentrosCustoSelecionados', filtrosExternos.codigosCentrosCustoSelecionados); + } if (filtrosExternos.codigosContasSelecionadas) { params.append('codigosContasSelecionadas', filtrosExternos.codigosContasSelecionadas); } @@ -403,9 +415,13 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { console.log('📋 Parâmetros enviados:', { dataInicio: filtrosExternos.dataInicio, dataFim: filtrosExternos.dataFim, + centroCusto: filtrosExternos.centroCusto, codigoGrupo: filtrosExternos.codigoGrupo, + codigoSubgrupo: filtrosExternos.codigoSubgrupo, codigoConta: filtrosExternos.codigoConta, - filial: filtrosExternos.filial, + excluirCentroCusto: filtrosExternos.excluirCentroCusto, + excluirCodigoConta: filtrosExternos.excluirCodigoConta, + codigosCentrosCustoSelecionados: filtrosExternos.codigosCentrosCustoSelecionados, codigosContasSelecionadas: filtrosExternos.codigosContasSelecionadas }); @@ -497,6 +513,54 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { }; const baseColumns = [ + { + field: "numero_lancamento", + headerName: "ID", + width: 100, + sortable: true, + resizable: true, + renderCell: (params: any) => params.value || "-", + }, + { + field: "data_lancamento", + headerName: "Dt Lanc", + width: 95, + sortable: true, + resizable: true, + renderCell: dateCellRenderer, + }, + { + field: "data_compensacao", + headerName: "Dt Comp", + width: 95, + sortable: true, + resizable: true, + renderCell: dateCellRenderer, + }, + { + field: "data_vencimento", + headerName: "Dt Venc", + width: 95, + sortable: true, + resizable: true, + renderCell: dateCellRenderer, + }, + { + field: "data_caixa", + headerName: "Dt Caixa", + width: 95, + sortable: true, + resizable: true, + renderCell: dateCellRenderer, + }, + { + field: "data_pagto", + headerName: "Dt Pagto", + width: 95, + sortable: true, + resizable: true, + renderCell: dateCellRenderer, + }, { field: "ano_mes_comp", headerName: "Ano/Mês Comp", @@ -506,27 +570,42 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { renderCell: (params: any) => params.value || "-", }, { - field: "filial", - headerName: "Filial", + field: "entidade", + headerName: "Entidade", width: 90, sortable: true, resizable: true, renderCell: (params: any) => params.value || "-", }, { - field: "codigo_grupo", - headerName: "Cod.Grupo", + field: "codigo_fornecedor", + headerName: "Cod. Fornec", width: 100, sortable: true, resizable: true, }, { - field: "grupo", - headerName: "Grupo", + field: "nome_fornecedor", + headerName: "Fornecedor", width: 200, sortable: true, resizable: true, }, + { + field: "codigo_centrocusto", + headerName: "Cod.CC", + width: 90, + sortable: true, + resizable: true, + }, + { + field: "centro_custo", + headerName: "Centro Custo", + width: 180, + sortable: true, + resizable: true, + renderCell: (params: any) => params.value || "-", + }, { field: "codigo_conta", headerName: "Cod.Conta", @@ -551,13 +630,54 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { renderCell: (params: any) => currencyCellRenderer(params, true), }, { - field: "tipo", - headerName: "Tipo", + field: "valor_previsto", + headerName: "VI.Pr", + type: "number" as const, + width: 85, + sortable: true, + resizable: true, + renderCell: (params: any) => currencyCellRenderer(params, false), + }, + { + field: "valor_confirmado", + headerName: "VI.Confirmado", + type: "number" as const, + width: 125, + sortable: true, + resizable: true, + renderCell: (params: any) => currencyCellRenderer(params, false), + }, + { + field: "historico", + headerName: "Histórico", + width: 250, + sortable: true, + resizable: true, + }, + { + field: "tipo_parceiro", + headerName: "Tipo Parc", width: 95, sortable: true, resizable: true, renderCell: (params: any) => params.value || "-", }, + { + field: "valor_pago", + headerName: "VI.Pago", + type: "number" as const, + width: 100, + sortable: true, + resizable: true, + renderCell: (params: any) => currencyCellRenderer(params, false), + }, + { + field: "historico2", + headerName: "Histórico 2", + width: 250, + sortable: true, + resizable: true, + }, ]; // Adicionar renderHeader com filtro Excel para todas as colunas @@ -637,6 +757,15 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { if (sortedAndFilteredData.length === 0) return; // Funções auxiliares para formatar valores exatamente como na grid + const formatDateValue = (value: any): string => { + if (!value) return "-"; + try { + return new Date(value).toLocaleDateString("pt-BR"); + } catch (error) { + return value; + } + }; + const formatCurrencyValue = (value: any, showZero: boolean = false): string | number => { if (value === null || value === undefined || value === "") return "-"; const numValue = typeof value === "string" ? parseFloat(value) : Number(value); @@ -654,18 +783,32 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { // Se a coluna tem renderCell, aplicar a mesma lógica if (column.renderCell) { + // Para datas + if (column.field.includes("data_")) { + return formatDateValue(value); + } + // Para valores monetários if (column.field === "valor") { return formatCurrencyValue(value, true); } + if (column.field === "valor_previsto" || column.field === "valor_confirmado" || column.field === "valor_pago") { + return formatCurrencyValue(value, false); + } // Para campos que retornam "-" se vazios - if (column.field === "filial" || column.field === "ano_mes_comp" || - column.field === "tipo") { + if (column.field === "centro_custo" || column.field === "numero_lancamento" || + column.field === "entidade" || column.field === "tipo_parceiro" || + column.field === "ano_mes_comp") { return value || "-"; } } + // Para datas sem renderCell explícito (mas que são datas) + if (column.field.includes("data_")) { + return formatDateValue(value); + } + // Valor padrão return value ?? ""; }; @@ -719,7 +862,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { return ( <> {/* Filtros Externos Ativos - Apenas quando maximizado */} - {isMaximized && (filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoConta) && ( + {isMaximized && (filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoSubgrupo || filtrosExternos.codigoConta || filtrosExternos.centroCusto) && (
Filtros aplicados pela tabela DRE Filial: @@ -734,11 +877,21 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { Grupo: {filtrosExternos.codigoGrupo} )} + {filtrosExternos.codigoSubgrupo && ( + + Subgrupo: {filtrosExternos.codigoSubgrupo} + + )} {filtrosExternos.codigoConta && ( Conta: {filtrosExternos.codigoConta} )} + {filtrosExternos.centroCusto && ( + + Centro Custo: {filtrosExternos.centroCusto} + + )}
)} @@ -891,7 +1044,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
{/* Filtros Externos Ativos - Centralizado */} - {(filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoConta) && ( + {(filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoSubgrupo || filtrosExternos.codigoConta || filtrosExternos.centroCusto) && (
Filtros aplicados pela tabela DRE Filial: @@ -899,20 +1052,30 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { {filtrosExternos.dataInicio && filtrosExternos.dataFim && ( Período: {filtrosExternos.dataInicio} a {filtrosExternos.dataFim} - - )} + + )} {filtrosExternos.codigoGrupo && ( Grupo: {filtrosExternos.codigoGrupo} - - )} + + )} + {filtrosExternos.codigoSubgrupo && ( + + Subgrupo: {filtrosExternos.codigoSubgrupo} + + )} {filtrosExternos.codigoConta && ( Conta: {filtrosExternos.codigoConta} - - )} -
- + + )} + {filtrosExternos.centroCusto && ( + + Centro Custo: {filtrosExternos.centroCusto} + + )} + + )} {/* Controls */} From 9696d384392dc8ecb9f3ffe179fa6f89fea00524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 16:19:55 -0300 Subject: [PATCH 06/12] =?UTF-8?q?fix:=20inicializa=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/dre-filial/teste.tsx | 133 +++++++++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 20 deletions(-) diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index 5632a3d..d7b79ff 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -40,7 +40,7 @@ interface DREItem { } interface HierarchicalRow { - type: "grupo" | "conta"; + type: "grupo" | "conta" | "calculado"; level: number; grupo?: string; codigo_grupo?: string; @@ -51,6 +51,7 @@ interface HierarchicalRow { valoresPorMes?: Record; percentuaisPorMes?: Record; percentualTotal?: number; + isCalculado?: boolean; } // Componente memoizado para linhas da tabela @@ -344,6 +345,12 @@ export default function Teste() { console.log('🖱️ Clique na linha:', row); console.log('📅 Mês selecionado:', mesSelecionado); + // 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; @@ -469,26 +476,39 @@ export default function Teste() { return valoresPorMes; }; - // Função para calcular percentuais (simplificada - sem faturamento líquido) + // Função para calcular percentuais baseado no CODGRUPO 01 (FATURAMENTO LÍQUIDO) const calcularPercentuaisPorMes = ( - valoresPorMes: Record + valoresPorMes: Record, + codigoGrupo?: string ): Record => { const percentuais: Record = {}; - // Calcular total geral por mês - const totaisPorMes: Record = {}; + // Se for CODGRUPO 01, sempre retornar 100% + if (codigoGrupo === "01") { + mesesDisponiveis.forEach(mes => { + percentuais[mes] = 100; + }); + return percentuais; + } + + // Calcular valores do grupo 01 por mês (base para cálculo) + const valoresGrupo01PorMes: Record = {}; mesesDisponiveis.forEach(mes => { - totaisPorMes[mes] = data - .filter(item => item.data_competencia === mes) + valoresGrupo01PorMes[mes] = data + .filter(item => { + const codgrupo = item.codgrupo || item.codigo_grupo || ""; + return codgrupo === "01" && item.data_competencia === mes; + }) .reduce((sum, item) => sum + parseFloat(item.valor), 0); }); + // Calcular percentuais baseado no grupo 01 Object.keys(valoresPorMes).forEach((mes) => { const valorAtual = valoresPorMes[mes]; - const totalMes = totaisPorMes[mes] || 0; + const valorGrupo01 = valoresGrupo01PorMes[mes] || 0; - if (totalMes !== 0) { - percentuais[mes] = (valorAtual / totalMes) * 100; + if (valorGrupo01 !== 0) { + percentuais[mes] = (valorAtual / valorGrupo01) * 100; } else { percentuais[mes] = 0; } @@ -497,12 +517,23 @@ export default function Teste() { return percentuais; }; - // Função para calcular percentual do total - const calcularPercentualTotal = (total: number): number => { - const totalGeral = data.reduce((sum, item) => sum + parseFloat(item.valor), 0); + // Função para calcular percentual do total baseado no CODGRUPO 01 + const calcularPercentualTotal = (total: number, codigoGrupo?: string): number => { + // Se for CODGRUPO 01, sempre retornar 100% + if (codigoGrupo === "01") { + return 100; + } - if (totalGeral !== 0) { - return (total / totalGeral) * 100; + // Calcular total do grupo 01 (base para cálculo) + const totalGrupo01 = data + .filter(item => { + const codgrupo = item.codgrupo || item.codigo_grupo || ""; + return codgrupo === "01"; + }) + .reduce((sum, item) => sum + parseFloat(item.valor), 0); + + if (totalGrupo01 !== 0) { + return (total / totalGrupo01) * 100; } else { return 0; } @@ -523,6 +554,12 @@ export default function Teste() { 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; @@ -533,7 +570,7 @@ export default function Teste() { return codA.localeCompare(codB); }); - sortedGrupos.forEach(([codgrupo, items]) => { + sortedGrupos.forEach(([codgrupo, items], index) => { // Calcular total do grupo const totalGrupo = items.reduce( (sum, item) => sum + parseFloat(item.valor), @@ -550,8 +587,8 @@ export default function Teste() { total: totalGrupo, isExpanded: expandedGrupos.has(codgrupo), valoresPorMes: valoresGrupoPorMes, - percentuaisPorMes: calcularPercentuaisPorMes(valoresGrupoPorMes), - percentualTotal: calcularPercentualTotal(totalGrupo), + percentuaisPorMes: calcularPercentuaisPorMes(valoresGrupoPorMes, codgrupo), + percentualTotal: calcularPercentualTotal(totalGrupo, codgrupo), }); if (expandedGrupos.has(codgrupo)) { @@ -596,11 +633,47 @@ export default function Teste() { total: totalConta, isExpanded: false, valoresPorMes: valoresContaPorMes, - percentuaisPorMes: calcularPercentuaisPorMes(valoresContaPorMes), - percentualTotal: calcularPercentualTotal(totalConta), + percentuaisPorMes: calcularPercentuaisPorMes(valoresContaPorMes, codgrupo), + percentualTotal: calcularPercentualTotal(totalConta, codgrupo), }); }); } + + // Adicionar linha calculada "MARGEM DE LOJA" 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 DE LOJA = CODGRUPO 01 - CODGRUPO 02 + const valoresGrupo01 = valoresPorGrupo["01"] || {}; + const valoresGrupo02 = valoresPorGrupo["02"] || {}; + + // Calcular valores por mês para MARGEM DE LOJA + const valoresMargemPorMes: Record = {}; + mesesDisponiveis.forEach(mes => { + const valor01 = valoresGrupo01[mes] || 0; + const valor02 = valoresGrupo02[mes] || 0; + valoresMargemPorMes[mes] = valor01 - valor02; + }); + + // Calcular total + const totalMargem = Object.values(valoresMargemPorMes).reduce((sum, val) => sum + val, 0); + + // Adicionar linha calculada + rows.push({ + type: "calculado", + level: 0, + grupo: "MARGEM DE LOJA", + codigo_grupo: "MARGEM", + total: totalMargem, + isExpanded: false, + valoresPorMes: valoresMargemPorMes, + percentuaisPorMes: calcularPercentuaisPorMes(valoresMargemPorMes, "MARGEM"), + percentualTotal: calcularPercentualTotal(totalMargem, "MARGEM"), + isCalculado: true, + }); + } }); return rows; @@ -623,6 +696,8 @@ export default function Teste() { 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: @@ -641,6 +716,8 @@ export default function Teste() { 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: @@ -684,6 +761,22 @@ export default function Teste() { ); + case "calculado": + return ( +
+
+ {/* = */} + +
+
+
+ + {row.grupo} + +
+
+
+ ); case "conta": return (
From 2599060fa1ded7718c60f96fb6bb86498c4618d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 16:30:55 -0300 Subject: [PATCH 07/12] =?UTF-8?q?fix:=20corre=C3=A7=C3=A3o=20do=20estado?= =?UTF-8?q?=20inicial=20da=20/dre-filial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/dre-filial/teste.tsx | 39 +++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index d7b79ff..a204e63 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -283,12 +283,7 @@ export default function Teste() { periodoAte: periodoAteValido })); - // Inicializar filtros do analítico também - setAnaliticoFiltros(prev => ({ - ...prev, - dataInicio: periodoDeValido, - dataFim: 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); @@ -853,6 +848,21 @@ export default function Teste() { setData(dadosFiltrados); setFiltrosAplicados(true); + // Limpar filtros do analítico ao aplicar novos filtros na tabela + setAnaliticoFiltros({ + dataInicio: "", + dataFim: "", + centroCusto: "", + codigoGrupo: "", + codigoSubgrupo: "", + codigoConta: "", + linhaSelecionada: "", + excluirCentroCusto: "", + excluirCodigoConta: "", + codigosCentrosCustoSelecionados: "", + codigosContasSelecionadas: "", + }); + // Extrair meses únicos dos dados filtrados const meses = [ ...new Set( @@ -889,6 +899,21 @@ export default function Teste() { setIsAllExpanded(false); setIsFilterOpen(false); + // Limpar filtros do analítico também + setAnaliticoFiltros({ + dataInicio: "", + dataFim: "", + centroCusto: "", + codigoGrupo: "", + codigoSubgrupo: "", + codigoConta: "", + linhaSelecionada: "", + excluirCentroCusto: "", + excluirCodigoConta: "", + codigosCentrosCustoSelecionados: "", + codigosContasSelecionadas: "", + }); + carregarPeriodosDisponiveis(); }; @@ -1271,7 +1296,7 @@ export default function Teste() {
)} - {/* Componente Analítico - Sempre visível */} + {/* Componente Analítico - Sempre visível, mas só carrega dados após clique */}
From 3b3c8cd2c7372dd792bdc338e7c22f68fc4f0b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 16:44:22 -0300 Subject: [PATCH 08/12] =?UTF-8?q?fix:=20inicializa=C3=A7=C3=A3o=20do=20ana?= =?UTF-8?q?litico?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/dre-filial/teste.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index a204e63..08a02e7 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -748,9 +748,10 @@ export default function Teste() { {row.grupo} {row.codigo_grupo && ( - - {row.codigo_grupo} - + // + // {row.codigo_grupo} + // + <> )} From c60bd7def83fed94fd04cb2ba2d33c3b9684d656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 17:22:48 -0300 Subject: [PATCH 09/12] fix: adinicionado ao filtro o compo de filial --- src/app/dre-filial/teste.tsx | 113 +++++++++++++++-------------------- 1 file changed, 47 insertions(+), 66 deletions(-) diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index 08a02e7..9460c48 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -194,22 +194,21 @@ export default function Teste() { const [filtros, setFiltros] = useState({ periodoDe: "", periodoAte: "", - conta: "Todas", + filial: "Todas", }); // Estados para multi-seleção - const [contasSelecionadas, setContasSelecionadas] = useState([]); - const [codigosContas, setCodigosContas] = useState>({}); + 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 [opcoesContas, setOpcoesContas] = useState([]); + const [opcoesFiliais, setOpcoesFiliais] = useState([]); // Estados para filtros de busca nos campos de seleção - const [filtroConta, setFiltroConta] = useState(""); + const [filtroFilial, setFiltroFilial] = useState(""); // Estados para analítico const [analiticoFiltros, setAnaliticoFiltros] = useState({ @@ -250,21 +249,12 @@ export default function Teste() { const gruposUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.grupo))].sort() as string[]; setOpcoesGrupos(gruposUnicos); - // Extrair contas únicas - const contasUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.conta))].sort() as string[]; - setOpcoesContas(contasUnicas); + // Extrair filiais únicas + const filiaisUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.filial || item.codfilial).filter(Boolean))].sort() as string[]; + setOpcoesFiliais(filiaisUnicas); - // Criar objeto de códigos das contas - const codigosContasObj: Record = {}; - dadosCompletos.forEach((item: DREItem) => { - if (item.conta && item.codigo_conta) { - codigosContasObj[item.conta] = item.codigo_conta.toString(); - } - }); - setCodigosContas(codigosContasObj); - - // Inicializar com todas as contas selecionadas - setContasSelecionadas(contasUnicas); + // Inicializar com todas as filiais selecionadas + setFiliaisSelecionadas(filiaisUnicas); // Inicializar filtros de período com o ano corrente const agora = new Date(); @@ -396,15 +386,6 @@ export default function Teste() { codigoContaFiltro = row.codigo_conta?.toString() || ""; } - // Obter códigos das contas selecionadas no filtro - const codigosContasSelecionadas = contasSelecionadas - .map(conta => { - const item = data.find((d: DREItem) => d.conta === conta); - return item?.codigo_conta?.toString(); - }) - .filter(codigo => codigo) - .join(','); - const novosFiltros = { dataInicio: dataInicioFiltro, dataFim: dataFimFiltro, @@ -416,7 +397,7 @@ export default function Teste() { excluirCentroCusto: "", excluirCodigoConta: "", codigosCentrosCustoSelecionados: "", - codigosContasSelecionadas, + codigosContasSelecionadas: "", }; console.log('🎯 Novos filtros para analítico:', novosFiltros); @@ -435,22 +416,22 @@ export default function Teste() { }); }, []); - const toggleConta = (conta: string) => { - setContasSelecionadas(prev => { - if (prev.includes(conta)) { - return prev.filter(c => c !== conta); + const toggleFilial = (filial: string) => { + setFiliaisSelecionadas(prev => { + if (prev.includes(filial)) { + return prev.filter(f => f !== filial); } else { - return [...prev, conta]; + return [...prev, filial]; } }); }; - const selecionarTodasContas = () => { - setContasSelecionadas(opcoesContas); + const selecionarTodasFiliais = () => { + setFiliaisSelecionadas(opcoesFiliais); }; - const limparContas = () => { - setContasSelecionadas([]); + const limparFiliais = () => { + setFiliaisSelecionadas([]); }; // Função auxiliar para calcular valores por mês @@ -838,10 +819,11 @@ export default function Teste() { }); } - // Filtro por conta (multi-seleção) - if (contasSelecionadas.length > 0) { + // Filtro por filial (multi-seleção) + if (filiaisSelecionadas.length > 0) { dadosFiltrados = dadosFiltrados.filter((item: DREItem) => { - return contasSelecionadas.includes(item.conta); + const filialItem = item.filial || item.codfilial || ""; + return filiaisSelecionadas.includes(filialItem); }); } @@ -889,10 +871,10 @@ export default function Teste() { setFiltros({ periodoDe: `${anoAtual}-01`, periodoAte: periodoAtual, - conta: "Todas", + filial: "Todas", }); - setContasSelecionadas([]); + setFiliaisSelecionadas([]); setData([]); setDadosFiltrados([]); setFiltrosAplicados(false); @@ -973,7 +955,7 @@ export default function Teste() { const resumoData = [ { 'Informação': 'Período', 'Valor': `${filtros.periodoDe} a ${filtros.periodoAte}` }, - { 'Informação': 'Conta', 'Valor': filtros.conta }, + { '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') }, ]; @@ -1093,16 +1075,16 @@ export default function Teste() { - {/* Conta */} + {/* Filial */}
- +
setFiltroConta(e.target.value)} + placeholder="Filtrar filiais..." + value={filtroFilial} + onChange={(e) => setFiltroFilial(e.target.value)} className="h-8 text-sm" />
- {opcoesContas - .filter(conta => { - if (!filtroConta) return true; - const termo = filtroConta.toLowerCase(); - const nomeCompleto = `${conta}${codigosContas[conta] ? ` - ${codigosContas[conta]}` : ''}`; - return nomeCompleto.toLowerCase().includes(termo); + {opcoesFiliais + .filter(filial => { + if (!filtroFilial) return true; + const termo = filtroFilial.toLowerCase(); + return filial.toLowerCase().includes(termo); }) - .map(conta => ( -
+ .map(filial => ( +
toggleConta(conta)} + id={`filial-${filial}`} + checked={filiaisSelecionadas.includes(filial)} + onCheckedChange={() => toggleFilial(filial)} />
))}
- {contasSelecionadas.length > 0 && ( + {filiaisSelecionadas.length > 0 && (
- {contasSelecionadas.length} conta(s) selecionada(s) + {filiaisSelecionadas.length} filial(is) selecionada(s)
)}
From 21e70256c72fb1dbeaa4dc4bc1d5b9a0850c40eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 17:41:58 -0300 Subject: [PATCH 10/12] fix: ajuste por colunas de filiais --- src/app/dre-filial/teste.tsx | 232 ++++++++++++++++++++++++++++------- 1 file changed, 187 insertions(+), 45 deletions(-) diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index 9460c48..acbfa33 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -49,7 +49,9 @@ interface HierarchicalRow { total?: number; isExpanded?: boolean; valoresPorMes?: Record; + valoresPorMesPorFilial?: Record>; // mes -> filial -> valor percentuaisPorMes?: Record; + percentuaisPorMesPorFilial?: Record>; // mes -> filial -> percentual percentualTotal?: number; isCalculado?: boolean; } @@ -63,6 +65,7 @@ const TableRow = memo(({ getIndentStyle, renderCellContent, mesesDisponiveis, + opcoesFiliais, formatCurrency, formatCurrencyWithColor, getFixedCellBackground @@ -74,6 +77,7 @@ const TableRow = memo(({ getIndentStyle: (level: number) => React.CSSProperties; renderCellContent: (row: HierarchicalRow) => React.ReactNode; mesesDisponiveis: string[]; + opcoesFiliais: string[]; formatCurrency: (value: number) => string; formatCurrencyWithColor: (value: number) => { formatted: string; isNegative: boolean }; getFixedCellBackground: (row: HierarchicalRow) => string; @@ -93,22 +97,25 @@ const TableRow = memo(({
- {/* Colunas de valores por mês */} - {mesesDisponiveis.map((mes) => ( - - handleRowClick(row, mes)} - title={ - row.valoresPorMes && row.valoresPorMes[mes] - ? formatCurrency(row.valoresPorMes[mes]) - : "-" - } - > - {row.valoresPorMes && row.valoresPorMes[mes] - ? (() => { - const { formatted, isNegative } = - formatCurrencyWithColor(row.valoresPorMes[mes]); + {/* Colunas de valores por mês e por filial - cada filial tem suas próprias colunas */} + {mesesDisponiveis.map((mes) => + (opcoesFiliais.length > 0 ? opcoesFiliais : ['']).map((filial: string) => ( + + handleRowClick(row, mes)} + 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 ( ); })() - : "-"} - - handleRowClick(row, mes)} - title={ - row.percentuaisPorMes && - row.percentuaisPorMes[mes] !== undefined - ? `${row.percentuaisPorMes[mes].toFixed(1)}%` - : "-" - } - > - {row.percentuaisPorMes && - row.percentuaisPorMes[mes] !== undefined - ? `${row.percentuaisPorMes[mes].toFixed(1)}%` - : "-"} - - - ))} + ) : !filial && row.valoresPorMes?.[mes] !== undefined ? ( + (() => { + const { formatted, isNegative } = formatCurrencyWithColor(row.valoresPorMes[mes]); + return ( + + {formatted} + + ); + })() + ) : ( + - + )} + + handleRowClick(row, mes)} + 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)}%` + ) : ( + - + )} + + + )) + )} {/* Coluna Total */} > => { + const valoresPorMesPorFilial: Record> = {}; + + // Extrair filiais únicas dos items se opcoesFiliais ainda não estiver disponível + const filiaisDisponiveis = opcoesFiliais.length > 0 + ? opcoesFiliais + : [...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; + }); + }); + + 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); + } + }); + + return valoresPorMesPorFilial; + }; + // Função para calcular percentuais baseado no CODGRUPO 01 (FATURAMENTO LÍQUIDO) const calcularPercentuaisPorMes = ( valoresPorMes: Record, @@ -493,6 +552,62 @@ export default function Teste() { return percentuais; }; + // Função para calcular percentuais por mês e por filial baseado no CODGRUPO 01 + const calcularPercentuaisPorMesPorFilial = ( + valoresPorMesPorFilial: Record>, + codigoGrupo?: string + ): Record> => { + const percentuaisPorMesPorFilial: Record> = {}; + + // Extrair filiais únicas dos valores se opcoesFiliais ainda não estiver disponível + const filiaisDisponiveis = opcoesFiliais.length > 0 + ? opcoesFiliais + : Object.keys(valoresPorMesPorFilial[mesesDisponiveis[0] || ""] || {}); + + // 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; + } + + // Calcular valores do grupo 01 por mês e por filial (base para cálculo) + const valoresGrupo01PorMesPorFilial: Record> = {}; + mesesDisponiveis.forEach(mes => { + valoresGrupo01PorMesPorFilial[mes] = {}; + filiaisDisponiveis.forEach(filial => { + valoresGrupo01PorMesPorFilial[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; + }) + .reduce((sum, item) => sum + parseFloat(item.valor), 0); + }); + }); + + // Calcular percentuais baseado no grupo 01 por filial + mesesDisponiveis.forEach(mes => { + percentuaisPorMesPorFilial[mes] = {}; + filiaisDisponiveis.forEach(filial => { + const valorAtual = valoresPorMesPorFilial[mes]?.[filial] || 0; + const valorGrupo01 = valoresGrupo01PorMesPorFilial[mes]?.[filial] || 0; + + if (valorGrupo01 !== 0) { + percentuaisPorMesPorFilial[mes][filial] = (valorAtual / valorGrupo01) * 100; + } else { + percentuaisPorMesPorFilial[mes][filial] = 0; + } + }); + }); + + return percentuaisPorMesPorFilial; + }; + // Função para calcular percentual do total baseado no CODGRUPO 01 const calcularPercentualTotal = (total: number, codigoGrupo?: string): number => { // Se for CODGRUPO 01, sempre retornar 100% @@ -553,6 +668,7 @@ export default function Teste() { 0 ); const valoresGrupoPorMes = calcularValoresPorMes(items); + const valoresGrupoPorMesPorFilial = calcularValoresPorMesPorFilial(items); // Linha do grupo (Level 0) rows.push({ @@ -563,7 +679,9 @@ export default function Teste() { total: totalGrupo, isExpanded: expandedGrupos.has(codgrupo), valoresPorMes: valoresGrupoPorMes, + valoresPorMesPorFilial: valoresGrupoPorMesPorFilial, percentuaisPorMes: calcularPercentuaisPorMes(valoresGrupoPorMes, codgrupo), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial(valoresGrupoPorMesPorFilial, codgrupo), percentualTotal: calcularPercentualTotal(totalGrupo, codgrupo), }); @@ -597,6 +715,7 @@ export default function Teste() { 0 ); const valoresContaPorMes = calcularValoresPorMes(contaItems); + const valoresContaPorMesPorFilial = calcularValoresPorMesPorFilial(contaItems); // Linha da conta (Level 1) rows.push({ @@ -609,7 +728,9 @@ export default function Teste() { total: totalConta, isExpanded: false, valoresPorMes: valoresContaPorMes, + valoresPorMesPorFilial: valoresContaPorMesPorFilial, percentuaisPorMes: calcularPercentuaisPorMes(valoresContaPorMes, codgrupo), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial(valoresContaPorMesPorFilial, codgrupo), percentualTotal: calcularPercentualTotal(totalConta, codgrupo), }); }); @@ -633,6 +754,20 @@ export default function Teste() { 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"]) : {}; + + mesesDisponiveis.forEach(mes => { + valoresMargemPorMesPorFilial[mes] = {}; + opcoesFiliais.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); @@ -645,7 +780,9 @@ export default function Teste() { total: totalMargem, isExpanded: false, valoresPorMes: valoresMargemPorMes, + valoresPorMesPorFilial: valoresMargemPorMesPorFilial, percentuaisPorMes: calcularPercentuaisPorMes(valoresMargemPorMes, "MARGEM"), + percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial(valoresMargemPorMesPorFilial, "MARGEM"), percentualTotal: calcularPercentualTotal(totalMargem, "MARGEM"), isCalculado: true, }); @@ -1232,16 +1369,20 @@ export default function Teste() { Descrição - {mesesDisponiveis.map((mes) => ( - - - {mes} - - - % - - - ))} + {mesesDisponiveis.map((mes) => + (opcoesFiliais.length > 0 ? opcoesFiliais : ['']).map((filial: string) => ( + + + {mes}{filial && <>
+ Filial - {filial}} + + + %{filial && <>
+ Filial - {filial}} + +
+ )) + )} Total @@ -1266,6 +1407,7 @@ export default function Teste() { getIndentStyle={getIndentStyle} renderCellContent={renderCellContent} mesesDisponiveis={mesesDisponiveis} + opcoesFiliais={opcoesFiliais} formatCurrency={formatCurrency} formatCurrencyWithColor={formatCurrencyWithColor} getFixedCellBackground={getFixedCellBackground} From 078c4842edba86d1ab21bbaac529487c6192d5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 17:58:11 -0300 Subject: [PATCH 11/12] =?UTF-8?q?fix:=20divis=C3=A3o=20de=20colunas=20por?= =?UTF-8?q?=20filial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/dre-filial/teste.tsx | 196 +++++++++++++++++++++-------------- 1 file changed, 117 insertions(+), 79 deletions(-) diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index acbfa33..0c80d28 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -66,6 +66,8 @@ const TableRow = memo(({ renderCellContent, mesesDisponiveis, opcoesFiliais, + filiaisSelecionadas, + filtrosAplicados, formatCurrency, formatCurrencyWithColor, getFixedCellBackground @@ -78,6 +80,8 @@ const TableRow = memo(({ 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; @@ -99,7 +103,12 @@ const TableRow = memo(({ {/* Colunas de valores por mês e por filial - cada filial tem suas próprias colunas */} {mesesDisponiveis.map((mes) => - (opcoesFiliais.length > 0 ? opcoesFiliais : ['']).map((filial: string) => ( + ((filtrosAplicados && filiaisSelecionadas.length > 0) ? filiaisSelecionadas : (opcoesFiliais.length > 0 ? opcoesFiliais : [''])).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 ( - )) + ); + }) )} {/* Coluna Total */} @@ -339,23 +349,23 @@ export default function Teste() { } }; - const formatCurrency = (value: string | number) => { + 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 formatCurrencyWithColor = (value: string | number) => { + 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 = (row: HierarchicalRow, mesSelecionado?: string) => { + const handleRowClick = React.useCallback((row: HierarchicalRow, mesSelecionado?: string) => { console.log('🖱️ Clique na linha:', row); console.log('📅 Mês selecionado:', mesSelecionado); @@ -431,7 +441,7 @@ export default function Teste() { console.log('🎯 Novos filtros para analítico:', novosFiltros); setAnaliticoFiltros(novosFiltros); - }; + }, [data]); const toggleGrupo = useCallback((codigoGrupo: string) => { setExpandedGrupos(prev => { @@ -464,7 +474,7 @@ export default function Teste() { }; // Função auxiliar para calcular valores por mês - const calcularValoresPorMes = (items: DREItem[]): Record => { + const calcularValoresPorMes = React.useCallback((items: DREItem[]): Record => { const valoresPorMes: Record = {}; mesesDisponiveis.forEach(mes => { @@ -479,16 +489,18 @@ export default function Teste() { }); return valoresPorMes; - }; + }, [mesesDisponiveis]); // Função auxiliar para calcular valores por mês e por filial - const calcularValoresPorMesPorFilial = (items: DREItem[]): Record> => { + const calcularValoresPorMesPorFilial = React.useCallback((items: DREItem[]): Record> => { const valoresPorMesPorFilial: Record> = {}; - // Extrair filiais únicas dos items se opcoesFiliais ainda não estiver disponível - const filiaisDisponiveis = opcoesFiliais.length > 0 - ? opcoesFiliais - : [...new Set(items.map(item => item.filial || item.codfilial).filter(Boolean))] as string[]; + // Usar filiais selecionadas se houver filtros aplicados, senão usar todas as opções disponíveis + const filiaisDisponiveis = (filtrosAplicados && filiaisSelecionadas.length > 0) + ? filiaisSelecionadas + : (opcoesFiliais.length > 0 + ? opcoesFiliais + : [...new Set(items.map(item => item.filial || item.codfilial).filter(Boolean))] as string[]); mesesDisponiveis.forEach(mes => { valoresPorMesPorFilial[mes] = {}; @@ -500,7 +512,7 @@ export default function Teste() { items.forEach((item) => { const anoMes = item.data_competencia; const filial = item.filial || item.codfilial || ""; - if (anoMes && valoresPorMesPorFilial[anoMes] && filial) { + if (anoMes && valoresPorMesPorFilial[anoMes] && filial && filiaisDisponiveis.includes(filial)) { if (!valoresPorMesPorFilial[anoMes][filial]) { valoresPorMesPorFilial[anoMes][filial] = 0; } @@ -509,10 +521,49 @@ export default function Teste() { }); return valoresPorMesPorFilial; - }; + }, [mesesDisponiveis, opcoesFiliais, filtrosAplicados, filiaisSelecionadas]); + + // Memoizar valores do grupo 01 por mês para evitar recálculos repetidos + const valoresGrupo01PorMesMemo = React.useMemo(() => { + const valores: Record = {}; + mesesDisponiveis.forEach(mes => { + valores[mes] = data + .filter(item => { + const codgrupo = item.codgrupo || item.codigo_grupo || ""; + return codgrupo === "01" && item.data_competencia === mes; + }) + .reduce((sum, item) => sum + parseFloat(item.valor), 0); + }); + return valores; + }, [data, mesesDisponiveis]); + + // Memoizar valores do grupo 01 por mês e por filial + const valoresGrupo01PorMesPorFilialMemo = React.useMemo(() => { + const valores: Record> = {}; + // Usar filiais selecionadas se houver filtros aplicados, senão usar todas as opções disponíveis + const filiaisDisponiveis = (filtrosAplicados && filiaisSelecionadas.length > 0) + ? filiaisSelecionadas + : (opcoesFiliais.length > 0 + ? opcoesFiliais + : [...new Set(data.map(item => item.filial || item.codfilial).filter(Boolean))] as string[]); + + mesesDisponiveis.forEach(mes => { + valores[mes] = {}; + 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; + }) + .reduce((sum, item) => sum + parseFloat(item.valor), 0); + }); + }); + return valores; + }, [data, mesesDisponiveis, opcoesFiliais, filtrosAplicados, filiaisSelecionadas]); // Função para calcular percentuais baseado no CODGRUPO 01 (FATURAMENTO LÍQUIDO) - const calcularPercentuaisPorMes = ( + const calcularPercentuaisPorMes = React.useCallback(( valoresPorMes: Record, codigoGrupo?: string ): Record => { @@ -525,22 +576,11 @@ export default function Teste() { }); return percentuais; } - - // Calcular valores do grupo 01 por mês (base para cálculo) - const valoresGrupo01PorMes: Record = {}; - mesesDisponiveis.forEach(mes => { - valoresGrupo01PorMes[mes] = data - .filter(item => { - const codgrupo = item.codgrupo || item.codigo_grupo || ""; - return codgrupo === "01" && item.data_competencia === mes; - }) - .reduce((sum, item) => sum + parseFloat(item.valor), 0); - }); - // Calcular percentuais baseado no grupo 01 + // Usar valores memoizados do grupo 01 Object.keys(valoresPorMes).forEach((mes) => { const valorAtual = valoresPorMes[mes]; - const valorGrupo01 = valoresGrupo01PorMes[mes] || 0; + const valorGrupo01 = valoresGrupo01PorMesMemo[mes] || 0; if (valorGrupo01 !== 0) { percentuais[mes] = (valorAtual / valorGrupo01) * 100; @@ -550,19 +590,21 @@ export default function Teste() { }); return percentuais; - }; + }, [mesesDisponiveis, valoresGrupo01PorMesMemo]); // Função para calcular percentuais por mês e por filial baseado no CODGRUPO 01 - const calcularPercentuaisPorMesPorFilial = ( + const calcularPercentuaisPorMesPorFilial = React.useCallback(( valoresPorMesPorFilial: Record>, codigoGrupo?: string ): Record> => { const percentuaisPorMesPorFilial: Record> = {}; - // Extrair filiais únicas dos valores se opcoesFiliais ainda não estiver disponível - const filiaisDisponiveis = opcoesFiliais.length > 0 - ? opcoesFiliais - : Object.keys(valoresPorMesPorFilial[mesesDisponiveis[0] || ""] || {}); + // Usar filiais selecionadas se houver filtros aplicados, senão usar todas as opções disponíveis + const filiaisDisponiveis = (filtrosAplicados && filiaisSelecionadas.length > 0) + ? filiaisSelecionadas + : (opcoesFiliais.length > 0 + ? opcoesFiliais + : Object.keys(valoresPorMesPorFilial[mesesDisponiveis[0] || ""] || {})); // Se for CODGRUPO 01, sempre retornar 100% para todas as filiais if (codigoGrupo === "01") { @@ -574,28 +616,13 @@ export default function Teste() { }); return percentuaisPorMesPorFilial; } - - // Calcular valores do grupo 01 por mês e por filial (base para cálculo) - const valoresGrupo01PorMesPorFilial: Record> = {}; - mesesDisponiveis.forEach(mes => { - valoresGrupo01PorMesPorFilial[mes] = {}; - filiaisDisponiveis.forEach(filial => { - valoresGrupo01PorMesPorFilial[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; - }) - .reduce((sum, item) => sum + parseFloat(item.valor), 0); - }); - }); - // Calcular percentuais baseado no grupo 01 por filial + // Usar valores memoizados do grupo 01 mesesDisponiveis.forEach(mes => { percentuaisPorMesPorFilial[mes] = {}; filiaisDisponiveis.forEach(filial => { const valorAtual = valoresPorMesPorFilial[mes]?.[filial] || 0; - const valorGrupo01 = valoresGrupo01PorMesPorFilial[mes]?.[filial] || 0; + const valorGrupo01 = valoresGrupo01PorMesPorFilialMemo[mes]?.[filial] || 0; if (valorGrupo01 !== 0) { percentuaisPorMesPorFilial[mes][filial] = (valorAtual / valorGrupo01) * 100; @@ -606,31 +633,34 @@ export default function Teste() { }); return percentuaisPorMesPorFilial; - }; + }, [mesesDisponiveis, opcoesFiliais, valoresGrupo01PorMesPorFilialMemo, filtrosAplicados, filiaisSelecionadas]); - // Função para calcular percentual do total baseado no CODGRUPO 01 - const calcularPercentualTotal = (total: number, codigoGrupo?: string): number => { - // Se for CODGRUPO 01, sempre retornar 100% - if (codigoGrupo === "01") { - return 100; - } - - // Calcular total do grupo 01 (base para cálculo) - const totalGrupo01 = data + // Memoizar total do grupo 01 + const totalGrupo01Memo = React.useMemo(() => { + return data .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; + } - if (totalGrupo01 !== 0) { - return (total / totalGrupo01) * 100; + // Usar total memoizado do grupo 01 + if (totalGrupo01Memo !== 0) { + return (total / totalGrupo01Memo) * 100; } else { return 0; } - }; + }, [totalGrupo01Memo]); - const buildHierarchicalData = (): HierarchicalRow[] => { + const buildHierarchicalData = React.useCallback((): HierarchicalRow[] => { const rows: HierarchicalRow[] = []; // Hierarquia simplificada: [grupo, conta] @@ -790,9 +820,9 @@ export default function Teste() { }); return rows; - }; + }, [data, mesesDisponiveis, expandedGrupos, opcoesFiliais, filtrosAplicados, filiaisSelecionadas, calcularValoresPorMes, calcularValoresPorMesPorFilial, calcularPercentuaisPorMes, calcularPercentuaisPorMesPorFilial, calcularPercentualTotal]); - const getRowStyle = (row: HierarchicalRow) => { + 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"; @@ -816,9 +846,9 @@ export default function Teste() { default: return style; } - }; + }, [linhaSelecionada]); - const getFixedCellBackground = (row: HierarchicalRow): string => { + const getFixedCellBackground = React.useCallback((row: HierarchicalRow): string => { const linhaId = `${row.type}-${row.codigo_grupo || ""}-${row.codigo_conta || ""}`; const isSelected = linhaSelecionada === linhaId; @@ -836,13 +866,13 @@ export default function Teste() { default: return "bg-white"; } - }; + }, [linhaSelecionada]); - const getIndentStyle = (level: number) => { + const getIndentStyle = React.useCallback((level: number) => { return { paddingLeft: `${level * 20}px` }; - }; + }, []); - const renderCellContent = (row: HierarchicalRow) => { + const renderCellContent = React.useCallback((row: HierarchicalRow) => { switch (row.type) { case "grupo": return ( @@ -917,7 +947,7 @@ export default function Teste() { default: return null; } - }; + }, [toggleGrupo, handleRowClick]); const toggleExpandAll = () => { if (isAllExpanded) { @@ -1109,7 +1139,13 @@ export default function Teste() { console.log('✅ Arquivo XLSX completo exportado:', nomeArquivo); }; - const hierarchicalData = buildHierarchicalData(); + // Memoizar dados hierárquicos para evitar recálculos desnecessários + const hierarchicalData = React.useMemo(() => { + if (!data.length || !mesesDisponiveis.length) { + return []; + } + return buildHierarchicalData(); + }, [buildHierarchicalData]); return (
@@ -1370,7 +1406,7 @@ export default function Teste() { Descrição {mesesDisponiveis.map((mes) => - (opcoesFiliais.length > 0 ? opcoesFiliais : ['']).map((filial: string) => ( + ((filtrosAplicados && filiaisSelecionadas.length > 0) ? filiaisSelecionadas : (opcoesFiliais.length > 0 ? opcoesFiliais : [''])).map((filial: string) => ( {mes}{filial && <>
@@ -1408,6 +1444,8 @@ export default function Teste() { renderCellContent={renderCellContent} mesesDisponiveis={mesesDisponiveis} opcoesFiliais={opcoesFiliais} + filiaisSelecionadas={filiaisSelecionadas} + filtrosAplicados={filtrosAplicados} formatCurrency={formatCurrency} formatCurrencyWithColor={formatCurrencyWithColor} getFixedCellBackground={getFixedCellBackground} From 021de27ab99189149700bc6fbe548a816c41e5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 9 Dec 2025 18:21:15 -0300 Subject: [PATCH 12/12] fix: ajuste analitico --- src/app/api/analitico-filial-oracle/route.ts | 47 +++++++++++++++----- src/app/dre-filial/analitico.tsx | 19 +++++++- src/app/dre-filial/teste.tsx | 34 ++++++++++++-- 3 files changed, 82 insertions(+), 18 deletions(-) diff --git a/src/app/api/analitico-filial-oracle/route.ts b/src/app/api/analitico-filial-oracle/route.ts index 9891e47..7d415af 100644 --- a/src/app/api/analitico-filial-oracle/route.ts +++ b/src/app/api/analitico-filial-oracle/route.ts @@ -16,6 +16,7 @@ export async function GET(request: NextRequest) { const codigoGrupo = searchParams.get('codigoGrupo'); const codigoSubgrupo = searchParams.get('codigoSubgrupo'); const codigoConta = searchParams.get('codigoConta'); + const codFilial = searchParams.get('codFilial'); // Parâmetros para exclusão de valores específicos const excluirCentroCusto = searchParams.get('excluirCentroCusto'); @@ -32,6 +33,7 @@ export async function GET(request: NextRequest) { codigoGrupo, codigoSubgrupo, codigoConta, + codFilial, excluirCentroCusto, excluirCodigoConta, codigosCentrosCustoSelecionados, @@ -46,13 +48,32 @@ export async function GET(request: NextRequest) { }); // Construir query SQL com filtros usando a view VB_DRE_FILIAL_DESPESA_ANALITICO - let sql = `SELECT * FROM VB_DRE_FILIAL_DESPESA_ANALITICO WHERE 1=1`; + // Fazer JOIN com FILIAL_CC quando houver filtro por filial + let sql = ''; const params: any[] = []; let paramIndex = 1; + + // Se houver filtro por filial, fazer JOIN com FILIAL_CC + if (codFilial) { + sql = `SELECT FC.CODFILIAL, VB.* +FROM VB_DRE_FILIAL_DESPESA_ANALITICO VB +INNER JOIN FILIAL_CC FC ON FC.CC = VB.CODIGOCENTROCUSTO +WHERE 1=1`; + } else { + sql = `SELECT * FROM VB_DRE_FILIAL_DESPESA_ANALITICO WHERE 1=1`; + } + + // Filtro por filial (usando JOIN com FILIAL_CC) + if (codFilial) { + sql += ` AND FC.CODFILIAL = :${paramIndex}`; + params.push(codFilial); + paramIndex++; + console.log('🏢 Adicionando filtro de filial (CODFILIAL):', codFilial); + } // Filtro por período (usando ANOMESCOMP) if (dataInicio && dataFim) { - sql += ` AND ANOMESCOMP >= :${paramIndex} AND ANOMESCOMP <= :${paramIndex + 1}`; + sql += ` AND VB.ANOMESCOMP >= :${paramIndex} AND VB.ANOMESCOMP <= :${paramIndex + 1}`; params.push(dataInicio, dataFim); paramIndex += 2; console.log('📅 Adicionando filtro de período:', dataInicio, 'a', dataFim); @@ -60,7 +81,7 @@ export async function GET(request: NextRequest) { // Filtro por código do grupo if (codigoGrupo) { - sql += ` AND CODGRUPO = :${paramIndex}`; + sql += ` AND VB.CODGRUPO = :${paramIndex}`; params.push(codigoGrupo); paramIndex++; console.log('📊 Adicionando filtro de grupo:', codigoGrupo); @@ -68,7 +89,7 @@ export async function GET(request: NextRequest) { // Filtro por subgrupo (DIRETO, INDIRETO, SEM CC, etc.) if (codigoSubgrupo) { - sql += ` AND SUBGRUPO = :${paramIndex}`; + sql += ` AND VB.SUBGRUPO = :${paramIndex}`; params.push(codigoSubgrupo); paramIndex++; console.log('📊 Adicionando filtro de subgrupo:', codigoSubgrupo); @@ -76,7 +97,7 @@ export async function GET(request: NextRequest) { // Filtro por código da conta if (codigoConta) { - sql += ` AND CODCONTA = :${paramIndex}`; + sql += ` AND VB.CODCONTA = :${paramIndex}`; params.push(codigoConta); paramIndex++; console.log('💰 Adicionando filtro de conta:', codigoConta); @@ -98,7 +119,7 @@ export async function GET(request: NextRequest) { if (centroCusto && centroCusto.trim() !== '') { // Quando há centroCusto individual (clique na célula), usar APENAS ele // Ignorar codigosCentrosCustoSelecionados do filtro geral para garantir filtro preciso - sql += ` AND CODIGOCENTROCUSTO = :${paramIndex}`; + sql += ` AND VB.CODIGOCENTROCUSTO = :${paramIndex}`; params.push(centroCusto); paramIndex++; console.log('🏢 PRIORIDADE: Filtrando APENAS por centroCusto individual (clique na célula):', centroCusto); @@ -109,7 +130,7 @@ export async function GET(request: NextRequest) { const codigosArray = codigosCentrosCustoSelecionados.split(',').filter(c => c.trim() !== ''); if (codigosArray.length > 0) { const placeholders = codigosArray.map(() => `:${paramIndex++}`).join(','); - sql += ` AND CODIGOCENTROCUSTO IN (${placeholders})`; + sql += ` AND VB.CODIGOCENTROCUSTO IN (${placeholders})`; params.push(...codigosArray); console.log('🏢 Filtrando por códigos de centros de custo selecionados (filtro geral):', codigosArray); console.log('📝 SQL após adicionar filtro IN:', sql.substring(0, 200) + '...'); @@ -121,7 +142,7 @@ export async function GET(request: NextRequest) { // Exclusão de centro de custo específico (quando desmarcado) // Só aplicar se não houver codigosCentrosCustoSelecionados, para evitar conflito if (excluirCentroCusto && !codigosCentrosCustoSelecionados) { - sql += ` AND CODIGOCENTROCUSTO != :${paramIndex}`; + sql += ` AND VB.CODIGOCENTROCUSTO != :${paramIndex}`; params.push(excluirCentroCusto); paramIndex++; console.log('🚫 Excluindo centro de custo:', excluirCentroCusto); @@ -129,7 +150,7 @@ export async function GET(request: NextRequest) { // Exclusão de código de conta específico (quando desmarcado) if (excluirCodigoConta) { - sql += ` AND CODCONTA != :${paramIndex}`; + sql += ` AND VB.CODCONTA != :${paramIndex}`; params.push(excluirCodigoConta); paramIndex++; console.log('🚫 Excluindo código de conta:', excluirCodigoConta); @@ -139,12 +160,12 @@ export async function GET(request: NextRequest) { if (codigosContasSelecionadas) { const codigosArray = codigosContasSelecionadas.split(','); const placeholders = codigosArray.map(() => `:${paramIndex++}`).join(','); - sql += ` AND CODCONTA IN (${placeholders})`; + sql += ` AND VB.CODCONTA IN (${placeholders})`; params.push(...codigosArray); console.log('💰 Filtrando por códigos de contas:', codigosArray); } - sql += ` ORDER BY DTVENC, CODFORNEC, CODCONTA`; + sql += ` ORDER BY VB.DTVENC, VB.CODFORNEC, VB.CODCONTA`; // Log detalhado da query SQL final console.log('═══════════════════════════════════════════════════════════════'); @@ -173,6 +194,8 @@ export async function GET(request: NextRequest) { codigosCentrosCustoSelecionados: codigosCentrosCustoSelecionados || 'N/A', temCodigoConta: !!codigoConta, codigoConta: codigoConta || 'N/A', + temCodFilial: !!codFilial, + codFilial: codFilial || 'N/A', temCodigosContasSelecionadas: !!codigosContasSelecionadas, codigosContasSelecionadas: codigosContasSelecionadas || 'N/A', temExcluirCentroCusto: !!excluirCentroCusto, @@ -204,7 +227,7 @@ export async function GET(request: NextRequest) { codigo_fornecedor: item.CODFORNEC || "", nome_fornecedor: item.FORNECEDOR || "", id: item.NUMLANC || 0, - codfilial: item.FILIAL || "001", // Usar FILIAL da view + codfilial: item.CODFILIAL || item.FILIAL || "001", // Usar CODFILIAL do JOIN ou FILIAL da view recnum: item.NUMLANC || 0, data_competencia: item.ANOMESCOMP || "", data_vencimento: item.DTVENC ? new Date(item.DTVENC).toISOString().split('T')[0] : "", diff --git a/src/app/dre-filial/analitico.tsx b/src/app/dre-filial/analitico.tsx index 6eee0f2..3ca8fa1 100644 --- a/src/app/dre-filial/analitico.tsx +++ b/src/app/dre-filial/analitico.tsx @@ -93,6 +93,7 @@ interface AnaliticoProps { codigoGrupo?: string; codigoSubgrupo?: string; codigoConta?: string; + codFilial?: string; linhaSelecionada?: string; excluirCentroCusto?: string; excluirCodigoConta?: string; @@ -397,6 +398,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { if (filtrosExternos.codigoConta) { params.append('codigoConta', filtrosExternos.codigoConta); } + if (filtrosExternos.codFilial) { + params.append('codFilial', filtrosExternos.codFilial); + } if (filtrosExternos.excluirCentroCusto) { params.append('excluirCentroCusto', filtrosExternos.excluirCentroCusto); } @@ -419,6 +423,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { codigoGrupo: filtrosExternos.codigoGrupo, codigoSubgrupo: filtrosExternos.codigoSubgrupo, codigoConta: filtrosExternos.codigoConta, + codFilial: filtrosExternos.codFilial, excluirCentroCusto: filtrosExternos.excluirCentroCusto, excluirCodigoConta: filtrosExternos.excluirCodigoConta, codigosCentrosCustoSelecionados: filtrosExternos.codigosCentrosCustoSelecionados, @@ -862,7 +867,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { return ( <> {/* Filtros Externos Ativos - Apenas quando maximizado */} - {isMaximized && (filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoSubgrupo || filtrosExternos.codigoConta || filtrosExternos.centroCusto) && ( + {isMaximized && (filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoSubgrupo || filtrosExternos.codigoConta || filtrosExternos.centroCusto || filtrosExternos.codFilial) && (
Filtros aplicados pela tabela DRE Filial: @@ -887,6 +892,11 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { Conta: {filtrosExternos.codigoConta} )} + {filtrosExternos.codFilial && ( + + Filial: {filtrosExternos.codFilial} + + )} {filtrosExternos.centroCusto && ( Centro Custo: {filtrosExternos.centroCusto} @@ -1044,7 +1054,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
{/* Filtros Externos Ativos - Centralizado */} - {(filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoSubgrupo || filtrosExternos.codigoConta || filtrosExternos.centroCusto) && ( + {(filtrosExternos.dataInicio || filtrosExternos.codigoGrupo || filtrosExternos.codigoSubgrupo || filtrosExternos.codigoConta || filtrosExternos.centroCusto || filtrosExternos.codFilial) && (
Filtros aplicados pela tabela DRE Filial: @@ -1069,6 +1079,11 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { Conta: {filtrosExternos.codigoConta} )} + {filtrosExternos.codFilial && ( + + Filial: {filtrosExternos.codFilial} + + )} {filtrosExternos.centroCusto && ( Centro Custo: {filtrosExternos.centroCusto} diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index 0c80d28..44e706c 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -74,7 +74,7 @@ const TableRow = memo(({ }: { row: HierarchicalRow; index: number; - handleRowClick: (row: HierarchicalRow, mes?: string) => void; + handleRowClick: (row: HierarchicalRow, mes?: string, filial?: string) => void; getRowStyle: (row: HierarchicalRow) => string; getIndentStyle: (level: number) => React.CSSProperties; renderCellContent: (row: HierarchicalRow) => React.ReactNode; @@ -112,7 +112,7 @@ const TableRow = memo(({ handleRowClick(row, mes)} + onClick={() => handleRowClick(row, mes, filial)} title={ filial && row.valoresPorMesPorFilial?.[mes]?.[filial] !== undefined ? formatCurrency(row.valoresPorMesPorFilial[mes][filial]) @@ -158,7 +158,7 @@ const TableRow = memo(({ handleRowClick(row, mes)} + onClick={() => handleRowClick(row, mes, filial)} title={ filial && row.percentuaisPorMesPorFilial?.[mes]?.[filial] !== undefined ? `${row.percentuaisPorMesPorFilial[mes][filial].toFixed(1)}%` @@ -257,6 +257,7 @@ export default function Teste() { codigoGrupo: "", codigoSubgrupo: "", codigoConta: "", + codFilial: "", linhaSelecionada: "", excluirCentroCusto: "", excluirCodigoConta: "", @@ -365,7 +366,7 @@ export default function Teste() { }, [formatCurrency]); // Função para lidar com clique nas linhas - const handleRowClick = React.useCallback((row: HierarchicalRow, mesSelecionado?: string) => { + const handleRowClick = React.useCallback((row: HierarchicalRow, mesSelecionado?: string, filialSelecionada?: string) => { console.log('🖱️ Clique na linha:', row); console.log('📅 Mês selecionado:', mesSelecionado); @@ -425,6 +426,28 @@ export default function Teste() { codigoContaFiltro = row.codigo_conta?.toString() || ""; } + // Determinar CODFILIAL baseado na filial selecionada + 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; + } + } + } + const novosFiltros = { dataInicio: dataInicioFiltro, dataFim: dataFimFiltro, @@ -432,6 +455,7 @@ export default function Teste() { codigoGrupo: codigoGrupoFiltro, codigoSubgrupo: "", // Não aplicável na hierarquia filial codigoConta: codigoContaFiltro, + codFilial: codFilialFiltro, linhaSelecionada: row.grupo || row.conta || "", excluirCentroCusto: "", excluirCodigoConta: "", @@ -1006,6 +1030,7 @@ export default function Teste() { codigoGrupo: "", codigoSubgrupo: "", codigoConta: "", + codFilial: "", linhaSelecionada: "", excluirCentroCusto: "", excluirCodigoConta: "", @@ -1057,6 +1082,7 @@ export default function Teste() { codigoGrupo: "", codigoSubgrupo: "", codigoConta: "", + codFilial: "", linhaSelecionada: "", excluirCentroCusto: "", excluirCodigoConta: "",