Files
sgse-app/packages/backend/convex/objetos.ts

247 lines
6.3 KiB
TypeScript

import { v } from 'convex/values';
import { mutation, query } from './_generated/server';
import type { Id } from './_generated/dataModel';
import { getCurrentUserFunction } from './auth';
export const list = query({
args: {
nome: v.optional(v.string()),
tipo: v.optional(v.union(v.literal('material'), v.literal('servico'))),
codigos: v.optional(v.string())
},
handler: async (ctx, args) => {
const nome = args.nome?.trim();
const codigos = args.codigos?.trim().toLowerCase();
const base =
nome && nome.length > 0
? await ctx.db
.query('objetos')
.withSearchIndex('search_nome', (q) => q.search('nome', nome))
.collect()
: await ctx.db.query('objetos').collect();
return base.filter((objeto) => {
const tipoOk = !args.tipo || objeto.tipo === args.tipo;
const codigosOk =
!codigos ||
(objeto.codigoEfisco || '').toLowerCase().includes(codigos) ||
(objeto.codigoCatmat || '').toLowerCase().includes(codigos) ||
(objeto.codigoCatserv || '').toLowerCase().includes(codigos);
return tipoOk && codigosOk;
});
}
});
export const search = query({
args: { query: v.string() },
handler: async (ctx, args) => {
return await ctx.db
.query('objetos')
.withSearchIndex('search_nome', (q) => q.search('nome', args.query))
.take(10);
}
});
export const create = mutation({
args: {
nome: v.string(),
valorEstimado: v.string(),
tipo: v.union(v.literal('material'), v.literal('servico')),
codigoEfisco: v.string(),
codigoCatmat: v.optional(v.string()),
codigoCatserv: v.optional(v.string()),
unidade: v.string(),
atas: v.optional(v.array(v.id('atas')))
},
handler: async (ctx, args) => {
const user = await getCurrentUserFunction(ctx);
if (!user) throw new Error('Unauthorized');
const objetoId = await ctx.db.insert('objetos', {
nome: args.nome,
valorEstimado: args.valorEstimado,
tipo: args.tipo,
codigoEfisco: args.codigoEfisco,
codigoCatmat: args.codigoCatmat,
codigoCatserv: args.codigoCatserv,
unidade: args.unidade,
criadoPor: user._id,
criadoEm: Date.now()
});
if (args.atas) {
for (const ataId of args.atas) {
await ctx.db.insert('atasObjetos', {
ataId,
objetoId
});
}
}
return objetoId;
}
});
export const update = mutation({
args: {
id: v.id('objetos'),
nome: v.string(),
valorEstimado: v.string(),
tipo: v.union(v.literal('material'), v.literal('servico')),
codigoEfisco: v.string(),
codigoCatmat: v.optional(v.string()),
codigoCatserv: v.optional(v.string()),
unidade: v.string(),
atas: v.optional(v.array(v.id('atas')))
},
handler: async (ctx, args) => {
const user = await getCurrentUserFunction(ctx);
if (!user) throw new Error('Unauthorized');
await ctx.db.patch(args.id, {
nome: args.nome,
valorEstimado: args.valorEstimado,
tipo: args.tipo,
codigoEfisco: args.codigoEfisco,
codigoCatmat: args.codigoCatmat,
codigoCatserv: args.codigoCatserv,
unidade: args.unidade
});
if (args.atas !== undefined) {
// Remove existing links
const existingLinks = await ctx.db
.query('atasObjetos')
.withIndex('by_objetoId', (q) => q.eq('objetoId', args.id))
.collect();
for (const link of existingLinks) {
await ctx.db.delete(link._id);
}
// Add new links
for (const ataId of args.atas) {
await ctx.db.insert('atasObjetos', {
ataId,
objetoId: args.id
});
}
}
}
});
export const getAtas = query({
args: { objetoId: v.id('objetos') },
handler: async (ctx, args) => {
const links = await ctx.db
.query('atasObjetos')
.withIndex('by_objetoId', (q) => q.eq('objetoId', args.objetoId))
.collect();
const atas = await Promise.all(links.map((link) => ctx.db.get(link.ataId)));
return atas.filter((ata) => ata !== null);
}
});
export const getAtasComLimite = query({
args: { objetoId: v.id('objetos') },
returns: v.array(
v.object({
_id: v.id('atas'),
numero: v.string(),
numeroSei: v.string(),
dataInicio: v.union(v.string(), v.null()),
dataFim: v.union(v.string(), v.null()),
quantidadeTotal: v.union(v.number(), v.null()),
limitePercentual: v.number(),
quantidadeUsada: v.number(),
limitePermitido: v.number(),
restante: v.number(),
isLocked: v.boolean()
})
),
handler: async (ctx, args) => {
const links = await ctx.db
.query('atasObjetos')
.withIndex('by_objetoId', (q) => q.eq('objetoId', args.objetoId))
.collect();
const results = [];
for (const link of links) {
const ata = await ctx.db.get(link.ataId);
if (!ata) continue;
let quantidadeUsada = link.quantidadeUsada ?? 0;
if (link.quantidadeUsada === undefined) {
const items = await ctx.db
.query('objetoItems')
.withIndex('by_ataId_and_objetoId', (q) =>
q.eq('ataId', link.ataId).eq('objetoId', link.objetoId)
)
.collect();
const sumByPedidoId = new Map<Id<'pedidos'>, number>();
for (const item of items) {
const prev = sumByPedidoId.get(item.pedidoId) ?? 0;
sumByPedidoId.set(item.pedidoId, prev + item.quantidade);
}
let total = 0;
for (const [pedidoId, sum] of sumByPedidoId.entries()) {
const pedido = await ctx.db.get(pedidoId);
if (pedido && pedido.status !== 'cancelado') {
total += sum;
}
}
quantidadeUsada = total;
}
const quantidadeTotal = link.quantidadeTotal ?? null;
const limitePercentualRaw = link.limitePercentual ?? 50;
const limitePercentual = Number.isFinite(limitePercentualRaw)
? Math.min(100, Math.max(0, limitePercentualRaw))
: 50;
const limitePermitido =
quantidadeTotal && quantidadeTotal > 0
? Math.floor(quantidadeTotal * (limitePercentual / 100))
: 0;
const restante = Math.max(0, limitePermitido - quantidadeUsada);
const isLocked =
!quantidadeTotal || quantidadeTotal <= 0 || quantidadeUsada >= limitePermitido;
results.push({
_id: ata._id,
numero: ata.numero,
numeroSei: ata.numeroSei,
dataInicio: ata.dataInicio ?? null,
dataFim: ata.dataFim ?? null,
quantidadeTotal,
limitePercentual,
quantidadeUsada,
limitePermitido,
restante,
isLocked
});
}
return results;
}
});
export const remove = mutation({
args: {
id: v.id('objetos')
},
handler: async (ctx, args) => {
const user = await getCurrentUserFunction(ctx);
if (!user) throw new Error('Unauthorized');
await ctx.db.delete(args.id);
}
});