import { string } from 'alga-js' import refreshTokenHelper from "../../utils/refreshTokenHelper" import errorHandlingHelper from "../../utils/errorHandlingHelper" import fetchHelper from "../../utils/fetchHelper" // GET /api/plentyone/products?orderSourceId=123&limit=50&offset=0&search= // Fetches local products for PlentyOne sync // The PlentyOne variation ID is stored in M_Product.SKU // Returns qty on hand filtered by the order source's M_Warehouse_ID const handleFunc = async (event: any, authToken: any = null) => { const token = authToken ?? await getTokenHelper(event) const query = getQuery(event) const orderSourceId = query.orderSourceId as string const limit = Number(query.limit) || 50 const offset = Number(query.offset) || 0 const search = (query.search as string)?.trim() || '' if(!orderSourceId) { return { status: 400, message: 'orderSourceId is required' } } // Load order source to verify it's PlentyOne and get warehouse info const os: any = await fetchHelper(event, `models/c_ordersource/${orderSourceId}`, 'GET', token, null) if(!os) { return { status: 404, message: 'Order Source not found' } } if(!(os?.Marketplace?.id === '6' || os?.Marketplace?.identifier === 'plentyone')) { return { status: 400, message: 'Selected Order Source is not PlentyOne' } } // Get the local iDempiere warehouse ID from order source const localWarehouseId = os?.M_Warehouse_ID?.id // Get PlentyOne warehouse ID for sync const plentyOneWarehouseId = os?.PlentyOne_Warehouse_ID || 102 // Get organization ID from order source const orgId = os?.AD_Org_ID?.id try { // Build filter for products - fetch all products for the organization // MPN filtering will be done in the frontend let filterParts = [] // Filter by organization if(orgId) { filterParts.push(`AD_Org_ID eq ${orgId}`) } if(search) { const escapedSearch = search.replace(/'/g, "''") filterParts.push(`(contains(mpn,'${escapedSearch}') or contains(Name,'${escapedSearch}') or contains(SKU,'${escapedSearch}'))`) } const filter = filterParts.length > 0 ? string.urlEncode(filterParts.join(' and ')) : '' const select = string.urlEncode('m_product_id,Name,SKU,mpn') // Fetch products with SKU (PlentyOne variation ID) // Expand M_Storage to get qty on hand per locator let url = `models/m_product?$select=${select}&$expand=${string.urlEncode('M_Storage($expand=M_Locator_ID)')}&$top=${limit}&$skip=${offset}&$orderby=${string.urlEncode('Name asc')}` if(filter) { url += `&$filter=${filter}` } const res: any = await fetchHelper( event, url, 'GET', token, null ) if(!res?.records) { return { status: 200, records: [], _page: { total: 0, limit, offset }, warehouseId: localWarehouseId, plentyOneWarehouseId } } // Process all records - MPN filtering will be done in the frontend const records = res.records.map((p: any) => { const storages = p?.M_Storage || [] let qtyOnHand = 0 let qtyOnHandAll = 0 if(Array.isArray(storages)) { for(const storage of storages) { const qty = Number(storage?.QtyOnHand || 0) qtyOnHandAll += qty // Filter by warehouse if localWarehouseId is set if(localWarehouseId) { const locator = storage?.M_Locator_ID const storageWarehouseId = locator?.M_Warehouse_ID?.id || locator?.m_warehouse_id if(String(storageWarehouseId) === String(localWarehouseId)) { qtyOnHand += qty } } else { // If no warehouse filter, sum all qtyOnHand += qty } } } return { id: Number(p.m_product_id || p.id), name: p.Name || '', sku: p.SKU || '', mpn: p.mpn || '', plentyOneVariationId: p.mpn || null, // MPN is the PlentyOne variation ID qtyOnHand, // Filtered by warehouse qtyOnHandAll, // Total across all warehouses (for reference) plentyOneStock: null // Will be fetched separately from PlentyOne } }) return { status: 200, records, _page: { total: res['@odata.count'] ?? records.length, limit, offset }, warehouseId: localWarehouseId, warehouseName: os?.M_Warehouse_ID?.identifier || null, plentyOneWarehouseId } } catch (err: any) { const data = errorHandlingHelper(err?.data ?? err, err?.data ?? err) return { status: Number(data?.status || 500), message: data?.message || 'Failed to fetch products' } } } 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 } } })