238 lines
8.4 KiB
JavaScript
238 lines
8.4 KiB
JavaScript
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, /均已填入|全部子单/);
|
||
});
|