feat: Implement order acceptance and analysis workflows with new pages, sidebar navigation, and backend queries for filtering and permissions.

This commit is contained in:
2025-12-04 17:10:06 -03:00
parent 68475f549a
commit 29577b8e63
7 changed files with 509 additions and 7 deletions

View File

@@ -270,6 +270,233 @@ export const checkExisting = query({
}
});
export const listForAcceptance = query({
args: {},
returns: v.array(
v.object({
_id: v.id('pedidos'),
_creationTime: v.number(),
numeroSei: v.optional(v.string()),
status: v.string(),
criadoPor: v.id('usuarios'),
criadoPorNome: v.string(),
criadoEm: v.number(),
atualizadoEm: v.number()
})
),
handler: async (ctx) => {
const user = await getUsuarioAutenticado(ctx);
// Security Check: Must be in Compras Sector
const config = await ctx.db.query('config').first();
if (!config || !config.comprasSetorId) return [];
if (!user.funcionarioId) return [];
const isInSector = await ctx.db
.query('funcionarioSetores')
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', user.funcionarioId!))
.filter((q) => q.eq(q.field('setorId'), config.comprasSetorId))
.first();
if (!isInSector) return [];
// Fetch orders waiting for acceptance
const orders = await ctx.db
.query('pedidos')
.withIndex('by_status', (q) => q.eq('status', 'aguardando_aceite'))
.collect();
// Enrich with creator name
return await Promise.all(
orders.map(async (o) => {
const creator = await ctx.db.get(o.criadoPor);
return {
_id: o._id,
_creationTime: o._creationTime,
numeroSei: o.numeroSei,
status: o.status,
criadoPor: o.criadoPor,
criadoPorNome: creator?.nome || 'Desconhecido',
criadoEm: o.criadoEm,
atualizadoEm: o.atualizadoEm
};
})
);
}
});
export const listMyAnalysis = query({
args: {},
returns: v.array(
v.object({
_id: v.id('pedidos'),
_creationTime: v.number(),
numeroSei: v.optional(v.string()),
status: v.string(),
criadoPor: v.id('usuarios'),
criadoPorNome: v.string(),
aceitoPor: v.optional(v.id('funcionarios')),
criadoEm: v.number(),
atualizadoEm: v.number()
})
),
handler: async (ctx) => {
const user = await getUsuarioAutenticado(ctx);
// Security Check: Must be in Compras Sector
const config = await ctx.db.query('config').first();
if (!config || !config.comprasSetorId) return [];
if (!user.funcionarioId) return [];
const isInSector = await ctx.db
.query('funcionarioSetores')
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', user.funcionarioId!))
.filter((q) => q.eq(q.field('setorId'), config.comprasSetorId))
.first();
if (!isInSector) return [];
// Fetch orders accepted by this user
// We don't have an index on aceitoPor yet, but we can filter or add index.
// Ideally we should add an index, but for now let's filter since volume might be low per user.
// Wait, we can't filter efficiently without index if table is huge.
// Let's assume we should add index or filter in memory if small.
// Given the schema change was just adding the field, let's filter.
// Actually, let's add the index in the schema update if possible, but I already did that step.
// I'll filter for now.
const orders = await ctx.db
.query('pedidos')
.filter((q) => q.eq(q.field('aceitoPor'), user.funcionarioId))
.collect();
return await Promise.all(
orders.map(async (o) => {
const creator = await ctx.db.get(o.criadoPor);
return {
_id: o._id,
_creationTime: o._creationTime,
numeroSei: o.numeroSei,
status: o.status,
criadoPor: o.criadoPor,
criadoPorNome: creator?.nome || 'Desconhecido',
aceitoPor: o.aceitoPor,
criadoEm: o.criadoEm,
atualizadoEm: o.atualizadoEm
};
})
);
}
});
export const listByItemCreator = query({
args: {},
returns: v.array(
v.object({
_id: v.id('pedidos'),
_creationTime: v.number(),
numeroSei: v.optional(v.string()),
status: v.string(),
criadoPor: v.id('usuarios'),
criadoPorNome: v.string(),
criadoEm: v.number(),
atualizadoEm: v.number()
})
),
handler: async (ctx) => {
const user = await getUsuarioAutenticado(ctx);
if (!user.funcionarioId) return [];
// Find all items added by this user
const myItems = await ctx.db
.query('objetoItems')
.withIndex('by_adicionadoPor', (q) => q.eq('adicionadoPor', user.funcionarioId!))
.collect();
// Get unique pedidoIds
const pedidoIds = [...new Set(myItems.map((i) => i.pedidoId))];
// Fetch orders
const orders = await Promise.all(pedidoIds.map((id) => ctx.db.get(id)));
// Filter out nulls and enrich
const validOrders = orders.filter((o) => o !== null);
return await Promise.all(
validOrders.map(async (o) => {
const creator = await ctx.db.get(o!.criadoPor);
return {
_id: o!._id,
_creationTime: o!._creationTime,
numeroSei: o!.numeroSei,
status: o!.status,
criadoPor: o!.criadoPor,
criadoPorNome: creator?.nome || 'Desconhecido',
criadoEm: o!.criadoEm,
atualizadoEm: o!.atualizadoEm
};
})
);
}
});
export const acceptOrder = mutation({
args: {
pedidoId: v.id('pedidos')
},
returns: v.null(),
handler: async (ctx, args) => {
const user = await getUsuarioAutenticado(ctx);
// Security Check: Must be in Compras Sector
const config = await ctx.db.query('config').first();
if (!config || !config.comprasSetorId) {
throw new Error('Setor de compras não configurado.');
}
if (!user.funcionarioId) {
throw new Error('Usuário sem funcionário vinculado.');
}
const isInSector = await ctx.db
.query('funcionarioSetores')
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', user.funcionarioId!))
.filter((q) => q.eq(q.field('setorId'), config.comprasSetorId))
.first();
if (!isInSector) {
throw new Error('Você não tem permissão para aceitar pedidos (Setor inválido).');
}
const pedido = await ctx.db.get(args.pedidoId);
if (!pedido) throw new Error('Pedido não encontrado.');
if (pedido.status !== 'aguardando_aceite') {
throw new Error('Este pedido não está aguardando aceite.');
}
if (pedido.aceitoPor) {
throw new Error('Este pedido já foi aceito por outro funcionário.');
}
await ctx.db.patch(args.pedidoId, {
status: 'em_analise',
aceitoPor: user.funcionarioId,
atualizadoEm: Date.now()
});
await ctx.db.insert('historicoPedidos', {
pedidoId: args.pedidoId,
usuarioId: user._id,
acao: 'aceite_pedido',
detalhes: JSON.stringify({ aceitoPor: user.funcionarioId }),
data: Date.now()
});
}
});
// ========== MUTATIONS ==========
export const create = mutation({