252 lines
9.1 KiB
JavaScript
252 lines
9.1 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 adapter = require('../erp_operation_adapter');
|
||
|
||
function tempAuditDir() {
|
||
return fs.mkdtempSync(path.join(os.tmpdir(), 'erp-operation-audit-'));
|
||
}
|
||
|
||
test('defaults old order templates to create_order and delegates route parsing', () => {
|
||
const result = adapter.handleIncomingMessage(`下单模式:团队-单个下单
|
||
订单性质:测试
|
||
产品名称:遇见老挝
|
||
出发日期:2026-10-03
|
||
|
||
人数:
|
||
成人:2
|
||
小孩占床:0
|
||
小孩不占床:0
|
||
婴儿:0
|
||
领队:0
|
||
|
||
用房:
|
||
SGL:0
|
||
TWN:0
|
||
TRP:0
|
||
DBL:1
|
||
HNM:0
|
||
TL:0
|
||
|
||
单价:
|
||
成人:500
|
||
小孩占床:0
|
||
小孩不占床:0
|
||
婴儿:0
|
||
领队:0
|
||
|
||
计调OP:测试
|
||
销售人:琳琳
|
||
备注:确认真实保存 ERP`, { auditDir: tempAuditDir() });
|
||
|
||
assert.equal(result.status, 'ready');
|
||
assert.equal(result.operation, 'create_order');
|
||
assert.equal(result.task.operation, 'create_order');
|
||
assert.equal(result.task.route, 'team_single');
|
||
assert.equal(result.task.delivery.deferConfirmationExport, true);
|
||
assert.equal(result.task.delivery.sendPdfFollowUp, false);
|
||
});
|
||
|
||
test('treats order templates with supplemental and pdf wording as create_order', () => {
|
||
const result = adapter.handleIncomingMessage(`下单模式:散拼-拼单/录入子单
|
||
订单性质:测试
|
||
产品/线路:老挝行程-广东 8D7N
|
||
出发日期:2026-09-19
|
||
渠道/预订客户:LW云南七彩金桥国旅(昆明景腾)
|
||
|
||
人数:
|
||
成人:2
|
||
小孩占床:1
|
||
小孩不占床:1
|
||
婴儿:0
|
||
领队:0
|
||
|
||
单价:
|
||
成人:500
|
||
小孩占床:300
|
||
小孩不占床:200
|
||
婴儿:0
|
||
领队:0
|
||
|
||
计调OP:测试
|
||
销售人:琳琳
|
||
|
||
航班信息:
|
||
去程:2026-09-19,CZ6091,广州-万象,08:20-10:10
|
||
回程:2026-09-26,CZ6092,万象-广州,18:30-22:10
|
||
|
||
酒店信息:
|
||
第1晚:万象湄公河精品酒店,2间双床房
|
||
|
||
接送机信息:
|
||
接机地点:万象机场到达口
|
||
送机地点:万象机场国际出发口
|
||
联系人:张三
|
||
电话:13800138000
|
||
|
||
游客名单:
|
||
1. 张三 / 男 / 成人 / 护照 E12345678 / 电话 13800138000
|
||
|
||
特殊要求:
|
||
1. 确认书需要返回 PDF
|
||
|
||
备注:补充资料包含航班、酒店、游客名单和特殊要求。确认真实保存 ERP。`, { auditDir: tempAuditDir() });
|
||
|
||
assert.equal(result.status, 'ready');
|
||
assert.equal(result.operation, 'create_order');
|
||
assert.equal(result.task.operation, 'create_order');
|
||
assert.equal(result.task.route, 'split_child');
|
||
assert.equal(result.task.fields.supplemental.flights.length, 2);
|
||
assert.equal(result.task.fields.supplemental.hotels[0], '第1晚:万象湄公河精品酒店,2间双床房');
|
||
assert.equal(result.task.fields.supplemental.transfer.pickupPlace, '万象机场到达口');
|
||
assert.equal(result.task.fields.supplemental.travelers[0].name, '张三');
|
||
assert.deepEqual(result.task.fields.supplemental.specialRequests, ['确认书需要返回 PDF']);
|
||
});
|
||
|
||
test('parses update_order by ERP identifier without requiring a route', () => {
|
||
const result = adapter.handleIncomingMessage(`操作类型:补充/修改订单
|
||
订单编号:D14079
|
||
|
||
修改内容:
|
||
航班信息:
|
||
去程:2026-09-19,CZ6091,广州-万象,08:20-10:10
|
||
特殊要求:
|
||
客人不吃牛肉`, { auditDir: tempAuditDir() });
|
||
|
||
assert.equal(result.status, 'ready');
|
||
assert.equal(result.operation, 'update_order');
|
||
assert.equal(result.task.operation, 'update_order');
|
||
assert.equal(result.task.identifier, 'D14079');
|
||
assert.match(result.task.updateText, /CZ6091/);
|
||
assert.equal(result.task.route, '');
|
||
});
|
||
|
||
test('adds structured update plan for update_order instructions', () => {
|
||
const result = adapter.handleIncomingMessage(`操作类型:修改订单
|
||
订单编号:D14079
|
||
|
||
修改内容:
|
||
成人减少1
|
||
DBL减少1
|
||
备注增加:客人不吃牛肉`, { auditDir: tempAuditDir() });
|
||
|
||
assert.equal(result.status, 'ready');
|
||
assert.equal(result.operation, 'update_order');
|
||
assert.deepEqual(result.task.updatePlan.actions, [
|
||
{ target: 'pax.adult', operation: 'delta', value: -1, source: '成人减少1' },
|
||
{ target: 'rooms.DBL', operation: 'delta', value: -1, source: 'DBL减少1' },
|
||
{ target: 'remark', operation: 'append', value: '客人不吃牛肉', source: '备注增加:客人不吃牛肉' },
|
||
]);
|
||
});
|
||
|
||
test('attaches traveler-list files to update_order instructions and redacts audit rows', () => {
|
||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'erp-operation-list-'));
|
||
const csvPath = path.join(tmp, 'travelers.csv');
|
||
fs.writeFileSync(
|
||
csvPath,
|
||
[
|
||
'序号,中文名,英文名,性别,身份证号码,出生日期,出生地,护照号码,签发日期,有效日期,签发地,备注',
|
||
'1,张三,ZHANG SAN,男,110101199001011234,1990-01-01,北京,E12345678,2025-01-01,2035-01-01,北京,',
|
||
'2,李四,LI SI,女,110101199201011234,1992-01-01,北京,E23456789,2025-01-01,2035-01-01,北京,',
|
||
].join('\n'),
|
||
'utf8'
|
||
);
|
||
|
||
const result = adapter.handleIncomingMessage(`操作类型:补充/修改订单
|
||
订单编号:D14079`, {
|
||
auditDir: tempAuditDir(),
|
||
attachments: [csvPath],
|
||
});
|
||
const audit = JSON.parse(fs.readFileSync(result.auditPath, 'utf8'));
|
||
|
||
assert.equal(result.status, 'ready');
|
||
assert.equal(result.operation, 'update_order');
|
||
assert.equal(result.task.updatePlan.supplemental.travelerList.totalCount, 2);
|
||
assert.equal(result.task.updatePlan.supplemental.travelerList.validationStatus, 'pending_order_pax');
|
||
assert.equal(result.task.updatePlan.actions.at(-1).target, 'supplemental.travelerList');
|
||
assert.equal(audit.task.updatePlan.supplemental.travelerList.rows[0].chineseName, '[redacted]');
|
||
assert.equal(audit.task.updatePlan.supplemental.travelerList.rows[0].passportNo, '[redacted]');
|
||
});
|
||
|
||
test('treats natural traveler-list import phrase with an identifier as update_order', () => {
|
||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'erp-operation-natural-list-'));
|
||
const csvPath = path.join(tmp, 'travelers.csv');
|
||
fs.writeFileSync(
|
||
csvPath,
|
||
[
|
||
'搴忓彿,涓枃鍚?鑻辨枃鍚?鎬у埆,韬唤璇佸彿鐮?鍑虹敓鏃ユ湡,鍑虹敓鍦?鎶ょ収鍙风爜,绛惧彂鏃ユ湡,鏈夋晥鏃ユ湡,绛惧彂鍦?澶囨敞',
|
||
'1,寮犱笁,ZHANG SAN,鐢?110101199001011234,1990-01-01,鍖椾含,E12345678,2025-01-01,2035-01-01,鍖椾含,',
|
||
'2,鏉庡洓,LI SI,濂?110101199201011234,1992-01-01,鍖椾含,E23456789,2025-01-01,2035-01-01,鍖椾含,',
|
||
].join('\n'),
|
||
'utf8'
|
||
);
|
||
|
||
const result = adapter.handleIncomingMessage('导入到 LLW-260805A-A', {
|
||
auditDir: tempAuditDir(),
|
||
attachments: [csvPath],
|
||
});
|
||
|
||
assert.equal(result.status, 'ready');
|
||
assert.equal(result.operation, 'update_order');
|
||
assert.equal(result.task.identifier, 'LLW-260805A-A');
|
||
assert.equal(result.task.updatePlan.supplemental.travelerList.sourceFile, csvPath);
|
||
assert.equal(result.task.updatePlan.supplemental.travelerList.validationStatus, 'pending_order_pax');
|
||
assert.equal(result.task.updatePlan.actions.at(-1).target, 'supplemental.travelerList');
|
||
});
|
||
|
||
test('asks clarification when update_order instructions are ambiguous', () => {
|
||
const result = adapter.handleIncomingMessage(`操作类型:修改订单
|
||
订单编号:D14079
|
||
|
||
修改内容:
|
||
人数减1
|
||
房间减少1间`, { auditDir: tempAuditDir() });
|
||
|
||
assert.equal(result.status, 'needs_clarification');
|
||
assert.equal(result.operation, 'update_order');
|
||
assert.deepEqual(result.missingFields, ['人数类别', '房型']);
|
||
assert.equal(result.task, null);
|
||
assert.match(result.customerMessage, /人数类别/);
|
||
});
|
||
|
||
test('parses export_confirmation by ERP identifier', () => {
|
||
const result = adapter.handleIncomingMessage(`操作类型:导出确认件
|
||
订单编号:LLW-260811A-A`, { auditDir: tempAuditDir() });
|
||
|
||
assert.equal(result.status, 'ready');
|
||
assert.equal(result.operation, 'export_confirmation');
|
||
assert.equal(result.task.operation, 'export_confirmation');
|
||
assert.equal(result.task.identifier, 'LLW-260811A-A');
|
||
assert.deepEqual(result.task.exportTypes, ['xingyou-confirm']);
|
||
});
|
||
|
||
test('parses explicit Liantai confirmation export request', () => {
|
||
const result = adapter.handleIncomingMessage(`操作类型:导出确认件
|
||
订单编号:LW-260919A-A
|
||
需要联泰确认书`, { auditDir: tempAuditDir() });
|
||
|
||
assert.equal(result.status, 'ready');
|
||
assert.deepEqual(result.task.exportTypes, ['xingyou-confirm', 'liantai-confirm']);
|
||
});
|
||
|
||
test('blocks update_order when identifier is missing', () => {
|
||
const result = adapter.handleIncomingMessage(`操作类型:补充/修改订单
|
||
修改内容:备注:客人不吃牛肉`, { auditDir: tempAuditDir() });
|
||
|
||
assert.equal(result.status, 'needs_clarification');
|
||
assert.equal(result.operation, 'update_order');
|
||
assert.deepEqual(result.missingFields, ['订单编号']);
|
||
assert.match(result.customerMessage, /订单编号/);
|
||
});
|
||
|
||
test('parseArgs supports input audit-dir and json flags', () => {
|
||
const args = adapter.parseArgs(['node', 'tool', '--input', 'message.txt', '--audit-dir', 'audit', '--json']);
|
||
|
||
assert.equal(args.input, 'message.txt');
|
||
assert.equal(args.auditDir, 'audit');
|
||
assert.equal(args.json, true);
|
||
});
|