feat: enhance ata and objeto management by adding configuration options for quantity limits and usage tracking, improving data integrity and user feedback in pedidos
This commit is contained in:
@@ -4,6 +4,21 @@ import type { Id } from './_generated/dataModel';
|
||||
import { getCurrentUserFunction } from './auth';
|
||||
import { internal } from './_generated/api';
|
||||
|
||||
function normalizeLimitePercentual(value: number | undefined): number {
|
||||
const fallback = 50;
|
||||
if (value === undefined) return fallback;
|
||||
if (!Number.isFinite(value)) return fallback;
|
||||
if (value < 0) return 0;
|
||||
if (value > 100) return 100;
|
||||
return value;
|
||||
}
|
||||
|
||||
function assertQuantidadeTotalValida(value: number) {
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
throw new Error('Quantidade do produto na ata deve ser maior que zero.');
|
||||
}
|
||||
}
|
||||
|
||||
export const list = query({
|
||||
args: {
|
||||
periodoInicio: v.optional(v.string()),
|
||||
@@ -71,6 +86,34 @@ export const getObjetos = query({
|
||||
}
|
||||
});
|
||||
|
||||
export const getObjetosConfig = query({
|
||||
args: { id: v.id('atas') },
|
||||
returns: v.array(
|
||||
v.object({
|
||||
objetoId: v.id('objetos'),
|
||||
quantidadeTotal: v.union(v.number(), v.null()),
|
||||
limitePercentual: v.union(v.number(), v.null())
|
||||
})
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: 'atas',
|
||||
acao: 'ver'
|
||||
});
|
||||
|
||||
const links = await ctx.db
|
||||
.query('atasObjetos')
|
||||
.withIndex('by_ataId', (q) => q.eq('ataId', args.id))
|
||||
.collect();
|
||||
|
||||
return links.map((l) => ({
|
||||
objetoId: l.objetoId,
|
||||
quantidadeTotal: l.quantidadeTotal ?? null,
|
||||
limitePercentual: l.limitePercentual ?? null
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
export const listByObjetoIds = query({
|
||||
args: {
|
||||
objetoIds: v.array(v.id('objetos'))
|
||||
@@ -108,7 +151,13 @@ export const create = mutation({
|
||||
dataFim: v.optional(v.string()),
|
||||
empresaId: v.id('empresas'),
|
||||
numeroSei: v.string(),
|
||||
objetosIds: v.array(v.id('objetos'))
|
||||
objetos: v.array(
|
||||
v.object({
|
||||
objetoId: v.id('objetos'),
|
||||
quantidadeTotal: v.number(),
|
||||
limitePercentual: v.optional(v.number())
|
||||
})
|
||||
)
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
@@ -131,10 +180,16 @@ export const create = mutation({
|
||||
});
|
||||
|
||||
// Vincular objetos
|
||||
for (const objetoId of args.objetosIds) {
|
||||
for (const cfg of args.objetos) {
|
||||
assertQuantidadeTotalValida(cfg.quantidadeTotal);
|
||||
const limitePercentual = normalizeLimitePercentual(cfg.limitePercentual);
|
||||
|
||||
await ctx.db.insert('atasObjetos', {
|
||||
ataId,
|
||||
objetoId
|
||||
objetoId: cfg.objetoId,
|
||||
quantidadeTotal: cfg.quantidadeTotal,
|
||||
limitePercentual,
|
||||
quantidadeUsada: 0
|
||||
});
|
||||
}
|
||||
|
||||
@@ -150,7 +205,13 @@ export const update = mutation({
|
||||
empresaId: v.id('empresas'),
|
||||
dataInicio: v.optional(v.string()),
|
||||
dataFim: v.optional(v.string()),
|
||||
objetosIds: v.array(v.id('objetos'))
|
||||
objetos: v.array(
|
||||
v.object({
|
||||
objetoId: v.id('objetos'),
|
||||
quantidadeTotal: v.number(),
|
||||
limitePercentual: v.optional(v.number())
|
||||
})
|
||||
)
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
@@ -171,22 +232,71 @@ export const update = mutation({
|
||||
});
|
||||
|
||||
// Atualizar objetos vinculados
|
||||
// Primeiro remove todos os vínculos existentes
|
||||
const existingLinks = await ctx.db
|
||||
.query('atasObjetos')
|
||||
.withIndex('by_ataId', (q) => q.eq('ataId', args.id))
|
||||
.collect();
|
||||
|
||||
const existingByObjeto = new Map<Id<'objetos'>, (typeof existingLinks)[number]>();
|
||||
for (const link of existingLinks) {
|
||||
await ctx.db.delete(link._id);
|
||||
existingByObjeto.set(link.objetoId, link);
|
||||
}
|
||||
|
||||
// Adiciona os novos vínculos
|
||||
for (const objetoId of args.objetosIds) {
|
||||
await ctx.db.insert('atasObjetos', {
|
||||
ataId: args.id,
|
||||
objetoId
|
||||
});
|
||||
const desiredObjetoIds = new Set<Id<'objetos'>>(args.objetos.map((o) => o.objetoId));
|
||||
|
||||
// Upsert dos vínculos desejados (preserva quantidadeUsada quando já existe)
|
||||
for (const cfg of args.objetos) {
|
||||
assertQuantidadeTotalValida(cfg.quantidadeTotal);
|
||||
const limitePercentual = normalizeLimitePercentual(cfg.limitePercentual);
|
||||
|
||||
const existing = existingByObjeto.get(cfg.objetoId);
|
||||
if (existing) {
|
||||
await ctx.db.patch(existing._id, {
|
||||
quantidadeTotal: cfg.quantidadeTotal,
|
||||
limitePercentual
|
||||
});
|
||||
} else {
|
||||
await ctx.db.insert('atasObjetos', {
|
||||
ataId: args.id,
|
||||
objetoId: cfg.objetoId,
|
||||
quantidadeTotal: cfg.quantidadeTotal,
|
||||
limitePercentual,
|
||||
quantidadeUsada: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Remoção de vínculos não selecionados (somente se não houver uso em pedidos não-cancelados)
|
||||
for (const link of existingLinks) {
|
||||
if (desiredObjetoIds.has(link.objetoId)) continue;
|
||||
|
||||
const items = await ctx.db
|
||||
.query('objetoItems')
|
||||
.withIndex('by_ataId_and_objetoId', (q) =>
|
||||
q.eq('ataId', args.id).eq('objetoId', link.objetoId)
|
||||
)
|
||||
.collect();
|
||||
|
||||
// Se existe qualquer item em pedido não cancelado, bloquear remoção do vínculo
|
||||
let inUse = false;
|
||||
const seenPedidos = new Set<Id<'pedidos'>>();
|
||||
for (const item of items) {
|
||||
if (seenPedidos.has(item.pedidoId)) continue;
|
||||
seenPedidos.add(item.pedidoId);
|
||||
const pedido = await ctx.db.get(item.pedidoId);
|
||||
if (pedido && pedido.status !== 'cancelado') {
|
||||
inUse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inUse) {
|
||||
throw new Error(
|
||||
'Não é possível remover este objeto da ata porque já existe uso em pedidos não cancelados.'
|
||||
);
|
||||
}
|
||||
|
||||
await ctx.db.delete(link._id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user