import refreshTokenHelper from "../../../../utils/refreshTokenHelper" import forceLogoutHelper from "../../../../utils/forceLogoutHelper" import errorHandlingHelper from "../../../../utils/errorHandlingHelper" import fetchHelper from "../../../../utils/fetchHelper" // Bulk upsert of cross-dock allocations (CUST_CrossDockAlloc) for ONE PO↔SO pair. // Called by the board's allocation confirm dialog. // // Body: { // poOrderId, soOrderId, // rows: [{ id?, poOrderLineId, soOrderLineId, qty }] // } // Per row: id + qty>0 → update qty; id + qty<=0 → delete; no id + qty>0 → create. // Every row is validated against the two orders' real lines (line belongs to the // order, products match) so the client can never persist a mismatched pair. const handleFunc = async (event: any, authToken: any = null) => { const token = authToken ?? await getTokenHelper(event) const body = await readBody(event) const organizationId = getCookie(event, 'logship_organization_id') const poOrderId = Number(body.poOrderId) const soOrderId = Number(body.soOrderId) const rows: any[] = Array.isArray(body.rows) ? body.rows : [] if (!poOrderId || !soOrderId) { return { status: 400, message: 'poOrderId and soOrderId are required' } } // One lines fetch per order — used to validate every row in memory. const [poLinesRes, soLinesRes]: any[] = await Promise.all([ fetchHelper(event, `models/c_orderline?$filter=C_Order_ID eq ${poOrderId}&$select=M_Product_ID`, 'GET', token, null), fetchHelper(event, `models/c_orderline?$filter=C_Order_ID eq ${soOrderId}&$select=M_Product_ID`, 'GET', token, null) ]) const productByLine = (res: any) => { const map: Record = {} for (const r of (res?.records ?? [])) map[Number(r.id)] = Number(r?.M_Product_ID?.id ?? 0) return map } const poLines = productByLine(poLinesRes) const soLines = productByLine(soLinesRes) const results: any[] = [] const errors: any[] = [] for (const row of rows) { const poLineId = Number(row.poOrderLineId) const soLineId = Number(row.soOrderLineId) const qty = Number(row.qty) const existingId = row.id ? Number(row.id) : null if (!(poLineId in poLines) || !(soLineId in soLines)) { errors.push({ poLineId, soLineId, message: 'line does not belong to the given order' }) continue } if (poLines[poLineId] !== soLines[soLineId] || !poLines[poLineId]) { errors.push({ poLineId, soLineId, message: 'products of PO line and SO line do not match' }) continue } try { if (existingId && qty > 0) { const res: any = await fetchHelper(event, 'models/cust_crossdockalloc/' + existingId, 'PUT', token, { qtyAllocated: qty, tableName: 'CUST_CrossDockAlloc' }) results.push({ id: existingId, action: 'updated', qty, res: res?.id }) } else if (existingId && qty <= 0) { await fetchHelper(event, 'models/cust_crossdockalloc/' + existingId, 'DELETE', token, null) results.push({ id: existingId, action: 'deleted' }) } else if (!existingId && qty > 0) { const res: any = await fetchHelper(event, 'models/cust_crossdockalloc', 'POST', token, { AD_Org_ID: { id: Number(organizationId), tableName: 'AD_Org' }, PO_OrderLine_ID: { id: poLineId, tableName: 'C_OrderLine' }, SO_OrderLine_ID: { id: soLineId, tableName: 'C_OrderLine' }, isActive: true, qtyAllocated: qty, description: row.description ?? '', tableName: 'CUST_CrossDockAlloc' }) results.push({ id: res?.id, action: 'created', qty }) } // no id + qty<=0 → nothing to do } catch (err: any) { errors.push({ poLineId, soLineId, message: err?.data?.detail ?? err?.message ?? 'save failed' }) } } return { status: 200, message: '', results, errors } } 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 })