256 lines
9.6 KiB
JavaScript
256 lines
9.6 KiB
JavaScript
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
|
|
}]);
|
|
});
|