const { dialogCursor, dialogMessagesSince, assertNoNewErpLoginRequired, } = require('./erp_session_guard'); function isSubmitSuccessText(value) { return /下单成功|操作成功|修改成功|成功|สำเร็จ|success/i.test(String(value || '')); } function createSubmitUnconfirmedError(source, evidence) { const error = new Error(`ERP submit result was not confirmed (${source || 'erp.submit'})`); error.code = 'erp_submit_unconfirmed'; error.details = evidence; return error; } async function readFrameBodyText(pageOrFrame) { return pageOrFrame.evaluate(() => document.body.innerText.replace(/\s+/g, ' ').trim()).catch(() => ''); } async function installSubmitAjaxProbe(pageOrFrame, minimumTimeout) { return pageOrFrame.evaluate((timeoutMs) => { window.__erpSubmitAjaxEvents = []; window.__erpSubmitAjaxMinimumTimeout = timeoutMs; const jq = window.jQuery || window.$; if (!jq || typeof jq.ajax !== 'function') { return { installed: false, reason: 'jquery_ajax_missing', minimumTimeout: timeoutMs }; } if (!jq.__erpSubmitAjaxOriginal) { const originalAjax = jq.ajax; jq.__erpSubmitAjaxOriginal = originalAjax; jq.ajax = function erpSubmitAjaxWithProbe(rawOptions, ...rest) { const eventList = window.__erpSubmitAjaxEvents || (window.__erpSubmitAjaxEvents = []); const options = typeof rawOptions === 'string' ? { ...(rest[0] || {}), url: rawOptions } : { ...(rawOptions || {}) }; const minTimeout = Number(window.__erpSubmitAjaxMinimumTimeout || 0); const currentTimeout = Number(options.timeout || 0); if (minTimeout && (!currentTimeout || currentTimeout < minTimeout)) { options.timeout = minTimeout; } const base = () => ({ url: options.url || '', type: options.type || options.method || '', dataType: options.dataType || '', timeout: Number(options.timeout || 0), }); const originalSuccess = options.success; const originalError = options.error; const originalComplete = options.complete; options.success = function successProbe(data, textStatus, jqXHR) { eventList.push({ event: 'success', ...base(), status: jqXHR && jqXHR.status || 0, textStatus: textStatus || '', responseText: String(jqXHR && jqXHR.responseText || data || '').slice(0, 2000), }); if (typeof originalSuccess === 'function') return originalSuccess.apply(this, arguments); return undefined; }; options.error = function errorProbe(jqXHR, textStatus, errorThrown) { eventList.push({ event: 'error', ...base(), status: jqXHR && jqXHR.status || 0, textStatus: textStatus || '', error: errorThrown && errorThrown.message ? errorThrown.message : String(errorThrown || ''), responseText: String(jqXHR && jqXHR.responseText || '').slice(0, 2000), }); if (typeof originalError === 'function') return originalError.apply(this, arguments); return undefined; }; options.complete = function completeProbe(jqXHR, textStatus) { eventList.push({ event: 'complete', ...base(), status: jqXHR && jqXHR.status || 0, textStatus: textStatus || '', }); if (typeof originalComplete === 'function') return originalComplete.apply(this, arguments); return undefined; }; return originalAjax.call(this, options); }; } return { installed: true, minimumTimeout: timeoutMs }; }, minimumTimeout).catch((error) => ({ installed: false, reason: error && error.message ? error.message : String(error), minimumTimeout, })); } async function readSubmitAjaxEvents(pageOrFrame) { return pageOrFrame.evaluate(() => Array.isArray(window.__erpSubmitAjaxEvents) ? window.__erpSubmitAjaxEvents : [] ).catch(() => []); } function isPrimarySubmitAjaxEvent(item = {}) { return /\/System\/DAT\/(?:orders|plan)\.asp/i.test(String(item.url || '')); } function isAjaxFailure(item = {}) { return item.event === 'error' || Number(item.status || 0) >= 400; } function ajaxEvidenceText(events = []) { return events.map((item) => { const parts = []; if (item.event === 'error') { parts.push(item.textStatus || '', item.error || ''); } parts.push(item.responseText || ''); return parts.filter(Boolean).join(' '); }).filter(Boolean).join('\n'); } async function submitInfoFormAndConfirm(pageOrFrame, dialogState = null, options = {}) { const source = options.source || 'erp.submit'; const waitMs = Number.isFinite(options.waitMs) ? options.waitMs : 30000; const ajaxTimeoutMs = Number.isFinite(options.ajaxTimeoutMs) ? options.ajaxTimeoutMs : Math.max(waitMs, 30000); const requireSuccess = options.requireSuccess !== false; const cursor = dialogCursor(dialogState); const ajaxProbe = await installSubmitAjaxProbe(pageOrFrame, ajaxTimeoutMs); await pageOrFrame.evaluate(() => { if (typeof SubmitInfoForm !== 'function') throw new Error('SubmitInfoForm not found'); SubmitInfoForm(); }); await pageOrFrame.waitForTimeout(waitMs); assertNoNewErpLoginRequired(dialogState, cursor, source); const newDialogs = dialogMessagesSince(dialogState, cursor); const ajaxEvents = await readSubmitAjaxEvents(pageOrFrame); const primaryAjaxEvents = ajaxEvents.filter(isPrimarySubmitAjaxEvent); const primaryAjaxFailed = primaryAjaxEvents.some(isAjaxFailure); const bodyText = await readFrameBodyText(pageOrFrame); const evidenceText = [ newDialogs.map((item) => item.message || '').join('\n'), ajaxEvidenceText(ajaxEvents), bodyText, ].filter(Boolean).join('\n'); const result = { ok: isSubmitSuccessText(evidenceText) && !primaryAjaxFailed, source, dialogs: newDialogs, ajaxProbe, ajaxEvents, primaryAjaxEvents, primaryAjaxFailed, bodyPreview: bodyText.slice(0, 1000), }; if (requireSuccess && !result.ok) { throw createSubmitUnconfirmedError(source, result); } return result; } module.exports = { isSubmitSuccessText, installSubmitAjaxProbe, readSubmitAjaxEvents, isPrimarySubmitAjaxEvent, ajaxEvidenceText, submitInfoFormAndConfirm, createSubmitUnconfirmedError, };