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,
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 (
<React.Fragment key={`${mes}-${filial || 'default'}`}>
<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]"
@ -167,7 +176,8 @@ const TableRow = memo(({
)}
</td>
</React.Fragment>
))
);
})
)}
{/* 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<string, number> => {
const calcularValoresPorMes = React.useCallback((items: DREItem[]): Record<string, number> => {
const valoresPorMes: Record<string, number> = {};
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<string, Record<string, number>> => {
const calcularValoresPorMesPorFilial = React.useCallback((items: DREItem[]): 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
const filiaisDisponiveis = opcoesFiliais.length > 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
: [...new Set(items.map(item => item.filial || item.codfilial).filter(Boolean))] as string[];
: [...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<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)
const calcularPercentuaisPorMes = (
const calcularPercentuaisPorMes = React.useCallback((
valoresPorMes: Record<string, number>,
codigoGrupo?: string
): Record<string, number> => {
@ -526,21 +577,10 @@ export default function Teste() {
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) => {
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<string, Record<string, number>>,
codigoGrupo?: string
): 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
const filiaisDisponiveis = opcoesFiliais.length > 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] || ""] || {});
: Object.keys(valoresPorMesPorFilial[mesesDisponiveis[0] || ""] || {}));
// Se for CODGRUPO 01, sempre retornar 100% para todas as filiais
if (codigoGrupo === "01") {
@ -575,27 +617,12 @@ export default function Teste() {
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 => {
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]);
if (totalGrupo01 !== 0) {
return (total / totalGrupo01) * 100;
// Função para calcular percentual do total baseado no CODGRUPO 01
const calcularPercentualTotal = React.useCallback((total: number, codigoGrupo?: string): number => {
// Se for CODGRUPO 01, sempre retornar 100%
if (codigoGrupo === "01") {
return 100;
}
// Usar total memoizado do grupo 01
if (totalGrupo01Memo !== 0) {
return (total / totalGrupo01Memo) * 100;
} else {
return 0;
}
};
}, [totalGrupo01Memo]);
const 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 (
<div className="w-full max-w-none mx-auto p-2">
@ -1370,7 +1406,7 @@ export default function Teste() {
Descrição
</th>
{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'}`}>
<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/>
@ -1408,6 +1444,8 @@ export default function Teste() {
renderCellContent={renderCellContent}
mesesDisponiveis={mesesDisponiveis}
opcoesFiliais={opcoesFiliais}
filiaisSelecionadas={filiaisSelecionadas}
filtrosAplicados={filtrosAplicados}
formatCurrency={formatCurrency}
formatCurrencyWithColor={formatCurrencyWithColor}
getFixedCellBackground={getFixedCellBackground}