import assert from 'node:assert/strict'; import test from 'node:test'; import reconciliation from '../chrome-extension/ltjt-order-assistant/split-plan-reconciliation.js'; const expected = { date: '2026-9-7', product_name: '广东直飞行程--衡阳(晚航班)', product_label: '广东直飞行程--衡阳(晚航班) 6D5N', customer_name: 'LW辽宁康辉', planned_capacity: 40, passenger_counts: { adult: 8, child_bed: 1, leader: 1 } }; test('matches a persisted split parent row after an ERP error response', () => { const row = { group_no: 'LW-260907A-B', text: '收客中 LW-260907A-B 测试ai员工账号 常规团. 广东直飞行程--衡阳(晚航班) 2026-9-7 6D5N 计划40 2026-9-12 成人8+小(占)1+领队1=合计10' }; const result = reconciliation.chooseUniqueRows([row], expected); assert.equal(result.selected.group_no, 'LW-260907A-B'); assert.equal(result.selected.match.customer_match, false); assert.equal(result.ambiguous, false); }); test('does not match a row from a missing requested date', () => { const result = reconciliation.matchRowText( 'LW-260907A-B 广东直飞行程--衡阳(晚航班) 2026-9-7 计划40 成人8+小(占)1+领队1=合计10', { ...expected, date: '2026-9-15' } ); assert.equal(result.matched, false); assert.ok(result.missing.includes('date')); }); test('matches a mother-plan row without child customer or passenger criteria', () => { const result = reconciliation.matchRowText( 'LW-260907A-B 广东直飞行程--衡阳(晚航班) 2026-9-7 计划40', { date: '2026-9-7', product_name: '广东直飞行程--衡阳(晚航班)', planned_capacity: 40 } ); assert.equal(result.matched, true); assert.equal(result.passenger_criteria_present, false); assert.deepEqual(result.missing, []); }); test('prefers a unique customer match when generic row criteria are ambiguous', () => { const rows = [ { group_no: 'LW-260907A-A', text: 'LW-260907A-A 广东直飞行程--衡阳(晚航班) 2026-9-7 计划40 成人8+小(占)1+领队1=合计10' }, { group_no: 'LW-260907A-B', text: 'LW-260907A-B LW辽宁康辉 广东直飞行程--衡阳(晚航班) 2026-9-7 计划40 成人8+小(占)1+领队1=合计10' } ]; const result = reconciliation.chooseUniqueRows(rows, expected); assert.equal(result.selected.group_no, 'LW-260907A-B'); assert.equal(result.ambiguous, false); }); test('extracts the concrete child ddid instead of the parent tid', () => { assert.equal(reconciliation.extractDdidFromAction('OPEN_update(14389,14457)'), '14457'); assert.equal(reconciliation.extractDdidFromAction("OPEN_update('14453','0','LW-260912A-A')"), '14453'); assert.equal(reconciliation.extractDdidFromAction("OPEN_update('14389',0)"), ''); assert.equal(reconciliation.extractDdidFromAction('/system/Business/plan_order.asp?tdid=14389&ddid=14457'), '14457'); }); test('extracts only concrete shared children for the exact parent tid', () => { const refs = reconciliation.extractSharedChildRefs([ { onclick: 'OPEN_update(14389,0)', text: '新增子单' }, { onclick: 'OPEN_update(14389,14457)', text: 'D14457-LW辽宁康辉' }, { href: '/System/Business/plan_order.asp?tdid=14389&ddid=14458', text: 'D14458-LW辽宁康辉' }, { onclick: 'OPEN_update(14390,14459)', text: '其他母团' } ], '14389'); assert.deepEqual(refs.map((ref) => ref.child_order_no), ['D14457', 'D14458']); assert.ok(refs.every((ref) => ref.tid === '14389' && ref.ddid !== '0')); }); test('classifies a per-date split-order fact with a concrete child', () => { const row = { group_no: 'LW-260907TEST-202609-E2E3-A', tid: '14389', text: 'LW-260907TEST-202609-E2E3-A 测试ai员工账号 LW辽宁康辉 广东直飞行程--衡阳(晚航班) 2026-9-7 计划40 成人8+小(占)1+领队1=合计10', child_reference_scan_complete: true, child_refs: [{ tid: '14389', ddid: '14457', child_order_no: 'D14457', value_redacted: true }] }; const fact = reconciliation.splitOrderFact(row, { ...expected, group_suffix: 'TEST-202609-E2E3-A', marker: 'TEST-202609', owner_account: '测试ai员工账号' }); assert.equal(fact.facts_determined, true); assert.equal(fact.outcome, 'concrete_shared_children_with_requested_facts'); assert.equal(fact.child_count, 1); assert.equal(fact.customer_persisted_on_native_list, true); assert.equal(fact.passenger_counts_persisted_on_native_list, true); }); test('keeps a parent-only split-order result explicit instead of inventing a child', () => { const fact = reconciliation.splitOrderFact({ group_no: 'LW-260907TEST-202609-E2E3-A', tid: '14389', text: 'LW-260907TEST-202609-E2E3-A 测试ai员工账号 LW辽宁康辉 广东直飞行程--衡阳(晚航班) 2026-9-7 计划40 成人8+小(占)1+领队1=合计10', child_reference_scan_complete: true, child_refs: [] }, { ...expected, group_suffix: 'TEST-202609-E2E3-A', marker: 'TEST-202609', owner_account: '测试ai员工账号' }); assert.equal(fact.facts_determined, true); assert.equal(fact.outcome, 'parent_row_facts_without_concrete_child'); assert.equal(fact.child_count, 0); }); test('does not call a split-order fact determined without a parent tid or complete child scan', () => { const fact = reconciliation.splitOrderFact({ group_no: 'LW-260907TEST-202609-E2E3-A', tid: '', text: 'LW-260907TEST-202609-E2E3-A 测试ai员工账号 LW辽宁康辉 广东直飞行程--衡阳(晚航班) 2026-9-7 计划40 成人8+小(占)1+领队1=合计10', child_reference_scan_complete: false, child_refs: [] }, { ...expected, group_suffix: 'TEST-202609-E2E3-A', marker: 'TEST-202609', owner_account: '测试ai员工账号' }); assert.equal(fact.facts_determined, false); assert.equal(fact.outcome, 'split_order_fact_indeterminate'); }); test('builds split-parent requery filters without retaining unrelated list state', () => { assert.deepEqual( reconciliation.splitParentExactSearchValues('2027-1-1', '2027-1-30', 'LW-270107A-B'), { S_chufariqi: '2027-1-1', S_chufarizhi: '2027-1-30', S_tuanxuhao: 'LW-270107A-B', S_kehuming: '', S_chanpinming: '', S_youkexinxi: '', S_lingdui: '', S_zhuangtai: '' } ); assert.deepEqual( reconciliation.splitParentDateSearchValues('2027-1-15'), { S_chufariqi: '2027-1-15', S_chufarizhi: '2027-1-15', S_tuanxuhao: '', S_kehuming: '', S_chanpinming: '', S_youkexinxi: '', S_lingdui: '', S_zhuangtai: '' } ); }); test('retries returned group-number queries without resubmitting and converges on visible rows', async () => { const calls = []; const waits = []; const result = await reconciliation.verifyReturnedGroupNumbers({ group_numbers: ['LW-270107A-B', 'LW-270115A-A', 'LW-270126A-A'], retry_delays_ms: [0, 750, 2000], wait: async (delay) => waits.push(delay), search_group: async (groupNumber, context) => { calls.push({ groupNumber, ...context }); return { found: context.attempt === 2, row_text_length: context.attempt === 2 ? 120 : 0 }; } }); assert.equal(result.complete, true); assert.equal(result.attempt_count, 2); assert.equal(result.retry_count, 1); assert.deepEqual(waits, [750]); assert.equal(calls.length, 6); assert.ok(result.rows.every((row) => row.status === 'parent_group_found' && row.attempt === 2)); assert.deepEqual(result.attempts.map((attempt) => attempt.missing_count), [3, 0]); }); test('keeps a persistently missing returned group incomplete after the bounded retry budget', async () => { const waits = []; const result = await reconciliation.verifyReturnedGroupNumbers({ group_numbers: ['LW-270107A-B'], retry_delays_ms: [0, 750, 2000], wait: async (delay) => waits.push(delay), search_group: async () => ({ found: false, row_text_length: 0 }) }); assert.equal(result.complete, false); assert.equal(result.attempt_count, 3); assert.equal(result.retry_count, 2); assert.deepEqual(waits, [750, 2000]); assert.equal(result.rows[0].status, 'parent_group_not_found'); assert.deepEqual(result.attempts.map((attempt) => attempt.missing_count), [1, 1, 1]); }); test('requires date reconciliation when any complete ERP response group remains unverified', () => { const common = { write_attempted: true, capture_success: true, group_numbers: ['LW-270107A-B', 'LW-270115A-A', 'LW-270126A-A'], requested_date_count: 3, fact_probe_enabled: false }; assert.equal(reconciliation.shouldReconcileSplitParent({ ...common, verification_complete: false }), true); assert.equal(reconciliation.shouldReconcileSplitParent({ ...common, verification_complete: true }), false); assert.equal(reconciliation.shouldReconcileSplitParent({ ...common, capture_success: false, verification_complete: true }), true); assert.equal(reconciliation.shouldReconcileSplitParent({ ...common, write_attempted: false, verification_complete: false }), false); }); test('prefers a successful date fallback row over an earlier exact-query miss', () => { assert.deepEqual(reconciliation.mergeVerificationRows([ { group_number: 'LW-270107A-B', source: 'erp_response_retry', status: 'parent_group_not_found', row_text_length: 0 }, { group_number: 'LW-270107A-B', source: 'post_write_list_fallback', status: 'parent_group_found', row_text_length: 120 } ]), [{ group_number: 'LW-270107A-B', source: 'post_write_list_fallback', status: 'parent_group_found', row_text_length: 120 }]); });