/** * Per-merchant "pick product identifier" — which m_product code the pickers of a * fulfillment partner match against (Value / UPC / SKU / MPN / ASIN). * * Source of truth is the List column **C_BPartner.PickProductIdentifier** * (reference "LogShip Product Identifier", keys: value, upc, sku, mpn, asin) on the * partner LINKED TO THE ORG (`ad_org.C_BPartner_ID`) — the merchant, not the * order's customer. A picklist mixes lines of several orgs, so the setting is * resolved per line org and attached to the line as `pickIdentifier` * (see server/api/processes/inouts/[id]/index.get.ts). * * Cached per org (10-min TTL, stale entry served when a refresh fails) and * fail-soft: any error resolves to null = "not configured", never breaks a picklist. */ import fetchHelper from './fetchHelper' export const PICK_IDENTIFIER_KEYS = ['value', 'upc', 'sku', 'mpn', 'asin'] const TTL_MS = 10 * 60 * 1000 const cache = new Map() // iDempiere returns a List column as { id: 'upc', identifier: 'UPC / EAN' }; a plain // GET may return the bare string. NULL columns are omitted entirely. export const normalizePickIdentifier = (raw: any): string | null => { const code = (raw && typeof raw === 'object') ? (raw.id ?? raw.identifier ?? '') : (raw ?? '') const key = code.toString().trim().toLowerCase() return PICK_IDENTIFIER_KEYS.includes(key) ? key : null } export const getOrgPickIdentifier = async (event: any, token: any, orgId: any): Promise => { const id = Number(orgId) if (!Number.isFinite(id) || id <= 0) return null const hit = cache.get(id) if (hit && Date.now() - hit.at < TTL_MS) return hit.key try { // To-one FK expand (not a child table) — one cheap call per org. const res: any = await fetchHelper( event, `models/ad_org/${id}?$select=C_BPartner_ID&$expand=C_BPartner_ID($select=PickProductIdentifier)`, 'GET', token, null ) const bp = res?.C_BPartner_ID const key = normalizePickIdentifier(bp?.PickProductIdentifier ?? bp?.pickProductIdentifier) cache.set(id, { key, at: Date.now() }) return key } catch (e: any) { console.error(`[PickIdentifier] org ${id}:`, e?.data ?? e?.message ?? e) return hit ? hit.key : null } } // Called by the partner update route so a changed setting shows up on the next picklist. export const invalidatePickIdentifierCache = () => cache.clear()