import refreshTokenHelper from '../../utils/refreshTokenHelper' import fetchHelper from '../../utils/fetchHelper' // iDempiere REST expects ISO-8601 with explicit timezone for timestamptz // columns — e.g. `2026-04-14T12:09:03Z` (no milliseconds). const toIDempiereTs = (iso: any): string | undefined => { if (!iso) return undefined try { const d = new Date(String(iso)) if (Number.isNaN(d.getTime())) return undefined return d.toISOString().replace(/\.\d{3}Z$/, 'Z') } catch { return undefined } } const writeOne = async (event: any, e: any, token: string, adOrgId: number) => { const body: Record = { AD_Org_ID: { id: adOrgId, tableName: 'AD_Org' }, sessionUID: e.sessionUID, actionType: e.actionType, isActive: true, tableName: 'CUST_CommissionActivity', } const actionTime = toIDempiereTs(e.actionTime) if (actionTime) body.actionTime = actionTime // Only include FK keys when we actually have an id — iDempiere rejects // explicit null for any FK column marked Mandatory, and treats "key missing" // as "leave blank" for non-mandatory ones. if (e.adUserId) body.AD_User_ID = { id: Number(e.adUserId), tableName: 'AD_User' } if (e.mInoutId) body.M_InOut_ID = { id: Number(e.mInoutId), tableName: 'M_InOut' } if (e.cOrderId) body.C_Order_ID = { id: Number(e.cOrderId), tableName: 'C_Order' } if (e.mProductId) body.M_Product_ID = { id: Number(e.mProductId), tableName: 'M_Product' } if (e.cBPartnerId) body.C_BPartner_ID = { id: Number(e.cBPartnerId), tableName: 'C_BPartner' } if (e.foreignAdOrgId) body.Foreign_AD_Org_ID = { id: Number(e.foreignAdOrgId), tableName: 'AD_Org' } if (e.qty != null) body.qty = e.qty if (e.durationMs != null) body.durationMs = e.durationMs if (e.shippingService) body.shippingService = e.shippingService if (e.description) body.description = e.description return fetchHelper(event, 'models/cust_commissionactivity', 'POST', token, body) } const isAuthError = (reason: any): boolean => { const code = String(reason?.statusCode ?? reason?.response?.status ?? '') return code === '401' || code === '403' } const handleBatch = async (event: any, events: any[]) => { let token: string | null = null try { token = await getTokenHelper(event) } catch { return } const adOrgId = Number(getCookie(event, 'logship_organization_id')) if (!token || !adOrgId) return const results = await Promise.allSettled(events.map((e) => writeOne(event, e, token as string, adOrgId))) const needsRefresh = results.some((r) => r.status === 'rejected' && isAuthError((r as any).reason)) const firstFailure = results.find((r) => r.status === 'rejected') as PromiseRejectedResult | undefined if (firstFailure && !needsRefresh) { const reason: any = firstFailure.reason console.error('[commission-activity] iDempiere write rejected:', { statusCode: reason?.statusCode ?? reason?.response?.status, message: reason?.message, data: reason?.data, }) } if (!needsRefresh) return try { const fresh: any = await refreshTokenHelper(event) if (!fresh) return const retry = await Promise.allSettled(events.map((e) => writeOne(event, e, fresh, adOrgId))) const retryFailure = retry.find((r) => r.status === 'rejected') as PromiseRejectedResult | undefined if (retryFailure) { const reason: any = retryFailure.reason console.error('[commission-activity] iDempiere write rejected after token refresh:', { statusCode: reason?.statusCode ?? reason?.response?.status, message: reason?.message, data: reason?.data, }) } } catch (e: any) { console.error('[commission-activity] token refresh failed:', e?.message) } } export default defineEventHandler(async (event) => { let body: any = null try { body = await readBody(event) } catch { body = null } const events: any[] = Array.isArray(body?.events) ? body.events : [] if (events.length) { handleBatch(event, events).catch(() => {}) } setResponseStatus(event, 204) return null })