refactor: simplify pedidos item management by removing modalidade from item configuration and validation, ensuring all items use the same ata while enhancing code clarity and maintainability
This commit is contained in:
@@ -514,30 +514,23 @@
|
|||||||
if (!pedido || !newItem.objetoId || !newItem.valorEstimado) return;
|
if (!pedido || !newItem.objetoId || !newItem.valorEstimado) return;
|
||||||
|
|
||||||
// Validação no front: garantir que todos os itens existentes do pedido
|
// Validação no front: garantir que todos os itens existentes do pedido
|
||||||
// utilizem a mesma combinação de modalidade e ata (quando houver).
|
// utilizem a mesma ata (quando houver).
|
||||||
if (items.length > 0) {
|
if (items.length > 0) {
|
||||||
const referenceItem = items[0];
|
const referenceItem = items[0];
|
||||||
|
|
||||||
const referenceModalidade = (referenceItem.modalidade as Modalidade | undefined) ?? undefined;
|
|
||||||
const referenceAtaId = (('ataId' in referenceItem ? referenceItem.ataId : undefined) ??
|
const referenceAtaId = (('ataId' in referenceItem ? referenceItem.ataId : undefined) ??
|
||||||
null) as string | null;
|
null) as string | null;
|
||||||
|
|
||||||
const newAtaId = newItem.ataId || null;
|
const newAtaId = newItem.ataId || null;
|
||||||
|
|
||||||
const sameModalidade = !referenceModalidade || newItem.modalidade === referenceModalidade;
|
|
||||||
const sameAta = referenceAtaId === newAtaId;
|
const sameAta = referenceAtaId === newAtaId;
|
||||||
|
|
||||||
if (!sameModalidade || !sameAta) {
|
if (!sameAta) {
|
||||||
const refModalidadeLabel = referenceModalidade
|
|
||||||
? formatModalidade(referenceModalidade)
|
|
||||||
: 'Não definida';
|
|
||||||
const refAtaLabel =
|
const refAtaLabel =
|
||||||
referenceAtaId === null ? 'sem Ata vinculada' : 'com uma Ata específica';
|
referenceAtaId === null ? 'sem Ata vinculada' : 'com uma Ata específica';
|
||||||
|
|
||||||
toast.error(
|
toast.error(
|
||||||
`Não é possível adicionar este item com esta combinação de modalidade e ata.\n\n` +
|
`Não é possível adicionar este item com esta ata.\n\n` +
|
||||||
`Este pedido já está utilizando Modalidade: ${refModalidadeLabel} e está ${refAtaLabel}.\n` +
|
`Este pedido já está vinculado a: ${refAtaLabel}.\n` +
|
||||||
`Todos os itens do pedido devem usar a mesma modalidade e a mesma ata (quando houver).`
|
`Todos os itens do pedido devem usar a mesma ata (quando houver).`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -550,7 +543,6 @@
|
|||||||
objetoId: newItem.objetoId as Id<'objetos'>,
|
objetoId: newItem.objetoId as Id<'objetos'>,
|
||||||
valorEstimado: newItem.valorEstimado,
|
valorEstimado: newItem.valorEstimado,
|
||||||
quantidade: newItem.quantidade,
|
quantidade: newItem.quantidade,
|
||||||
modalidade: newItem.modalidade,
|
|
||||||
acaoId: newItem.acaoId ? (newItem.acaoId as Id<'acoes'>) : undefined,
|
acaoId: newItem.acaoId ? (newItem.acaoId as Id<'acoes'>) : undefined,
|
||||||
ataId: newItem.ataId ? (newItem.ataId as Id<'atas'>) : undefined
|
ataId: newItem.ataId ? (newItem.ataId as Id<'atas'>) : undefined
|
||||||
});
|
});
|
||||||
@@ -1664,22 +1656,8 @@
|
|||||||
placeholder="R$ 0,00"
|
placeholder="R$ 0,00"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<label for="modalidade-select" class="mb-1 block text-xs font-medium text-gray-500"
|
{#if newItem.objetoId && permissions?.canEditAta}
|
||||||
>Modalidade</label
|
|
||||||
>
|
|
||||||
<select
|
|
||||||
id="modalidade-select"
|
|
||||||
bind:value={newItem.modalidade}
|
|
||||||
class="w-full rounded-md border-gray-300 text-sm shadow-sm"
|
|
||||||
>
|
|
||||||
<option value="consumo">Consumo</option>
|
|
||||||
<option value="dispensa">Dispensa</option>
|
|
||||||
<option value="inexgibilidade">Inexigibilidade</option>
|
|
||||||
<option value="adesao">Adesão</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
{#if newItem.objetoId}
|
|
||||||
<div>
|
<div>
|
||||||
<label for="ata-select" class="mb-1 block text-xs font-medium text-gray-500"
|
<label for="ata-select" class="mb-1 block text-xs font-medium text-gray-500"
|
||||||
>Ata (Opcional)</label
|
>Ata (Opcional)</label
|
||||||
@@ -1900,7 +1878,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-sm whitespace-nowrap text-gray-600">
|
<td class="px-6 py-4 text-sm whitespace-nowrap text-gray-600">
|
||||||
{#if pedido.status === 'em_rascunho' || pedido.status === 'precisa_ajustes' || pedido.status === 'em_analise' || pedido.status === 'aguardando_aceite'}
|
{#if permissions?.canEditModalidade}
|
||||||
<select
|
<select
|
||||||
class="rounded border px-2 py-1 text-xs"
|
class="rounded border px-2 py-1 text-xs"
|
||||||
value={ensureEditingItem(item).modalidade}
|
value={ensureEditingItem(item).modalidade}
|
||||||
@@ -1919,7 +1897,7 @@
|
|||||||
<option value="adesao">Adesão</option>
|
<option value="adesao">Adesão</option>
|
||||||
</select>
|
</select>
|
||||||
{:else}
|
{:else}
|
||||||
{item.modalidade}
|
{formatModalidade(item.modalidade as Modalidade) || '-'}
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-sm whitespace-nowrap text-gray-600">
|
<td class="px-6 py-4 text-sm whitespace-nowrap text-gray-600">
|
||||||
@@ -1942,7 +1920,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-sm whitespace-nowrap text-gray-600">
|
<td class="px-6 py-4 text-sm whitespace-nowrap text-gray-600">
|
||||||
{#if pedido.status === 'em_rascunho' || pedido.status === 'precisa_ajustes' || pedido.status === 'em_analise' || pedido.status === 'aguardando_aceite'}
|
{#if permissions?.canEditAta}
|
||||||
<select
|
<select
|
||||||
class="rounded border px-2 py-1 text-xs"
|
class="rounded border px-2 py-1 text-xs"
|
||||||
value={ensureEditingItem(item).ataId}
|
value={ensureEditingItem(item).ataId}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||||
import type { Doc, Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
import type { Doc, Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||||
import type { FunctionReturnType } from 'convex/server';
|
|
||||||
import { useConvexClient, useQuery } from 'convex-svelte';
|
import { useConvexClient, useQuery } from 'convex-svelte';
|
||||||
import { Plus, Trash2, X, Info } from 'lucide-svelte';
|
import { Plus, Trash2, X, Info } from 'lucide-svelte';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import { formatarDataBR } from '$lib/utils/datas';
|
|
||||||
|
|
||||||
const client = useConvexClient();
|
const client = useConvexClient();
|
||||||
|
|
||||||
@@ -27,40 +25,28 @@
|
|||||||
let warning = $state<string | null>(null);
|
let warning = $state<string | null>(null);
|
||||||
|
|
||||||
// Item selection state
|
// Item selection state
|
||||||
|
// Nota: modalidade é opcional aqui pois será definida pelo Setor de Compras posteriormente
|
||||||
type SelectedItem = {
|
type SelectedItem = {
|
||||||
objeto: Doc<'objetos'>;
|
objeto: Doc<'objetos'>;
|
||||||
quantidade: number;
|
quantidade: number;
|
||||||
modalidade: 'dispensa' | 'inexgibilidade' | 'adesao' | 'consumo';
|
|
||||||
acaoId?: Id<'acoes'>;
|
acaoId?: Id<'acoes'>;
|
||||||
ataId?: Id<'atas'>;
|
|
||||||
ataNumero?: string; // For display
|
|
||||||
ata?: FunctionReturnType<typeof api.objetos.getAtasComLimite>[number]; // dados mínimos p/ exibir detalhes
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let selectedItems = $state<SelectedItem[]>([]);
|
let selectedItems = $state<SelectedItem[]>([]);
|
||||||
let selectedObjetoIds = $derived(selectedItems.map((i) => i.objeto._id));
|
let selectedObjetoIds = $derived(selectedItems.map((i) => i.objeto._id));
|
||||||
let hasMixedModalidades = $derived(new Set(selectedItems.map((i) => i.modalidade)).size > 1);
|
|
||||||
|
|
||||||
// Item configuration modal
|
// Item configuration modal
|
||||||
let showItemModal = $state(false);
|
let showItemModal = $state(false);
|
||||||
let itemConfig = $state<{
|
let itemConfig = $state<{
|
||||||
objeto: Doc<'objetos'> | null;
|
objeto: Doc<'objetos'> | null;
|
||||||
quantidade: number;
|
quantidade: number;
|
||||||
modalidade: 'dispensa' | 'inexgibilidade' | 'adesao' | 'consumo';
|
|
||||||
acaoId: string; // using string to handle empty select
|
acaoId: string; // using string to handle empty select
|
||||||
ataId: string; // using string to handle empty select
|
|
||||||
}>({
|
}>({
|
||||||
objeto: null,
|
objeto: null,
|
||||||
quantidade: 1,
|
quantidade: 1,
|
||||||
modalidade: 'consumo',
|
acaoId: ''
|
||||||
acaoId: '',
|
|
||||||
ataId: ''
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type AtasComLimite = FunctionReturnType<typeof api.objetos.getAtasComLimite>;
|
|
||||||
let availableAtas = $state<AtasComLimite>([]);
|
|
||||||
|
|
||||||
// Item Details Modal
|
|
||||||
let showDetailsModal = $state(false);
|
let showDetailsModal = $state(false);
|
||||||
let detailsItem = $state<SelectedItem | null>(null);
|
let detailsItem = $state<SelectedItem | null>(null);
|
||||||
|
|
||||||
@@ -75,16 +61,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function openItemModal(objeto: Doc<'objetos'>) {
|
async function openItemModal(objeto: Doc<'objetos'>) {
|
||||||
// Fetch linked Atas for this object
|
|
||||||
const linkedAtas = await client.query(api.objetos.getAtasComLimite, { objetoId: objeto._id });
|
|
||||||
availableAtas = linkedAtas;
|
|
||||||
|
|
||||||
itemConfig = {
|
itemConfig = {
|
||||||
objeto,
|
objeto,
|
||||||
quantidade: 1,
|
quantidade: 1,
|
||||||
modalidade: 'consumo',
|
acaoId: ''
|
||||||
acaoId: '',
|
|
||||||
ataId: ''
|
|
||||||
};
|
};
|
||||||
showItemModal = true;
|
showItemModal = true;
|
||||||
searchQuery = ''; // Clear search
|
searchQuery = ''; // Clear search
|
||||||
@@ -93,24 +73,17 @@
|
|||||||
function closeItemModal() {
|
function closeItemModal() {
|
||||||
showItemModal = false;
|
showItemModal = false;
|
||||||
itemConfig.objeto = null;
|
itemConfig.objeto = null;
|
||||||
availableAtas = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmAddItem() {
|
function confirmAddItem() {
|
||||||
if (!itemConfig.objeto) return;
|
if (!itemConfig.objeto) return;
|
||||||
|
|
||||||
const selectedAta = availableAtas.find((a) => a._id === itemConfig.ataId);
|
|
||||||
|
|
||||||
selectedItems = [
|
selectedItems = [
|
||||||
...selectedItems,
|
...selectedItems,
|
||||||
{
|
{
|
||||||
objeto: itemConfig.objeto,
|
objeto: itemConfig.objeto,
|
||||||
quantidade: itemConfig.quantidade,
|
quantidade: itemConfig.quantidade,
|
||||||
modalidade: itemConfig.modalidade,
|
acaoId: itemConfig.acaoId ? (itemConfig.acaoId as Id<'acoes'>) : undefined
|
||||||
acaoId: itemConfig.acaoId ? (itemConfig.acaoId as Id<'acoes'>) : undefined,
|
|
||||||
ataId: itemConfig.ataId ? (itemConfig.ataId as Id<'atas'>) : undefined,
|
|
||||||
ataNumero: selectedAta?.numero,
|
|
||||||
ata: selectedAta
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
checkExisting();
|
checkExisting();
|
||||||
@@ -131,7 +104,6 @@
|
|||||||
criadoEm: number;
|
criadoEm: number;
|
||||||
matchingItems?: {
|
matchingItems?: {
|
||||||
objetoId: Id<'objetos'>;
|
objetoId: Id<'objetos'>;
|
||||||
modalidade: SelectedItem['modalidade'];
|
|
||||||
quantidade: number;
|
quantidade: number;
|
||||||
}[];
|
}[];
|
||||||
}[]
|
}[]
|
||||||
@@ -157,36 +129,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatModalidade(modalidade: SelectedItem['modalidade']) {
|
|
||||||
switch (modalidade) {
|
|
||||||
case 'consumo':
|
|
||||||
return 'Consumo';
|
|
||||||
case 'dispensa':
|
|
||||||
return 'Dispensa';
|
|
||||||
case 'inexgibilidade':
|
|
||||||
return 'Inexigibilidade';
|
|
||||||
case 'adesao':
|
|
||||||
return 'Adesão';
|
|
||||||
default:
|
|
||||||
return modalidade;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getModalidadeBadgeClasses(modalidade: SelectedItem['modalidade']) {
|
|
||||||
switch (modalidade) {
|
|
||||||
case 'consumo':
|
|
||||||
return 'bg-blue-100 text-blue-800';
|
|
||||||
case 'dispensa':
|
|
||||||
return 'bg-yellow-100 text-yellow-800';
|
|
||||||
case 'inexgibilidade':
|
|
||||||
return 'bg-purple-100 text-purple-800';
|
|
||||||
case 'adesao':
|
|
||||||
return 'bg-green-100 text-green-800';
|
|
||||||
default:
|
|
||||||
return 'bg-gray-100 text-gray-800';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAcaoNome(acaoId: Id<'acoes'> | undefined) {
|
function getAcaoNome(acaoId: Id<'acoes'> | undefined) {
|
||||||
if (!acaoId) return '-';
|
if (!acaoId) return '-';
|
||||||
const acao = acoes.find((a) => a._id === acaoId);
|
const acao = acoes.find((a) => a._id === acaoId);
|
||||||
@@ -206,8 +148,7 @@
|
|||||||
.map((match) => {
|
.map((match) => {
|
||||||
// Find name from selected items (might be multiple with same object, just pick one name)
|
// Find name from selected items (might be multiple with same object, just pick one name)
|
||||||
const item = selectedItems.find((p) => p.objeto._id === match.objetoId);
|
const item = selectedItems.find((p) => p.objeto._id === match.objetoId);
|
||||||
const modalidadeLabel = formatModalidade(match.modalidade);
|
return `${item?.objeto.nome}: ${match.quantidade} un.`;
|
||||||
return `${item?.objeto.nome} (${modalidadeLabel}): ${match.quantidade} un.`;
|
|
||||||
})
|
})
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
@@ -218,9 +159,7 @@
|
|||||||
if (!pedido.matchingItems || pedido.matchingItems.length === 0) return null;
|
if (!pedido.matchingItems || pedido.matchingItems.length === 0) return null;
|
||||||
|
|
||||||
for (const match of pedido.matchingItems) {
|
for (const match of pedido.matchingItems) {
|
||||||
const item = selectedItems.find(
|
const item = selectedItems.find((p) => p.objeto._id === match.objetoId);
|
||||||
(p) => p.objeto._id === match.objetoId && p.modalidade === match.modalidade
|
|
||||||
);
|
|
||||||
if (item) {
|
if (item) {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@@ -239,16 +178,11 @@
|
|||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.set('obj', matchedItem.objeto._id);
|
params.set('obj', matchedItem.objeto._id);
|
||||||
params.set('qtd', String(matchedItem.quantidade));
|
params.set('qtd', String(matchedItem.quantidade));
|
||||||
params.set('mod', matchedItem.modalidade);
|
|
||||||
|
|
||||||
if (matchedItem.acaoId) {
|
if (matchedItem.acaoId) {
|
||||||
params.set('acao', matchedItem.acaoId);
|
params.set('acao', matchedItem.acaoId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchedItem.ataId) {
|
|
||||||
params.set('ata', matchedItem.ataId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return `/pedidos/${pedido._id}?${params.toString()}` as `/pedidos/${string}`;
|
return `/pedidos/${pedido._id}?${params.toString()}` as `/pedidos/${string}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,13 +195,11 @@
|
|||||||
|
|
||||||
checking = true;
|
checking = true;
|
||||||
try {
|
try {
|
||||||
// Importante: ação (acaoId) NÃO entra no filtro de similaridade.
|
// Importante: O filtro considera apenas objetoId (modalidade não é mais usada na criação).
|
||||||
// O filtro considera apenas combinação de objeto + modalidade.
|
|
||||||
const itensFiltro =
|
const itensFiltro =
|
||||||
selectedItems.length > 0
|
selectedItems.length > 0
|
||||||
? selectedItems.map((item) => ({
|
? selectedItems.map((item) => ({
|
||||||
objetoId: item.objeto._id,
|
objetoId: item.objeto._id
|
||||||
modalidade: item.modalidade
|
|
||||||
}))
|
}))
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
@@ -292,11 +224,6 @@
|
|||||||
|
|
||||||
async function handleSubmit(e: Event) {
|
async function handleSubmit(e: Event) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (hasMixedModalidades) {
|
|
||||||
error =
|
|
||||||
'Não é possível criar o pedido com itens de modalidades diferentes. Ajuste os itens antes de continuar.';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
creating = true;
|
creating = true;
|
||||||
error = null;
|
error = null;
|
||||||
try {
|
try {
|
||||||
@@ -312,9 +239,7 @@
|
|||||||
objetoId: item.objeto._id,
|
objetoId: item.objeto._id,
|
||||||
valorEstimado: item.objeto.valorEstimado,
|
valorEstimado: item.objeto.valorEstimado,
|
||||||
quantidade: item.quantidade,
|
quantidade: item.quantidade,
|
||||||
modalidade: item.modalidade,
|
acaoId: item.acaoId
|
||||||
acaoId: item.acaoId,
|
|
||||||
ataId: item.ataId
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -417,20 +342,6 @@
|
|||||||
<div class="flex-1 space-y-2">
|
<div class="flex-1 space-y-2">
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<p class="font-semibold text-gray-900">{item.objeto.nome}</p>
|
<p class="font-semibold text-gray-900">{item.objeto.nome}</p>
|
||||||
<span
|
|
||||||
class={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold ${getModalidadeBadgeClasses(
|
|
||||||
item.modalidade
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
{formatModalidade(item.modalidade)}
|
|
||||||
</span>
|
|
||||||
{#if item.ataNumero}
|
|
||||||
<span
|
|
||||||
class="inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800"
|
|
||||||
>
|
|
||||||
Ata {item.ataNumero}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{#if item.acaoId}
|
{#if item.acaoId}
|
||||||
<span
|
<span
|
||||||
class="inline-flex items-center rounded-full bg-indigo-100 px-2.5 py-0.5 text-xs font-medium text-indigo-800"
|
class="inline-flex items-center rounded-full bg-indigo-100 px-2.5 py-0.5 text-xs font-medium text-indigo-800"
|
||||||
@@ -480,15 +391,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Warnings Section -->
|
<!-- Warnings Section -->
|
||||||
{#if hasMixedModalidades}
|
|
||||||
<div class="mb-3 rounded-lg border border-red-400 bg-red-50 px-4 py-3 text-sm text-red-800">
|
|
||||||
<p class="font-semibold">Modalidades diferentes detectadas</p>
|
|
||||||
<p>
|
|
||||||
Não é possível criar o pedido com itens de modalidades diferentes. Ajuste os itens para
|
|
||||||
usar uma única modalidade.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if warning}
|
{#if warning}
|
||||||
<div
|
<div
|
||||||
@@ -508,22 +410,13 @@
|
|||||||
<p class="mb-3 font-semibold text-yellow-900">Pedidos similares encontrados:</p>
|
<p class="mb-3 font-semibold text-yellow-900">Pedidos similares encontrados:</p>
|
||||||
<ul class="space-y-2">
|
<ul class="space-y-2">
|
||||||
{#each existingPedidos as pedido (pedido._id)}
|
{#each existingPedidos as pedido (pedido._id)}
|
||||||
{@const first = getFirstMatchingSelectedItem(pedido)}
|
|
||||||
<li class="flex flex-col rounded-lg bg-white px-4 py-3 shadow-sm">
|
<li class="flex flex-col rounded-lg bg-white px-4 py-3 shadow-sm">
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<p class="text-sm font-medium text-gray-900">
|
<p class="text-sm font-medium text-gray-900">
|
||||||
Pedido {pedido.numeroSei || 'sem número SEI'} — {formatStatus(pedido.status)}
|
Pedido {pedido.numeroSei || 'sem número SEI'} — {formatStatus(pedido.status)}
|
||||||
</p>
|
</p>
|
||||||
{#if first}
|
|
||||||
<span
|
|
||||||
class={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold ${getModalidadeBadgeClasses(
|
|
||||||
first.modalidade
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
Modalidade: {formatModalidade(first.modalidade)}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{#if getMatchingInfo(pedido)}
|
{#if getMatchingInfo(pedido)}
|
||||||
<p class="mt-1 text-xs text-blue-700">
|
<p class="mt-1 text-xs text-blue-700">
|
||||||
{getMatchingInfo(pedido)}
|
{getMatchingInfo(pedido)}
|
||||||
@@ -553,7 +446,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={creating || selectedItems.length === 0 || hasMixedModalidades}
|
disabled={creating || selectedItems.length === 0}
|
||||||
class="rounded-lg bg-blue-600 px-6 py-2.5 font-semibold text-white shadow-md transition hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
class="rounded-lg bg-blue-600 px-6 py-2.5 font-semibold text-white shadow-md transition hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{creating ? 'Criando...' : 'Criar Pedido'}
|
{creating ? 'Criando...' : 'Criar Pedido'}
|
||||||
@@ -600,62 +493,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<label class="mb-2 block text-sm font-medium text-gray-700" for="modalidade">
|
|
||||||
Modalidade
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="modalidade"
|
|
||||||
class="w-full rounded-lg border border-gray-300 px-4 py-2.5 transition focus:border-blue-500 focus:ring-2 focus:ring-blue-200 focus:outline-none"
|
|
||||||
bind:value={itemConfig.modalidade}
|
|
||||||
>
|
|
||||||
<option value="consumo">Consumo</option>
|
|
||||||
<option value="dispensa">Dispensa</option>
|
|
||||||
<option value="inexgibilidade">Inexigibilidade</option>
|
|
||||||
<option value="adesao">Adesão</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if availableAtas.length > 0}
|
|
||||||
<div class="rounded-lg border-2 border-green-200 bg-green-50 p-4">
|
|
||||||
<div class="mb-2 flex items-center gap-2">
|
|
||||||
<span class="rounded-full bg-green-600 px-2 py-0.5 text-xs font-bold text-white">
|
|
||||||
{availableAtas.length}
|
|
||||||
{availableAtas.length === 1 ? 'Ata' : 'Atas'}
|
|
||||||
</span>
|
|
||||||
<span class="text-sm font-semibold text-green-900">disponível para este objeto</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<label class="mb-2 block text-sm font-medium text-gray-700" for="itemAta">
|
|
||||||
Selecionar Ata (Opcional)
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="itemAta"
|
|
||||||
class="w-full rounded-lg border border-green-300 bg-white px-4 py-2.5 transition focus:border-green-500 focus:ring-2 focus:ring-green-200 focus:outline-none"
|
|
||||||
bind:value={itemConfig.ataId}
|
|
||||||
>
|
|
||||||
<option value="">Nenhuma</option>
|
|
||||||
{#each availableAtas as ata (ata._id)}
|
|
||||||
{@const reason =
|
|
||||||
ata.lockReason === 'nao_configurada'
|
|
||||||
? 'não configurada'
|
|
||||||
: ata.lockReason === 'limite_atingido'
|
|
||||||
? 'limite atingido'
|
|
||||||
: ata.lockReason === 'vigencia_expirada'
|
|
||||||
? `vigência encerrada em ${
|
|
||||||
ata.dataFimEfetiva || ata.dataFim
|
|
||||||
? formatarDataBR((ata.dataFimEfetiva || ata.dataFim) as string)
|
|
||||||
: '-'
|
|
||||||
}`
|
|
||||||
: null}
|
|
||||||
<option value={ata._id} disabled={ata.isLocked}>
|
|
||||||
Ata {ata.numero} (SEI: {ata.numeroSei}){reason ? ` (${reason})` : ''}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="mb-2 block text-sm font-medium text-gray-700" for="itemAcao">
|
<label class="mb-2 block text-sm font-medium text-gray-700" for="itemAcao">
|
||||||
Ação (Opcional)
|
Ação (Opcional)
|
||||||
@@ -723,7 +560,7 @@
|
|||||||
<div class="rounded-lg bg-gray-50 p-4">
|
<div class="rounded-lg bg-gray-50 p-4">
|
||||||
<h4 class="mb-2 font-semibold text-gray-800">Pedido</h4>
|
<h4 class="mb-2 font-semibold text-gray-800">Pedido</h4>
|
||||||
<p class="text-gray-700"><strong>Quantidade:</strong> {detailsItem.quantidade}</p>
|
<p class="text-gray-700"><strong>Quantidade:</strong> {detailsItem.quantidade}</p>
|
||||||
<p class="text-gray-700"><strong>Modalidade:</strong> {detailsItem.modalidade}</p>
|
|
||||||
{#if detailsItem.acaoId}
|
{#if detailsItem.acaoId}
|
||||||
<p class="text-gray-700">
|
<p class="text-gray-700">
|
||||||
<strong>Ação:</strong>
|
<strong>Ação:</strong>
|
||||||
@@ -731,32 +568,6 @@
|
|||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if detailsItem.ata}
|
|
||||||
<div class="rounded-lg border border-green-100 bg-green-50 p-4">
|
|
||||||
<h4 class="mb-2 font-semibold text-green-900">Ata de Registro de Preços</h4>
|
|
||||||
<p class="text-green-800"><strong>Número:</strong> {detailsItem.ata.numero}</p>
|
|
||||||
<p class="text-green-800">
|
|
||||||
<strong>Processo SEI:</strong>
|
|
||||||
{detailsItem.ata.numeroSei}
|
|
||||||
</p>
|
|
||||||
{#if detailsItem.ata.dataInicio}
|
|
||||||
<p class="text-green-800">
|
|
||||||
<strong>Vigência:</strong>
|
|
||||||
{formatarDataBR(detailsItem.ata.dataInicio)} até {detailsItem.ata
|
|
||||||
.dataFimEfetiva || detailsItem.ata.dataFim
|
|
||||||
? formatarDataBR(
|
|
||||||
(detailsItem.ata.dataFimEfetiva || detailsItem.ata.dataFim) as string
|
|
||||||
)
|
|
||||||
: 'Indefinido'}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="rounded-lg bg-gray-50 p-4">
|
|
||||||
<p class="text-sm text-gray-500 italic">Nenhuma Ata vinculada a este item.</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-6 flex justify-end">
|
<div class="mt-6 flex justify-end">
|
||||||
|
|||||||
@@ -159,12 +159,11 @@ async function getUsuarioAutenticado(ctx: QueryCtx | MutationCtx) {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Garante que todos os itens de um pedido utilizem a mesma
|
// Garante que todos os itens de um pedido utilizem a mesma ata (quando houver).
|
||||||
// combinação de modalidade e ata (quando houver).
|
// Nota: Modalidade não é mais validada aqui, pois é definida apenas pelo Setor de Compras.
|
||||||
async function ensurePedidoModalidadeAtaConsistency(
|
async function ensurePedidoAtaConsistency(
|
||||||
ctx: MutationCtx,
|
ctx: MutationCtx,
|
||||||
pedidoId: Id<'pedidos'>,
|
pedidoId: Id<'pedidos'>,
|
||||||
modalidade: Doc<'objetoItems'>['modalidade'],
|
|
||||||
ataId: Id<'atas'> | undefined,
|
ataId: Id<'atas'> | undefined,
|
||||||
ignoreItemId?: Id<'objetoItems'>
|
ignoreItemId?: Id<'objetoItems'>
|
||||||
) {
|
) {
|
||||||
@@ -185,12 +184,11 @@ async function ensurePedidoModalidadeAtaConsistency(
|
|||||||
const normalizedItemAtaId = (('ataId' in item ? item.ataId : undefined) ??
|
const normalizedItemAtaId = (('ataId' in item ? item.ataId : undefined) ??
|
||||||
null) as Id<'atas'> | null;
|
null) as Id<'atas'> | null;
|
||||||
|
|
||||||
const modalidadeMismatch = !!item.modalidade && !!modalidade && item.modalidade !== modalidade;
|
|
||||||
const ataMismatch = normalizedItemAtaId !== normalizedNewAtaId;
|
const ataMismatch = normalizedItemAtaId !== normalizedNewAtaId;
|
||||||
|
|
||||||
if (modalidadeMismatch || ataMismatch) {
|
if (ataMismatch) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Todos os itens do pedido devem usar a mesma modalidade e a mesma ata (quando houver). Ajuste os itens existentes ou crie um novo pedido para a nova combinação.'
|
'Todos os itens do pedido devem usar a mesma ata (quando houver). Ajuste os itens existentes ou crie um novo pedido para a nova ata.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -580,16 +578,11 @@ export const getHistory = query({
|
|||||||
export const checkExisting = query({
|
export const checkExisting = query({
|
||||||
args: {
|
args: {
|
||||||
numeroSei: v.optional(v.string()),
|
numeroSei: v.optional(v.string()),
|
||||||
|
// Modalidade removida do filtro - agora busca apenas por objetoId
|
||||||
itensFiltro: v.optional(
|
itensFiltro: v.optional(
|
||||||
v.array(
|
v.array(
|
||||||
v.object({
|
v.object({
|
||||||
objetoId: v.id('objetos'),
|
objetoId: v.id('objetos')
|
||||||
modalidade: v.union(
|
|
||||||
v.literal('dispensa'),
|
|
||||||
v.literal('inexgibilidade'),
|
|
||||||
v.literal('adesao'),
|
|
||||||
v.literal('consumo')
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -608,7 +601,6 @@ export const checkExisting = query({
|
|||||||
v.literal('cancelado'),
|
v.literal('cancelado'),
|
||||||
v.literal('concluido')
|
v.literal('concluido')
|
||||||
),
|
),
|
||||||
// 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()),
|
descricaoAjuste: v.optional(v.string()),
|
||||||
@@ -618,12 +610,6 @@ export const checkExisting = query({
|
|||||||
v.array(
|
v.array(
|
||||||
v.object({
|
v.object({
|
||||||
objetoId: v.id('objetos'),
|
objetoId: v.id('objetos'),
|
||||||
modalidade: v.union(
|
|
||||||
v.literal('dispensa'),
|
|
||||||
v.literal('inexgibilidade'),
|
|
||||||
v.literal('adesao'),
|
|
||||||
v.literal('consumo')
|
|
||||||
),
|
|
||||||
quantidade: v.number()
|
quantidade: v.number()
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -654,7 +640,7 @@ export const checkExisting = query({
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3) Filtro por itens (objetoId + modalidade), se informado, e coleta de matchingItems
|
// 3) Filtro por itens (apenas objetoId), se informado, e coleta de matchingItems
|
||||||
const resultados = [];
|
const resultados = [];
|
||||||
|
|
||||||
const itensFiltro = args.itensFiltro ?? [];
|
const itensFiltro = args.itensFiltro ?? [];
|
||||||
@@ -663,31 +649,21 @@ export const checkExisting = query({
|
|||||||
let include = true;
|
let include = true;
|
||||||
let matchingItems: {
|
let matchingItems: {
|
||||||
objetoId: Id<'objetos'>;
|
objetoId: Id<'objetos'>;
|
||||||
modalidade: NonNullable<Doc<'objetoItems'>['modalidade']>;
|
|
||||||
quantidade: number;
|
quantidade: number;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
// Se houver filtro de itens, verificamos se o pedido tem ALGUM dos itens (objetoId + modalidade)
|
// Se houver filtro de itens, verificamos se o pedido tem ALGUM dos itens (apenas objetoId)
|
||||||
if (itensFiltro.length > 0) {
|
if (itensFiltro.length > 0) {
|
||||||
const items = await ctx.db
|
const items = await ctx.db
|
||||||
.query('objetoItems')
|
.query('objetoItems')
|
||||||
.withIndex('by_pedidoId', (q) => q.eq('pedidoId', pedido._id))
|
.withIndex('by_pedidoId', (q) => q.eq('pedidoId', pedido._id))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
const matching = items.filter((i) =>
|
const matching = items.filter((i) => itensFiltro.some((f) => f.objetoId === i.objetoId));
|
||||||
itensFiltro.some(
|
|
||||||
(f) => f.objetoId === i.objetoId && f.modalidade === (i.modalidade ?? 'consumo')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (matching.length > 0) {
|
if (matching.length > 0) {
|
||||||
matchingItems = matching.map((i) => ({
|
matchingItems = matching.map((i) => ({
|
||||||
objetoId: i.objetoId,
|
objetoId: i.objetoId,
|
||||||
modalidade: (i.modalidade ?? 'consumo') as
|
|
||||||
| 'dispensa'
|
|
||||||
| 'inexgibilidade'
|
|
||||||
| 'adesao'
|
|
||||||
| 'consumo',
|
|
||||||
quantidade: i.quantidade
|
quantidade: i.quantidade
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
@@ -1442,9 +1418,27 @@ export const addItem = mutation({
|
|||||||
const modalidade =
|
const modalidade =
|
||||||
args.modalidade ?? userProductItems.find((i) => !!i.modalidade)?.modalidade ?? undefined;
|
args.modalidade ?? userProductItems.find((i) => !!i.modalidade)?.modalidade ?? undefined;
|
||||||
|
|
||||||
// Regra global: todos os itens do pedido devem ter a mesma
|
// Regra global: todos os itens do pedido devem ter a mesma ata (quando houver).
|
||||||
// modalidade e a mesma ata (quando houver).
|
await ensurePedidoAtaConsistency(ctx, args.pedidoId, args.ataId);
|
||||||
await ensurePedidoModalidadeAtaConsistency(ctx, args.pedidoId, modalidade, args.ataId);
|
|
||||||
|
// Bloqueia ataId se não for Compras em análise
|
||||||
|
if (args.ataId) {
|
||||||
|
const config = await ctx.db.query('config').first();
|
||||||
|
let isInComprasSector = false;
|
||||||
|
if (config?.comprasSetorId) {
|
||||||
|
const funcionarioSetores = await ctx.db
|
||||||
|
.query('funcionarioSetores')
|
||||||
|
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', user.funcionarioId!))
|
||||||
|
.filter((q) => q.eq(q.field('setorId'), config.comprasSetorId))
|
||||||
|
.first();
|
||||||
|
isInComprasSector = !!funcionarioSetores;
|
||||||
|
}
|
||||||
|
if (!(pedido.status === 'em_analise' && isInComprasSector)) {
|
||||||
|
throw new Error(
|
||||||
|
'Apenas funcionários do Setor de Compras podem vincular uma Ata, e somente quando o pedido está em análise.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- CHECK ANALYSIS MODE ---
|
// --- CHECK ANALYSIS MODE ---
|
||||||
// Em pedidos em análise, a inclusão de itens deve passar por fluxo de aprovação.
|
// Em pedidos em análise, a inclusão de itens deve passar por fluxo de aprovação.
|
||||||
@@ -1846,9 +1840,35 @@ export const updateItem = mutation({
|
|||||||
const pedido = await ctx.db.get(item.pedidoId);
|
const pedido = await ctx.db.get(item.pedidoId);
|
||||||
if (!pedido) throw new Error('Pedido não encontrado.');
|
if (!pedido) throw new Error('Pedido não encontrado.');
|
||||||
|
|
||||||
// Apenas quem adicionou o item pode editá-lo
|
// Apenas quem adicionou o item pode editá-lo (outras propriedades)
|
||||||
const isOwner = item.adicionadoPor === user.funcionarioId;
|
const isOwner = item.adicionadoPor === user.funcionarioId;
|
||||||
if (!isOwner) {
|
|
||||||
|
// Verificar permissão para editar modalidade e ata
|
||||||
|
// Somente Setor de Compras pode editar modalidade e ata, e apenas quando em análise
|
||||||
|
const config = await ctx.db.query('config').first();
|
||||||
|
let isInComprasSector = false;
|
||||||
|
if (config?.comprasSetorId) {
|
||||||
|
const funcionarioSetores = await ctx.db
|
||||||
|
.query('funcionarioSetores')
|
||||||
|
.withIndex('by_funcionarioId', (q) => q.eq('funcionarioId', user.funcionarioId!))
|
||||||
|
.filter((q) => q.eq(q.field('setorId'), config.comprasSetorId))
|
||||||
|
.first();
|
||||||
|
isInComprasSector = !!funcionarioSetores;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canEditComprasFields = pedido.status === 'em_analise' && isInComprasSector;
|
||||||
|
|
||||||
|
// Se está tentando editar a modalidade ou a ata, verificar permissão
|
||||||
|
const isChangingModalidade = item.modalidade !== args.modalidade;
|
||||||
|
const isChangingAta = (item.ataId ?? null) !== (args.ataId ?? null);
|
||||||
|
if ((isChangingModalidade || isChangingAta) && !canEditComprasFields) {
|
||||||
|
throw new Error(
|
||||||
|
'Apenas funcionários do Setor de Compras podem editar a modalidade ou a ata, e somente quando o pedido está em análise.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Para outras propriedades, apenas o dono do item pode editar
|
||||||
|
if (!isOwner && !canEditComprasFields) {
|
||||||
throw new Error('Apenas quem adicionou este item pode editá-lo.');
|
throw new Error('Apenas quem adicionou este item pode editá-lo.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1860,14 +1880,8 @@ export const updateItem = mutation({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Regra global: ao alterar um item, garantir que os demais itens do pedido
|
// Regra global: ao alterar um item, garantir que os demais itens do pedido
|
||||||
// continuem (ou passem a estar) com a mesma modalidade e ata.
|
// continuem (ou passem a estar) com a mesma ata.
|
||||||
await ensurePedidoModalidadeAtaConsistency(
|
await ensurePedidoAtaConsistency(ctx, item.pedidoId, args.ataId, args.itemId);
|
||||||
ctx,
|
|
||||||
item.pedidoId,
|
|
||||||
args.modalidade,
|
|
||||||
args.ataId,
|
|
||||||
args.itemId
|
|
||||||
);
|
|
||||||
|
|
||||||
// Em pedidos em análise ou aguardando aceite, geramos uma solicitação em vez de alterar diretamente
|
// Em pedidos em análise ou aguardando aceite, geramos uma solicitação em vez de alterar diretamente
|
||||||
if (pedido.status === 'em_analise' || pedido.status === 'aguardando_aceite') {
|
if (pedido.status === 'em_analise' || pedido.status === 'aguardando_aceite') {
|
||||||
@@ -1954,6 +1968,8 @@ export const getPermissions = query({
|
|||||||
canCancel: false,
|
canCancel: false,
|
||||||
canCompleteAdjustments: false,
|
canCompleteAdjustments: false,
|
||||||
canManageRequests: false,
|
canManageRequests: false,
|
||||||
|
canEditModalidade: false,
|
||||||
|
canEditAta: false,
|
||||||
currentFuncionarioId: user?.funcionarioId ?? null
|
currentFuncionarioId: user?.funcionarioId ?? null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -2004,6 +2020,9 @@ export const getPermissions = query({
|
|||||||
isCreator &&
|
isCreator &&
|
||||||
hasOnlyCreatorItems,
|
hasOnlyCreatorItems,
|
||||||
canManageRequests: pedido.status === 'em_analise' && isInComprasSector,
|
canManageRequests: pedido.status === 'em_analise' && isInComprasSector,
|
||||||
|
// Somente Setor de Compras pode editar modalidade e ata, e apenas quando o pedido está em análise
|
||||||
|
canEditModalidade: pedido.status === 'em_analise' && isInComprasSector,
|
||||||
|
canEditAta: pedido.status === 'em_analise' && isInComprasSector,
|
||||||
currentFuncionarioId: user.funcionarioId
|
currentFuncionarioId: user.funcionarioId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -2524,14 +2543,8 @@ export const approveItemRequest = mutation({
|
|||||||
// We trust the request data structure matches addItem args
|
// We trust the request data structure matches addItem args
|
||||||
const newItem = data;
|
const newItem = data;
|
||||||
|
|
||||||
// Regra global: todos os itens do pedido devem ter a mesma
|
// Regra global: todos os itens do pedido devem ter a mesma ata (quando houver).
|
||||||
// modalidade e ata (quando houver).
|
await ensurePedidoAtaConsistency(ctx, request.pedidoId, newItem.ataId);
|
||||||
await ensurePedidoModalidadeAtaConsistency(
|
|
||||||
ctx,
|
|
||||||
request.pedidoId,
|
|
||||||
newItem.modalidade,
|
|
||||||
newItem.ataId
|
|
||||||
);
|
|
||||||
// Note: We MUST use the original requester's ID (request.solicitadoPor) as addedBy?
|
// Note: We MUST use the original requester's ID (request.solicitadoPor) as addedBy?
|
||||||
// Or should we attribute it to the requester? YES.
|
// Or should we attribute it to the requester? YES.
|
||||||
// BUT `addItem` logic usually checks if `existingItem.adicionadoPor === user`.
|
// BUT `addItem` logic usually checks if `existingItem.adicionadoPor === user`.
|
||||||
@@ -2625,13 +2638,7 @@ export const approveItemRequest = mutation({
|
|||||||
const item = await ctx.db.get(itemId);
|
const item = await ctx.db.get(itemId);
|
||||||
if (item) {
|
if (item) {
|
||||||
// Regra global também se aplica na alteração de detalhes aprovada
|
// Regra global também se aplica na alteração de detalhes aprovada
|
||||||
await ensurePedidoModalidadeAtaConsistency(
|
await ensurePedidoAtaConsistency(ctx, item.pedidoId, para.ataId, itemId);
|
||||||
ctx,
|
|
||||||
item.pedidoId,
|
|
||||||
para.modalidade,
|
|
||||||
para.ataId,
|
|
||||||
itemId
|
|
||||||
);
|
|
||||||
|
|
||||||
const oldAtaId = ('ataId' in item ? item.ataId : undefined) ?? undefined;
|
const oldAtaId = ('ataId' in item ? item.ataId : undefined) ?? undefined;
|
||||||
const newAtaId = para.ataId;
|
const newAtaId = para.ataId;
|
||||||
|
|||||||
Reference in New Issue
Block a user