@@ -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) => {
|
||||
@@ -177,7 +300,8 @@ export const create = mutation({
|
||||
descricao: v.string(),
|
||||
data: v.string(),
|
||||
responsavelId: v.id('funcionarios'),
|
||||
acaoId: v.optional(v.id('acoes'))
|
||||
acaoId: v.optional(v.id('acoes')),
|
||||
sourcePlanningId: v.optional(v.id('planejamentosPedidos'))
|
||||
},
|
||||
returns: v.id('planejamentosPedidos'),
|
||||
handler: async (ctx, args) => {
|
||||
@@ -192,7 +316,7 @@ export const create = mutation({
|
||||
if (!descricao) throw new Error('Informe uma descrição.');
|
||||
if (!data) throw new Error('Informe uma data.');
|
||||
|
||||
return await ctx.db.insert('planejamentosPedidos', {
|
||||
const newItemId = await ctx.db.insert('planejamentosPedidos', {
|
||||
titulo,
|
||||
descricao,
|
||||
data,
|
||||
@@ -203,6 +327,30 @@ export const create = mutation({
|
||||
criadoEm: now,
|
||||
atualizadoEm: now
|
||||
});
|
||||
|
||||
const sourcePlanningId = args.sourcePlanningId;
|
||||
|
||||
if (sourcePlanningId) {
|
||||
const sourceItems = await ctx.db
|
||||
.query('planejamentoItens')
|
||||
.withIndex('by_planejamentoId', (q) => q.eq('planejamentoId', sourcePlanningId))
|
||||
.collect();
|
||||
|
||||
for (const item of sourceItems) {
|
||||
await ctx.db.insert('planejamentoItens', {
|
||||
planejamentoId: newItemId,
|
||||
objetoId: item.objetoId,
|
||||
quantidade: item.quantidade,
|
||||
valorEstimado: item.valorEstimado,
|
||||
numeroDfd: item.numeroDfd,
|
||||
// Não copiamos o pedidoId pois é um novo planejamento
|
||||
criadoEm: now,
|
||||
atualizadoEm: now
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return newItemId;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user