import fetchHelper from "../../../utils/fetchHelper" import getTokenHelper from "../../../utils/getTokenHelper" import refreshTokenHelper from "../../../utils/refreshTokenHelper" import errorHandlingHelper from "../../../utils/errorHandlingHelper" /** * Live stock quantities for a set of products — used to asynchronously enrich * search results AFTER they render (stock is volatile, so it is NOT held in ES). * Also returns a combined `isBom` flag (standard IsBOM OR custom isJtlBom), since * those flags are not in the ES product index. * * POST /api/materials/products/quantities body: { ids: number[] } * -> { quantities: { : { qtyonhand, qtyreserved, qtyordered, qtyavailable, isBom } } } */ const num = (v: any) => (typeof v === 'number' ? v : parseFloat(v) || 0) const handleFunc = async (event: any, ids: number[], authToken: string | null = null) => { const token = authToken ?? await getTokenHelper(event) const filter = '(' + ids.map(id => `M_Product_ID eq ${id}`).join(' or ') + ')' const storageUrl = `models/m_storage?$filter=${filter}&$expand=M_Locator_ID&$select=M_Product_ID,QtyOnHand,QtyReserved,QtyOrdered` // NB: isJtlBom is a lowercase-first custom column — PascalCase 'IsJtlBom' is rejected by $select. const productUrl = `models/m_product?$filter=${filter}&$select=M_Product_ID,IsBOM,isJtlBom` const [res, prodRes]: any = await Promise.all([ fetchHelper(event, storageUrl, 'GET', token, null), // best-effort: a bom-fetch failure must never break the (critical) quantities fetchHelper(event, productUrl, 'GET', token, null).catch(() => null) ]) const q: Record = {} for (const id of ids) q[id] = { qtyonhand: 0, qtyreserved: 0, qtyordered: 0, qtyavailable: 0, isBom: false } for (const row of (res?.records || [])) { const pid = row?.M_Product_ID?.id ?? row?.M_Product_ID if (pid == null || !q[pid]) continue q[pid].qtyonhand += num(row.QtyOnHand) q[pid].qtyreserved += num(row.QtyReserved) q[pid].qtyordered += num(row.QtyOrdered) } for (const row of (prodRes?.records || [])) { const pid = row?.id ?? row?.M_Product_ID?.id ?? row?.M_Product_ID if (pid == null || !q[pid]) continue q[pid].isBom = row.IsBOM === true || row.IsBOM === 'Y' || row.isJtlBom === true || row.isJtlBom === 'Y' } for (const id of ids) q[id].qtyavailable = q[id].qtyonhand - q[id].qtyreserved return { quantities: q } } export default defineEventHandler(async (event) => { const body = await readBody(event) const ids: number[] = Array.from(new Set((body?.ids || []).map((x: any) => Number(x)).filter(Boolean))) if (!ids.length) return { quantities: {} } let data: any = {} try { data = await handleFunc(event, ids) } catch (err: any) { try { const authToken = await refreshTokenHelper(event) data = await handleFunc(event, ids, authToken) } catch (error: any) { data = errorHandlingHelper(err?.data ?? err, error?.data ?? error) } } return data })