import { string } from 'alga-js' import refreshTokenHelper from "../../../utils/refreshTokenHelper" import getTokenHelper from "../../../utils/getTokenHelper" import forceLogoutHelper from "../../../utils/forceLogoutHelper" import errorHandlingHelper from "../../../utils/errorHandlingHelper" import fetchHelper from "../../../utils/fetchHelper" import getReturnLocatorIds from "../../../utils/returnLocatorHelper" // Full per-product detail for the Qty-Fix tool. // - `locators` : the MOVABLE on-hand stock (qty > 0, excluding return // locators) — these are the lines that get zeroed/added. // - `returnQtyOnHand` : return-locator stock, reported only (never moved). // - `availableLocators` : every active locator in the org (default first) so the // target side can pick where the qty should land. const handleFunc = async (event: any, authToken: any = null) => { const token = authToken ?? await getTokenHelper(event) const query = getQuery(event) const productId = query?.product_id as string const orgId = query?.org_id as string if (!productId || !orgId) { return { status: 400, message: 'product_id and org_id are required' } } const prodRes: any = await fetchHelper(event, `models/m_product/${productId}`, 'GET', token, null) if (!prodRes || !prodRes.id) { return { status: 404, message: 'Product not found' } } const returnLocatorIds = await getReturnLocatorIds(event, token, orgId) const storagesRes: any = await fetchHelper( event, `models/m_storage?$filter=${string.urlEncode('M_Product_ID eq ' + productId)}&$expand=M_Locator_ID&$top=1000`, 'GET', token, null ) const storages = Array.isArray(storagesRes?.records) ? storagesRes.records : [] const locatorMap: Record = {} let qtyOnHand = 0 let returnQtyOnHand = 0 for (const s of storages) { const qty = Number(s?.QtyOnHand ?? 0) if (!qty) continue const loc = s?.M_Locator_ID || {} const locId = loc?.id ?? loc const isReturn = locId !== undefined && locId !== null && returnLocatorIds.has(Number(locId)) if (isReturn) { // Return stock is reported but NOT part of the movable on-hand list. returnQtyOnHand += qty continue } qtyOnHand += qty const whId = loc?.M_Warehouse_ID?.id ?? loc?.M_Warehouse_ID ?? null const code = (loc?.X || loc?.Y || loc?.Z) ? [loc.X, loc.Y, loc.Z].filter(Boolean).join(' - ') : (loc?.Value || loc?.identifier || String(locId)) if (!locatorMap[locId]) { locatorMap[locId] = { locatorId: locId, code, warehouseId: whId, qtyOnHand: 0 } } locatorMap[locId].qtyOnHand += qty } const locators = Object.values(locatorMap).sort((a: any, b: any) => b.qtyOnHand - a.qtyOnHand) const availRes: any = await fetchHelper( event, `models/m_locator?$filter=${string.urlEncode('AD_Org_ID eq ' + orgId + ' AND IsActive eq true')}&$orderby=${string.urlEncode('IsDefault desc, PriorityNo desc')}&$top=5000`, 'GET', token, null ) const availableLocators = (availRes?.records || []).map((loc: any) => ({ id: loc.id, code: (loc.X || loc.Y || loc.Z) ? [loc.X, loc.Y, loc.Z].filter(Boolean).join(' - ') : (loc.Value || loc.identifier || loc.Name || String(loc.id)), warehouseId: loc.M_Warehouse_ID?.id ?? loc.M_Warehouse_ID ?? null, isDefault: loc.IsDefault === true || loc.IsDefault === 'Y', priorityNo: loc.PriorityNo ?? null, isReturn: returnLocatorIds.has(Number(loc.id)) })) return { status: 200, product: { id: prodRes.id, Name: prodRes.Name, Value: prodRes.Value, SKU: prodRes.SKU, UPC: prodRes.UPC, MPN: prodRes.mpn || null, isJtlBom: (prodRes.isJtlBom ?? prodRes.IsJtlBom) === true || (prodRes.isJtlBom ?? prodRes.IsJtlBom) === 'Y' }, qtyOnHand, returnQtyOnHand, locators, availableLocators } } export default defineEventHandler(async (event) => { let data: any = {} try { data = await handleFunc(event) } catch (err: any) { try { let authToken: any = await refreshTokenHelper(event) data = await handleFunc(event, authToken) } catch (error: any) { data = errorHandlingHelper(err?.data ?? err, error?.data ?? error) forceLogoutHelper(event, data) } } return data })