const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const registry = require('../erp_order_registry'); const handlers = require('../erp_operation_handlers'); function tempRegistryPath() { return path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'erp-op-handler-')), 'orders.jsonl'); } test('executeExportConfirmation exports confirmation source files without PDF conversion', async () => { const registryPath = tempRegistryPath(); registry.appendRawRecord({ identifier: 'LW-260919A-A', route: 'team_batch', groupNo: 'LW-260919A-A', departureDate: '2026-09-19', product: '老挝行程-广东 8D7N', customer: 'LW customer', ddid: '14067', tid: '14067', updatedAt: '2026-06-22T01:00:00.000Z', }, { registryPath }); const calls = []; const result = await handlers.executeExportConfirmation({ operation: 'export_confirmation', identifier: 'LW-260919A-A', exportTypes: ['xingyou-confirm'], }, { config: { paths: { orderRegistry: registryPath } }, operationsByRoute: { team_batch: { exportDocuments: async (plan, submitResult, updateResult, options) => { calls.push(['exportDocuments', plan.route, submitResult.groups[0].groupNo, options.exportTypes.join(',')]); return { sources: [ { groupNo: 'LW-260919A-A', type: 'xingyou-confirm', path: 'downloads/xingyou.doc' }, { groupNo: 'LW-260919A-A', type: 'liantai-confirm', path: 'downloads/liantai.doc' }, { groupNo: 'LW-260919A-A', type: 'job-order', path: 'downloads/job.doc' }, ], }; }, }, }, }); assert.equal(result.status, 'completed'); assert.equal(result.identifiers.groupNo, 'LW-260919A-A'); assert.deepEqual(calls, [ ['exportDocuments', 'team_batch', 'LW-260919A-A', 'xingyou-confirm'], ]); assert.equal(result.artifacts.length, 1); assert.equal(result.artifacts[0].type, 'xingyou-confirm'); assert.equal(result.artifacts[0].path, 'downloads/xingyou.doc'); assert.equal(result.stageResults.pdf, undefined); assert.doesNotMatch(result.customerMessage, /PDF/i); assert.doesNotMatch(result.customerMessage, /JOB/i); }); test('executeExportConfirmation blocks when the identifier is not registered', async () => { const result = await handlers.executeExportConfirmation({ operation: 'export_confirmation', identifier: 'D99999', }, { config: { paths: { orderRegistry: tempRegistryPath() } }, }); assert.equal(result.status, 'blocked'); assert.equal(result.reason, 'order_record_not_found'); assert.match(result.customerMessage, /D99999/); }); test('executeExportConfirmation blocks split child export when departure date is missing', async () => { const registryPath = tempRegistryPath(); registry.appendRawRecord({ identifier: 'D14079', route: 'split_child', childOrderNo: 'D14079', parentGroupNo: 'LW-260919A-A', updatedAt: '2026-06-22T01:00:00.000Z', }, { registryPath }); let exportCalled = false; const result = await handlers.executeExportConfirmation({ operation: 'export_confirmation', identifier: 'D14079', }, { config: { paths: { orderRegistry: registryPath } }, operationsByRoute: { split_child: { exportDocuments: async () => { exportCalled = true; throw new Error('export should not run without departure date'); }, convertToPdf: async () => { throw new Error('convert should not run without departure date'); }, }, }, }); assert.equal(result.status, 'blocked'); assert.equal(result.reason, 'order_record_missing_departure_date'); assert.equal(exportCalled, false); assert.match(result.customerMessage, /D14079/); assert.doesNotMatch(result.customerMessage, /Invalid date|split_child|departureDate|registry|code/i); }); test('executeUpdateOrder finds the order but blocks until field mapping is implemented', async () => { const registryPath = tempRegistryPath(); registry.appendRawRecord({ identifier: 'D14079', route: 'split_child', childOrderNo: 'D14079', parentGroupNo: 'LW-260919A-A', updatedAt: '2026-06-22T01:00:00.000Z', }, { registryPath }); const result = await handlers.executeUpdateOrder({ operation: 'update_order', identifier: 'D14079', updateText: '航班信息:CZ6091', }, { config: { paths: { orderRegistry: registryPath } }, }); assert.equal(result.status, 'blocked'); assert.equal(result.reason, 'update_order_operations_missing'); assert.equal(result.orderRecord.route, 'split_child'); assert.match(result.customerMessage, /D14079/); }); test('executeUpdateOrder calls the route updateOrder operation with the structured plan', async () => { const registryPath = tempRegistryPath(); registry.appendRawRecord({ identifier: 'D14079', route: 'split_child', childOrderNo: 'D14079', parentGroupNo: 'LW-260919A-A', departureDate: '2026-09-19', product: '老挝行程-广东 8D7N', customer: 'LW云南七彩金桥国旅', updatedAt: '2026-06-22T01:00:00.000Z', }, { registryPath }); let received = null; const updatePlan = { status: 'ready', actions: [ { target: 'pax.adult', operation: 'delta', value: -1, source: '成人减少1' }, { target: 'rooms.DBL', operation: 'delta', value: -1, source: 'DBL减少1' }, ], missingFields: [], }; const result = await handlers.executeUpdateOrder({ operation: 'update_order', identifier: 'D14079', updatePlan, }, { config: { paths: { orderRegistry: registryPath } }, operationsByRoute: { split_child: { updateOrder: async (plan, planArg, recordArg, optionsArg) => { received = { plan, planArg, recordArg, optionsArg }; return { status: 'completed', updatedActions: planArg.actions, customerMessage: '已修改订单 D14079。', }; }, }, }, }); assert.equal(result.status, 'completed'); assert.equal(result.operation, 'update_order'); assert.equal(result.route, 'split_child'); assert.match(result.customerMessage, /已修改子单 D14079/); assert.match(result.customerMessage, /母团下其他历史子单未修改/); assert.equal(received.plan.route, 'split_child'); assert.equal(received.plan.parentGroupNo, 'LW-260919A-A'); assert.deepEqual(received.planArg.actions, updatePlan.actions); assert.equal(received.recordArg.childOrderNo, 'D14079'); }); test('executeUpdateOrder scopes split child supplemental updates to the target child in the customer message', async () => { const registryPath = tempRegistryPath(); registry.appendRawRecord({ identifier: 'D14080', route: 'split_child', childOrderNo: 'D14080', parentGroupNo: 'LW-260919A-A', departureDate: '2026-09-19', product: '老挝行程-广东 8D7N', customer: 'LW河北中游天下旅行社有限公司', updatedAt: '2026-06-22T01:00:00.000Z', }, { registryPath }); const updatePlan = { status: 'ready', actions: [ { target: 'supplemental.flights', operation: 'upsert', value: [{ flightNo: 'CZ6091' }], source: '航班信息' }, { target: 'supplemental.hotels', operation: 'upsert', value: ['第1晚:万象酒店,1间双床房'], source: '酒店信息' }, { target: 'supplemental.transfer', operation: 'upsert', value: { pickupPlace: '万象机场' }, source: '接送机信息' }, { target: 'supplemental.specialRequests', operation: 'append', value: ['不吃牛肉'], source: '特殊要求' }, ], missingFields: [], }; const result = await handlers.executeUpdateOrder({ operation: 'update_order', identifier: 'D14080', updatePlan, }, { config: { paths: { orderRegistry: registryPath } }, operationsByRoute: { split_child: { updateOrder: async () => ({ status: 'completed', updatedActions: updatePlan.actions, customerMessage: '旧文案:母团下现有子单均已填入。', }), }, }, }); assert.equal(result.status, 'completed'); assert.match(result.customerMessage, /已修改子单 D14080/); assert.match(result.customerMessage, /共执行 4 项变更/); assert.match(result.customerMessage, /母团下其他历史子单未修改/); assert.doesNotMatch(result.customerMessage, /均已填入|全部子单/); });