修复变更、增加筛选条件
This commit is contained in:
@@ -10,10 +10,14 @@ export type BusinessRouteId =
|
||||
| 'passenger_list_import_independent'
|
||||
| 'passenger_list_import_shared_child'
|
||||
| 'arrangement_guide_create'
|
||||
| 'arrangement_guide_update'
|
||||
| 'arrangement_vehicle_create'
|
||||
| 'arrangement_vehicle_update'
|
||||
| 'arrangement_hotel_create'
|
||||
| 'arrangement_transport_create'
|
||||
| 'arrangement_transport_update'
|
||||
| 'arrangement_other_create'
|
||||
| 'arrangement_other_update'
|
||||
| 'order_update_shared_plan'
|
||||
| 'order_update_shared_child'
|
||||
| 'order_update_independent'
|
||||
@@ -75,8 +79,8 @@ export function effectiveParserModeForRoute(route: BusinessRouteDefinition, conf
|
||||
return route.parserPolicy === 'program_only' ? 'program' : 'ai';
|
||||
}
|
||||
|
||||
const PROGRAM_VERSION = 'ltjt-program-parser-v1.0.7';
|
||||
export const BUSINESS_INPUT_CONTRACT_VERSION = 'business-input-templates-0.5.127';
|
||||
const PROGRAM_VERSION = 'ltjt-program-parser-v1.0.11';
|
||||
export const BUSINESS_INPUT_CONTRACT_VERSION = 'business-input-templates-0.5.131';
|
||||
|
||||
const lookupFields = ({ customerRequired = true } = {}): ProgramFieldDefinition[] => [
|
||||
{ key: 'order_no', label: '单号', aliases: ['团号', '订单号'], kind: 'text' },
|
||||
@@ -236,6 +240,7 @@ export const BUSINESS_ROUTES: readonly BusinessRouteDefinition[] = Object.freeze
|
||||
{ key: 'group_no', label: '团号', aliases: ['单号'], required: true, kind: 'text' },
|
||||
{ key: 'supplier', label: '结算单位搜索', aliases: ['供应商搜索', '结算单位'], required: true, kind: 'named_ref' },
|
||||
{ key: 'vehicle_range', label: '用车日期', aliases: ['日期'], required: true, kind: 'date_range' },
|
||||
{ key: 'vehicle_type', label: '用车类型', aliases: ['车型'], kind: 'text' },
|
||||
{ key: 'quantity', label: '数量', required: true, kind: 'positive_integer' },
|
||||
{ key: 'remark', label: '备注', kind: 'text' }
|
||||
]
|
||||
@@ -272,6 +277,7 @@ export const BUSINESS_ROUTES: readonly BusinessRouteDefinition[] = Object.freeze
|
||||
{ key: 'supplier', label: '结算单位搜索', aliases: ['供应商搜索', '结算单位'], required: true, kind: 'named_ref' },
|
||||
{ key: 'quantity', label: '数量', required: true, kind: 'positive_integer' },
|
||||
{ key: 'date', label: '交通日期', aliases: ['业务日期'], required: true, kind: 'date' },
|
||||
{ key: 'ticket_description', label: '车票说明', aliases: ['车票说明搜索'], kind: 'text' },
|
||||
{ key: 'item', label: '车票/控位说明', aliases: ['项目', '说明'], kind: 'text' },
|
||||
{ key: 'remark', label: '备注', kind: 'text' }
|
||||
]
|
||||
@@ -343,14 +349,19 @@ export const BUSINESS_ROUTES: readonly BusinessRouteDefinition[] = Object.freeze
|
||||
routeId: 'arrangement_hotel_update',
|
||||
releaseOrder: 9,
|
||||
directive: '安排变更',
|
||||
aliases: [],
|
||||
aliases: ['安排酒店变更'],
|
||||
action: 'arrangement_hotel',
|
||||
discriminator: 'update',
|
||||
programVersion: PROGRAM_VERSION,
|
||||
inputContractVersion: BUSINESS_INPUT_CONTRACT_VERSION,
|
||||
fields: [
|
||||
{ key: 'group_no', label: '团号', aliases: ['单号'], required: true, kind: 'text' },
|
||||
{ key: 'hotel_changes', label: '目标变更', aliases: ['变更目标'], required: true, kind: 'hotel_changes' },
|
||||
{ key: 'hotel_changes', label: '目标变更', aliases: ['变更目标'], kind: 'hotel_changes' },
|
||||
{ key: 'start_date', label: '入住日期', kind: 'date' },
|
||||
{ key: 'end_date', label: '离店日期', aliases: ['离房日期', '退房日期'], kind: 'date' },
|
||||
{ key: 'resource', label: '酒店搜索', aliases: ['酒店'], kind: 'named_ref' },
|
||||
{ key: 'room_count', label: '间数', aliases: ['房间数'], kind: 'positive_integer' },
|
||||
{ key: 'remark', label: '备注', kind: 'text' },
|
||||
{ key: 'arrangement_type', label: '安排类型', kind: 'text' },
|
||||
{ key: 'business_reason', label: '业务原因', kind: 'text' }
|
||||
]
|
||||
@@ -391,6 +402,84 @@ export const BUSINESS_ROUTES: readonly BusinessRouteDefinition[] = Object.freeze
|
||||
{ key: 'export_type', label: '文件类型', aliases: ['导出类型'], required: true, kind: 'export_type' },
|
||||
{ key: 'visitor_name', label: '游客姓名', aliases: ['接机牌姓名'], kind: 'text' }
|
||||
]
|
||||
},
|
||||
{
|
||||
routeId: 'arrangement_guide_update',
|
||||
releaseOrder: 20,
|
||||
directive: '安排导游变更',
|
||||
aliases: [],
|
||||
action: 'arrangement_guide',
|
||||
discriminator: 'update',
|
||||
parserPolicy: 'program_only',
|
||||
programVersion: PROGRAM_VERSION,
|
||||
inputContractVersion: BUSINESS_INPUT_CONTRACT_VERSION,
|
||||
fields: [
|
||||
{ key: 'group_no', label: '团号', aliases: ['单号'], required: true, kind: 'text' },
|
||||
{ key: 'resource', label: '导游搜索', aliases: ['导游'], required: true, kind: 'named_ref' },
|
||||
{ key: 'remark', label: '备注', kind: 'text' }
|
||||
]
|
||||
},
|
||||
{
|
||||
routeId: 'arrangement_vehicle_update',
|
||||
releaseOrder: 21,
|
||||
directive: '安排用车变更',
|
||||
aliases: [],
|
||||
action: 'arrangement_vehicle',
|
||||
discriminator: 'update',
|
||||
parserPolicy: 'program_only',
|
||||
programVersion: PROGRAM_VERSION,
|
||||
inputContractVersion: BUSINESS_INPUT_CONTRACT_VERSION,
|
||||
fields: [
|
||||
{ key: 'group_no', label: '团号', aliases: ['单号'], required: true, kind: 'text' },
|
||||
{ key: 'supplier', label: '结算单位搜索', aliases: ['供应商搜索', '结算单位'], required: true, kind: 'named_ref' },
|
||||
{ key: 'vehicle_range', label: '用车日期', aliases: ['日期'], required: true, kind: 'date_range' },
|
||||
{ key: 'vehicle_type', label: '用车类型', aliases: ['车型'], kind: 'text' },
|
||||
{ key: 'quantity', label: '数量', required: true, kind: 'positive_integer' },
|
||||
{ key: 'remark', label: '备注', kind: 'text' }
|
||||
]
|
||||
},
|
||||
{
|
||||
routeId: 'arrangement_transport_update',
|
||||
releaseOrder: 22,
|
||||
directive: '安排大交通变更',
|
||||
aliases: [],
|
||||
action: 'arrangement_transport',
|
||||
discriminator: 'update',
|
||||
parserPolicy: 'program_only',
|
||||
programVersion: PROGRAM_VERSION,
|
||||
inputContractVersion: BUSINESS_INPUT_CONTRACT_VERSION,
|
||||
fields: [
|
||||
{ key: 'group_no', label: '团号', aliases: ['单号'], required: true, kind: 'text' },
|
||||
{ key: 'supplier', label: '结算单位搜索', aliases: ['供应商搜索', '结算单位'], required: true, kind: 'named_ref' },
|
||||
{ key: 'quantity', label: '数量', required: true, kind: 'positive_integer' },
|
||||
{ key: 'date', label: '交通日期', aliases: ['业务日期'], required: true, kind: 'date' },
|
||||
{ key: 'ticket_description', label: '车票说明', aliases: ['车票说明搜索'], kind: 'text' },
|
||||
{ key: 'item', label: '车票/控位说明', aliases: ['项目', '说明'], kind: 'text' },
|
||||
{ key: 'remark', label: '备注', kind: 'text' }
|
||||
]
|
||||
},
|
||||
{
|
||||
routeId: 'arrangement_other_update',
|
||||
releaseOrder: 23,
|
||||
directive: '安排其他/备案变更',
|
||||
aliases: ['安排其他变更', '安排备案变更'],
|
||||
action: 'arrangement_other',
|
||||
discriminator: 'update',
|
||||
parserPolicy: 'program_only',
|
||||
programVersion: PROGRAM_VERSION,
|
||||
inputContractVersion: BUSINESS_INPUT_CONTRACT_VERSION,
|
||||
fields: [
|
||||
{ key: 'group_no', label: '团号', aliases: ['单号'], required: true, kind: 'text' },
|
||||
{ key: 'supplier', label: '结算单位搜索', aliases: ['供应商搜索', '结算单位'], required: true, kind: 'named_ref' },
|
||||
{ key: 'date', label: '业务日期', aliases: ['交通日期'], required: true, kind: 'date' },
|
||||
{ key: 'item', label: '项目', aliases: ['备案说明'], kind: 'text' },
|
||||
{ key: 'quantity', label: '数量', kind: 'positive_integer' },
|
||||
{ key: 'remark', label: '备注', kind: 'text' },
|
||||
{ key: 'filing_number', label: '备案号', kind: 'text' },
|
||||
{ key: 'filing_date', label: '备案日期', kind: 'date' },
|
||||
{ key: 'entry_port', label: '入境口岸', kind: 'text' },
|
||||
{ key: 'exit_port', label: '出境口岸', kind: 'text' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { writeEmergencyDiagnostic } from './diagnostics.js';
|
||||
const { Pool } = pg;
|
||||
let pool: pg.Pool | null = null;
|
||||
|
||||
export const REQUIRED_SCHEMA_VERSION = '023_leader_summary_webhook_delivery';
|
||||
export const REQUIRED_SCHEMA_VERSION = '024_arrangement_update_routes';
|
||||
|
||||
export interface DatabaseReadiness {
|
||||
ready: boolean;
|
||||
|
||||
@@ -584,7 +584,7 @@ function parseHotelChanges(rawValue: string, context: DateContext, nextOccurrenc
|
||||
if (!parsed.ok) return { ok: false, code: 'program_invalid_value', message: parsed.message, field: 'hotel_changes' };
|
||||
changes.room_count = parsed.value;
|
||||
} else {
|
||||
return { ok: false, code: 'program_business_rule_blocked', message: '安排变更当前只支持酒店离店日期和房间数。', field: 'hotel_changes' };
|
||||
return { ok: false, code: 'program_business_rule_blocked', message: '目标变更只支持酒店离店日期和房间数;更换酒店请使用安排酒店变更,并填写原入住日期、原离店日期、酒店搜索和间数。', field: 'hotel_changes' };
|
||||
}
|
||||
}
|
||||
return Object.keys(changes).length
|
||||
@@ -807,6 +807,7 @@ function buildOperation(route: BusinessRouteDefinition, values: Map<string, unkn
|
||||
existing_refs: { identifier: values.get('group_no') },
|
||||
arrangement: {
|
||||
mode: 'create', supplier: values.get('supplier'), start_date: range.start_date, end_date: range.end_date,
|
||||
...(values.has('vehicle_type') ? { vehicle_type: values.get('vehicle_type') } : {}),
|
||||
quantity: values.get('quantity'), ...(values.get('remark') ? { remark: values.get('remark') } : {})
|
||||
}
|
||||
}) };
|
||||
@@ -826,6 +827,7 @@ function buildOperation(route: BusinessRouteDefinition, values: Map<string, unkn
|
||||
return { ok: true, operation: operation(route.action, {
|
||||
existing_refs: { identifier: values.get('group_no') }, arrangement: {
|
||||
mode: 'create', supplier: values.get('supplier'), quantity: values.get('quantity'), date: values.get('date'),
|
||||
...(values.has('ticket_description') ? { ticket_description: values.get('ticket_description') } : {}),
|
||||
...(values.get('item') ? { item: values.get('item') } : {}), ...(values.get('remark') ? { remark: values.get('remark') } : {})
|
||||
}
|
||||
}) };
|
||||
@@ -867,11 +869,59 @@ function buildOperation(route: BusinessRouteDefinition, values: Map<string, unkn
|
||||
...(special ? { special_requests: special } : {})
|
||||
}) };
|
||||
}
|
||||
case 'arrangement_guide_update':
|
||||
case 'arrangement_vehicle_update':
|
||||
case 'arrangement_transport_update':
|
||||
case 'arrangement_other_update': {
|
||||
const guide = route.action === 'arrangement_guide';
|
||||
const vehicle = route.action === 'arrangement_vehicle';
|
||||
const other = route.action === 'arrangement_other';
|
||||
const filing = {
|
||||
...(values.get('filing_number') ? { number: values.get('filing_number') } : {}),
|
||||
...(values.get('entry_port') ? { entry_port: values.get('entry_port') } : {}),
|
||||
...(values.get('exit_port') ? { exit_port: values.get('exit_port') } : {})
|
||||
};
|
||||
return { ok: true, operation: operation(route.action, {
|
||||
existing_refs: { identifier: values.get('group_no') },
|
||||
arrangement: {
|
||||
mode: 'update',
|
||||
...(!guide ? { selection: {
|
||||
...(vehicle ? values.get('vehicle_range') as JsonRecord : { date: values.get('date') }),
|
||||
...(other && values.get('filing_date') ? { filing_date: values.get('filing_date') } : {})
|
||||
} } : {}),
|
||||
...(vehicle && values.has('vehicle_type') ? { vehicle_type: values.get('vehicle_type') } : {}),
|
||||
...(route.action === 'arrangement_transport' && values.has('ticket_description') ? { ticket_description: values.get('ticket_description') } : {}),
|
||||
changes: {
|
||||
...(guide ? { resource: values.get('resource') } : { supplier: values.get('supplier') }),
|
||||
...(values.has('quantity') ? { quantity: values.get('quantity') } : {}),
|
||||
...(values.has('remark') ? { remark: values.get('remark') } : {}),
|
||||
...(other && Object.keys(filing).length ? { filing } : {})
|
||||
}
|
||||
}
|
||||
}) };
|
||||
}
|
||||
case 'arrangement_hotel_update': {
|
||||
const type = compactText(values.get('arrangement_type'));
|
||||
if (type && !['酒店', '安排酒店'].includes(type)) {
|
||||
return { ok: false, code: 'program_business_rule_blocked', message: '安排变更程序模式当前只支持酒店。', field: 'arrangement_type' };
|
||||
}
|
||||
const datedFields = ['start_date', 'end_date', 'resource', 'room_count'];
|
||||
if (datedFields.some((key) => values.has(key))) {
|
||||
if (values.get('hotel_changes')) return { ok: false, code: 'program_field_conflict', message: '按日期变更酒店时,请使用入住日期、离店日期、酒店搜索和间数,不要混填目标变更。', field: 'hotel_changes' };
|
||||
const missing = datedFields.find((key) => !values.has(key));
|
||||
if (missing) return { ok: false, code: 'program_missing_required', message: '请补齐原安排的入住日期、离店日期,以及新酒店搜索和间数。', field: missing };
|
||||
if (String(values.get('end_date')) <= String(values.get('start_date'))) return { ok: false, code: 'program_invalid_value', message: '原安排的离店日期必须晚于入住日期。', field: 'end_date' };
|
||||
return { ok: true, operation: operation(route.action, {
|
||||
existing_refs: { identifier: values.get('group_no') },
|
||||
arrangement: {
|
||||
mode: 'update',
|
||||
selection: { start_date: values.get('start_date'), end_date: values.get('end_date') },
|
||||
changes: { resource: values.get('resource'), room_count: values.get('room_count'), ...(values.has('remark') ? { remark: values.get('remark') } : {}) }
|
||||
},
|
||||
...(special ? { special_requests: special } : {})
|
||||
}) };
|
||||
}
|
||||
if (!values.get('hotel_changes')) return { ok: false, code: 'program_missing_required', message: '请填写目标变更,或提供原安排的入住日期、离店日期、新酒店搜索和间数。', field: 'hotel_changes' };
|
||||
const changes = parseHotelChanges(String(values.get('hotel_changes') || ''), context, true);
|
||||
if (!changes.ok) return changes;
|
||||
return { ok: true, operation: operation(route.action, {
|
||||
|
||||
@@ -405,6 +405,9 @@ function arrangementReply(
|
||||
const contextArrangement = object(context.arrangement);
|
||||
const target = object(object(object(result.report).requery).subpage).arrangement_target;
|
||||
const mode = firstText(contextArrangement.mode, context.mode, arrangement.mode);
|
||||
const updates = mode === 'update'
|
||||
? { ...object(arrangement.changes), ...object(contextArrangement.changes) }
|
||||
: {};
|
||||
const id = identifier ? `团号:${identifier}` : '';
|
||||
if (mode === 'clear') {
|
||||
return {
|
||||
@@ -413,16 +416,19 @@ function arrangementReply(
|
||||
fields: { identifier, mode: 'clear' }
|
||||
};
|
||||
}
|
||||
const ref = object(contextArrangement.resource || arrangement.resource || contextArrangement.supplier || arrangement.supplier);
|
||||
const ref = object(updates.resource || updates.supplier || contextArrangement.resource || arrangement.resource || contextArrangement.supplier || arrangement.supplier);
|
||||
const item = firstText(
|
||||
updates.item,
|
||||
contextArrangement.item,
|
||||
arrangement.item,
|
||||
object(target).item_value
|
||||
);
|
||||
const start = firstText(contextArrangement.start_date, arrangement.start_date);
|
||||
const end = firstText(contextArrangement.end_date, arrangement.end_date);
|
||||
const end = firstText(updates.end_date, contextArrangement.end_date, arrangement.end_date);
|
||||
const date = firstText(contextArrangement.date, arrangement.date);
|
||||
const quantity = firstNumber(
|
||||
updates.room_count,
|
||||
updates.quantity,
|
||||
contextArrangement.quantity,
|
||||
arrangement.quantity,
|
||||
contextArrangement.room_count,
|
||||
|
||||
@@ -1893,10 +1893,9 @@ function operationsDashboardRouteFromOperation(value: unknown, fallbackText: unk
|
||||
if (referenceKind === 'independent_order') return businessRouteById('passenger_list_import_independent');
|
||||
if (referenceKind === 'shared_child_order') return businessRouteById('passenger_list_import_shared_child');
|
||||
}
|
||||
if (action === 'arrangement_hotel') {
|
||||
if (action.startsWith('arrangement_')) {
|
||||
const mode = text(jsonObject(data.arrangement).mode || data.mode).trim();
|
||||
if (mode === 'create') return businessRouteById('arrangement_hotel_create');
|
||||
if (mode === 'update') return businessRouteById('arrangement_hotel_update');
|
||||
return candidates.find((route) => route.discriminator === mode) || null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user