/* ════════════════════════════════════════════════════════════════ QIP Suicide Prevention Accreditation Program Registration qip-suicide-pre-acc-prgm-registration-script.js Architecture identical to AGPAL / ADA: 1. waitForToken / dataversePost / fetchLeadRef / spinner 2. On "Proceed to Pay": create Lead → Contact → PaymentEvidence → redirect to shared /agpal-stripe-checkout 3. Stripe return handled by shared /agpal-stripe-return — no changes. Fee structure (from portalContext / Content Snippets): spap/base-fee 1876 spap/additional-site-fee 300 spap/program-fee 150 agpal/cc-surcharge-rate 0.01927 agpal/gst-rate 0.10 Lead subject = page H1 title: "QIP Suicide Prevention Accreditation Program Registration – {orgName}" ════════════════════════════════════════════════════════════════ */ (function () { 'use strict'; var SPAP = { /* ── State ────────────────────────────────────────────────── */ currentPageKey: '1', pageStructure: [], additionalSiteCount: 0, programCount: 0, sigCtx: null, sigDrawing: false, isBusy: false, leadId: null, leadRef: null, paymentEvidenceId: null, LABELS: { '1': 'Contact', '2': 'Organisation', '3': 'Address', 'site1': 'Site 2', 'site2': 'Site 3', 'site3': 'Site 4', 'site4': 'Site 5', 'prog1': 'Program 1', 'prog2': 'Program 2', 'prog3': 'Program 3', 'prog4': 'Program 4', 'prog5': 'Program 5', 'fee': 'Fee', 'submit': 'Submit' }, ORDINALS: ['FIRST', 'SECOND', 'THIRD', 'FOURTH'], /* sessionStorage keys — distinct from AGPAL / ADA keys */ leadIdKey: 'spap_registration_lead_id', leadRefKey: 'spap_registration_lead_ref', evidenceIdKey: 'spap_registration_evidence_id', paymentInProgressKey: 'spap_payment_in_progress', /* ── Config (from portalContext, populated by Liquid) ─────── */ config: { baseFee: parseFloat((window.portalContext || {}).spapBaseFee) || 1876, additionalSiteFee: parseFloat((window.portalContext || {}).spapAdditionalSiteFee) || 300, programFee: parseFloat((window.portalContext || {}).spapProgramFee) || 150, ccSurchargeRate: parseFloat((window.portalContext || {}).ccSurchargeRate) || 0.01927, gstRate: parseFloat((window.portalContext || {}).gstRate) || 0.10, contactTypeId: (window.portalContext || {}).contactTypeId || '' } }; /* ════════════════════════════════════════════════════════════ SPINNER ════════════════════════════════════════════════════════════ */ function injectSpinner() { if (document.getElementById('spap-spinner-overlay')) return; var style = document.createElement('style'); style.textContent = '@keyframes spap-spin{to{transform:rotate(360deg)}}' + '#spap-spinner-overlay{display:none;position:fixed;inset:0;background:rgba(0,53,84,0.65);' + 'z-index:99999;align-items:center;justify-content:center;flex-direction:column;gap:18px;}'; document.head.appendChild(style); var overlay = document.createElement('div'); overlay.id = 'spap-spinner-overlay'; overlay.setAttribute('aria-live', 'assertive'); overlay.innerHTML = '
' + '
' + 'Please wait\u2026
'; document.body.appendChild(overlay); } function setBusy(busy, msg) { SPAP.isBusy = busy; var overlay = document.getElementById('spap-spinner-overlay'); var msgEl = document.getElementById('spap-spinner-msg'); if (overlay) overlay.style.display = busy ? 'flex' : 'none'; if (msgEl && msg) msgEl.textContent = msg; if (msgEl && !busy) msgEl.textContent = 'Please wait\u2026'; document.querySelectorAll('button').forEach(function (btn) { if (busy) { btn.setAttribute('data-was-disabled', btn.disabled ? '1' : '0'); btn.disabled = true; } else { btn.disabled = btn.getAttribute('data-was-disabled') === '1'; } }); } /* ════════════════════════════════════════════════════════════ CSRF TOKEN ════════════════════════════════════════════════════════════ */ function waitForToken() { return new Promise(function (resolve, reject) { var pc = window.portalContext || {}; var cached = pc.csrfToken || pc.requestVerificationToken; if (cached && cached.length > 20) { resolve(cached); return; } var domEl = document.querySelector('input[name="__RequestVerificationToken"]'); if (domEl && domEl.value) { resolve(domEl.value); return; } var csrfEl = document.getElementById('csrf-token-value'); if (csrfEl && csrfEl.value && csrfEl.value.length > 20) { resolve(csrfEl.value); return; } var attempts = 0; var poll = setInterval(function () { attempts++; var pv = (window.portalContext || {}).csrfToken; if (pv && pv.length > 20) { clearInterval(poll); resolve(pv); return; } var el = document.getElementById('csrf-token-value'); if (el && el.value && el.value.length > 20) { clearInterval(poll); resolve(el.value); return; } if (attempts >= 30) { clearInterval(poll); fetch('/Account/Login', { credentials: 'same-origin', headers: { 'Accept': 'text/html' } }) .then(function (r) { return r.text(); }) .then(function (html) { var m = html.match(/name="__RequestVerificationToken"[^>]*value="([^"]+)"/); if (!m) m = html.match(/value="([^"]{40,})"[^>]*name="__RequestVerificationToken"/); if (m && m[1]) { resolve(m[1]); } else { reject(new Error('CSRF token not found')); } }) .catch(function (e) { reject(new Error('CSRF fetch failed: ' + e.message)); }); } }, 150); }); } /* ════════════════════════════════════════════════════════════ DATAVERSE API ════════════════════════════════════════════════════════════ */ function extractGuid(data, xhrOrResponse) { if (data && typeof data === 'object') { var fromBody = data.leadid || data.contactid || data.ongc_paymentevidenceid; if (!fromBody) { var odataId = (data['@odata.id']) || ''; var mo = odataId.match(/\(([0-9a-f-]{36})\)/i); if (mo) fromBody = mo[1]; } if (fromBody) return fromBody; } var headerVal = ''; if (xhrOrResponse) { if (typeof xhrOrResponse.headers === 'object' && typeof xhrOrResponse.headers.get === 'function') { headerVal = xhrOrResponse.headers.get('OData-EntityId') || ''; } if (!headerVal && typeof xhrOrResponse.getResponseHeader === 'function') { headerVal = xhrOrResponse.getResponseHeader('OData-EntityId') || ''; } } if (headerVal) { var m = headerVal.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i); if (m) return m[0]; } return null; } function parseApiError(x) { try { if (x.responseJSON && x.responseJSON.error) return x.responseJSON.error.message; } catch (e) { } try { var p = JSON.parse(x.responseText); return (p && p.error && p.error.message) || 'Unknown error'; } catch (e) { } return 'Unknown error'; } async function dataversePost(entitySet, payload) { var url = '/_api/' + entitySet; if (window.webapi && typeof window.webapi.safeAjax === 'function') { return new Promise(function (resolve, reject) { window.webapi.safeAjax({ type: 'POST', url: url, contentType: 'application/json', data: JSON.stringify(payload), success: function (data, s, xhr) { resolve({ id: extractGuid(data, xhr), data: data || {} }); }, error: function (xhr) { reject(new Error(parseApiError(xhr))); } }); }); } var token = await waitForToken(); var response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', '__RequestVerificationToken': token, 'OData-MaxVersion': '4.0', 'OData-Version': '4.0', 'Prefer': 'return=representation' }, body: JSON.stringify(payload) }); if (!response.ok) { var errMsg = 'API request failed'; try { var errJson = await response.json(); errMsg = (errJson && errJson.error && errJson.error.message) || errMsg; } catch (e) { } throw new Error(errMsg); } var data = {}; try { data = await response.json(); } catch (e) { } return { id: extractGuid(data, response), data: data }; } async function fetchLeadRef(leadId) { try { var token = await waitForToken(); var r = await fetch('/_api/leads(' + leadId + ')?$select=ongc_leadrefid,ongc_clientid', { method: 'GET', headers: { '__RequestVerificationToken': token, 'OData-MaxVersion': '4.0', 'OData-Version': '4.0', 'Accept': 'application/json' } }); if (!r.ok) { console.warn('[SPAP] fetchLeadRef HTTP ' + r.status); return null; } var d = await r.json(); var ref = (d && (d.ongc_leadrefid || d.ongc_clientid)) || null; console.log('[SPAP] fetchLeadRef:', ref); return ref; } catch (e) { console.warn('[SPAP] fetchLeadRef error:', e.message); return null; } } /* ════════════════════════════════════════════════════════════ SESSION STORAGE ════════════════════════════════════════════════════════════ */ function clearSession() { [SPAP.leadIdKey, SPAP.leadRefKey, SPAP.evidenceIdKey, SPAP.paymentInProgressKey].forEach(function (k) { try { sessionStorage.removeItem(k); } catch (e) { } }); SPAP.leadId = null; SPAP.leadRef = null; SPAP.paymentEvidenceId = null; } function saveSession() { try { if (SPAP.leadId) sessionStorage.setItem(SPAP.leadIdKey, SPAP.leadId); if (SPAP.leadRef) sessionStorage.setItem(SPAP.leadRefKey, SPAP.leadRef); if (SPAP.paymentEvidenceId) sessionStorage.setItem(SPAP.evidenceIdKey, SPAP.paymentEvidenceId); } catch (e) { } } /* ════════════════════════════════════════════════════════════ HELPERS ════════════════════════════════════════════════════════════ */ function round2(n) { return Math.round(n * 100) / 100; } function gVal(id) { var el = document.getElementById(id); return el ? el.value.trim() : ''; } function stripEmpty(payload) { Object.keys(payload).forEach(function (k) { if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k]; }); return payload; } /* ════════════════════════════════════════════════════════════ FEE CALCULATION ════════════════════════════════════════════════════════════ */ function getFeeBase() { return SPAP.config.baseFee + (SPAP.additionalSiteCount * SPAP.config.additionalSiteFee) + (SPAP.programCount * SPAP.config.programFee); } function calcFees() { var base = getFeeBase(); var surcharge = round2(base * SPAP.config.ccSurchargeRate); var gst = round2((base + surcharge) * SPAP.config.gstRate); var total = round2(base + surcharge + gst); return { base: round2(base), surcharge: surcharge, gst: gst, total: total }; } function spapCalculateFee() { var base = getFeeBase(); var fmt = base.toLocaleString('en-AU', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); var el = document.getElementById('spapFeeAmount'); if (el) el.textContent = fmt; /* Populate surcharge % display from portalContext — avoids Liquid math on string values */ var surEl = document.getElementById('spapSurchargeDisplay'); if (surEl) { var rate = parseFloat(SPAP.config.ccSurchargeRate) || 0.01927; var pct = (rate * 100).toFixed(3).replace(/\.?0+$/, ''); /* e.g. "1.927" */ surEl.textContent = pct; } } /* ════════════════════════════════════════════════════════════ PAGE STRUCTURE ════════════════════════════════════════════════════════════ */ function spapRebuildStructure() { var s = ['1', '2', '3']; for (var i = 1; i <= SPAP.additionalSiteCount; i++) s.push('site' + i); for (var j = 1; j <= SPAP.programCount; j++) s.push('prog' + j); s.push('fee'); s.push('submit'); SPAP.pageStructure = s; spapBuildProgress(); spapCalculateFee(); } /* ════════════════════════════════════════════════════════════ PROGRESS BAR ════════════════════════════════════════════════════════════ */ function spapBuildProgress() { var c = document.getElementById('spapProgressSteps'); if (!c) return; c.innerHTML = ''; var total = SPAP.pageStructure.length; var currIdx = SPAP.pageStructure.indexOf(SPAP.currentPageKey); SPAP.pageStructure.forEach(function (key, idx) { var isCurr = SPAP.currentPageKey === key; var isDone = currIdx > idx; var label = SPAP.LABELS[key] || key; var div = document.createElement('div'); div.className = 'spap-step-item' + (isCurr ? ' active' : '') + (isDone ? ' done' : ''); div.innerHTML = '
' + (isDone ? '✓' : (idx + 1)) + '
' + '
' + label + '
'; c.appendChild(div); }); var counter = document.getElementById('spapPageCounter'); if (counter) counter.textContent = 'Page ' + (currIdx + 1) + ' of ' + total; } /* ════════════════════════════════════════════════════════════ PAGE NAVIGATION ════════════════════════════════════════════════════════════ */ function spapShowByKey(key) { if (SPAP.isBusy) return; SPAP.currentPageKey = key; document.querySelectorAll('.spap-form-page').forEach(function (el) { el.classList.remove('active'); }); var target; if (key === 'fee') { target = document.getElementById('spapFeePage'); } else if (key === 'submit') { target = document.getElementById('spapSubmitPage'); } else if (key.indexOf('site') === 0 || key.indexOf('prog') === 0) { target = document.querySelector('.spap-form-page[data-dynkey="' + key + '"]'); } else { target = document.querySelector('.spap-form-page[data-page="' + key + '"]'); } if (target) { target.classList.add('active'); window.scrollTo({ top: 0, behavior: 'smooth' }); } spapBuildProgress(); if (key === 'submit') spapInitSig(); if (key === 'fee') spapCalculateFee(); } function spapNavNext(currentKey) { var idx = SPAP.pageStructure.indexOf(currentKey); if (idx >= 0 && idx < SPAP.pageStructure.length - 1) spapShowByKey(SPAP.pageStructure[idx + 1]); } function spapNavPrev(currentKey) { var idx = SPAP.pageStructure.indexOf(currentKey); if (idx > 0) spapShowByKey(SPAP.pageStructure[idx - 1]); } function spapGoToPage(n) { spapShowByKey(String(n)); } function spapPage3Next() { spapNavNext('3'); } function spapFeeNext() { spapNavNext('fee'); } function spapFeeBack() { spapNavPrev('fee'); } function spapSubmitBack() { spapNavPrev('submit'); } function spapSaveResume() { alert('Your progress has been saved. You can resume using the link sent to your registered email address.'); } /* ════════════════════════════════════════════════════════════ CONDITIONAL UI ════════════════════════════════════════════════════════════ */ function spapToggleSector() { var v = gVal('spapSector'); var fld = document.getElementById('spapSectorOtherField'); if (fld) fld.style.display = (v === 'other') ? 'block' : 'none'; } function spapTogglePostal(val) { var yesChk = document.getElementById('spapPostalYes'); var noChk = document.getElementById('spapPostalNo'); var block = document.getElementById('spapPostalBlock'); if (val === 'yes') { if (yesChk) yesChk.checked = true; if (noChk) noChk.checked = false; if (block) block.style.display = 'block'; } else { if (yesChk) yesChk.checked = false; if (noChk) noChk.checked = true; if (block) block.style.display = 'none'; } } /* ════════════════════════════════════════════════════════════ ADDITIONAL SITES HANDLER ════════════════════════════════════════════════════════════ */ function spapOnAdditionalSitesChange() { var v = gVal('spapAdditionalSites'); var hintEl = document.getElementById('spapSiteHint'); var over5El = document.getElementById('spapSiteOver5Msg'); if (hintEl) hintEl.style.display = 'none'; if (over5El) over5El.style.display = 'none'; spapRemoveDynPages('site'); SPAP.additionalSiteCount = 0; if (v === '0') { if (hintEl) hintEl.style.display = 'block'; } else if (v === '5plus') { if (hintEl) hintEl.style.display = 'block'; if (over5El) over5El.style.display = 'block'; } else if (v !== '') { var count = parseInt(v) || 0; if (hintEl) hintEl.style.display = 'block'; SPAP.additionalSiteCount = count; spapBuildSitePages(count); } spapRebuildStructure(); } function spapBuildSitePages(count) { var formShell = document.getElementById('spapFormShell'); var feePage = document.getElementById('spapFeePage'); var stateOpts = spapStateOptions(); for (var i = 1; i <= count; i++) { var key = 'site' + i; if (document.querySelector('.spap-form-page[data-dynkey="' + key + '"]')) continue; var ord = SPAP.ORDINALS[i - 1] || (i + 'TH'); var siteN = i + 1; var pg = document.createElement('div'); pg.className = 'spap-form-page'; pg.setAttribute('data-dynkey', key); pg.innerHTML = '
' + '
' + ord + ' ADDITIONAL SITE (SITE ' + siteN + ')
' + spapFieldWrap('Site ' + siteN + ' name', true, '') + spapFieldWrap('Site ' + siteN + ' street address', true, '' + '
Address Line 1
' + '' + '
' + '
City
' + '
State
' + '
Postcode
' + '
') + spapFieldWrap('Management level on site at Site ' + siteN, true, '') + spapFieldWrap('Site ' + siteN + ': Preferred Accreditation Contact Name', true, '
' + '
Prefix
' + '
First Name
' + '
Last Name
' + '
') + '
' + spapFieldWrap('Site ' + siteN + ': Contact Phone', true, '') + spapFieldWrap('Site ' + siteN + ': Contact Email', true, '') + '
' + '
' + '
' + '' + '
' + '' + '' + '
'; formShell.insertBefore(pg, feePage); } } /* ════════════════════════════════════════════════════════════ PROGRAM COUNT HANDLER ════════════════════════════════════════════════════════════ */ function spapOnProgramCountChange() { var v = gVal('spapProgramCount'); var over5El = document.getElementById('spapProgOver5Msg'); if (over5El) over5El.style.display = 'none'; spapRemoveDynPages('prog'); SPAP.programCount = 0; if (v === 'more') { if (over5El) over5El.style.display = 'block'; } else if (v !== '') { SPAP.programCount = parseInt(v) || 0; spapBuildProgramPages(SPAP.programCount); } spapRebuildStructure(); } function spapBuildProgramPages(count) { var formShell = document.getElementById('spapFormShell'); var feePage = document.getElementById('spapFeePage'); for (var i = 1; i <= count; i++) { var key = 'prog' + i; if (document.querySelector('.spap-form-page[data-dynkey="' + key + '"]')) continue; var pg = document.createElement('div'); pg.className = 'spap-form-page'; pg.setAttribute('data-dynkey', key); pg.innerHTML = '
' + '
Program ' + i + ' Details
' + spapFieldWrap('Program ' + i + ' name', false, '' + '
Please list each program name provided by your organisation in scope of Suicide Prevention
') + spapFieldWrap('Program ' + i + ' sites', false, '' + '
Please enter the site names this program is delivered to.
') + spapFieldWrap('FTE of staff delivering services under Program ' + i, false, '') + '
' + '
' + '' + '
' + '' + '' + '
'; formShell.insertBefore(pg, feePage); } } function spapRemoveDynPages(prefix) { document.querySelectorAll('.spap-form-page[data-dynkey^="' + prefix + '"]').forEach(function (el) { el.remove(); }); } /* ════════════════════════════════════════════════════════════ HTML HELPERS ════════════════════════════════════════════════════════════ */ function spapFieldWrap(label, required, inner) { return '
' + inner + '
'; } function spapStateOptions() { return '' + '' + '' + '' + ''; } function spapPrefixOptions() { return '' + '' + ''; } /* ════════════════════════════════════════════════════════════ SIGNATURE CANVAS ════════════════════════════════════════════════════════════ */ function spapInitSig() { var c = document.getElementById('spapSigCanvas'); if (!c) return; var fresh = c.cloneNode(true); c.parentNode.replaceChild(fresh, c); c = fresh; c.width = c.parentElement.clientWidth || 680; c.height = 110; SPAP.sigCtx = c.getContext('2d'); SPAP.sigCtx.strokeStyle = '#1A2B35'; SPAP.sigCtx.lineWidth = 2.2; SPAP.sigCtx.lineCap = 'round'; SPAP.sigCtx.lineJoin = 'round'; var ctx = SPAP.sigCtx; function pos(e) { var r = c.getBoundingClientRect(); var pt = e.touches ? e.touches[0] : e; return { x: pt.clientX - r.left, y: pt.clientY - r.top }; } c.addEventListener('mousedown', function (e) { SPAP.sigDrawing = true; ctx.beginPath(); var p = pos(e); ctx.moveTo(p.x, p.y); }); c.addEventListener('mousemove', function (e) { if (!SPAP.sigDrawing) return; var p = pos(e); ctx.lineTo(p.x, p.y); ctx.stroke(); }); c.addEventListener('mouseup', function () { SPAP.sigDrawing = false; }); c.addEventListener('mouseleave', function () { SPAP.sigDrawing = false; }); c.addEventListener('touchstart', function (e) { e.preventDefault(); SPAP.sigDrawing = true; ctx.beginPath(); var p = pos(e); ctx.moveTo(p.x, p.y); }, { passive: false }); c.addEventListener('touchmove', function (e) { e.preventDefault(); if (!SPAP.sigDrawing) return; var p = pos(e); ctx.lineTo(p.x, p.y); ctx.stroke(); }, { passive: false }); c.addEventListener('touchend', function () { SPAP.sigDrawing = false; }); } function spapClrSig() { var c = document.getElementById('spapSigCanvas'); if (SPAP.sigCtx && c) SPAP.sigCtx.clearRect(0, 0, c.width, c.height); } function spapIsSigEmpty() { var c = document.getElementById('spapSigCanvas'); if (!c || !SPAP.sigCtx) return true; var d = SPAP.sigCtx.getImageData(0, 0, c.width, c.height).data; for (var i = 3; i < d.length; i += 4) { if (d[i] > 0) return false; } return true; } /* ════════════════════════════════════════════════════════════ DATAVERSE PAYLOAD BUILDERS ════════════════════════════════════════════════════════════ */ function buildLeadPayload() { var orgName = gVal('spapOrgName'); var yesChk = document.getElementById('spapPostalYes'); var diffPostal = yesChk ? yesChk.checked : false; /* Collect programs summary for description */ var programs = []; for (var i = 1; i <= SPAP.programCount; i++) { programs.push({ name: gVal('spapP' + i + 'Name'), sites: gVal('spapP' + i + 'Sites'), fte: parseFloat(gVal('spapP' + i + 'FTE')) || 0 }); } /* Collect additional sites summary */ var sites = []; for (var s = 1; s <= SPAP.additionalSiteCount; s++) { sites.push({ name: gVal('spapSite' + s + 'Name'), city: gVal('spapSite' + s + 'City'), state: gVal('spapSite' + s + 'State'), postcode: gVal('spapSite' + s + 'Postcode') }); } var descSummary = 'Sector: ' + gVal('spapSector') + (gVal('spapSectorOther') ? ' (' + gVal('spapSectorOther') + ')' : '') + ' | Grant: ' + gVal('spapGrant') + ' | Mgmt Level: ' + gVal('spapManagementLevel') + ' | Programs (' + SPAP.programCount + '): ' + JSON.stringify(programs) + ' | Additional Sites (' + SPAP.additionalSiteCount + '): ' + JSON.stringify(sites); var payload = { /* Lead subject = page title */ subject: 'QIP Suicide Prevention Accreditation Program Registration \u2013 ' + orgName, /* Accreditation contact fields map to Lead owner */ firstname: gVal('spapContactFirst'), lastname: gVal('spapContactLast') || orgName, jobtitle: gVal('spapContactPosition'), emailaddress1: gVal('spapContactEmail'), telephone1: gVal('spapContactPhone'), /* Organisation fields */ companyname: orgName, fax: gVal('spapOrgFax'), websiteurl: gVal('spapOrgWebsite'), mobilephone: gVal('spapOrgPhone'), leadsourcecode: 8, description: descSummary, /* Street address */ address1_line1: gVal('spapStreetAddr0'), address1_line2: gVal('spapStreetAddr1'), address1_city: gVal('spapStreetCity'), address1_stateorprovince: gVal('spapStreetState'), address1_postalcode: gVal('spapStreetPostcode'), address1_country: 'Australia', /* Postal address */ address2_line1: diffPostal ? gVal('spapPostalAddr0') : '', address2_line2: diffPostal ? gVal('spapPostalAddr1') : '', address2_city: diffPostal ? gVal('spapPostalCity') : '', address2_stateorprovince: diffPostal ? gVal('spapPostalState') : '', address2_postalcode: diffPostal ? gVal('spapPostalPostcode') : '', address2_country: diffPostal ? 'Australia' : '', /* Custom ONGC fields */ ongc_enquirytype: 3, /* 3 = QIP SPAP */ ongc_practicename: orgName, ongc_practiceabn: gVal('spapOrgABN'), ongc_practicephone: gVal('spapOrgPhone'), ongc_postalsameasstreet: !diffPostal }; return stripEmpty(payload); } function buildContactPayload() { var payload = { firstname: gVal('spapContactFirst'), lastname: gVal('spapContactLast'), jobtitle: gVal('spapContactPosition'), emailaddress1: gVal('spapContactEmail'), telephone1: gVal('spapContactPhone'), mobilephone: gVal('spapOrgPhone') }; if (SPAP.leadId) { payload['originatingleadid@odata.bind'] = '/leads(' + SPAP.leadId + ')'; } if (SPAP.config.contactTypeId) { payload['ongc_ContactType@odata.bind'] = '/ongc_contacttypes(' + SPAP.config.contactTypeId + ')'; } return stripEmpty(payload); } function buildPaymentEvidencePayload() { var fees = calcFees(); var payload = { ongc_payername: (gVal('spapContactFirst') + ' ' + gVal('spapContactLast')).trim(), ongc_paymentamount: fees.base, ongc_surchargefee: fees.surcharge, ongc_gst: fees.gst, ongc_totalamount: fees.total, ongc_paymentstatus: 'pending-cc', ongc_transactionstatus: 'pending', ongc_paymentdate: new Date().toISOString(), ongc_responsemessage: 'Pending \u2013 QIP SPAP Stripe credit-card checkout initiated' }; if (SPAP.leadId) { payload['ongc_Lead@odata.bind'] = '/leads(' + SPAP.leadId + ')'; } return stripEmpty(payload); } /* ════════════════════════════════════════════════════════════ RECORD CREATION CHAIN ════════════════════════════════════════════════════════════ */ async function createAllRecords() { /* ── 1. Lead — critical ── */ setBusy(true, 'Creating registration record\u2026'); var lead = await dataversePost('leads', buildLeadPayload()); SPAP.leadId = lead.id; if (!SPAP.leadId) throw new Error('Lead created but GUID not returned. Check Dataverse Web API permissions for the lead entity.'); console.log('[SPAP] Lead created:', SPAP.leadId); /* ── 2. Fetch autonumber reference ── */ setBusy(true, 'Fetching registration reference\u2026'); var fetchedRef = await fetchLeadRef(SPAP.leadId); SPAP.leadRef = fetchedRef || null; console.log('[SPAP] leadRef:', SPAP.leadRef); saveSession(); /* ── 3. Contact — non-blocking ── */ setBusy(true, 'Creating contact record\u2026'); try { var cr = await Promise.allSettled([dataversePost('contacts', buildContactPayload())]); if (cr[0].status === 'rejected') console.warn('[SPAP] Contact failed (non-fatal):', cr[0].reason && cr[0].reason.message); else console.log('[SPAP] Contact created:', cr[0].value && cr[0].value.id); } catch (e) { console.warn('[SPAP] Contact error (non-fatal):', e.message); } /* ── 4. PaymentEvidence — critical ── */ setBusy(true, 'Creating payment record\u2026'); var evidence = await dataversePost('ongc_paymentevidences', buildPaymentEvidencePayload()); if (evidence && evidence.id) { SPAP.paymentEvidenceId = evidence.id; console.log('[SPAP] Evidence created:', evidence.id); } else { console.warn('[SPAP] Evidence GUID not returned — return page will create fallback record'); } saveSession(); } /* ════════════════════════════════════════════════════════════ STRIPE REDIRECT (shared /agpal-stripe-checkout page) ════════════════════════════════════════════════════════════ */ function initiateStripePayment() { var fees = calcFees(); var params = new URLSearchParams({ leadId: SPAP.leadId || '', leadRef: SPAP.leadRef || '', paymentEvidenceId: SPAP.paymentEvidenceId || '', amount: fees.total.toFixed(2), base: fees.base.toFixed(2), surcharge: fees.surcharge.toFixed(2), gst: fees.gst.toFixed(2), email: gVal('spapContactEmail'), name: (gVal('spapContactFirst') + ' ' + gVal('spapContactLast')).trim(), ref: 'QIP Suicide Prevention Accreditation Program Registration \u2013 ' + gVal('spapOrgName'), returnUrl: window.location.href.split('?')[0] }); try { sessionStorage.setItem(SPAP.paymentInProgressKey, '1'); if (SPAP.leadId) sessionStorage.setItem(SPAP.leadIdKey, SPAP.leadId); if (SPAP.leadRef) sessionStorage.setItem(SPAP.leadRefKey, SPAP.leadRef); if (SPAP.paymentEvidenceId) sessionStorage.setItem(SPAP.evidenceIdKey, SPAP.paymentEvidenceId); } catch (e) { } /* location.replace prevents Back returning to mid-payment form */ window.location.replace('/agpal-stripe-checkout?' + params.toString()); } /* ════════════════════════════════════════════════════════════ MAIN SUBMIT ENTRY POINT ════════════════════════════════════════════════════════════ */ async function spapSubmitForm() { var auth = document.getElementById('spapChkAuth') && document.getElementById('spapChkAuth').checked; var tc = document.getElementById('spapChkTC') && document.getElementById('spapChkTC').checked; var conf = document.getElementById('spapChkConfirm') && document.getElementById('spapChkConfirm').checked; var af = gVal('spapAuthFirst'); var al = gVal('spapAuthLast'); var errEl = document.getElementById('spapSubmitError'); if (!auth || !tc || !conf || !af || !al || spapIsSigEmpty()) { if (errEl) { errEl.style.display = 'flex'; errEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); } return; } if (errEl) errEl.style.display = 'none'; try { await createAllRecords(); setBusy(true, 'Redirecting to payment\u2026'); initiateStripePayment(); } catch (err) { console.error('[SPAP] Submit failed:', err); if (errEl) { errEl.style.display = 'flex'; errEl.textContent = 'Could not submit registration: ' + (err.message || 'Unknown error') + '. Please try again.'; errEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); } setBusy(false); } } /* ════════════════════════════════════════════════════════════ INIT ════════════════════════════════════════════════════════════ */ document.addEventListener('DOMContentLoaded', function () { injectSpinner(); clearSession(); var noChk = document.getElementById('spapPostalNo'); if (noChk) noChk.checked = true; spapRebuildStructure(); }); window.addEventListener('resize', function () { if (SPAP.sigCtx) spapInitSig(); }); /* ── Expose all globals used by inline onclick handlers in HTML ── */ window.SPAP = SPAP; window.spapGoToPage = spapGoToPage; window.spapNavNext = spapNavNext; window.spapNavPrev = spapNavPrev; window.spapPage3Next = spapPage3Next; window.spapFeeNext = spapFeeNext; window.spapFeeBack = spapFeeBack; window.spapSubmitBack = spapSubmitBack; window.spapSaveResume = spapSaveResume; window.spapToggleSector = spapToggleSector; window.spapTogglePostal = spapTogglePostal; window.spapOnAdditionalSitesChange = spapOnAdditionalSitesChange; window.spapOnProgramCountChange = spapOnProgramCountChange; window.spapCalculateFee = spapCalculateFee; window.spapClrSig = spapClrSig; window.spapSubmitForm = spapSubmitForm; }());