@d19n/youfibre-odin-sdk 2.0.157 → 2.0.158
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -89,6 +89,20 @@ export interface YfKciEffect {
|
|
|
89
89
|
readonly timeBlockFrom: string;
|
|
90
90
|
/** JSONPath for the supplier-side appointment reservation reference. */
|
|
91
91
|
readonly supplierReferenceFrom: string;
|
|
92
|
+
/**
|
|
93
|
+
* When set, this dispatch is a *reschedule* (not a first-time booking):
|
|
94
|
+
* the dispatcher passes this value as the schedule-appointment
|
|
95
|
+
* `RescheduleReason`, which makes field-service update the existing
|
|
96
|
+
* ServiceAppointment in place AND record a `SA_RESCHEDULE` ChangeReason
|
|
97
|
+
* (Owner derived from the reason; supplier reasons → SUPPLIER).
|
|
98
|
+
*
|
|
99
|
+
* NOTE: `ChangeReasonRescheduleReason` does NOT map 1:1 to KCI types —
|
|
100
|
+
* the KCI only tells us *that* the supplier moved the slot, not the
|
|
101
|
+
* granular operational reason. We therefore stamp a single neutral
|
|
102
|
+
* supplier-owned value (`SCHEDULE_CHANGED`) for every supplier-driven
|
|
103
|
+
* KCI reschedule rather than inventing a per-KCI mapping.
|
|
104
|
+
*/
|
|
105
|
+
readonly rescheduleReason?: string;
|
|
92
106
|
};
|
|
93
107
|
readonly notes: string;
|
|
94
108
|
}
|
|
@@ -139,6 +153,13 @@ export declare type KciDispatchDecision = {
|
|
|
139
153
|
entityKey: YfKciTargetEntity;
|
|
140
154
|
properties: ReadonlyArray<string>;
|
|
141
155
|
};
|
|
156
|
+
/**
|
|
157
|
+
* Present only for reschedule rows. The dispatcher forwards it as the
|
|
158
|
+
* schedule-appointment `RescheduleReason`, triggering in-place SA update
|
|
159
|
+
* + `SA_RESCHEDULE` ChangeReason creation. Absent for first-booking-style
|
|
160
|
+
* schedule-appointment effects.
|
|
161
|
+
*/
|
|
162
|
+
rescheduleReason?: string;
|
|
142
163
|
} | {
|
|
143
164
|
kind: 'PROPERTY_WRITE';
|
|
144
165
|
row: KciEffectRow;
|
|
@@ -189,9 +189,16 @@ exports.KCI_EFFECT_ROWS = [
|
|
|
189
189
|
yfEffect: {
|
|
190
190
|
kind: 'WO_SCHEDULE_APPOINTMENT',
|
|
191
191
|
scheduleAppointment: {
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
// NET now stamps the rebooked slot on the flat KCI envelope fields
|
|
193
|
+
// (appointmentDate / appointmentTimeBlock) rather than the nested
|
|
194
|
+
// provideServiceOrder.appointmentTimeslot. appointmentTimeBlock is
|
|
195
|
+
// already the AM/PM block — no WEEKDAY_ prefix to trim.
|
|
196
|
+
dateFrom: 'appointmentDate',
|
|
197
|
+
timeBlockFrom: 'appointmentTimeBlock',
|
|
194
198
|
supplierReferenceFrom: 'provideServiceOrder.appointmentReservationReference',
|
|
199
|
+
// REAPPOINTED is a supplier reschedule → update existing SA in place
|
|
200
|
+
// + record the SA_RESCHEDULE ChangeReason.
|
|
201
|
+
rescheduleReason: 'SCHEDULE_CHANGED',
|
|
195
202
|
},
|
|
196
203
|
// Co-effect: REAPPOINTED's dto also carries an updated
|
|
197
204
|
// `provideServiceOrder.committedDate` for the rebooked appointment.
|
|
@@ -321,6 +328,30 @@ exports.KCI_EFFECT_ROWS = [
|
|
|
321
328
|
},
|
|
322
329
|
logAgainstOrder: true,
|
|
323
330
|
},
|
|
331
|
+
{
|
|
332
|
+
// Supplier rescheduled the REMEDIATION visit. Move the REMEDIATION WO's
|
|
333
|
+
// appointment to the new slot and land it back in Scheduled, recording an
|
|
334
|
+
// SA_RESCHEDULE ChangeReason. Emitted on the parent INSTALL's PROVIDE
|
|
335
|
+
// order; tenantWorkOrderReference points at the REMEDIATION WO.
|
|
336
|
+
id: 'provide-update-remediation-rescheduled',
|
|
337
|
+
reasonCode: 'UPDATE',
|
|
338
|
+
serviceOrderKind: 'PROVIDE',
|
|
339
|
+
supplierCodes: ['NETOMNIA_REMEDIATION_WORK_ORDER_RESCHEDULED'],
|
|
340
|
+
yfEffect: {
|
|
341
|
+
kind: 'WO_SCHEDULE_APPOINTMENT',
|
|
342
|
+
scheduleAppointment: {
|
|
343
|
+
dateFrom: 'appointmentDate',
|
|
344
|
+
timeBlockFrom: 'appointmentTimeBlock',
|
|
345
|
+
supplierReferenceFrom: 'provideServiceOrder.appointmentReservationReference',
|
|
346
|
+
rescheduleReason: 'SCHEDULE_CHANGED',
|
|
347
|
+
},
|
|
348
|
+
notes: 'Supplier-confirmed REMEDIATION reschedule. Update the WO ' +
|
|
349
|
+
'ServiceAppointment Date/TimeBlock in place, land the WO in Scheduled, ' +
|
|
350
|
+
'and record the SA_RESCHEDULE ChangeReason. No Order stage / CommittedDate ' +
|
|
351
|
+
'effect (REMEDIATION is its own WO; parent INSTALL stage unchanged).',
|
|
352
|
+
},
|
|
353
|
+
logAgainstOrder: true,
|
|
354
|
+
},
|
|
324
355
|
// ═══════════════════════════════════════════════════════════════════
|
|
325
356
|
// REPAIR — SERVICE WO state changes (post-install fault / service visit)
|
|
326
357
|
//
|
|
@@ -365,6 +396,30 @@ exports.KCI_EFFECT_ROWS = [
|
|
|
365
396
|
},
|
|
366
397
|
logAgainstOrder: true,
|
|
367
398
|
},
|
|
399
|
+
{
|
|
400
|
+
// Supplier rescheduled the SERVICE (repair) visit. Move the SERVICE WO's
|
|
401
|
+
// appointment to the new slot and land it back in Scheduled (from any
|
|
402
|
+
// non-terminal stage, incl. EnRoute/InProgress), recording an
|
|
403
|
+
// SA_RESCHEDULE ChangeReason. Detected REPAIR via the
|
|
404
|
+
// NETOMNIA_SERVICE_WORK_ORDER_ prefix in detectKciKind.
|
|
405
|
+
id: 'repair-update-service-rescheduled',
|
|
406
|
+
reasonCode: 'UPDATE',
|
|
407
|
+
serviceOrderKind: 'REPAIR',
|
|
408
|
+
supplierCodes: ['NETOMNIA_SERVICE_WORK_ORDER_RESCHEDULED'],
|
|
409
|
+
yfEffect: {
|
|
410
|
+
kind: 'WO_SCHEDULE_APPOINTMENT',
|
|
411
|
+
scheduleAppointment: {
|
|
412
|
+
dateFrom: 'appointmentDate',
|
|
413
|
+
timeBlockFrom: 'appointmentTimeBlock',
|
|
414
|
+
supplierReferenceFrom: 'provideServiceOrder.appointmentReservationReference',
|
|
415
|
+
rescheduleReason: 'SCHEDULE_CHANGED',
|
|
416
|
+
},
|
|
417
|
+
notes: 'Supplier-confirmed SERVICE reschedule. Update the WO ServiceAppointment ' +
|
|
418
|
+
'Date/TimeBlock in place, land the WO in Scheduled, and record the ' +
|
|
419
|
+
'SA_RESCHEDULE ChangeReason. No Order stage effect (changesOrderStage:false).',
|
|
420
|
+
},
|
|
421
|
+
logAgainstOrder: true,
|
|
422
|
+
},
|
|
368
423
|
// ── PROVIDE — informational / no-action ──────────────────────────
|
|
369
424
|
// Generic UPDATE row — fallback for KCIs that don't carry a recognized
|
|
370
425
|
// supplierCode. Walker matches this only after rejecting all
|
|
@@ -543,13 +598,15 @@ function decideKciDispatch(dto) {
|
|
|
543
598
|
if (!row.yfEffect.scheduleAppointment) {
|
|
544
599
|
throw new Error(`bridge invariant: WO_SCHEDULE_APPOINTMENT row ${row.id} missing scheduleAppointment`);
|
|
545
600
|
}
|
|
546
|
-
return Object.assign({ kind: 'WO_SCHEDULE_APPOINTMENT', row, dateFrom: row.yfEffect.scheduleAppointment.dateFrom, timeBlockFrom: row.yfEffect.scheduleAppointment.timeBlockFrom, supplierReferenceFrom: row.yfEffect.scheduleAppointment.supplierReferenceFrom }, (row.yfEffect.propertyWrite
|
|
601
|
+
return Object.assign(Object.assign({ kind: 'WO_SCHEDULE_APPOINTMENT', row, dateFrom: row.yfEffect.scheduleAppointment.dateFrom, timeBlockFrom: row.yfEffect.scheduleAppointment.timeBlockFrom, supplierReferenceFrom: row.yfEffect.scheduleAppointment.supplierReferenceFrom }, (row.yfEffect.propertyWrite
|
|
547
602
|
? {
|
|
548
603
|
propertyWrite: {
|
|
549
604
|
entityKey: row.yfEffect.propertyWrite.entityKey,
|
|
550
605
|
properties: row.yfEffect.propertyWrite.properties,
|
|
551
606
|
},
|
|
552
607
|
}
|
|
608
|
+
: {})), (row.yfEffect.scheduleAppointment.rescheduleReason
|
|
609
|
+
? { rescheduleReason: row.yfEffect.scheduleAppointment.rescheduleReason }
|
|
553
610
|
: {}));
|
|
554
611
|
case 'PROPERTY_WRITE':
|
|
555
612
|
if (!row.yfEffect.propertyWrite) {
|
|
@@ -140,10 +140,39 @@ function run() {
|
|
|
140
140
|
const decision = (0, kci_effect_bridge_1.decideKciDispatch)(makeProvideDto('RESUMED'));
|
|
141
141
|
assert('kind=LOG_KCI_ONLY', decision.kind === 'LOG_KCI_ONLY');
|
|
142
142
|
}
|
|
143
|
-
console.log('Test: REAPPOINTED → WO_SCHEDULE_APPOINTMENT (
|
|
143
|
+
console.log('Test: REAPPOINTED → WO_SCHEDULE_APPOINTMENT (reschedule: flat fields + rescheduleReason)');
|
|
144
144
|
{
|
|
145
145
|
const decision = (0, kci_effect_bridge_1.decideKciDispatch)(makeProvideDto('REAPPOINTED'));
|
|
146
146
|
assert('kind=WO_SCHEDULE_APPOINTMENT', decision.kind === 'WO_SCHEDULE_APPOINTMENT');
|
|
147
|
+
if (decision.kind === 'WO_SCHEDULE_APPOINTMENT') {
|
|
148
|
+
assert('dateFrom=appointmentDate', decision.dateFrom === 'appointmentDate');
|
|
149
|
+
assert('timeBlockFrom=appointmentTimeBlock', decision.timeBlockFrom === 'appointmentTimeBlock');
|
|
150
|
+
assert('rescheduleReason=SCHEDULE_CHANGED', decision.rescheduleReason === 'SCHEDULE_CHANGED');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
console.log('Test: SERVICE WORK_ORDER_RESCHEDULED → WO_SCHEDULE_APPOINTMENT (REPAIR reschedule)');
|
|
154
|
+
{
|
|
155
|
+
const dto = makeProvideDto('UPDATE');
|
|
156
|
+
dto.supplierCodes = ['NETOMNIA_SERVICE_WORK_ORDER_RESCHEDULED'];
|
|
157
|
+
const decision = (0, kci_effect_bridge_1.decideKciDispatch)(dto);
|
|
158
|
+
assert('detectKciKind=REPAIR', (0, kci_effect_bridge_1.detectKciKind)(dto) === 'REPAIR');
|
|
159
|
+
assert('kind=WO_SCHEDULE_APPOINTMENT', decision.kind === 'WO_SCHEDULE_APPOINTMENT');
|
|
160
|
+
if (decision.kind === 'WO_SCHEDULE_APPOINTMENT') {
|
|
161
|
+
assert('row=repair-update-service-rescheduled', decision.row.id === 'repair-update-service-rescheduled');
|
|
162
|
+
assert('rescheduleReason=SCHEDULE_CHANGED', decision.rescheduleReason === 'SCHEDULE_CHANGED');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
console.log('Test: REMEDIATION WORK_ORDER_RESCHEDULED → WO_SCHEDULE_APPOINTMENT (PROVIDE reschedule)');
|
|
166
|
+
{
|
|
167
|
+
const dto = makeProvideDto('UPDATE');
|
|
168
|
+
dto.supplierCodes = ['NETOMNIA_REMEDIATION_WORK_ORDER_RESCHEDULED'];
|
|
169
|
+
const decision = (0, kci_effect_bridge_1.decideKciDispatch)(dto);
|
|
170
|
+
assert('detectKciKind=PROVIDE', (0, kci_effect_bridge_1.detectKciKind)(dto) === 'PROVIDE');
|
|
171
|
+
assert('kind=WO_SCHEDULE_APPOINTMENT', decision.kind === 'WO_SCHEDULE_APPOINTMENT');
|
|
172
|
+
if (decision.kind === 'WO_SCHEDULE_APPOINTMENT') {
|
|
173
|
+
assert('row=provide-update-remediation-rescheduled', decision.row.id === 'provide-update-remediation-rescheduled');
|
|
174
|
+
assert('rescheduleReason=SCHEDULE_CHANGED', decision.rescheduleReason === 'SCHEDULE_CHANGED');
|
|
175
|
+
}
|
|
147
176
|
}
|
|
148
177
|
console.log('Test: WARNING → LOG_KCI_ONLY');
|
|
149
178
|
{
|
|
@@ -496,6 +496,15 @@ export interface FibreCafeKciDto {
|
|
|
496
496
|
* supplierCode discriminator needed).
|
|
497
497
|
*/
|
|
498
498
|
tenantWorkOrderReference?: string | null;
|
|
499
|
+
/**
|
|
500
|
+
* Rebooked appointment slot — set on REAPPOINTED and the SERVICE /
|
|
501
|
+
* REMEDIATION *_RESCHEDULED UPDATE KCIs. NET stamps these flat envelope
|
|
502
|
+
* fields directly (in preference to the nested
|
|
503
|
+
* provideServiceOrder.appointmentTimeslot). `appointmentDate` is the ISO
|
|
504
|
+
* appointment date; `appointmentTimeBlock` is the AM/PM block.
|
|
505
|
+
*/
|
|
506
|
+
appointmentDate?: string;
|
|
507
|
+
appointmentTimeBlock?: 'AM' | 'PM';
|
|
499
508
|
/** Full Odin WorkOrder record — sent for WO stage/appointment updates. */
|
|
500
509
|
workOrder?: OdinRecord<WorkOrderProperties>;
|
|
501
510
|
/** Partial Odin ServiceAppointment record — sent for WO stage/appointment updates. */
|