fix: Adição da relação sintetico x analítico

This commit is contained in:
Alessandro Gonçaalves 2025-10-21 10:08:00 -03:00
parent e6c957e28a
commit 241141f794
3 changed files with 314 additions and 51 deletions

View File

@ -6,6 +6,7 @@ import {
getCoreRowModel,
getSortedRowModel,
getFilteredRowModel,
ColumnFiltersState,
} from "@tanstack/react-table";
import { useVirtualizer } from "@tanstack/react-virtual";
import { Input } from "@/components/ui/input";
@ -47,6 +48,15 @@ interface AnaliticoItem {
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;
}
interface AnaliticoProps {
@ -64,36 +74,67 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
const [data, setData] = React.useState<AnaliticoItem[]>([]);
const [loading, setLoading] = React.useState(false);
const [globalFilter, setGlobalFilter] = React.useState("");
const [columnFilters, setColumnFilters] = React.useState<any[]>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [open, setOpen] = React.useState(false);
const [conditions, setConditions] = React.useState([
{ column: "", operator: "contains", value: "" },
]);
// Estado para armazenar filtros externos (vindos do teste.tsx)
const [filtrosExternos, setFiltrosExternos] = React.useState(filtros);
// Atualizar filtros externos quando os props mudarem, mas preservar filtros internos
React.useEffect(() => {
setFiltrosExternos(filtros);
}, [filtros]);
const fetchData = React.useCallback(async () => {
// Só faz a requisição se tiver dataInicio e dataFim
if (!filtros.dataInicio || !filtros.dataFim) {
// Só faz a requisição se tiver dataInicio e dataFim nos filtros externos
if (!filtrosExternos.dataInicio || !filtrosExternos.dataFim) {
setData([]);
return;
}
setLoading(true);
try {
const params = new URLSearchParams({
dataInicio: filtros.dataInicio,
dataFim: filtros.dataFim,
...(filtros.centroCusto && { centroCusto: filtros.centroCusto }),
...(filtros.codigoGrupo && { codigoGrupo: filtros.codigoGrupo }),
...(filtros.codigoSubgrupo && {
codigoSubgrupo: filtros.codigoSubgrupo,
}),
...(filtros.codigoConta && { codigoConta: filtros.codigoConta }),
});
const response = await fetch(`/api/analitico?${params}`);
// Usar a nova API do Oracle
const response = await fetch("/api/analitico-oracle");
if (response.ok) {
const result = await response.json();
setData(result as AnaliticoItem[]);
// Aplicar filtros externos (vindos do teste.tsx) nos dados retornados
let filteredData = result as AnaliticoItem[];
// Filtro por período se especificado nos filtros externos
if (filtrosExternos.dataInicio && filtrosExternos.dataFim) {
filteredData = filteredData.filter((item: AnaliticoItem) => {
const dataItem = item.data_competencia || item.ano_mes_comp;
return dataItem && dataItem >= filtrosExternos.dataInicio && dataItem <= filtrosExternos.dataFim;
});
}
// Filtro por centro de custo se especificado nos filtros externos
if (filtrosExternos.centroCusto) {
filteredData = filteredData.filter((item: AnaliticoItem) =>
item.codigo_centrocusto === filtrosExternos.centroCusto
);
}
// Filtro por código do grupo se especificado nos filtros externos
if (filtrosExternos.codigoGrupo) {
filteredData = filteredData.filter((item: AnaliticoItem) =>
item.codgrupo === filtrosExternos.codigoGrupo
);
}
// Filtro por código da conta se especificado nos filtros externos
if (filtrosExternos.codigoConta) {
filteredData = filteredData.filter((item: AnaliticoItem) =>
item.codigo_conta === filtrosExternos.codigoConta
);
}
setData(filteredData);
} else {
console.error("Erro ao buscar dados:", await response.text());
}
@ -102,7 +143,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
} finally {
setLoading(false);
}
}, [filtros]);
}, [filtrosExternos]);
React.useEffect(() => {
fetchData();
@ -132,7 +173,10 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
accessorKey: "entidade",
header: "Entidade",
filterFn: "advancedText",
cell: () => "-",
cell: ({ getValue }: { getValue: () => string }) => {
const value = getValue();
return value || "-";
},
},
{
accessorKey: "codigo_fornecedor",
@ -181,19 +225,58 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
accessorKey: "valor_previsto",
header: "Valor Previsto",
filterFn: "advancedText",
cell: () => "-",
cell: ({ getValue }: { getValue: () => number }) => {
const value = getValue();
if (!value || value === 0) return "-";
const formatted = new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(value);
const isNegative = value < 0;
return (
<span className={isNegative ? "text-red-600" : "text-gray-900"}>
{formatted}
</span>
);
},
},
{
accessorKey: "valor_confirmado",
header: "Valor Confirmado",
filterFn: "advancedText",
cell: () => "-",
cell: ({ getValue }: { getValue: () => number }) => {
const value = getValue();
if (!value || value === 0) return "-";
const formatted = new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(value);
const isNegative = value < 0;
return (
<span className={isNegative ? "text-red-600" : "text-gray-900"}>
{formatted}
</span>
);
},
},
{
accessorKey: "valor_pago",
header: "Valor Pago",
filterFn: "advancedText",
cell: () => "-",
cell: ({ getValue }: { getValue: () => number }) => {
const value = getValue();
if (!value || value === 0) return "-";
const formatted = new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(value);
const isNegative = value < 0;
return (
<span className={isNegative ? "text-red-600" : "text-gray-900"}>
{formatted}
</span>
);
},
},
{
accessorKey: "historico",
@ -209,7 +292,10 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
accessorKey: "numero_lancamento",
header: "Número do Lançamento",
filterFn: "advancedText",
cell: () => "-",
cell: ({ getValue }: { getValue: () => number }) => {
const value = getValue();
return value || "-";
},
},
],
[]
@ -217,7 +303,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
const filterFns = React.useMemo(
() => ({
advancedText: (row: any, columnId: string, filters: any[]) => {
advancedText: (row: any, columnId: string, filters: ColumnFiltersState) => {
if (!filters || filters.length === 0) return true;
// Se veio um único filtro (objeto), transforma em array
@ -256,7 +342,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
const table = useReactTable({
data,
columns,
columns: columns as any,
state: { globalFilter, columnFilters },
onGlobalFilterChange: setGlobalFilter,
onColumnFiltersChange: setColumnFilters,
@ -306,6 +392,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
setConditions([{ column: "", operator: "contains", value: "" }]);
setColumnFilters([]);
setGlobalFilter("");
// Não limpar os filtros externos - eles vêm do teste.tsx
};
const [totalValor, setTotalValor] = React.useState(0);
@ -335,18 +422,32 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
// Usar EXATAMENTE a mesma lógica do totalValor
const filteredData = table.getRowModel().rows.map((row) => row.original);
const valorRealizado = filteredData.reduce((sum, item) => {
const valor =
typeof item.valor === "string" ? parseFloat(item.valor) : item.valor;
const valor = typeof item.valor === "string" ? parseFloat(item.valor) : item.valor;
return sum + (isNaN(valor) ? 0 : valor);
}, 0);
const valorPrevisto = filteredData.reduce((sum, item) => {
const valor = typeof item.valor_previsto === "string" ? parseFloat(item.valor_previsto) : (item.valor_previsto || 0);
return sum + (isNaN(valor) ? 0 : valor);
}, 0);
const valorConfirmado = filteredData.reduce((sum, item) => {
const valor = typeof item.valor_confirmado === "string" ? parseFloat(item.valor_confirmado) : (item.valor_confirmado || 0);
return sum + (isNaN(valor) ? 0 : valor);
}, 0);
const valorPago = filteredData.reduce((sum, item) => {
const valor = typeof item.valor_pago === "string" ? parseFloat(item.valor_pago) : (item.valor_pago || 0);
return sum + (isNaN(valor) ? 0 : valor);
}, 0);
return {
valorRealizado,
valorPrevisto: 0, // Sempre 0 pois não há dados
valorConfirmado: 0, // Sempre 0 pois não há dados
valorPago: 0, // Sempre 0 pois não há dados
valorPrevisto,
valorConfirmado,
valorPago,
};
}, [table, data, columnFilters, globalFilter]);
}, [table]);
const exportToExcel = () => {
if (data.length === 0) return;
@ -442,6 +543,38 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
</div>
</div>
{/* Filtros Externos Ativos */}
{(filtrosExternos.dataInicio || filtrosExternos.centroCusto || filtrosExternos.codigoGrupo || filtrosExternos.codigoConta) && (
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
<span className="text-sm font-medium text-blue-900">Filtros aplicados pela tabela DRE Gerencial:</span>
</div>
<div className="flex flex-wrap gap-2 text-xs text-blue-800">
{filtrosExternos.dataInicio && filtrosExternos.dataFim && (
<span className="px-2 py-1 bg-blue-100 rounded">
Período: {filtrosExternos.dataInicio} a {filtrosExternos.dataFim}
</span>
)}
{filtrosExternos.centroCusto && (
<span className="px-2 py-1 bg-blue-100 rounded">
Centro: {filtrosExternos.centroCusto}
</span>
)}
{filtrosExternos.codigoGrupo && (
<span className="px-2 py-1 bg-blue-100 rounded">
Grupo: {filtrosExternos.codigoGrupo}
</span>
)}
{filtrosExternos.codigoConta && (
<span className="px-2 py-1 bg-blue-100 rounded">
Conta: {filtrosExternos.codigoConta}
</span>
)}
</div>
</div>
)}
{/* Controls */}
<div className="flex gap-3 flex-wrap">
<Input
@ -472,7 +605,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
className="bg-white border-gray-300 hover:bg-red-50 hover:border-red-300 text-gray-700"
>
<X className="w-4 h-4 mr-2" />
Limpar Filtros
Limpar Filtros Avançados
</Button>
)}
{data.length > 0 && (
@ -590,7 +723,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
)}
</div>
<div className="w-[100px] text-gray-500 whitespace-nowrap">
-
{row.original.entidade || "-"}
</div>
<div className="w-[160px] font-medium text-gray-900 whitespace-nowrap">
{row.original.codigo_fornecedor}
@ -625,14 +758,47 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
currency: "BRL",
}).format(row.original.valor)}
</div>
<div className="w-[120px] text-gray-500 text-right whitespace-nowrap">
-
<div className="w-[120px] text-right whitespace-nowrap">
{row.original.valor_previsto && row.original.valor_previsto !== 0 ? (
<span className={`font-semibold ${
row.original.valor_previsto < 0 ? "text-red-600" : "text-gray-900"
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(row.original.valor_previsto)}
</span>
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[130px] text-gray-500 text-right whitespace-nowrap">
-
<div className="w-[130px] text-right whitespace-nowrap">
{row.original.valor_confirmado && row.original.valor_confirmado !== 0 ? (
<span className={`font-semibold ${
row.original.valor_confirmado < 0 ? "text-red-600" : "text-gray-900"
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(row.original.valor_confirmado)}
</span>
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[110px] text-gray-500 text-right whitespace-nowrap">
-
<div className="w-[110px] text-right whitespace-nowrap">
{row.original.valor_pago && row.original.valor_pago !== 0 ? (
<span className={`font-semibold ${
row.original.valor_pago < 0 ? "text-red-600" : "text-gray-900"
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(row.original.valor_pago)}
</span>
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div
className="w-[200px] text-gray-700 truncate"
@ -647,7 +813,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
{row.original.historico2}
</div>
<div className="w-[50px] text-gray-500 whitespace-nowrap">
-
{row.original.numero_lancamento || "-"}
</div>
</div>
);
@ -682,14 +848,47 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
currency: "BRL",
}).format(columnTotals.valorRealizado)}
</div>
<div className="w-[120px] text-right whitespace-nowrap text-gray-500">
-
<div className="w-[120px] text-right whitespace-nowrap">
{columnTotals.valorPrevisto !== 0 ? (
<span className={`font-bold ${
columnTotals.valorPrevisto < 0 ? "text-red-600" : "text-green-600"
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(columnTotals.valorPrevisto)}
</span>
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[130px] text-right whitespace-nowrap text-gray-500">
-
<div className="w-[130px] text-right whitespace-nowrap">
{columnTotals.valorConfirmado !== 0 ? (
<span className={`font-bold ${
columnTotals.valorConfirmado < 0 ? "text-red-600" : "text-green-600"
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(columnTotals.valorConfirmado)}
</span>
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[110px] text-right whitespace-nowrap text-gray-500">
-
<div className="w-[110px] text-right whitespace-nowrap">
{columnTotals.valorPago !== 0 ? (
<span className={`font-bold ${
columnTotals.valorPago < 0 ? "text-red-600" : "text-green-600"
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(columnTotals.valorPago)}
</span>
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[200px] whitespace-nowrap"></div>
<div className="w-[200px] whitespace-nowrap"></div>
@ -748,6 +947,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
<DialogTitle className="text-xl font-semibold text-gray-900">
Filtros Avançados
</DialogTitle>
<p className="text-sm text-gray-600">
Estes filtros são aplicados sobre os dados filtrados pela tabela DRE Gerencial.
</p>
</DialogHeader>
<div className="space-y-4 max-h-96 overflow-y-auto bg-white">
@ -873,7 +1075,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
onClick={clearFilters}
className="flex-1 border-gray-300 text-gray-700 hover:bg-gray-50"
>
Limpar todos
Limpar filtros avançados
</Button>
<Button
onClick={applyFilters}

View File

@ -988,9 +988,9 @@ export default function Teste() {
</div>
<SheetFooter className="flex gap-3">
<Button variant="outline" onClick={limparFiltros} className="flex-1">
{/* <Button variant="outline" onClick={limparFiltros} className="flex-1">
Limpar filtros
</Button>
</Button> */}
<Button variant="outline" onClick={() => setIsFilterOpen(false)} className="flex-1">
Cancelar
</Button>

View File

@ -0,0 +1,61 @@
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...');
// Query para buscar dados da tabela DRE_RESULTADO_ANALITICO
const sql = `SELECT * FROM DRE_RESULTADO_ANALITICO ORDER BY DTVENC, CODFORNEC, CODCONTA`;
const data = await executeOracleQuery(sql);
console.log('✅ Query executada com sucesso:', data.length, 'registros encontrados');
// Transformar os dados do Oracle para o formato esperado pelo componente
const transformedData = data.map((item: any) => ({
codigo_grupo: item.CODGRUPO || "",
codigo_subgrupo: "", // Não existe na tabela Oracle
codigo_fornecedor: item.CODFORNEC || "",
nome_fornecedor: item.FORNECEDOR || "",
id: item.NUMLANC || 0,
codfilial: "001", // Valor padrão
recnum: item.NUMLANC || 0,
data_competencia: item.ANOMESCOMP || "",
data_vencimento: item.DTVENC ? new Date(item.DTVENC).toISOString().split('T')[0] : "",
data_pagamento: "", // Não existe na tabela Oracle
data_caixa: item.DTCAIXA ? new Date(item.DTCAIXA).toISOString().split('T')[0] : "",
codigo_conta: item.CODCONTA || "",
conta: item.CONTA || "",
codigo_centrocusto: item.CODIGOCENTROCUSTO || "",
valor: 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 || 0,
valor_confirmado: item.VLCONFIRMADO || 0,
valor_pago: item.VLPAGO || 0,
numero_lancamento: item.NUMLANC || 0,
ano_mes_comp: item.ANOMESCOMP || "",
codgrupo: item.CODGRUPO || ""
}));
return NextResponse.json(transformedData);
} catch (error) {
console.error('❌ Erro ao buscar dados analíticos 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 }
);
}
}