import refreshTokenHelper from "../../../utils/refreshTokenHelper" import errorHandlingHelper from "../../../utils/errorHandlingHelper" import fetchHelper from "../../../utils/fetchHelper" // POST /api/plentyone/stocks/sync // Syncs local qty on hand to PlentyOne directly via REST API // Body: { orderSourceId, variationId, quantity, sku?, productId?, itemId? } // // PlentyOne Stock Correction REST API (working endpoint): // PUT /rest/items/{itemId}/variations/{variationId}/stock/correction const handleFunc = async (event: any, authToken: any = null) => { const token = authToken ?? await getTokenHelper(event) const body = await readBody(event) const { orderSourceId, variationId, quantity, sku, productId } = body // variationId = SKU (numeric ID) const effectiveVariationId = variationId || sku if(!orderSourceId) { return { status: 400, message: 'orderSourceId is required', success: false } } if(!effectiveVariationId) { return { status: 400, message: 'variationId (SKU) is required', success: false } } if(quantity === undefined || quantity === null) { return { status: 400, message: 'quantity is required', success: false } } // Load order source to verify it's PlentyOne and get credentials const os: any = await fetchHelper(event, `models/c_ordersource/${orderSourceId}`, 'GET', token, null) if(!os) { return { status: 404, message: 'Order Source not found', success: false } } if(!(os?.Marketplace?.id === '6' || os?.Marketplace?.identifier === 'plentyone')) { return { status: 400, message: 'Selected Order Source is not PlentyOne', success: false } } // Get PlentyOne credentials from order source const baseUrl = os?.marketplace_url || os?.SHOPWARE_URL || '' const clientId = os?.marketplace_key || '' const clientSecret = os?.marketplace_secret || '' if(!baseUrl) { return { status: 400, message: 'No marketplace_url configured for this Order Source', success: false } } if(!clientId || !clientSecret) { return { status: 400, message: 'No marketplace_key or marketplace_secret configured for this Order Source', success: false } } // Get PlentyOne warehouse ID (default: 102) const warehouseId = os?.PlentyOne_Warehouse_ID || 102 try { // Normalize base URL let host = baseUrl.trim() if(!/^https?:\/\//i.test(host)) host = `https://${host}` host = host.replace(/\/$/, '') // Remove /rest suffix if present const baseHost = host.replace(/\/rest\/?$/, '') const apiBase = `${baseHost}/rest` // Get OAuth access token from PlentyOne const tokenUrl = `${baseHost}/rest/login` let accessToken = '' try { const tokenRes: any = await $fetch(tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: { username: clientId, password: clientSecret } }) accessToken = tokenRes?.accessToken || tokenRes?.access_token || '' } catch (tokenErr: any) { console.error('PlentyOne OAuth error:', tokenErr?.data || tokenErr?.message) return { status: 401, message: 'Failed to authenticate with PlentyOne. Check marketplace_key and marketplace_secret.', success: false } } if(!accessToken) { return { status: 401, message: 'No access token received from PlentyOne', success: false } } const authHeaders = { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', 'Accept': 'application/json' } const numericVariationId = Number(effectiveVariationId) const targetQty = Number(quantity) // First, get the itemId for this variation from PlentyOne // We need itemId for the working endpoint: PUT /rest/items/{itemId}/variations/{variationId}/stock/correction let itemId = body.itemId ? Number(body.itemId) : null if(!itemId) { // Fetch variation to get itemId try { const variationUrl = `${apiBase}/items/variations/${numericVariationId}` const variationRes: any = await $fetch(variationUrl, { method: 'GET', headers: authHeaders }) itemId = variationRes?.itemId || variationRes?.item_id console.log('PlentyOne variation lookup:', { variationId: numericVariationId, itemId }) } catch (varErr: any) { console.error('Failed to fetch variation itemId:', varErr?.message) // Try alternative: get from stock endpoint try { const stockUrl = `${apiBase}/stockmanagement/stock?variationId=${numericVariationId}&warehouseId=${warehouseId}` const stockRes: any = await $fetch(stockUrl, { method: 'GET', headers: authHeaders }) if(stockRes?.entries?.[0]?.itemId) { itemId = stockRes.entries[0].itemId console.log('PlentyOne itemId from stock:', itemId) } } catch (stockErr: any) { console.error('Failed to fetch itemId from stock:', stockErr?.message) } } } if(!itemId) { return { status: 400, success: false, message: 'Could not determine itemId for this variation. Please provide itemId in the request.', variationId: numericVariationId } } // Stock correction endpoint (working endpoint discovered via testing) // PUT /rest/items/{itemId}/variations/{variationId}/stock/correction const correctionUrl = `${apiBase}/items/${itemId}/variations/${numericVariationId}/stock/correction` const correctionBody = { warehouseId: warehouseId, quantity: targetQty, storageLocationId: 0 } console.log('PlentyOne stock correction request:', { url: correctionUrl, body: correctionBody }) const result: any = await $fetch(correctionUrl, { method: 'PUT', headers: authHeaders, body: correctionBody }) console.log('PlentyOne stock correction response:', JSON.stringify(result).slice(0, 800)) // Find this variation's updated stock in the response let updatedStock = null if(Array.isArray(result)) { const thisVariation = result.find((r: any) => Number(r.variationId) === numericVariationId && Number(r.warehouseId) === Number(warehouseId) ) if(thisVariation) { updatedStock = thisVariation.physicalStock ?? thisVariation.stockPhysical } } return { status: 200, success: true, message: `Stock corrected to ${updatedStock ?? targetQty} for variation ${numericVariationId} in warehouse ${warehouseId}`, variationId: numericVariationId, itemId, quantity: targetQty, updatedStock, warehouseId, plentyOneResponse: result } } catch (err: any) { const errData = err?.data || err const errMsg = errData?.message || errData?.error?.message || err?.message || 'Failed to sync stock to PlentyOne' const errStatus = err?.status || err?.response?.status || errData?.status || 500 // Log detailed error for debugging console.error('PlentyOne stock sync error:', { variationId: effectiveVariationId, quantity, warehouseId, error: errData }) return { status: errStatus, success: false, message: errMsg, error: errData, variationId: effectiveVariationId } } } 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, success: false } } } })