fix: divisão de colunas por filial

This commit is contained in:
Alessandro Gonçaalves 2025-12-09 17:58:11 -03:00
parent 21e70256c7
commit 078c4842ed
1 changed files with 117 additions and 79 deletions

View File

@ -66,6 +66,8 @@ const TableRow = memo(({
renderCellContent, renderCellContent,
mesesDisponiveis, mesesDisponiveis,
opcoesFiliais, opcoesFiliais,
filiaisSelecionadas,
filtrosAplicados,
formatCurrency, formatCurrency,
formatCurrencyWithColor, formatCurrencyWithColor,
getFixedCellBackground getFixedCellBackground
@ -78,6 +80,8 @@ const TableRow = memo(({
renderCellContent: (row: HierarchicalRow) => React.ReactNode; renderCellContent: (row: HierarchicalRow) => React.ReactNode;
mesesDisponiveis: string[]; mesesDisponiveis: string[];
opcoesFiliais: string[]; opcoesFiliais: string[];
filiaisSelecionadas: string[];
filtrosAplicados: boolean;
formatCurrency: (value: number) => string; formatCurrency: (value: number) => string;
formatCurrencyWithColor: (value: number) => { formatted: string; isNegative: boolean }; formatCurrencyWithColor: (value: number) => { formatted: string; isNegative: boolean };
getFixedCellBackground: (row: HierarchicalRow) => string; 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 */} {/* Colunas de valores por mês e por filial - cada filial tem suas próprias colunas */}
{mesesDisponiveis.map((mes) => {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 (
<React.Fragment key={`${mes}-${filial || 'default'}`}> <React.Fragment key={`${mes}-${filial || 'default'}`}>
<td <td
className="px-2 py-1 text-right font-semibold cursor-pointer hover:bg-blue-50/50 transition-colors duration-200 whitespace-nowrap overflow-hidden w-[120px] min-w-[120px]" className="px-2 py-1 text-right font-semibold cursor-pointer hover:bg-blue-50/50 transition-colors duration-200 whitespace-nowrap overflow-hidden w-[120px] min-w-[120px]"
@ -167,7 +176,8 @@ const TableRow = memo(({
)} )}
</td> </td>
</React.Fragment> </React.Fragment>
)) );
})
)} )}
{/* Coluna Total */} {/* 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; const numValue = typeof value === "string" ? parseFloat(value) : value;
return numValue.toLocaleString("pt-BR", { return numValue.toLocaleString("pt-BR", {
style: "currency", style: "currency",
currency: "BRL", currency: "BRL",
}); });
}; }, []);
const formatCurrencyWithColor = (value: string | number) => { 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 formatted = formatCurrency(value);
const isNegative = numValue < 0; const isNegative = numValue < 0;
return { formatted, isNegative }; return { formatted, isNegative };
}; }, [formatCurrency]);
// Função para lidar com clique nas linhas // 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('🖱️ Clique na linha:', row);
console.log('📅 Mês selecionado:', mesSelecionado); console.log('📅 Mês selecionado:', mesSelecionado);
@ -431,7 +441,7 @@ export default function Teste() {
console.log('🎯 Novos filtros para analítico:', novosFiltros); console.log('🎯 Novos filtros para analítico:', novosFiltros);
setAnaliticoFiltros(novosFiltros); setAnaliticoFiltros(novosFiltros);
}; }, [data]);
const toggleGrupo = useCallback((codigoGrupo: string) => { const toggleGrupo = useCallback((codigoGrupo: string) => {
setExpandedGrupos(prev => { setExpandedGrupos(prev => {
@ -464,7 +474,7 @@ export default function Teste() {
}; };
// Função auxiliar para calcular valores por mês // Função auxiliar para calcular valores por mês
const calcularValoresPorMes = (items: DREItem[]): Record<string, number> => { const calcularValoresPorMes = React.useCallback((items: DREItem[]): Record<string, number> => {
const valoresPorMes: Record<string, number> = {}; const valoresPorMes: Record<string, number> = {};
mesesDisponiveis.forEach(mes => { mesesDisponiveis.forEach(mes => {
@ -479,16 +489,18 @@ export default function Teste() {
}); });
return valoresPorMes; return valoresPorMes;
}; }, [mesesDisponiveis]);
// Função auxiliar para calcular valores por mês e por filial // Função auxiliar para calcular valores por mês e por filial
const calcularValoresPorMesPorFilial = (items: DREItem[]): Record<string, Record<string, number>> => { const calcularValoresPorMesPorFilial = React.useCallback((items: DREItem[]): Record<string, Record<string, number>> => {
const valoresPorMesPorFilial: Record<string, Record<string, number>> = {}; const valoresPorMesPorFilial: Record<string, Record<string, number>> = {};
// Extrair filiais únicas dos items se opcoesFiliais ainda não estiver disponível // Usar filiais selecionadas se houver filtros aplicados, senão usar todas as opções disponíveis
const filiaisDisponiveis = opcoesFiliais.length > 0 const filiaisDisponiveis = (filtrosAplicados && filiaisSelecionadas.length > 0)
? opcoesFiliais ? filiaisSelecionadas
: [...new Set(items.map(item => item.filial || item.codfilial).filter(Boolean))] as string[]; : (opcoesFiliais.length > 0
? opcoesFiliais
: [...new Set(items.map(item => item.filial || item.codfilial).filter(Boolean))] as string[]);
mesesDisponiveis.forEach(mes => { mesesDisponiveis.forEach(mes => {
valoresPorMesPorFilial[mes] = {}; valoresPorMesPorFilial[mes] = {};
@ -500,7 +512,7 @@ export default function Teste() {
items.forEach((item) => { items.forEach((item) => {
const anoMes = item.data_competencia; const anoMes = item.data_competencia;
const filial = item.filial || item.codfilial || ""; const filial = item.filial || item.codfilial || "";
if (anoMes && valoresPorMesPorFilial[anoMes] && filial) { if (anoMes && valoresPorMesPorFilial[anoMes] && filial && filiaisDisponiveis.includes(filial)) {
if (!valoresPorMesPorFilial[anoMes][filial]) { if (!valoresPorMesPorFilial[anoMes][filial]) {
valoresPorMesPorFilial[anoMes][filial] = 0; valoresPorMesPorFilial[anoMes][filial] = 0;
} }
@ -509,10 +521,49 @@ export default function Teste() {
}); });
return valoresPorMesPorFilial; 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<string, number> = {};
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<string, Record<string, number>> = {};
// 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) // Função para calcular percentuais baseado no CODGRUPO 01 (FATURAMENTO LÍQUIDO)
const calcularPercentuaisPorMes = ( const calcularPercentuaisPorMes = React.useCallback((
valoresPorMes: Record<string, number>, valoresPorMes: Record<string, number>,
codigoGrupo?: string codigoGrupo?: string
): Record<string, number> => { ): Record<string, number> => {
@ -525,22 +576,11 @@ export default function Teste() {
}); });
return percentuais; return percentuais;
} }
// Calcular valores do grupo 01 por mês (base para cálculo)
const valoresGrupo01PorMes: Record<string, number> = {};
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) => { Object.keys(valoresPorMes).forEach((mes) => {
const valorAtual = valoresPorMes[mes]; const valorAtual = valoresPorMes[mes];
const valorGrupo01 = valoresGrupo01PorMes[mes] || 0; const valorGrupo01 = valoresGrupo01PorMesMemo[mes] || 0;
if (valorGrupo01 !== 0) { if (valorGrupo01 !== 0) {
percentuais[mes] = (valorAtual / valorGrupo01) * 100; percentuais[mes] = (valorAtual / valorGrupo01) * 100;
@ -550,19 +590,21 @@ export default function Teste() {
}); });
return percentuais; return percentuais;
}; }, [mesesDisponiveis, valoresGrupo01PorMesMemo]);
// Função para calcular percentuais por mês e por filial baseado no CODGRUPO 01 // Função para calcular percentuais por mês e por filial baseado no CODGRUPO 01
const calcularPercentuaisPorMesPorFilial = ( const calcularPercentuaisPorMesPorFilial = React.useCallback((
valoresPorMesPorFilial: Record<string, Record<string, number>>, valoresPorMesPorFilial: Record<string, Record<string, number>>,
codigoGrupo?: string codigoGrupo?: string
): Record<string, Record<string, number>> => { ): Record<string, Record<string, number>> => {
const percentuaisPorMesPorFilial: Record<string, Record<string, number>> = {}; const percentuaisPorMesPorFilial: Record<string, Record<string, number>> = {};
// Extrair filiais únicas dos valores se opcoesFiliais ainda não estiver disponível // Usar filiais selecionadas se houver filtros aplicados, senão usar todas as opções disponíveis
const filiaisDisponiveis = opcoesFiliais.length > 0 const filiaisDisponiveis = (filtrosAplicados && filiaisSelecionadas.length > 0)
? opcoesFiliais ? filiaisSelecionadas
: Object.keys(valoresPorMesPorFilial[mesesDisponiveis[0] || ""] || {}); : (opcoesFiliais.length > 0
? opcoesFiliais
: Object.keys(valoresPorMesPorFilial[mesesDisponiveis[0] || ""] || {}));
// Se for CODGRUPO 01, sempre retornar 100% para todas as filiais // Se for CODGRUPO 01, sempre retornar 100% para todas as filiais
if (codigoGrupo === "01") { if (codigoGrupo === "01") {
@ -574,28 +616,13 @@ export default function Teste() {
}); });
return percentuaisPorMesPorFilial; return percentuaisPorMesPorFilial;
} }
// Calcular valores do grupo 01 por mês e por filial (base para cálculo)
const valoresGrupo01PorMesPorFilial: Record<string, Record<string, number>> = {};
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 => { mesesDisponiveis.forEach(mes => {
percentuaisPorMesPorFilial[mes] = {}; percentuaisPorMesPorFilial[mes] = {};
filiaisDisponiveis.forEach(filial => { filiaisDisponiveis.forEach(filial => {
const valorAtual = valoresPorMesPorFilial[mes]?.[filial] || 0; const valorAtual = valoresPorMesPorFilial[mes]?.[filial] || 0;
const valorGrupo01 = valoresGrupo01PorMesPorFilial[mes]?.[filial] || 0; const valorGrupo01 = valoresGrupo01PorMesPorFilialMemo[mes]?.[filial] || 0;
if (valorGrupo01 !== 0) { if (valorGrupo01 !== 0) {
percentuaisPorMesPorFilial[mes][filial] = (valorAtual / valorGrupo01) * 100; percentuaisPorMesPorFilial[mes][filial] = (valorAtual / valorGrupo01) * 100;
@ -606,31 +633,34 @@ export default function Teste() {
}); });
return percentuaisPorMesPorFilial; return percentuaisPorMesPorFilial;
}; }, [mesesDisponiveis, opcoesFiliais, valoresGrupo01PorMesPorFilialMemo, filtrosAplicados, filiaisSelecionadas]);
// Função para calcular percentual do total baseado no CODGRUPO 01 // Memoizar total do grupo 01
const calcularPercentualTotal = (total: number, codigoGrupo?: string): number => { const totalGrupo01Memo = React.useMemo(() => {
// Se for CODGRUPO 01, sempre retornar 100% return data
if (codigoGrupo === "01") {
return 100;
}
// Calcular total do grupo 01 (base para cálculo)
const totalGrupo01 = data
.filter(item => { .filter(item => {
const codgrupo = item.codgrupo || item.codigo_grupo || ""; const codgrupo = item.codgrupo || item.codigo_grupo || "";
return codgrupo === "01"; return codgrupo === "01";
}) })
.reduce((sum, item) => sum + parseFloat(item.valor), 0); .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) { // Usar total memoizado do grupo 01
return (total / totalGrupo01) * 100; if (totalGrupo01Memo !== 0) {
return (total / totalGrupo01Memo) * 100;
} else { } else {
return 0; return 0;
} }
}; }, [totalGrupo01Memo]);
const buildHierarchicalData = (): HierarchicalRow[] => { const buildHierarchicalData = React.useCallback((): HierarchicalRow[] => {
const rows: HierarchicalRow[] = []; const rows: HierarchicalRow[] = [];
// Hierarquia simplificada: [grupo, conta] // Hierarquia simplificada: [grupo, conta]
@ -790,9 +820,9 @@ export default function Teste() {
}); });
return rows; 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 = 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";
@ -816,9 +846,9 @@ export default function Teste() {
default: default:
return style; 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 linhaId = `${row.type}-${row.codigo_grupo || ""}-${row.codigo_conta || ""}`;
const isSelected = linhaSelecionada === linhaId; const isSelected = linhaSelecionada === linhaId;
@ -836,13 +866,13 @@ export default function Teste() {
default: default:
return "bg-white"; return "bg-white";
} }
}; }, [linhaSelecionada]);
const getIndentStyle = (level: number) => { const getIndentStyle = React.useCallback((level: number) => {
return { paddingLeft: `${level * 20}px` }; return { paddingLeft: `${level * 20}px` };
}; }, []);
const renderCellContent = (row: HierarchicalRow) => { const renderCellContent = React.useCallback((row: HierarchicalRow) => {
switch (row.type) { switch (row.type) {
case "grupo": case "grupo":
return ( return (
@ -917,7 +947,7 @@ export default function Teste() {
default: default:
return null; return null;
} }
}; }, [toggleGrupo, handleRowClick]);
const toggleExpandAll = () => { const toggleExpandAll = () => {
if (isAllExpanded) { if (isAllExpanded) {
@ -1109,7 +1139,13 @@ export default function Teste() {
console.log('✅ Arquivo XLSX completo exportado:', nomeArquivo); 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 ( return (
<div className="w-full max-w-none mx-auto p-2"> <div className="w-full max-w-none mx-auto p-2">
@ -1370,7 +1406,7 @@ export default function Teste() {
Descrição Descrição
</th> </th>
{mesesDisponiveis.map((mes) => {mesesDisponiveis.map((mes) =>
(opcoesFiliais.length > 0 ? opcoesFiliais : ['']).map((filial: string) => ( ((filtrosAplicados && filiaisSelecionadas.length > 0) ? filiaisSelecionadas : (opcoesFiliais.length > 0 ? opcoesFiliais : [''])).map((filial: string) => (
<React.Fragment key={`${mes}-${filial || 'default'}`}> <React.Fragment key={`${mes}-${filial || 'default'}`}>
<th className="px-2 py-2 text-right text-xs font-semibold text-gray-700 uppercase tracking-wide w-[120px] min-w-[120px] bg-gradient-to-r from-blue-50 to-indigo-50"> <th className="px-2 py-2 text-right text-xs font-semibold text-gray-700 uppercase tracking-wide w-[120px] min-w-[120px] bg-gradient-to-r from-blue-50 to-indigo-50">
{mes}{filial && <><br/> {mes}{filial && <><br/>
@ -1408,6 +1444,8 @@ export default function Teste() {
renderCellContent={renderCellContent} renderCellContent={renderCellContent}
mesesDisponiveis={mesesDisponiveis} mesesDisponiveis={mesesDisponiveis}
opcoesFiliais={opcoesFiliais} opcoesFiliais={opcoesFiliais}
filiaisSelecionadas={filiaisSelecionadas}
filtrosAplicados={filtrosAplicados}
formatCurrency={formatCurrency} formatCurrency={formatCurrency}
formatCurrencyWithColor={formatCurrencyWithColor} formatCurrencyWithColor={formatCurrencyWithColor}
getFixedCellBackground={getFixedCellBackground} getFixedCellBackground={getFixedCellBackground}