feat: Implement pedido adjustment workflow with description field and dedicated mutations.
This commit is contained in:
@@ -286,15 +286,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSolicitarAjustes() {
|
|
||||||
if (!confirm('Solicitar ajustes?')) return;
|
|
||||||
try {
|
|
||||||
await client.mutation(api.pedidos.solicitarAjustes, { pedidoId });
|
|
||||||
} catch (e) {
|
|
||||||
alert('Erro: ' + (e as Error).message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCancelar() {
|
async function handleCancelar() {
|
||||||
if (!confirm('Cancelar pedido?')) return;
|
if (!confirm('Cancelar pedido?')) return;
|
||||||
try {
|
try {
|
||||||
@@ -600,6 +591,40 @@
|
|||||||
alert('Erro ao dividir pedido: ' + (e as Error).message);
|
alert('Erro ao dividir pedido: ' + (e as Error).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjustment Modal State
|
||||||
|
let showRequestAdjustmentsModal = $state(false);
|
||||||
|
let adjustmentDescription = $state('');
|
||||||
|
|
||||||
|
function openRequestAdjustmentsModal() {
|
||||||
|
adjustmentDescription = '';
|
||||||
|
showRequestAdjustmentsModal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmRequestAdjustments() {
|
||||||
|
if (!adjustmentDescription.trim()) {
|
||||||
|
alert('Por favor, informe a descrição dos ajustes necessários.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await client.mutation(api.pedidos.solicitarAjustes, {
|
||||||
|
pedidoId,
|
||||||
|
descricao: adjustmentDescription
|
||||||
|
});
|
||||||
|
showRequestAdjustmentsModal = false;
|
||||||
|
} catch (e) {
|
||||||
|
alert('Erro: ' + (e as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleConcluirAjustes() {
|
||||||
|
if (!confirm('Concluir ajustes e retornar para análise?')) return;
|
||||||
|
try {
|
||||||
|
await client.mutation(api.pedidos.concluirAjustes, { pedidoId });
|
||||||
|
} catch (e) {
|
||||||
|
alert('Erro: ' + (e as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container mx-auto p-6">
|
<div class="container mx-auto p-6">
|
||||||
@@ -660,6 +685,14 @@
|
|||||||
⚠️ Este pedido não possui número SEI. Adicione um número SEI quando disponível.
|
⚠️ Este pedido não possui número SEI. Adicione um número SEI quando disponível.
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if pedido.status === 'precisa_ajustes' && pedido.descricaoAjuste}
|
||||||
|
<div class="mt-2 rounded-md border border-orange-200 bg-orange-50 p-4">
|
||||||
|
<h3 class="flex items-center gap-2 text-sm font-semibold text-orange-800">
|
||||||
|
<AlertTriangle size={16} /> Ajustes Solicitados:
|
||||||
|
</h3>
|
||||||
|
<p class="mt-1 text-sm whitespace-pre-wrap text-orange-700">{pedido.descricaoAjuste}</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@@ -692,13 +725,22 @@
|
|||||||
|
|
||||||
{#if permissions?.canRequestAdjustments}
|
{#if permissions?.canRequestAdjustments}
|
||||||
<button
|
<button
|
||||||
onclick={handleSolicitarAjustes}
|
onclick={openRequestAdjustmentsModal}
|
||||||
class="flex items-center gap-2 rounded bg-orange-500 px-4 py-2 text-white hover:bg-orange-600"
|
class="flex items-center gap-2 rounded bg-orange-500 px-4 py-2 text-white hover:bg-orange-600"
|
||||||
>
|
>
|
||||||
<AlertTriangle size={18} /> Solicitar Ajustes
|
<AlertTriangle size={18} /> Solicitar Ajustes
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if permissions?.canCompleteAdjustments}
|
||||||
|
<button
|
||||||
|
onclick={handleConcluirAjustes}
|
||||||
|
class="flex items-center gap-2 rounded bg-teal-600 px-4 py-2 text-white hover:bg-teal-700"
|
||||||
|
>
|
||||||
|
<CheckCircle size={18} /> Concluir Ajustes
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if permissions?.canCancel}
|
{#if permissions?.canCancel}
|
||||||
<button
|
<button
|
||||||
onclick={handleCancelar}
|
onclick={handleCancelar}
|
||||||
@@ -1313,4 +1355,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if showRequestAdjustmentsModal}
|
||||||
|
<div
|
||||||
|
class="fixed inset-0 z-50 flex h-full w-full items-center justify-center overflow-y-auto bg-black/40"
|
||||||
|
>
|
||||||
|
<div class="relative w-full max-w-md rounded-lg bg-white p-6 shadow-xl">
|
||||||
|
<h3 class="mb-4 text-lg font-bold">Solicitar Ajustes</h3>
|
||||||
|
<p class="mb-2 text-sm text-gray-600">
|
||||||
|
Descreva os ajustes necessários para este pedido. O status será alterado para "Precisa de
|
||||||
|
Ajustes".
|
||||||
|
</p>
|
||||||
|
<textarea
|
||||||
|
bind:value={adjustmentDescription}
|
||||||
|
class="mb-4 w-full rounded border p-2 text-sm"
|
||||||
|
rows="5"
|
||||||
|
placeholder="Ex: O valor do Item 1 está acima do mercado..."
|
||||||
|
></textarea>
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<button
|
||||||
|
onclick={() => (showRequestAdjustmentsModal = false)}
|
||||||
|
class="rounded bg-gray-200 px-4 py-2 text-gray-700 hover:bg-gray-300"
|
||||||
|
>
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onclick={confirmRequestAdjustments}
|
||||||
|
class="rounded bg-orange-500 px-4 py-2 text-white hover:bg-orange-600"
|
||||||
|
>
|
||||||
|
Confirmar Solicitação
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ export const list = query({
|
|||||||
),
|
),
|
||||||
// acaoId removed from return
|
// acaoId removed from return
|
||||||
criadoPor: v.id('usuarios'),
|
criadoPor: v.id('usuarios'),
|
||||||
|
aceitoPor: v.optional(v.id('funcionarios')),
|
||||||
|
descricaoAjuste: v.optional(v.string()),
|
||||||
criadoEm: v.number(),
|
criadoEm: v.number(),
|
||||||
atualizadoEm: v.number()
|
atualizadoEm: v.number()
|
||||||
})
|
})
|
||||||
@@ -58,6 +60,8 @@ export const get = query({
|
|||||||
),
|
),
|
||||||
acaoId: v.optional(v.id('acoes')),
|
acaoId: v.optional(v.id('acoes')),
|
||||||
criadoPor: v.id('usuarios'),
|
criadoPor: v.id('usuarios'),
|
||||||
|
aceitoPor: v.optional(v.id('funcionarios')),
|
||||||
|
descricaoAjuste: v.optional(v.string()),
|
||||||
criadoEm: v.number(),
|
criadoEm: v.number(),
|
||||||
atualizadoEm: v.number()
|
atualizadoEm: v.number()
|
||||||
}),
|
}),
|
||||||
@@ -175,6 +179,8 @@ export const checkExisting = query({
|
|||||||
),
|
),
|
||||||
// acaoId removed
|
// acaoId removed
|
||||||
criadoPor: v.id('usuarios'),
|
criadoPor: v.id('usuarios'),
|
||||||
|
aceitoPor: v.optional(v.id('funcionarios')),
|
||||||
|
descricaoAjuste: v.optional(v.string()),
|
||||||
criadoEm: v.number(),
|
criadoEm: v.number(),
|
||||||
atualizadoEm: v.number(),
|
atualizadoEm: v.number(),
|
||||||
matchingItems: v.optional(
|
matchingItems: v.optional(
|
||||||
@@ -259,6 +265,8 @@ export const checkExisting = query({
|
|||||||
numeroSei: pedido.numeroSei,
|
numeroSei: pedido.numeroSei,
|
||||||
status: pedido.status,
|
status: pedido.status,
|
||||||
criadoPor: pedido.criadoPor,
|
criadoPor: pedido.criadoPor,
|
||||||
|
aceitoPor: pedido.aceitoPor,
|
||||||
|
descricaoAjuste: pedido.descricaoAjuste,
|
||||||
criadoEm: pedido.criadoEm,
|
criadoEm: pedido.criadoEm,
|
||||||
atualizadoEm: pedido.atualizadoEm,
|
atualizadoEm: pedido.atualizadoEm,
|
||||||
matchingItems: matchingItems.length > 0 ? matchingItems : undefined
|
matchingItems: matchingItems.length > 0 ? matchingItems : undefined
|
||||||
@@ -318,6 +326,8 @@ export const listForAcceptance = query({
|
|||||||
status: o.status,
|
status: o.status,
|
||||||
criadoPor: o.criadoPor,
|
criadoPor: o.criadoPor,
|
||||||
criadoPorNome: creator?.nome || 'Desconhecido',
|
criadoPorNome: creator?.nome || 'Desconhecido',
|
||||||
|
aceitoPor: o.aceitoPor,
|
||||||
|
descricaoAjuste: o.descricaoAjuste,
|
||||||
criadoEm: o.criadoEm,
|
criadoEm: o.criadoEm,
|
||||||
atualizadoEm: o.atualizadoEm
|
atualizadoEm: o.atualizadoEm
|
||||||
};
|
};
|
||||||
@@ -337,6 +347,7 @@ export const listMyAnalysis = query({
|
|||||||
criadoPor: v.id('usuarios'),
|
criadoPor: v.id('usuarios'),
|
||||||
criadoPorNome: v.string(),
|
criadoPorNome: v.string(),
|
||||||
aceitoPor: v.optional(v.id('funcionarios')),
|
aceitoPor: v.optional(v.id('funcionarios')),
|
||||||
|
descricaoAjuste: v.optional(v.string()),
|
||||||
criadoEm: v.number(),
|
criadoEm: v.number(),
|
||||||
atualizadoEm: v.number()
|
atualizadoEm: v.number()
|
||||||
})
|
})
|
||||||
@@ -383,6 +394,7 @@ export const listMyAnalysis = query({
|
|||||||
criadoPor: o.criadoPor,
|
criadoPor: o.criadoPor,
|
||||||
criadoPorNome: creator?.nome || 'Desconhecido',
|
criadoPorNome: creator?.nome || 'Desconhecido',
|
||||||
aceitoPor: o.aceitoPor,
|
aceitoPor: o.aceitoPor,
|
||||||
|
descricaoAjuste: o.descricaoAjuste,
|
||||||
criadoEm: o.criadoEm,
|
criadoEm: o.criadoEm,
|
||||||
atualizadoEm: o.atualizadoEm
|
atualizadoEm: o.atualizadoEm
|
||||||
};
|
};
|
||||||
@@ -434,6 +446,8 @@ export const listByItemCreator = query({
|
|||||||
status: o!.status,
|
status: o!.status,
|
||||||
criadoPor: o!.criadoPor,
|
criadoPor: o!.criadoPor,
|
||||||
criadoPorNome: creator?.nome || 'Desconhecido',
|
criadoPorNome: creator?.nome || 'Desconhecido',
|
||||||
|
aceitoPor: o!.aceitoPor,
|
||||||
|
descricaoAjuste: o!.descricaoAjuste,
|
||||||
criadoEm: o!.criadoEm,
|
criadoEm: o!.criadoEm,
|
||||||
atualizadoEm: o!.atualizadoEm
|
atualizadoEm: o!.atualizadoEm
|
||||||
};
|
};
|
||||||
@@ -1000,7 +1014,11 @@ export const getPermissions = query({
|
|||||||
(pedido.status === 'em_rascunho' || pedido.status === 'precisa_ajustes') && hasAddedItems,
|
(pedido.status === 'em_rascunho' || pedido.status === 'precisa_ajustes') && hasAddedItems,
|
||||||
canStartAnalysis: pedido.status === 'aguardando_aceite' && isInComprasSector,
|
canStartAnalysis: pedido.status === 'aguardando_aceite' && isInComprasSector,
|
||||||
canConclude: pedido.status === 'em_analise' && isInComprasSector,
|
canConclude: pedido.status === 'em_analise' && isInComprasSector,
|
||||||
canRequestAdjustments: pedido.status === 'em_analise' && isInComprasSector,
|
canRequestAdjustments:
|
||||||
|
pedido.status === 'em_analise' &&
|
||||||
|
isInComprasSector &&
|
||||||
|
pedido.aceitoPor === user.funcionarioId,
|
||||||
|
canCompleteAdjustments: pedido.status === 'precisa_ajustes' && hasAddedItems,
|
||||||
canCancel:
|
canCancel:
|
||||||
pedido.status !== 'cancelado' &&
|
pedido.status !== 'cancelado' &&
|
||||||
pedido.status !== 'concluido' &&
|
pedido.status !== 'concluido' &&
|
||||||
@@ -1161,7 +1179,10 @@ export const concluirPedido = mutation({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const solicitarAjustes = mutation({
|
export const solicitarAjustes = mutation({
|
||||||
args: { pedidoId: v.id('pedidos') },
|
args: {
|
||||||
|
pedidoId: v.id('pedidos'),
|
||||||
|
descricao: v.string()
|
||||||
|
},
|
||||||
returns: v.null(),
|
returns: v.null(),
|
||||||
handler: async (ctx, args) => {
|
handler: async (ctx, args) => {
|
||||||
const user = await getUsuarioAutenticado(ctx);
|
const user = await getUsuarioAutenticado(ctx);
|
||||||
@@ -1172,32 +1193,29 @@ export const solicitarAjustes = mutation({
|
|||||||
throw new Error('O pedido deve estar em análise para solicitar ajustes.');
|
throw new Error('O pedido deve estar em análise para solicitar ajustes.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Security Check: Must be in Compras Sector
|
// Security Check: Only the user who accepted the order can request adjustments
|
||||||
const config = await ctx.db.query('config').first();
|
if (pedido.aceitoPor !== user.funcionarioId) {
|
||||||
if (!config || !config.comprasSetorId) throw new Error('Setor de compras não configurado.');
|
throw new Error('Apenas o funcionário que aceitou o pedido pode solicitar ajustes.');
|
||||||
|
}
|
||||||
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('Acesso não autorizado (Setor de Compras).');
|
|
||||||
|
|
||||||
const oldStatus = pedido.status;
|
const oldStatus = pedido.status;
|
||||||
const newStatus = 'precisa_ajustes';
|
const newStatus = 'precisa_ajustes';
|
||||||
|
|
||||||
await ctx.db.patch(args.pedidoId, {
|
await ctx.db.patch(args.pedidoId, {
|
||||||
status: newStatus,
|
status: newStatus,
|
||||||
aceitoPor: undefined, // Clear accepted by since it's back to adjustments
|
descricaoAjuste: args.descricao,
|
||||||
atualizadoEm: Date.now()
|
atualizadoEm: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
await ctx.db.insert('historicoPedidos', {
|
await ctx.db.insert('historicoPedidos', {
|
||||||
pedidoId: args.pedidoId,
|
pedidoId: args.pedidoId,
|
||||||
usuarioId: user._id,
|
usuarioId: user._id,
|
||||||
acao: 'alteracao_status',
|
acao: 'solicitacao_ajuste',
|
||||||
detalhes: JSON.stringify({ de: oldStatus, para: newStatus }),
|
detalhes: JSON.stringify({
|
||||||
|
de: oldStatus,
|
||||||
|
para: newStatus,
|
||||||
|
descricao: args.descricao
|
||||||
|
}),
|
||||||
data: Date.now()
|
data: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1205,7 +1223,65 @@ export const solicitarAjustes = mutation({
|
|||||||
pedidoId: args.pedidoId,
|
pedidoId: args.pedidoId,
|
||||||
oldStatus,
|
oldStatus,
|
||||||
newStatus,
|
newStatus,
|
||||||
actorId: user._id
|
actorId: user._id,
|
||||||
|
customMessage: args.descricao
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const concluirAjustes = mutation({
|
||||||
|
args: { pedidoId: v.id('pedidos') },
|
||||||
|
returns: v.null(),
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
const user = await getUsuarioAutenticado(ctx);
|
||||||
|
if (!user.funcionarioId) throw new Error('Usuário sem funcionário vinculado.');
|
||||||
|
|
||||||
|
const pedido = await ctx.db.get(args.pedidoId);
|
||||||
|
if (!pedido) throw new Error('Pedido não encontrado.');
|
||||||
|
|
||||||
|
if (pedido.status !== 'precisa_ajustes') {
|
||||||
|
throw new Error('O pedido não está aguardando ajustes.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Security Check: Must have items in the order
|
||||||
|
const requestorItem = await ctx.db
|
||||||
|
.query('objetoItems')
|
||||||
|
.withIndex('by_pedidoId', (q) => q.eq('pedidoId', args.pedidoId))
|
||||||
|
.filter((q) => q.eq(q.field('adicionadoPor'), user.funcionarioId))
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (!requestorItem) {
|
||||||
|
throw new Error('Você deve ter itens no pedido para concluir os ajustes.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldStatus = pedido.status;
|
||||||
|
const newStatus = 'em_analise';
|
||||||
|
const descricaoAnterior = pedido.descricaoAjuste;
|
||||||
|
|
||||||
|
await ctx.db.patch(args.pedidoId, {
|
||||||
|
status: newStatus,
|
||||||
|
descricaoAjuste: undefined, // Clear the adjustment description from the active field
|
||||||
|
atualizadoEm: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
await ctx.db.insert('historicoPedidos', {
|
||||||
|
pedidoId: args.pedidoId,
|
||||||
|
usuarioId: user._id,
|
||||||
|
acao: 'conclusao_ajustes',
|
||||||
|
detalhes: JSON.stringify({
|
||||||
|
de: oldStatus,
|
||||||
|
para: newStatus,
|
||||||
|
descricaoResolvida: descricaoAnterior
|
||||||
|
}),
|
||||||
|
data: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
await ctx.scheduler.runAfter(0, internal.pedidos.notifyStatusChange, {
|
||||||
|
pedidoId: args.pedidoId,
|
||||||
|
oldStatus,
|
||||||
|
newStatus,
|
||||||
|
actorId: user._id,
|
||||||
|
customMessage: 'Ajustes concluídos. O pedido retornou para análise.'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1273,7 +1349,8 @@ export const notifyStatusChange = internalMutation({
|
|||||||
pedidoId: v.id('pedidos'),
|
pedidoId: v.id('pedidos'),
|
||||||
oldStatus: v.string(),
|
oldStatus: v.string(),
|
||||||
newStatus: v.string(),
|
newStatus: v.string(),
|
||||||
actorId: v.id('usuarios')
|
actorId: v.id('usuarios'),
|
||||||
|
customMessage: v.optional(v.string())
|
||||||
},
|
},
|
||||||
returns: v.null(),
|
returns: v.null(),
|
||||||
handler: async (ctx, args) => {
|
handler: async (ctx, args) => {
|
||||||
@@ -1330,6 +1407,11 @@ export const notifyStatusChange = internalMutation({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let description = `Status alterado de "${args.oldStatus}" para "${args.newStatus}" por ${actorName}.`;
|
||||||
|
if (args.customMessage) {
|
||||||
|
description += `\n\nDetalhes: ${args.customMessage}`;
|
||||||
|
}
|
||||||
|
|
||||||
// Send Notifications
|
// Send Notifications
|
||||||
for (const recipientId of recipients) {
|
for (const recipientId of recipients) {
|
||||||
const recipientIdTyped = recipientId as Id<'usuarios'>;
|
const recipientIdTyped = recipientId as Id<'usuarios'>;
|
||||||
@@ -1339,7 +1421,7 @@ export const notifyStatusChange = internalMutation({
|
|||||||
usuarioId: recipientIdTyped,
|
usuarioId: recipientIdTyped,
|
||||||
tipo: 'alerta_seguranca', // Using alerta_seguranca as the closest match for system notifications
|
tipo: 'alerta_seguranca', // Using alerta_seguranca as the closest match for system notifications
|
||||||
titulo: `Pedido ${pedido.numeroSei || 'sem número SEI'} atualizado`,
|
titulo: `Pedido ${pedido.numeroSei || 'sem número SEI'} atualizado`,
|
||||||
descricao: `Status alterado de "${args.oldStatus}" para "${args.newStatus}" por ${actorName}.`,
|
descricao: description,
|
||||||
lida: false,
|
lida: false,
|
||||||
criadaEm: Date.now(),
|
criadaEm: Date.now(),
|
||||||
remetenteId: args.actorId
|
remetenteId: args.actorId
|
||||||
@@ -1353,7 +1435,7 @@ export const notifyStatusChange = internalMutation({
|
|||||||
destinatario: recipientUser.email,
|
destinatario: recipientUser.email,
|
||||||
destinatarioId: recipientIdTyped,
|
destinatarioId: recipientIdTyped,
|
||||||
assunto: `Atualização no Pedido ${pedido.numeroSei || 'sem número SEI'}`,
|
assunto: `Atualização no Pedido ${pedido.numeroSei || 'sem número SEI'}`,
|
||||||
corpo: `O pedido ${pedido.numeroSei || 'sem número SEI'} teve seu status alterado de "${args.oldStatus}" para "${args.newStatus}" por ${actorName}.`,
|
corpo: `O pedido ${pedido.numeroSei || 'sem número SEI'} teve seu status alterado de "${args.oldStatus}" para "${args.newStatus}" por ${actorName}.<br/><br/>${args.customMessage ? `<strong>Detalhes:</strong> ${args.customMessage}` : ''}`,
|
||||||
enviadoPor: args.actorId
|
enviadoPor: args.actorId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const pedidosTables = {
|
|||||||
// acaoId removed
|
// acaoId removed
|
||||||
criadoPor: v.id('usuarios'),
|
criadoPor: v.id('usuarios'),
|
||||||
aceitoPor: v.optional(v.id('funcionarios')),
|
aceitoPor: v.optional(v.id('funcionarios')),
|
||||||
|
descricaoAjuste: v.optional(v.string()), // Required when status is 'precisa_ajustes'
|
||||||
criadoEm: v.number(),
|
criadoEm: v.number(),
|
||||||
atualizadoEm: v.number()
|
atualizadoEm: v.number()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user