diff --git a/src/app/dre-filial/teste.tsx b/src/app/dre-filial/teste.tsx index 5424875..0c849f4 100644 --- a/src/app/dre-filial/teste.tsx +++ b/src/app/dre-filial/teste.tsx @@ -1,16 +1,16 @@ -'use client'; +"use client"; -import { Button } from '@/components/ui/button'; -import { Checkbox } from '@/components/ui/checkbox'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from '@/components/ui/select'; +} from "@/components/ui/select"; import { Sheet, SheetContent, @@ -19,7 +19,7 @@ import { SheetHeader, SheetTitle, SheetTrigger, -} from '@/components/ui/sheet'; +} from "@/components/ui/sheet"; import { ChevronDown, ChevronRight, @@ -28,10 +28,10 @@ import { LoaderPinwheel, Maximize2, Minimize2, -} from 'lucide-react'; -import React, { memo, useCallback, useEffect, useState } from 'react'; -import * as XLSX from 'xlsx'; -import AnaliticoComponent from './analitico'; +} from "lucide-react"; +import React, { memo, useCallback, useEffect, useState } from "react"; +import * as XLSX from "xlsx"; +import AnaliticoComponent from "./analitico"; interface DREItem { codfilial: string; @@ -48,7 +48,7 @@ interface DREItem { } interface HierarchicalRow { - type: 'grupo' | 'conta' | 'calculado'; + type: "grupo" | "conta" | "calculado"; level: number; grupo?: string; codigo_grupo?: string; @@ -61,6 +61,8 @@ interface HierarchicalRow { percentuaisPorMes?: Record; percentuaisPorMesPorFilial?: Record>; // mes -> filial -> percentual percentualTotal?: number; + totalPorFilial?: Record; // Novo: total por filial para o período completo + percentualPorFilial?: Record; // Novo: percentual por filial para o período completo isCalculado?: boolean; } @@ -124,7 +126,7 @@ const TableRow = memo( ? filiaisSelecionadas : opcoesFiliais.length > 0 ? opcoesFiliais - : ['']; + : [""]; return ( @@ -139,7 +141,7 @@ const TableRow = memo( return null; } return ( - + handleRowClick(row, mes, filial)} @@ -152,7 +154,7 @@ const TableRow = memo( ) : row.valoresPorMes?.[mes] !== undefined ? formatCurrency(row.valoresPorMes[mes]) - : '-' + : "-" } > {filial && @@ -167,8 +169,8 @@ const TableRow = memo( {formatted} @@ -183,8 +185,8 @@ const TableRow = memo( {formatted} @@ -207,7 +209,7 @@ const TableRow = memo( ].toFixed(1)}%` : row.percentuaisPorMes?.[mes] !== undefined ? `${row.percentuaisPorMes[mes].toFixed(1)}%` - : '-' + : "-" } > {filial && @@ -235,7 +237,7 @@ const TableRow = memo( title={ row.valoresPorMes?.[mes] !== undefined ? formatCurrency(row.valoresPorMes[mes]) - : '-' + : "-" } > {row.valoresPorMes?.[mes] !== undefined ? ( @@ -247,8 +249,8 @@ const TableRow = memo( {formatted} @@ -265,7 +267,7 @@ const TableRow = memo( title={ row.percentuaisPorMes?.[mes] !== undefined ? `${row.percentuaisPorMes[mes].toFixed(1)}%` - : '-' + : "-" } > {row.percentuaisPorMes?.[mes] !== undefined ? ( @@ -278,11 +280,82 @@ const TableRow = memo( ); })} + {/* Colunas Totalizadoras por Filial */} + {(() => { + const filiaisParaTotalizador = + filtrosAplicados && filiaisSelecionadas.length > 0 + ? filiaisSelecionadas + : opcoesFiliais.length > 0 + ? opcoesFiliais + : [""]; + + return filiaisParaTotalizador.map((filial: string) => { + if ( + filtrosAplicados && + filiaisSelecionadas.length > 0 && + !filiaisSelecionadas.includes(filial) + ) { + return null; + } + return ( + + handleRowClick(row, undefined, filial)} + title={ + row.totalPorFilial?.[filial] !== undefined + ? formatCurrency(row.totalPorFilial[filial]) + : "-" + } + > + {row.totalPorFilial?.[filial] !== undefined && + row.totalPorFilial[filial] !== 0 ? ( + (() => { + const valor = row.totalPorFilial[filial]; + const { formatted, isNegative } = + formatCurrencyWithColor(valor); + return ( + + {formatted} + + ); + })() + ) : ( + - + )} + + handleRowClick(row, undefined, filial)} + title={ + row.percentualPorFilial?.[filial] !== undefined + ? `${row.percentualPorFilial[filial].toFixed(1)}%` + : "-" + } + > + {row.percentualPorFilial?.[filial] !== undefined && + row.percentualPorFilial[filial] !== 0 ? ( + `${row.percentualPorFilial[filial].toFixed(1)}%` + ) : ( + - + )} + + + ); + }); + })()} + {/* Coluna Total */} handleRowClick(row)} - title={row.total ? formatCurrency(row.total) : '-'} + title={row.total ? formatCurrency(row.total) : "-"} > {(() => { const { formatted, isNegative } = formatCurrencyWithColor( @@ -291,7 +364,7 @@ const TableRow = memo( return ( {formatted} @@ -307,19 +380,19 @@ const TableRow = memo( title={ row.percentualTotal !== undefined ? `${row.percentualTotal.toFixed(1)}%` - : '-' + : "-" } > {row.percentualTotal !== undefined ? `${row.percentualTotal.toFixed(1)}%` - : '-'} + : "-"} ); } ); -TableRow.displayName = 'TableRow'; +TableRow.displayName = "TableRow"; export default function Teste() { // Função para ordenar filiais numericamente (1, 2, 3...) ao invés de alfabeticamente (1, 10, 11...) @@ -357,9 +430,9 @@ export default function Teste() { // Estados para filtros const [filtros, setFiltros] = useState({ - periodoDe: '', - periodoAte: '', - filial: 'Todas', + periodoDe: "", + periodoAte: "", + filial: "Todas", }); // Estados para multi-seleção @@ -373,22 +446,22 @@ export default function Teste() { const [opcoesFiliais, setOpcoesFiliais] = useState([]); // Estados para filtros de busca nos campos de seleção - const [filtroFilial, setFiltroFilial] = useState(''); + const [filtroFilial, setFiltroFilial] = useState(""); // Estados para analítico const [analiticoFiltros, setAnaliticoFiltros] = useState({ - dataInicio: '', - dataFim: '', - centroCusto: '', - codigoGrupo: '', - codigoSubgrupo: '', - codigoConta: '', - codFilial: '', - linhaSelecionada: '', - excluirCentroCusto: '', - excluirCodigoConta: '', - codigosCentrosCustoSelecionados: '', - codigosContasSelecionadas: '', + dataInicio: "", + dataFim: "", + centroCusto: "", + codigoGrupo: "", + codigoSubgrupo: "", + codigoConta: "", + codFilial: "", + linhaSelecionada: "", + excluirCentroCusto: "", + excluirCodigoConta: "", + codigosCentrosCustoSelecionados: "", + codigosContasSelecionadas: "", }); const [linhaSelecionada, setLinhaSelecionada] = useState(null); const [isAllExpanded, setIsAllExpanded] = useState(false); @@ -400,7 +473,7 @@ export default function Teste() { const carregarPeriodosDisponiveis = async () => { try { - const response = await fetch('/api/dre-filial-oracle'); + const response = await fetch("/api/dre-filial-oracle"); if (!response.ok) { throw new Error(`Erro HTTP: ${response.status}`); } @@ -437,7 +510,7 @@ export default function Teste() { // 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 mesAtual = String(agora.getMonth() + 1).padStart(2, "0"); const periodoAtual = `${anoAtual}-${mesAtual}`; const primeiroMesAno = `${anoAtual}-01`; @@ -457,7 +530,7 @@ export default function Teste() { // NÃO inicializar filtros do analítico - só serão definidos após clique em célula } catch (error) { - console.error('Erro ao carregar períodos:', error); + console.error("Erro ao carregar períodos:", error); } }; @@ -465,7 +538,7 @@ export default function Teste() { try { setLoading(true); setError(null); - const response = await fetch('/api/dre-filial-oracle'); + const response = await fetch("/api/dre-filial-oracle"); if (!response.ok) { throw new Error(`Erro ao carregar dados: ${response.status}`); @@ -485,23 +558,23 @@ export default function Teste() { setMesesDisponiveis(meses); } catch (err) { - setError(err instanceof Error ? err.message : 'Erro desconhecido'); + setError(err instanceof Error ? err.message : "Erro desconhecido"); } finally { setLoading(false); } }; const formatCurrency = React.useCallback((value: string | number) => { - const numValue = typeof value === 'string' ? parseFloat(value) : value; - return numValue.toLocaleString('pt-BR', { - style: 'currency', - currency: 'BRL', + const numValue = typeof value === "string" ? parseFloat(value) : value; + return numValue.toLocaleString("pt-BR", { + style: "currency", + currency: "BRL", }); }, []); const formatCurrencyWithColor = React.useCallback( (value: string | number) => { - const numValue = typeof value === 'string' ? parseFloat(value) : value; + const numValue = typeof value === "string" ? parseFloat(value) : value; const formatted = formatCurrency(value); const isNegative = numValue < 0; return { formatted, isNegative }; @@ -516,18 +589,18 @@ export default function Teste() { mesSelecionado?: string, filialSelecionada?: string ) => { - console.log('🖱️ Clique na linha:', row); - console.log('📅 Mês selecionado:', mesSelecionado); - console.log('🏢 Filial selecionada:', filialSelecionada); + console.log("🖱️ Clique na linha:", row); + console.log("📅 Mês selecionado:", mesSelecionado); + console.log("🏢 Filial selecionada:", filialSelecionada); // Linhas calculadas não devem abrir o componente analítico - if (row.type === 'calculado') { - console.log('⚠️ Linha calculada - não abre componente analítico'); + if (row.type === "calculado") { + console.log("⚠️ Linha calculada - não abre componente analítico"); return; } if (!data.length) { - console.log('⚠️ Sem dados disponíveis'); + console.log("⚠️ Sem dados disponíveis"); return; } @@ -544,10 +617,10 @@ export default function Teste() { const dataFimFiltro = mesSelecionado || dataFimStr; // Determinar filtros baseado na hierarquia [grupo, conta] - let codigoGrupoFiltro = ''; - let codigoContaFiltro = ''; + let codigoGrupoFiltro = ""; + let codigoContaFiltro = ""; - if (row.type === 'grupo' || row.type === 'conta') { + 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 @@ -558,18 +631,18 @@ export default function Teste() { // Se é um totalizador (mesSelecionado presente mas filialSelecionada não), não filtrar por filial // Caso contrário, filtrar por filial se especificada if (filialSelecionada) { - const itemFilial = item.filial || item.codfilial || ''; + const itemFilial = item.filial || item.codfilial || ""; if (itemFilial !== filialSelecionada) { return false; } } - if (row.type === 'grupo') { + if (row.type === "grupo") { return ( item.codigo_grupo === row.codigo_grupo || item.codgrupo === row.codigo_grupo ); - } else if (row.type === 'conta') { + } else if (row.type === "conta") { return ( (item.codigo_grupo === row.codigo_grupo || item.codgrupo === row.codigo_grupo) && @@ -583,18 +656,18 @@ export default function Teste() { if (itemsCorrespondentes.length > 0) { const primeiroItem = itemsCorrespondentes[0]; codigoGrupoFiltro = - primeiroItem.codgrupo || primeiroItem.codigo_grupo || ''; + primeiroItem.codgrupo || primeiroItem.codigo_grupo || ""; } } // Filtrar por conta se for nível conta - if (row.type === 'conta') { - codigoContaFiltro = row.codigo_conta?.toString() || ''; + if (row.type === "conta") { + codigoContaFiltro = row.codigo_conta?.toString() || ""; } // Determinar CODFILIAL baseado na filial selecionada // Se é um totalizador (mesSelecionado presente mas filialSelecionada não), incluir todas as filiais selecionadas - let codFilialFiltro = ''; + 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 @@ -603,7 +676,7 @@ export default function Teste() { } else { // Buscar o CODFILIAL correspondente ao nome da filial nos dados const itemComFilial = data.find((item: DREItem) => { - const itemFilial = item.filial || item.codfilial || ''; + const itemFilial = item.filial || item.codfilial || ""; return itemFilial === filialSelecionada; }); if (itemComFilial) { @@ -626,7 +699,7 @@ export default function Teste() { return filial; } else { const itemComFilial = data.find((item: DREItem) => { - const itemFilial = item.filial || item.codfilial || ''; + const itemFilial = item.filial || item.codfilial || ""; return itemFilial === filial; }); return itemComFilial?.codfilial || filial; @@ -635,9 +708,9 @@ export default function Teste() { .filter(Boolean); // Passar como string separada por vírgula para a API processar com IN - codFilialFiltro = codigosFiliais.join(','); + codFilialFiltro = codigosFiliais.join(","); console.log( - '📊 Totalizador: incluindo todas as filiais selecionadas:', + "📊 Totalizador: incluindo todas as filiais selecionadas:", codigosFiliais ); } @@ -645,22 +718,22 @@ export default function Teste() { const novosFiltros = { dataInicio: dataInicioFiltro, dataFim: dataFimFiltro, - centroCusto: '', // Não aplicável na hierarquia filial + centroCusto: "", // Não aplicável na hierarquia filial codigoGrupo: codigoGrupoFiltro, - codigoSubgrupo: '', // Não aplicável na hierarquia filial + codigoSubgrupo: "", // Não aplicável na hierarquia filial codigoConta: codigoContaFiltro, codFilial: codFilialFiltro, // Vazio para totalizador = todas as filiais do mês - linhaSelecionada: row.grupo || row.conta || '', - excluirCentroCusto: '', - excluirCodigoConta: '', - codigosCentrosCustoSelecionados: '', - codigosContasSelecionadas: '', + linhaSelecionada: row.grupo || row.conta || "", + excluirCentroCusto: "", + excluirCodigoConta: "", + codigosCentrosCustoSelecionados: "", + codigosContasSelecionadas: "", }; - console.log('🎯 Novos filtros para analítico:', novosFiltros); + console.log("🎯 Novos filtros para analítico:", novosFiltros); console.log( - '📊 É totalizador?', - mesSelecionado && !filialSelecionada ? 'SIM' : 'NÃO' + "📊 É totalizador?", + mesSelecionado && !filialSelecionada ? "SIM" : "NÃO" ); setAnaliticoFiltros(novosFiltros); }, @@ -739,7 +812,7 @@ export default function Teste() { items.forEach((item) => { const anoMes = item.data_competencia; - const filial = item.filial || item.codfilial || ''; + const filial = item.filial || item.codfilial || ""; if (anoMes && valoresPorMesPorFilial[anoMes] && filial) { if (!valoresPorMesPorFilial[anoMes][filial]) { valoresPorMesPorFilial[anoMes][filial] = 0; @@ -759,8 +832,8 @@ export default function Teste() { mesesDisponiveis.forEach((mes) => { valores[mes] = data .filter((item) => { - const codgrupo = item.codgrupo || item.codigo_grupo || ''; - return codgrupo === '01' && item.data_competencia === mes; + const codgrupo = item.codgrupo || item.codigo_grupo || ""; + return codgrupo === "01" && item.data_competencia === mes; }) .reduce((sum, item) => sum + parseFloat(item.valor), 0); }); @@ -782,10 +855,10 @@ export default function Teste() { filiaisDisponiveis.forEach((filial) => { valores[mes][filial] = data .filter((item) => { - const codgrupo = item.codgrupo || item.codigo_grupo || ''; - const itemFilial = item.filial || item.codfilial || ''; + const codgrupo = item.codgrupo || item.codigo_grupo || ""; + const itemFilial = item.filial || item.codfilial || ""; return ( - codgrupo === '01' && + codgrupo === "01" && item.data_competencia === mes && itemFilial === filial ); @@ -805,7 +878,7 @@ export default function Teste() { const percentuais: Record = {}; // Se for CODGRUPO 01, sempre retornar 100% - if (codigoGrupo === '01') { + if (codigoGrupo === "01") { mesesDisponiveis.forEach((mes) => { percentuais[mes] = 100; }); @@ -841,13 +914,13 @@ export default function Teste() { > = {}; // Extrair filiais únicas dos próprios valoresPorMesPorFilial para evitar dependência de arrays externos - const primeiroMes = mesesDisponiveis[0] || ''; + const primeiroMes = mesesDisponiveis[0] || ""; const filiaisDisponiveis = Object.keys( valoresPorMesPorFilial[primeiroMes] || {} ); // Se for CODGRUPO 01, sempre retornar 100% para todas as filiais - if (codigoGrupo === '01') { + if (codigoGrupo === "01") { mesesDisponiveis.forEach((mes) => { percentuaisPorMesPorFilial[mes] = {}; filiaisDisponiveis.forEach((filial) => { @@ -883,8 +956,8 @@ export default function Teste() { const totalGrupo01Memo = React.useMemo(() => { return data .filter((item) => { - const codgrupo = item.codgrupo || item.codigo_grupo || ''; - return codgrupo === '01'; + const codgrupo = item.codgrupo || item.codigo_grupo || ""; + return codgrupo === "01"; }) .reduce((sum, item) => sum + parseFloat(item.valor), 0); }, [data]); @@ -893,7 +966,7 @@ export default function Teste() { const calcularPercentualTotal = React.useCallback( (total: number, codigoGrupo?: string): number => { // Se for CODGRUPO 01, sempre retornar 100% - if (codigoGrupo === '01') { + if (codigoGrupo === "01") { return 100; } @@ -907,13 +980,64 @@ export default function Teste() { [totalGrupo01Memo] ); + // Função para calcular total por filial (soma de todos os meses) + const calcularTotalPorFilial = React.useCallback( + ( + valoresPorMesPorFilial: Record>, + filiais: string[] + ): Record => { + const totalPorFilial: Record = {}; + filiais.forEach((filial) => { + totalPorFilial[filial] = 0; + Object.keys(valoresPorMesPorFilial).forEach((mes) => { + totalPorFilial[filial] += valoresPorMesPorFilial[mes]?.[filial] || 0; + }); + }); + return totalPorFilial; + }, + [] + ); + + // Função para calcular percentual por filial baseado no grupo 01 + const calcularPercentualPorFilial = React.useCallback( + ( + totalPorFilial: Record, + codigoGrupo: string | undefined, + filiais: string[] + ): Record => { + const percentualPorFilial: Record = {}; + + if (codigoGrupo === "01") { + filiais.forEach((filial) => { + percentualPorFilial[filial] = 100; + }); + return percentualPorFilial; + } + + filiais.forEach((filial) => { + const valorAtual = totalPorFilial[filial] || 0; + const totalGrupo01Filial = Object.values( + valoresGrupo01PorMesPorFilialMemo + ).reduce((sum, mesValores) => sum + (mesValores[filial] || 0), 0); + + if (totalGrupo01Filial !== 0) { + percentualPorFilial[filial] = (valorAtual / totalGrupo01Filial) * 100; + } else { + percentualPorFilial[filial] = 0; + } + }); + return percentualPorFilial; + }, + [valoresGrupo01PorMesPorFilialMemo] + ); + const buildHierarchicalData = React.useCallback((): HierarchicalRow[] => { const rows: HierarchicalRow[] = []; // Hierarquia simplificada: [grupo, conta] // Agrupar por CODGRUPO const gruposPorCodigo = data.reduce((acc, item) => { - const codgrupo = item.codgrupo || item.codigo_grupo || ''; + const codgrupo = item.codgrupo || item.codigo_grupo || ""; if (!codgrupo) return acc; if (!acc[codgrupo]) { acc[codgrupo] = []; @@ -959,9 +1083,24 @@ export default function Teste() { const valoresGrupoPorMes = calcularValoresPorMes(items); const valoresGrupoPorMesPorFilial = calcularValoresPorMesPorFilial(items); + // Calcular totais por filial para o grupo + const primeiroMesGrupo = mesesDisponiveis[0] || ""; + const filiaisDisponiveisGrupo = Object.keys( + valoresGrupoPorMesPorFilial[primeiroMesGrupo] || {} + ); + const totalPorFilialGrupo = calcularTotalPorFilial( + valoresGrupoPorMesPorFilial, + filiaisDisponiveisGrupo + ); + const percentualPorFilialGrupo = calcularPercentualPorFilial( + totalPorFilialGrupo, + codgrupo, + filiaisDisponiveisGrupo + ); + // Linha do grupo (Level 0) rows.push({ - type: 'grupo', + type: "grupo", level: 0, grupo: items[0]?.grupo || codgrupo, codigo_grupo: codgrupo, @@ -978,12 +1117,14 @@ export default function Teste() { codgrupo ), percentualTotal: calcularPercentualTotal(totalGrupo, codgrupo), + totalPorFilial: totalPorFilialGrupo, + percentualPorFilial: percentualPorFilialGrupo, }); if (expandedGrupos.has(codgrupo)) { // Agrupar por conta dentro do grupo const contas = items.reduce((acc, item) => { - const conta = item.conta || ''; + const conta = item.conta || ""; if (!conta) return acc; if (!acc[conta]) { acc[conta] = []; @@ -1015,9 +1156,24 @@ export default function Teste() { const valoresContaPorMesPorFilial = calcularValoresPorMesPorFilial(contaItems); + // Calcular totais por filial para a conta + const primeiroMesConta = mesesDisponiveis[0] || ""; + const filiaisDisponiveisConta = Object.keys( + valoresContaPorMesPorFilial[primeiroMesConta] || {} + ); + const totalPorFilialConta = calcularTotalPorFilial( + valoresContaPorMesPorFilial, + filiaisDisponiveisConta + ); + const percentualPorFilialConta = calcularPercentualPorFilial( + totalPorFilialConta, + codgrupo, + filiaisDisponiveisConta + ); + // Linha da conta (Level 1) rows.push({ - type: 'conta', + type: "conta", level: 1, grupo: items[0]?.grupo || codgrupo, codigo_grupo: codgrupo, @@ -1036,6 +1192,8 @@ export default function Teste() { codgrupo ), percentualTotal: calcularPercentualTotal(totalConta, codgrupo), + totalPorFilial: totalPorFilialConta, + percentualPorFilial: percentualPorFilialConta, }); }); } @@ -1046,12 +1204,12 @@ export default function Teste() { const proximoNumero = proximoCodigo ? parseInt(proximoCodigo) : 999; if ( - codgrupo === '02' || + codgrupo === "02" || (parseInt(codgrupo) === 2 && proximoNumero > 2) ) { // Calcular MARGEM BRUTA = CODGRUPO 01 + CODGRUPO 02 - const valoresGrupo01 = valoresPorGrupo['01'] || {}; - const valoresGrupo02 = valoresPorGrupo['02'] || {}; + const valoresGrupo01 = valoresPorGrupo["01"] || {}; + const valoresGrupo02 = valoresPorGrupo["02"] || {}; // Calcular valores por mês para MARGEM BRUTA const valoresMargemPorMes: Record = {}; @@ -1066,15 +1224,15 @@ export default function Teste() { string, Record > = {}; - const valoresGrupo01PorFilial = gruposPorCodigo['01'] - ? calcularValoresPorMesPorFilial(gruposPorCodigo['01']) + const valoresGrupo01PorFilial = gruposPorCodigo["01"] + ? calcularValoresPorMesPorFilial(gruposPorCodigo["01"]) : {}; - const valoresGrupo02PorFilial = gruposPorCodigo['02'] - ? calcularValoresPorMesPorFilial(gruposPorCodigo['02']) + const valoresGrupo02PorFilial = gruposPorCodigo["02"] + ? calcularValoresPorMesPorFilial(gruposPorCodigo["02"]) : {}; // Extrair filiais únicas dos valores calculados - const primeiroMes = mesesDisponiveis[0] || ''; + const primeiroMes = mesesDisponiveis[0] || ""; const filiaisDisponiveis = [ ...new Set([ ...Object.keys(valoresGrupo01PorFilial[primeiroMes] || {}), @@ -1102,26 +1260,39 @@ export default function Teste() { valoresMargemBrutaPorMesPorFilial = valoresMargemPorMesPorFilial; totalMargemBruta = totalMargem; + // Calcular totais por filial para MARGEM BRUTA + const totalMargemPorFilial = calcularTotalPorFilial( + valoresMargemPorMesPorFilial, + filiaisDisponiveis + ); + const percentualMargemPorFilial = calcularPercentualPorFilial( + totalMargemPorFilial, + "MARGEM", + filiaisDisponiveis + ); + // Adicionar linha calculada rows.push({ - type: 'calculado', + type: "calculado", level: 0, - grupo: 'MARGEM BRUTA', - codigo_grupo: 'MARGEM', + grupo: "MARGEM BRUTA", + codigo_grupo: "MARGEM", total: totalMargem, isExpanded: false, valoresPorMes: valoresMargemPorMes, valoresPorMesPorFilial: valoresMargemPorMesPorFilial, percentuaisPorMes: calcularPercentuaisPorMes( valoresMargemPorMes, - 'MARGEM' + "MARGEM" ), percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( valoresMargemPorMesPorFilial, - 'MARGEM' + "MARGEM" ), - percentualTotal: calcularPercentualTotal(totalMargem, 'MARGEM'), + percentualTotal: calcularPercentualTotal(totalMargem, "MARGEM"), isCalculado: true, + totalPorFilial: totalMargemPorFilial, + percentualPorFilial: percentualMargemPorFilial, }); } @@ -1133,14 +1304,14 @@ export default function Teste() { : 999; if ( - (codgrupo === '04' || + (codgrupo === "04" || (parseInt(codgrupo) === 4 && proximoNumeroDespesas > 4)) && valoresMargemBrutaPorMes !== null && valoresMargemBrutaPorMesPorFilial !== null && totalMargemBruta !== null ) { // Calcular RESULTADO LOJA = MARGEM BRUTA + DESPESAS (grupo 04) - const valoresGrupo04 = valoresPorGrupo['04'] || {}; + const valoresGrupo04 = valoresPorGrupo["04"] || {}; // Calcular valores por mês para RESULTADO LOJA const valoresResultadoPorMes: Record = {}; @@ -1155,12 +1326,12 @@ export default function Teste() { string, Record > = {}; - const valoresGrupo04PorFilial = gruposPorCodigo['04'] - ? calcularValoresPorMesPorFilial(gruposPorCodigo['04']) + const valoresGrupo04PorFilial = gruposPorCodigo["04"] + ? calcularValoresPorMesPorFilial(gruposPorCodigo["04"]) : {}; // Extrair filiais únicas dos valores calculados - const primeiroMes = mesesDisponiveis[0] || ''; + const primeiroMes = mesesDisponiveis[0] || ""; const filiaisDisponiveis = [ ...new Set([ ...Object.keys( @@ -1187,26 +1358,39 @@ export default function Teste() { 0 ); + // Calcular totais por filial para RESULTADO LOJA + const totalResultadoPorFilial = calcularTotalPorFilial( + valoresResultadoPorMesPorFilial, + filiaisDisponiveis + ); + const percentualResultadoPorFilial = calcularPercentualPorFilial( + totalResultadoPorFilial, + "RESULTADO", + filiaisDisponiveis + ); + // Adicionar linha calculada rows.push({ - type: 'calculado', + type: "calculado", level: 0, - grupo: 'RESULTADO LOJA', - codigo_grupo: 'RESULTADO', + grupo: "RESULTADO LOJA", + codigo_grupo: "RESULTADO", total: totalResultado, isExpanded: false, valoresPorMes: valoresResultadoPorMes, valoresPorMesPorFilial: valoresResultadoPorMesPorFilial, percentuaisPorMes: calcularPercentuaisPorMes( valoresResultadoPorMes, - 'RESULTADO' + "RESULTADO" ), percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( valoresResultadoPorMesPorFilial, - 'RESULTADO' + "RESULTADO" ), - percentualTotal: calcularPercentualTotal(totalResultado, 'RESULTADO'), + percentualTotal: calcularPercentualTotal(totalResultado, "RESULTADO"), isCalculado: true, + totalPorFilial: totalResultadoPorFilial, + percentualPorFilial: percentualResultadoPorFilial, }); } }); @@ -1221,15 +1405,17 @@ export default function Teste() { calcularPercentuaisPorMes, calcularPercentuaisPorMesPorFilial, calcularPercentualTotal, + calcularTotalPorFilial, + calcularPercentualPorFilial, ]); 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'; + "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 linhaId = `${row.type}-${row.codigo_grupo || ""}-${ + row.codigo_conta || "" }`; const isSelected = linhaSelecionada === linhaId; @@ -1237,15 +1423,15 @@ export default function Teste() { if (isSelected) { style += - ' bg-gradient-to-r from-green-100 to-emerald-100 border-l-4 border-green-500 shadow-lg'; + " bg-gradient-to-r from-green-100 to-emerald-100 border-l-4 border-green-500 shadow-lg"; } switch (row.type) { - case 'grupo': + 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': + 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': + case "conta": return `${style} bg-white font-normal text-gray-600`; default: return style; @@ -1256,24 +1442,24 @@ export default function Teste() { const getFixedCellBackground = React.useCallback( (row: HierarchicalRow): string => { - const linhaId = `${row.type}-${row.codigo_grupo || ''}-${ - row.codigo_conta || '' + 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'; + return "bg-gradient-to-r from-green-100 to-emerald-100"; } switch (row.type) { - case 'grupo': - return 'bg-gradient-to-r from-blue-50 to-indigo-50'; - case 'calculado': - return 'bg-gradient-to-r from-purple-50 to-pink-50'; - case 'conta': - return 'bg-white'; + case "grupo": + return "bg-gradient-to-r from-blue-50 to-indigo-50"; + case "calculado": + return "bg-gradient-to-r from-purple-50 to-pink-50"; + case "conta": + return "bg-white"; default: - return 'bg-white'; + return "bg-white"; } }, [linhaSelecionada] @@ -1286,7 +1472,7 @@ export default function Teste() { const renderCellContent = React.useCallback( (row: HierarchicalRow) => { switch (row.type) { - case 'grupo': + case "grupo": return (
); - case 'calculado': + case "calculado": return (
@@ -1331,7 +1517,7 @@ export default function Teste() {
); - case 'conta': + case "conta": return (
@@ -1384,7 +1570,7 @@ export default function Teste() { setLoading(true); setError(null); - const response = await fetch('/api/dre-filial-oracle'); + const response = await fetch("/api/dre-filial-oracle"); if (!response.ok) { throw new Error(`Erro HTTP: ${response.status}`); } @@ -1407,7 +1593,7 @@ export default function Teste() { // Filtro por filial (multi-seleção) if (filiaisSelecionadas.length > 0) { dadosFiltrados = dadosFiltrados.filter((item: DREItem) => { - const filialItem = item.filial || item.codfilial || ''; + const filialItem = item.filial || item.codfilial || ""; return filiaisSelecionadas.includes(filialItem); }); } @@ -1418,18 +1604,18 @@ export default function Teste() { // Limpar filtros do analítico ao aplicar novos filtros na tabela setAnaliticoFiltros({ - dataInicio: '', - dataFim: '', - centroCusto: '', - codigoGrupo: '', - codigoSubgrupo: '', - codigoConta: '', - codFilial: '', - linhaSelecionada: '', - excluirCentroCusto: '', - excluirCodigoConta: '', - codigosCentrosCustoSelecionados: '', - codigosContasSelecionadas: '', + dataInicio: "", + dataFim: "", + centroCusto: "", + codigoGrupo: "", + codigoSubgrupo: "", + codigoConta: "", + codFilial: "", + linhaSelecionada: "", + excluirCentroCusto: "", + excluirCodigoConta: "", + codigosCentrosCustoSelecionados: "", + codigosContasSelecionadas: "", }); // Extrair meses únicos dos dados filtrados @@ -1440,7 +1626,7 @@ export default function Teste() { ].sort() as string[]; setMesesDisponiveis(meses); } catch (err) { - setError(err instanceof Error ? err.message : 'Erro desconhecido'); + setError(err instanceof Error ? err.message : "Erro desconhecido"); } finally { setLoading(false); } @@ -1450,13 +1636,13 @@ export default function Teste() { const limparFiltros = () => { const agora = new Date(); const anoAtual = agora.getFullYear(); - const mesAtual = String(agora.getMonth() + 1).padStart(2, '0'); + const mesAtual = String(agora.getMonth() + 1).padStart(2, "0"); const periodoAtual = `${anoAtual}-${mesAtual}`; setFiltros({ periodoDe: `${anoAtual}-01`, periodoAte: periodoAtual, - filial: 'Todas', + filial: "Todas", }); setFiliaisSelecionadas([]); @@ -1469,18 +1655,18 @@ export default function Teste() { // Limpar filtros do analítico também setAnaliticoFiltros({ - dataInicio: '', - dataFim: '', - centroCusto: '', - codigoGrupo: '', - codigoSubgrupo: '', - codigoConta: '', - codFilial: '', - linhaSelecionada: '', - excluirCentroCusto: '', - excluirCodigoConta: '', - codigosCentrosCustoSelecionados: '', - codigosContasSelecionadas: '', + dataInicio: "", + dataFim: "", + centroCusto: "", + codigoGrupo: "", + codigoSubgrupo: "", + codigoConta: "", + codFilial: "", + linhaSelecionada: "", + excluirCentroCusto: "", + excluirCodigoConta: "", + codigosCentrosCustoSelecionados: "", + codigosContasSelecionadas: "", }); carregarPeriodosDisponiveis(); @@ -1494,7 +1680,7 @@ export default function Teste() { // Hierarquia simplificada: [grupo, conta] // Agrupar por CODGRUPO const gruposPorCodigo = data.reduce((acc, item) => { - const codgrupo = item.codgrupo || item.codigo_grupo || ''; + const codgrupo = item.codgrupo || item.codigo_grupo || ""; if (!codgrupo) return acc; if (!acc[codgrupo]) { acc[codgrupo] = []; @@ -1541,9 +1727,24 @@ export default function Teste() { const valoresGrupoPorMesPorFilial = calcularValoresPorMesPorFilial(items); + // Calcular totais por filial para o grupo + const primeiroMesGrupoCompleto = mesesDisponiveis[0] || ""; + const filiaisDisponiveisGrupoCompleto = Object.keys( + valoresGrupoPorMesPorFilial[primeiroMesGrupoCompleto] || {} + ); + const totalPorFilialGrupoCompleto = calcularTotalPorFilial( + valoresGrupoPorMesPorFilial, + filiaisDisponiveisGrupoCompleto + ); + const percentualPorFilialGrupoCompleto = calcularPercentualPorFilial( + totalPorFilialGrupoCompleto, + codgrupo, + filiaisDisponiveisGrupoCompleto + ); + // Linha do grupo (Level 0) rows.push({ - type: 'grupo', + type: "grupo", level: 0, grupo: items[0]?.grupo || codgrupo, codigo_grupo: codgrupo, @@ -1560,12 +1761,14 @@ export default function Teste() { codgrupo ), percentualTotal: calcularPercentualTotal(totalGrupo, codgrupo), + totalPorFilial: totalPorFilialGrupoCompleto, + percentualPorFilial: percentualPorFilialGrupoCompleto, }); // SEMPRE incluir todas as contas (não depende de expandedGrupos) // Agrupar por conta dentro do grupo const contas = items.reduce((acc, item) => { - const conta = item.conta || ''; + const conta = item.conta || ""; if (!conta) return acc; if (!acc[conta]) { acc[conta] = []; @@ -1597,9 +1800,24 @@ export default function Teste() { const valoresContaPorMesPorFilial = calcularValoresPorMesPorFilial(contaItems); + // Calcular totais por filial para a conta + const primeiroMesContaCompleta = mesesDisponiveis[0] || ""; + const filiaisDisponiveisContaCompleta = Object.keys( + valoresContaPorMesPorFilial[primeiroMesContaCompleta] || {} + ); + const totalPorFilialContaCompleta = calcularTotalPorFilial( + valoresContaPorMesPorFilial, + filiaisDisponiveisContaCompleta + ); + const percentualPorFilialContaCompleta = calcularPercentualPorFilial( + totalPorFilialContaCompleta, + codgrupo, + filiaisDisponiveisContaCompleta + ); + // Linha da conta (Level 1) rows.push({ - type: 'conta', + type: "conta", level: 1, grupo: items[0]?.grupo || codgrupo, codigo_grupo: codgrupo, @@ -1618,6 +1836,8 @@ export default function Teste() { codgrupo ), percentualTotal: calcularPercentualTotal(totalConta, codgrupo), + totalPorFilial: totalPorFilialContaCompleta, + percentualPorFilial: percentualPorFilialContaCompleta, }); }); @@ -1627,12 +1847,12 @@ export default function Teste() { const proximoNumero = proximoCodigo ? parseInt(proximoCodigo) : 999; if ( - codgrupo === '02' || + codgrupo === "02" || (parseInt(codgrupo) === 2 && proximoNumero > 2) ) { // Calcular MARGEM BRUTA = CODGRUPO 01 + CODGRUPO 02 - const valoresGrupo01 = valoresPorGrupo['01'] || {}; - const valoresGrupo02 = valoresPorGrupo['02'] || {}; + const valoresGrupo01 = valoresPorGrupo["01"] || {}; + const valoresGrupo02 = valoresPorGrupo["02"] || {}; // Calcular valores por mês para MARGEM BRUTA const valoresMargemPorMes: Record = {}; @@ -1647,15 +1867,15 @@ export default function Teste() { string, Record > = {}; - const valoresGrupo01PorFilial = gruposPorCodigo['01'] - ? calcularValoresPorMesPorFilial(gruposPorCodigo['01']) + const valoresGrupo01PorFilial = gruposPorCodigo["01"] + ? calcularValoresPorMesPorFilial(gruposPorCodigo["01"]) : {}; - const valoresGrupo02PorFilial = gruposPorCodigo['02'] - ? calcularValoresPorMesPorFilial(gruposPorCodigo['02']) + const valoresGrupo02PorFilial = gruposPorCodigo["02"] + ? calcularValoresPorMesPorFilial(gruposPorCodigo["02"]) : {}; // Extrair filiais únicas dos valores calculados - const primeiroMes = mesesDisponiveis[0] || ''; + const primeiroMes = mesesDisponiveis[0] || ""; const filiaisDisponiveis = [ ...new Set([ ...Object.keys(valoresGrupo01PorFilial[primeiroMes] || {}), @@ -1683,26 +1903,39 @@ export default function Teste() { valoresMargemBrutaPorMesPorFilial = valoresMargemPorMesPorFilial; totalMargemBruta = totalMargem; + // Calcular totais por filial para MARGEM BRUTA + const totalMargemPorFilialCompleta = calcularTotalPorFilial( + valoresMargemPorMesPorFilial, + filiaisDisponiveis + ); + const percentualMargemPorFilialCompleta = calcularPercentualPorFilial( + totalMargemPorFilialCompleta, + "MARGEM", + filiaisDisponiveis + ); + // Adicionar linha calculada rows.push({ - type: 'calculado', + type: "calculado", level: 0, - grupo: 'MARGEM BRUTA', - codigo_grupo: 'MARGEM', + grupo: "MARGEM BRUTA", + codigo_grupo: "MARGEM", total: totalMargem, isExpanded: false, valoresPorMes: valoresMargemPorMes, valoresPorMesPorFilial: valoresMargemPorMesPorFilial, percentuaisPorMes: calcularPercentuaisPorMes( valoresMargemPorMes, - 'MARGEM' + "MARGEM" ), percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( valoresMargemPorMesPorFilial, - 'MARGEM' + "MARGEM" ), - percentualTotal: calcularPercentualTotal(totalMargem, 'MARGEM'), + percentualTotal: calcularPercentualTotal(totalMargem, "MARGEM"), isCalculado: true, + totalPorFilial: totalMargemPorFilialCompleta, + percentualPorFilial: percentualMargemPorFilialCompleta, }); } @@ -1714,14 +1947,14 @@ export default function Teste() { : 999; if ( - (codgrupo === '04' || + (codgrupo === "04" || (parseInt(codgrupo) === 4 && proximoNumeroDespesas > 4)) && valoresMargemBrutaPorMes !== null && valoresMargemBrutaPorMesPorFilial !== null && totalMargemBruta !== null ) { // Calcular RESULTADO LOJA = MARGEM BRUTA + DESPESAS (grupo 04) - const valoresGrupo04 = valoresPorGrupo['04'] || {}; + const valoresGrupo04 = valoresPorGrupo["04"] || {}; // Calcular valores por mês para RESULTADO LOJA const valoresResultadoPorMes: Record = {}; @@ -1736,12 +1969,12 @@ export default function Teste() { string, Record > = {}; - const valoresGrupo04PorFilial = gruposPorCodigo['04'] - ? calcularValoresPorMesPorFilial(gruposPorCodigo['04']) + const valoresGrupo04PorFilial = gruposPorCodigo["04"] + ? calcularValoresPorMesPorFilial(gruposPorCodigo["04"]) : {}; // Extrair filiais únicas dos valores calculados - const primeiroMes = mesesDisponiveis[0] || ''; + const primeiroMes = mesesDisponiveis[0] || ""; const filiaisDisponiveis = [ ...new Set([ ...Object.keys( @@ -1768,29 +2001,43 @@ export default function Teste() { 0 ); + // Calcular totais por filial para RESULTADO LOJA + const totalResultadoPorFilialCompleta = calcularTotalPorFilial( + valoresResultadoPorMesPorFilial, + filiaisDisponiveis + ); + const percentualResultadoPorFilialCompleta = + calcularPercentualPorFilial( + totalResultadoPorFilialCompleta, + "RESULTADO", + filiaisDisponiveis + ); + // Adicionar linha calculada rows.push({ - type: 'calculado', + type: "calculado", level: 0, - grupo: 'RESULTADO LOJA', - codigo_grupo: 'RESULTADO', + grupo: "RESULTADO LOJA", + codigo_grupo: "RESULTADO", total: totalResultado, isExpanded: false, valoresPorMes: valoresResultadoPorMes, valoresPorMesPorFilial: valoresResultadoPorMesPorFilial, percentuaisPorMes: calcularPercentuaisPorMes( valoresResultadoPorMes, - 'RESULTADO' + "RESULTADO" ), percentuaisPorMesPorFilial: calcularPercentuaisPorMesPorFilial( valoresResultadoPorMesPorFilial, - 'RESULTADO' + "RESULTADO" ), percentualTotal: calcularPercentualTotal( totalResultado, - 'RESULTADO' + "RESULTADO" ), isCalculado: true, + totalPorFilial: totalResultadoPorFilialCompleta, + percentualPorFilial: percentualResultadoPorFilialCompleta, }); } }); @@ -1804,15 +2051,17 @@ export default function Teste() { calcularPercentuaisPorMes, calcularPercentuaisPorMesPorFilial, calcularPercentualTotal, + calcularTotalPorFilial, + calcularPercentualPorFilial, ]); const exportarXLSX = () => { if (!data.length) { - console.log('⚠️ Nenhum dado para exportar'); + console.log("⚠️ Nenhum dado para exportar"); return; } - console.log('📊 Exportando TODOS os dados expandidos para XLSX...'); + console.log("📊 Exportando TODOS os dados expandidos para XLSX..."); const dadosCompletosExpandidos = buildHierarchicalDataCompleta(); @@ -1822,7 +2071,7 @@ export default function Teste() { ? filiaisSelecionadas : opcoesFiliais.length > 0 ? opcoesFiliais - : [''] + : [""] ); const dadosExportacao = dadosCompletosExpandidos.map((row, index) => { @@ -1830,10 +2079,10 @@ export default function Teste() { 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 || '', + Grupo: row.grupo || "", + "Código Grupo": row.codigo_grupo || "", + Conta: row.conta || "", + "Código Conta": row.codigo_conta || "", Total: row.total || 0, }; @@ -1903,42 +2152,42 @@ export default function Teste() { colWidths.push({ wch: 12 }); // % Total }); - ws['!cols'] = colWidths; + ws["!cols"] = colWidths; - XLSX.utils.book_append_sheet(wb, ws, 'DRE Filial Completo'); + XLSX.utils.book_append_sheet(wb, ws, "DRE Filial Completo"); const resumoData = [ { - Informação: 'Período', + Informação: "Período", Valor: `${filtros.periodoDe} a ${filtros.periodoAte}`, }, { - Informação: 'Filiais', + Informação: "Filiais", Valor: filiaisSelecionadas.length > 0 - ? filiaisSelecionadas.join(', ') - : 'Todas', + ? filiaisSelecionadas.join(", ") + : "Todas", }, { - Informação: 'Total de Registros', + Informação: "Total de Registros", Valor: dadosCompletosExpandidos.length, }, { - Informação: 'Data de Exportação', - Valor: new Date().toLocaleString('pt-BR'), + 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'); + wsResumo["!cols"] = [{ wch: 20 }, { wch: 30 }]; + XLSX.utils.book_append_sheet(wb, wsResumo, "Resumo"); - const dataAtual = new Date().toISOString().split('T')[0]; + 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); + console.log("✅ Arquivo XLSX completo exportado:", nomeArquivo); }; // Memoizar dados hierárquicos para evitar recálculos desnecessários @@ -2253,11 +2502,11 @@ export default function Teste() {
{/* Table Header */} @@ -2271,13 +2520,13 @@ export default function Teste() { ? filiaisSelecionadas : opcoesFiliais.length > 0 ? opcoesFiliais - : ['']; + : [""]; return ( {/* Cabeçalhos de filiais para este mês */} {filiaisParaMes.map((filial: string) => ( - + + + + ); + }); + })()} @@ -2332,8 +2619,8 @@ export default function Teste() { {/* Table Body */} {hierarchicalData.map((row, index) => { - const linhaId = `${row.type}-${row.codigo_grupo || ''}-${ - row.codigo_conta || '' + const linhaId = `${row.type}-${row.codigo_grupo || ""}-${ + row.codigo_conta || "" }`; const isSelected = linhaSelecionada === linhaId; return (
{mes} {filial && ( @@ -2320,6 +2569,44 @@ export default function Teste() { ); })} + {/* Cabeçalhos de totalizador por filial */} + {(() => { + const filiaisParaTotalizador = + filtrosAplicados && filiaisSelecionadas.length > 0 + ? filiaisSelecionadas + : opcoesFiliais.length > 0 + ? opcoesFiliais + : [""]; + + return filiaisParaTotalizador.map((filial: string) => { + if ( + filtrosAplicados && + filiaisSelecionadas.length > 0 && + !filiaisSelecionadas.includes(filial) + ) { + return null; + } + return ( + + + Total +
+ + Filial - {filial} + +
+ %
+ + Filial - {filial} + +
Total