feat: Add prefill functionality for pedidos and enhance item matching logic with modalidade support
This commit is contained in:
@@ -117,6 +117,8 @@
|
|||||||
});
|
});
|
||||||
let addingItem = $state(false);
|
let addingItem = $state(false);
|
||||||
|
|
||||||
|
let hasAppliedPrefill = $state(false);
|
||||||
|
|
||||||
// Edit SEI State
|
// Edit SEI State
|
||||||
let editingSei = $state(false);
|
let editingSei = $state(false);
|
||||||
let seiValue = $state('');
|
let seiValue = $state('');
|
||||||
@@ -139,6 +141,47 @@
|
|||||||
selectedObjeto = null;
|
selectedObjeto = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (hasAppliedPrefill) return;
|
||||||
|
if (objetosQuery.isLoading || acoesQuery.isLoading) return;
|
||||||
|
|
||||||
|
const url = $page.url;
|
||||||
|
const obj = url.searchParams.get('obj');
|
||||||
|
const qtdStr = url.searchParams.get('qtd');
|
||||||
|
const mod = url.searchParams.get('mod') as Modalidade | null;
|
||||||
|
const acao = url.searchParams.get('acao');
|
||||||
|
const ata = url.searchParams.get('ata');
|
||||||
|
|
||||||
|
if (!obj) return;
|
||||||
|
|
||||||
|
const objeto = objetos.find((o) => o._id === obj);
|
||||||
|
if (!objeto) return;
|
||||||
|
|
||||||
|
let quantidade = parseInt(qtdStr || '1', 10);
|
||||||
|
if (!Number.isFinite(quantidade) || quantidade <= 0) {
|
||||||
|
quantidade = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalidade: Modalidade =
|
||||||
|
mod === 'dispensa' || mod === 'inexgibilidade' || mod === 'adesao' || mod === 'consumo'
|
||||||
|
? mod
|
||||||
|
: 'consumo';
|
||||||
|
|
||||||
|
showAddItem = true;
|
||||||
|
newItem = {
|
||||||
|
objetoId: obj,
|
||||||
|
valorEstimado: maskCurrencyBRL(objeto.valorEstimado || ''),
|
||||||
|
quantidade,
|
||||||
|
modalidade,
|
||||||
|
acaoId: acao || '',
|
||||||
|
ataId: ata || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
void loadAtasForObjeto(obj);
|
||||||
|
|
||||||
|
hasAppliedPrefill = true;
|
||||||
|
});
|
||||||
|
|
||||||
async function handleAddItem() {
|
async function handleAddItem() {
|
||||||
if (!newItem.objetoId || !newItem.valorEstimado) return;
|
if (!newItem.objetoId || !newItem.valorEstimado) return;
|
||||||
addingItem = true;
|
addingItem = true;
|
||||||
|
|||||||
@@ -125,7 +125,11 @@
|
|||||||
numeroSei?: string;
|
numeroSei?: string;
|
||||||
status: string;
|
status: string;
|
||||||
criadoEm: number;
|
criadoEm: number;
|
||||||
matchingItems?: { objetoId: Id<'objetos'>; quantidade: number }[];
|
matchingItems?: {
|
||||||
|
objetoId: Id<'objetos'>;
|
||||||
|
modalidade: SelectedItem['modalidade'];
|
||||||
|
quantidade: number;
|
||||||
|
}[];
|
||||||
}[]
|
}[]
|
||||||
>([]);
|
>([]);
|
||||||
let checking = $state(false);
|
let checking = $state(false);
|
||||||
@@ -175,20 +179,64 @@
|
|||||||
return `Contém: ${details}`;
|
return `Contém: ${details}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFirstMatchingSelectedItem(pedido: (typeof existingPedidos)[0]) {
|
||||||
|
if (!pedido.matchingItems || pedido.matchingItems.length === 0) return null;
|
||||||
|
|
||||||
|
for (const match of pedido.matchingItems) {
|
||||||
|
const item = selectedItems.find(
|
||||||
|
(p) => p.objeto._id === match.objetoId && p.modalidade === match.modalidade
|
||||||
|
);
|
||||||
|
if (item) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPedidoHref(pedido: (typeof existingPedidos)[0]) {
|
||||||
|
const matchedItem = getFirstMatchingSelectedItem(pedido);
|
||||||
|
|
||||||
|
if (!matchedItem) {
|
||||||
|
return resolve(`/pedidos/${pedido._id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('obj', matchedItem.objeto._id);
|
||||||
|
params.set('qtd', String(matchedItem.quantidade));
|
||||||
|
params.set('mod', matchedItem.modalidade);
|
||||||
|
|
||||||
|
if (matchedItem.acaoId) {
|
||||||
|
params.set('acao', matchedItem.acaoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedItem.ataId) {
|
||||||
|
params.set('ata', matchedItem.ataId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(`/pedidos/${pedido._id}?${params.toString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
async function checkExisting() {
|
async function checkExisting() {
|
||||||
warning = null;
|
warning = null;
|
||||||
existingPedidos = [];
|
existingPedidos = [];
|
||||||
|
|
||||||
const hasFilters = formData.numeroSei || selectedObjetoIds.length > 0;
|
const hasFilters = formData.numeroSei || selectedItems.length > 0;
|
||||||
if (!hasFilters) return;
|
if (!hasFilters) return;
|
||||||
|
|
||||||
checking = true;
|
checking = true;
|
||||||
try {
|
try {
|
||||||
// Note: checkExisting query might need update to handle item-level acaoId if we want to filter by it.
|
const itensFiltro =
|
||||||
// Currently we only filter by numeroSei and objetoIds.
|
selectedItems.length > 0
|
||||||
|
? selectedItems.map((item) => ({
|
||||||
|
objetoId: item.objeto._id,
|
||||||
|
modalidade: item.modalidade
|
||||||
|
}))
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const result = await client.query(api.pedidos.checkExisting, {
|
const result = await client.query(api.pedidos.checkExisting, {
|
||||||
numeroSei: formData.numeroSei || undefined,
|
numeroSei: formData.numeroSei || undefined,
|
||||||
objetoIds: selectedObjetoIds.length ? (selectedObjetoIds as Id<'objetos'>[]) : undefined
|
itensFiltro
|
||||||
});
|
});
|
||||||
|
|
||||||
existingPedidos = result;
|
existingPedidos = result;
|
||||||
@@ -407,7 +455,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
href={resolve(`/pedidos/${pedido._id}`)}
|
href={buildPedidoHref(pedido)}
|
||||||
class="text-sm font-medium text-blue-600 hover:text-blue-800"
|
class="text-sm font-medium text-blue-600 hover:text-blue-800"
|
||||||
>
|
>
|
||||||
Abrir
|
Abrir
|
||||||
|
|||||||
@@ -145,9 +145,20 @@ export const getHistory = query({
|
|||||||
|
|
||||||
export const checkExisting = query({
|
export const checkExisting = query({
|
||||||
args: {
|
args: {
|
||||||
acaoId: v.optional(v.id('acoes')), // Used to filter items
|
|
||||||
numeroSei: v.optional(v.string()),
|
numeroSei: v.optional(v.string()),
|
||||||
objetoIds: v.optional(v.array(v.id('objetos')))
|
itensFiltro: v.optional(
|
||||||
|
v.array(
|
||||||
|
v.object({
|
||||||
|
objetoId: v.id('objetos'),
|
||||||
|
modalidade: v.union(
|
||||||
|
v.literal('dispensa'),
|
||||||
|
v.literal('inexgibilidade'),
|
||||||
|
v.literal('adesao'),
|
||||||
|
v.literal('consumo')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
returns: v.array(
|
returns: v.array(
|
||||||
v.object({
|
v.object({
|
||||||
@@ -170,6 +181,12 @@ 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()
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -200,38 +217,36 @@ export const checkExisting = query({
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3) Filtro por acaoId (via items)
|
// 3) Filtro por itens (objetoId + modalidade), se informado, e coleta de matchingItems
|
||||||
if (args.acaoId) {
|
|
||||||
// This is expensive, but for now we iterate. Better would be to query items by acaoId first.
|
|
||||||
// Optimization: Query items by acaoId and get unique pedidoIds.
|
|
||||||
const itemsComAcao = await ctx.db
|
|
||||||
.query('objetoItems')
|
|
||||||
.withIndex('by_acaoId', (q) => q.eq('acaoId', args.acaoId))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
const pedidoIdsComAcao = new Set(itemsComAcao.map((i) => i.pedidoId));
|
|
||||||
pedidosAbertos = pedidosAbertos.filter((p) => pedidoIdsComAcao.has(p._id));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4) Filtro por objetos (se informado) e coleta de matchingItems
|
|
||||||
const resultados = [];
|
const resultados = [];
|
||||||
|
|
||||||
|
const itensFiltro = args.itensFiltro ?? [];
|
||||||
|
|
||||||
for (const pedido of pedidosAbertos) {
|
for (const pedido of pedidosAbertos) {
|
||||||
let include = true;
|
let include = true;
|
||||||
let matchingItems: { objetoId: Id<'objetos'>; quantidade: number }[] = [];
|
let matchingItems: {
|
||||||
|
objetoId: Id<'objetos'>;
|
||||||
|
modalidade: Doc<'objetoItems'>['modalidade'];
|
||||||
|
quantidade: number;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
// Se houver filtro de objetos, verificamos se o pedido tem ALGUM dos objetos
|
// Se houver filtro de itens, verificamos se o pedido tem ALGUM dos itens (objetoId + modalidade)
|
||||||
if (args.objetoIds && args.objetoIds.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) => args.objetoIds?.includes(i.objetoId));
|
const matching = items.filter((i) =>
|
||||||
|
itensFiltro.some(
|
||||||
|
(f) => f.objetoId === i.objetoId && f.modalidade === i.modalidade
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
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,
|
||||||
quantidade: i.quantidade
|
quantidade: i.quantidade
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user