/** * Lead qualification: expected monthly order volume (AD_User.MonthlyOrders, custom * integer column). Website forms deliver it as a range ("501-2500"), a plain number * (FluentForm range slider) or free text ("> 1.000", "bis 500", "2.500+") — this turns * any of those into one integer, or null when nothing usable is in the value. * * Ranges save their MIDPOINT (0-500 → 250, 501-2500 → 1500) so the list stays sortable * by a representative number; the raw range text stays in the lead's Comments. */ const toInt = (s: string): number => Number(String(s).replace(/[.\s,']/g, '')) export const parseMonthlyOrders = (raw: any): number | null => { if (raw === null || raw === undefined) return null if (typeof raw === 'number') return Number.isFinite(raw) && raw > 0 ? Math.round(raw) : null const s = String(raw).trim().toLowerCase() if (!s) return null const num = '(\\d[\\d.,\\s\']*)' let m = s.match(new RegExp(`^${num}\\s*(?:-|–|—|bis|to|a)\\s*${num}$`)) if (m) { const lo = toInt(m[1]); const hi = toInt(m[2]) if (Number.isFinite(lo) && Number.isFinite(hi) && hi >= lo) return Math.round((lo + hi) / 2) || null } m = s.match(new RegExp(`^(?:>|>=|ab|über|ueber|more than|over|mehr als|from)\\s*${num}\\+?$`)) || s.match(new RegExp(`^${num}\\s*\\+$`)) if (m) { const n = toInt(m[1]); return Number.isFinite(n) && n > 0 ? n : null } m = s.match(new RegExp(`^(?:<|<=|bis|bis zu|up to|under|unter|less than|max\\.?)\\s*${num}$`)) if (m) { const n = toInt(m[1]); return Number.isFinite(n) && n > 0 ? n : null } m = s.match(new RegExp(`^${num}$`)) if (m) { const n = toInt(m[1]); return Number.isFinite(n) && n > 0 ? n : null } return null } /** * POST the lead with the optional qualification extras (monthlyOrders, shopSystem, * avgParcelWeight, …) and when iDempiere rejects the record while extras were included * retry WITHOUT them — a website inquiry must never be lost because of a qualification * column. Empty / null extras are dropped before the post. */ export const createLeadWithExtras = async (post: (body: any) => Promise, body: any, extras: Record, tag = 'leads') => { const clean = Object.fromEntries(Object.entries(extras || {}).filter(([, v]) => v !== null && v !== undefined && String(v).trim() !== '')) if (!Object.keys(clean).length) return post(body) try { return await post({ ...body, ...clean }) } catch (err: any) { console.error(`[${tag}] create with qualification extras failed — retrying without:`, Object.keys(clean).join(','), err?.data?.detail ?? err?.data?.title ?? err?.message ?? err) return post(body) } }