import { string } from 'alga-js' import refreshTokenHelper from "../../utils/refreshTokenHelper" import errorHandlingHelper from "../../utils/errorHandlingHelper" import fetchHelper from "../../utils/fetchHelper" import getReturnLocatorIds from "../../utils/returnLocatorHelper" // Sums QtyOnHand from a product's M_Storage rows, excluding storages that sit // on "return" locators (M_LocatorType with all three IsAvailableFor* flags = false). const sumSellableQty = (storages: any[], returnLocatorIds: Set) => { let qtyOnHand = 0 let returnQtyOnHand = 0 if (!Array.isArray(storages)) return { qtyOnHand, returnQtyOnHand } for (const row of storages) { const qty = Number(row?.QtyOnHand || 0) const locator = row?.M_Locator_ID const locatorId = locator?.id ?? locator const isReturn = locatorId !== undefined && locatorId !== null && returnLocatorIds.has(Number(locatorId)) if (isReturn) { returnQtyOnHand += qty } else { qtyOnHand += qty } } return { qtyOnHand, returnQtyOnHand } } const handleFunc = async (event: any, authToken: any = null) => { const token = authToken ?? await getTokenHelper(event) const query = getQuery(event) const jfsku = (query?.jfsku as string)?.trim() const merchantSku = (query?.merchantSku as string)?.trim() if(!jfsku && !merchantSku) { return { status: 400, message: 'jfsku or merchantSku is required' } } try { let product: any = null let storages: any[] = [] const productSelect = string.urlEncode('m_product_id,Name,SKU,jfsku,AD_Org_ID') const storageExpand = string.urlEncode('M_Storage($expand=M_Locator_ID)') if(jfsku) { const escaped = jfsku.replace(/'/g, "''") const filter = string.urlEncode(`jfsku eq '${escaped}'`) const res: any = await fetchHelper( event, `models/m_product?$select=${productSelect}&$expand=${storageExpand}&$filter=${filter}`, 'GET', token, null ) if(res?.records?.length) { product = res.records[0] storages = product?.M_Storage || [] } // Last-resort fallback: some installations used jsfsku column; try only if strict jfsku failed if(!product) { const filter2 = string.urlEncode(`jsfsku eq '${escaped}'`) const res2: any = await fetchHelper( event, `models/m_product?$select=${string.urlEncode('m_product_id,Name,SKU,jsfsku,AD_Org_ID')}&$expand=${storageExpand}&$filter=${filter2}`, 'GET', token, null ) if(res2?.records?.length) { product = res2.records[0] storages = product?.M_Storage || [] } } } // Fallback by merchantSku (SKU) if provided and nothing matched above if(!product && merchantSku) { const escapedSku = merchantSku.replace(/'/g, "''") const res3: any = await fetchHelper( event, `models/m_product?$select=${productSelect}&$expand=${storageExpand}&$filter=${string.urlEncode(`SKU eq '${escapedSku}'`)}`, 'GET', token, null ) if(res3?.records?.length) { product = res3.records[0] storages = product?.M_Storage || [] } } if(!product) { return { status: 404, message: 'Product not found', jfsku, merchantSku, qtyOnHand: 0, returnQtyOnHand: 0 } } const productOrgId = product?.AD_Org_ID?.id ?? product?.AD_Org_ID ?? null const returnLocatorIds = await getReturnLocatorIds(event, token, productOrgId) const { qtyOnHand, returnQtyOnHand } = sumSellableQty(storages, returnLocatorIds) return { status: 200, productId: Number(product?.m_product_id || product?.id), jfsku: product?.jfsku || jfsku || product?.jsfsku || null, merchantSku: product?.SKU || merchantSku || null, qtyOnHand, // excludes return-locator stock returnQtyOnHand // stock on return-only locators (informational) } } catch (err: any) { const data = errorHandlingHelper(err?.data ?? err, err?.data ?? err) return { status: Number(data?.status || 500), message: data?.message || 'Failed to get qty on hand' } } } export default defineEventHandler(async (event) => { try { return await handleFunc(event) } catch (err: any) { try { const authToken = await refreshTokenHelper(event) return await handleFunc(event, authToken) } catch (error: any) { const data = errorHandlingHelper(err?.data ?? err, error?.data ?? error) if([401, 402, 403, 407].includes(Number(data.status))) { //@ts-ignore setCookie(event, 'user', null) } return data } } })