feat: implement filtering and PDF/Excel report generation for planejamentos.
This commit is contained in:
@@ -19,26 +19,47 @@ function normalizeOptionalString(value: string | undefined): string | undefined
|
||||
export const list = query({
|
||||
args: {
|
||||
status: v.optional(v.union(v.literal('rascunho'), v.literal('gerado'), v.literal('cancelado'))),
|
||||
responsavelId: v.optional(v.id('funcionarios'))
|
||||
statuses: v.optional(
|
||||
v.array(v.union(v.literal('rascunho'), v.literal('gerado'), v.literal('cancelado')))
|
||||
),
|
||||
responsavelId: v.optional(v.id('funcionarios')),
|
||||
acaoId: v.optional(v.id('acoes')),
|
||||
texto: v.optional(v.string()),
|
||||
periodoInicio: v.optional(v.number()),
|
||||
periodoFim: v.optional(v.number())
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const status = args.status;
|
||||
const responsavelId = args.responsavelId;
|
||||
const { periodoInicio, periodoFim, texto } = args;
|
||||
|
||||
let base: Doc<'planejamentosPedidos'>[] = [];
|
||||
let base = await ctx.db.query('planejamentosPedidos').collect();
|
||||
|
||||
if (responsavelId) {
|
||||
base = await ctx.db
|
||||
.query('planejamentosPedidos')
|
||||
.withIndex('by_responsavelId', (q) => q.eq('responsavelId', responsavelId))
|
||||
.collect();
|
||||
} else if (status) {
|
||||
base = await ctx.db
|
||||
.query('planejamentosPedidos')
|
||||
.withIndex('by_status', (q) => q.eq('status', status))
|
||||
.collect();
|
||||
} else {
|
||||
base = await ctx.db.query('planejamentosPedidos').collect();
|
||||
// Filtros em memória (devido à complexidade de múltiplos índices)
|
||||
if (args.responsavelId) {
|
||||
base = base.filter((p) => p.responsavelId === args.responsavelId);
|
||||
}
|
||||
if (args.acaoId) {
|
||||
base = base.filter((p) => p.acaoId === args.acaoId);
|
||||
}
|
||||
|
||||
// Status simples ou múltiplo
|
||||
if (args.statuses && args.statuses.length > 0) {
|
||||
base = base.filter((p) => args.statuses!.includes(p.status));
|
||||
} else if (args.status) {
|
||||
base = base.filter((p) => p.status === args.status);
|
||||
}
|
||||
|
||||
if (periodoInicio) {
|
||||
base = base.filter((p) => p.data >= new Date(periodoInicio).toISOString().split('T')[0]);
|
||||
}
|
||||
if (periodoFim) {
|
||||
base = base.filter((p) => p.data <= new Date(periodoFim).toISOString().split('T')[0]);
|
||||
}
|
||||
|
||||
if (texto) {
|
||||
const t = texto.toLowerCase();
|
||||
base = base.filter(
|
||||
(p) => p.titulo.toLowerCase().includes(t) || p.descricao.toLowerCase().includes(t)
|
||||
);
|
||||
}
|
||||
|
||||
base.sort((a, b) => b.criadoEm - a.criadoEm);
|
||||
@@ -59,6 +80,108 @@ export const list = query({
|
||||
}
|
||||
});
|
||||
|
||||
export const gerarRelatorio = query({
|
||||
args: {
|
||||
statuses: v.optional(
|
||||
v.array(v.union(v.literal('rascunho'), v.literal('gerado'), v.literal('cancelado')))
|
||||
),
|
||||
responsavelId: v.optional(v.id('funcionarios')),
|
||||
acaoId: v.optional(v.id('acoes')),
|
||||
texto: v.optional(v.string()),
|
||||
periodoInicio: v.optional(v.number()),
|
||||
periodoFim: v.optional(v.number())
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
// Reutilizar lógica de filtro
|
||||
let base = await ctx.db.query('planejamentosPedidos').collect();
|
||||
|
||||
if (args.responsavelId) {
|
||||
base = base.filter((p) => p.responsavelId === args.responsavelId);
|
||||
}
|
||||
if (args.acaoId) {
|
||||
base = base.filter((p) => p.acaoId === args.acaoId);
|
||||
}
|
||||
if (args.statuses && args.statuses.length > 0) {
|
||||
base = base.filter((p) => args.statuses!.includes(p.status));
|
||||
}
|
||||
if (args.periodoInicio) {
|
||||
base = base.filter(
|
||||
(p) => p.data >= new Date(args.periodoInicio!).toISOString().split('T')[0]
|
||||
);
|
||||
}
|
||||
if (args.periodoFim) {
|
||||
base = base.filter((p) => p.data <= new Date(args.periodoFim!).toISOString().split('T')[0]);
|
||||
}
|
||||
if (args.texto) {
|
||||
const t = args.texto.toLowerCase();
|
||||
base = base.filter(
|
||||
(p) => p.titulo.toLowerCase().includes(t) || p.descricao.toLowerCase().includes(t)
|
||||
);
|
||||
}
|
||||
|
||||
base.sort((a, b) => b.criadoEm - a.criadoEm);
|
||||
|
||||
// Enriquecer dados
|
||||
const planejamentosEnriquecidos = await Promise.all(
|
||||
base.map(async (p) => {
|
||||
const [responsavel, acao, itens] = await Promise.all([
|
||||
ctx.db.get(p.responsavelId),
|
||||
p.acaoId ? ctx.db.get(p.acaoId) : Promise.resolve(null),
|
||||
ctx.db
|
||||
.query('planejamentoItens')
|
||||
.withIndex('by_planejamentoId', (q) => q.eq('planejamentoId', p._id))
|
||||
.collect()
|
||||
]);
|
||||
|
||||
let valorEstimadoTotal = 0;
|
||||
for (const item of itens) {
|
||||
// Corrigir string '1.000,00' -> number
|
||||
const val = parseFloat(
|
||||
item.valorEstimado.replace(/\./g, '').replace(',', '.').replace('R$', '').trim()
|
||||
);
|
||||
if (!isNaN(val)) valorEstimadoTotal += val * item.quantidade;
|
||||
}
|
||||
|
||||
return {
|
||||
...p,
|
||||
responsavelNome: responsavel?.nome ?? 'Desconhecido',
|
||||
acaoNome: acao?.nome ?? undefined,
|
||||
itensCount: itens.length,
|
||||
valorEstimadoTotal
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// Calcular resumo
|
||||
const totalPlanejamentos = base.length;
|
||||
const totalValorEstimado = planejamentosEnriquecidos.reduce(
|
||||
(acc, curr) => acc + curr.valorEstimadoTotal,
|
||||
0
|
||||
);
|
||||
|
||||
const totalPorStatus = [
|
||||
{ status: 'rascunho', count: 0 },
|
||||
{ status: 'gerado', count: 0 },
|
||||
{ status: 'cancelado', count: 0 }
|
||||
];
|
||||
|
||||
base.forEach((p) => {
|
||||
const st = totalPorStatus.find((s) => s.status === p.status);
|
||||
if (st) st.count++;
|
||||
});
|
||||
|
||||
return {
|
||||
filtros: args,
|
||||
resumo: {
|
||||
totalPlanejamentos,
|
||||
totalValorEstimado,
|
||||
totalPorStatus
|
||||
},
|
||||
planejamentos: planejamentosEnriquecidos
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export const get = query({
|
||||
args: { id: v.id('planejamentosPedidos') },
|
||||
handler: async (ctx, args) => {
|
||||
|
||||
Reference in New Issue
Block a user