@d19n/youfibre-odin-sdk 2.0.251 → 2.0.253
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.
- package/dist/actions-v2/UpdateCallAnalyticsDto.d.ts +10 -3
- package/dist/actions-v2/UpdateCallOnInboundCallAnsweredDto.d.ts +10 -3
- package/dist/actions-v2/UpdateCallOnNumberCallStatusDto.d.ts +10 -3
- package/dist/entities-v2/Call.d.ts +18 -4
- package/dist/entities-v2/Call.js +4 -0
- package/dist/wholesale-provider/kci-effect-bridge.d.ts +10 -0
- package/dist/wholesale-provider/kci-effect-bridge.js +54 -9
- package/dist/wholesale-provider/test/kci-effect-bridge.spec.js +21 -1
- package/package.json +1 -1
|
@@ -22,17 +22,17 @@ export interface UpdateCallAnalyticsMessage extends OdinRecordUpdatedEvent<Updat
|
|
|
22
22
|
* Properties for UpdateCallAnalytics action
|
|
23
23
|
*
|
|
24
24
|
* @property Required fields: 3
|
|
25
|
-
* @property Optional fields:
|
|
25
|
+
* @property Optional fields: 2
|
|
26
26
|
*/
|
|
27
27
|
export interface UpdateCallAnalyticsProperties {
|
|
28
28
|
/**
|
|
29
29
|
* TimeInProgress
|
|
30
30
|
*
|
|
31
31
|
* TimeInProgress (Communications - Call)
|
|
32
|
-
* @type {
|
|
32
|
+
* @type {NUMBER}
|
|
33
33
|
* @required
|
|
34
34
|
*/
|
|
35
|
-
TimeInProgress:
|
|
35
|
+
TimeInProgress: number;
|
|
36
36
|
/**
|
|
37
37
|
* CustomerHoldTotalSec
|
|
38
38
|
*
|
|
@@ -49,6 +49,13 @@ export interface UpdateCallAnalyticsProperties {
|
|
|
49
49
|
* @required
|
|
50
50
|
*/
|
|
51
51
|
AgentHoldTotalSec: string;
|
|
52
|
+
/**
|
|
53
|
+
* TimeInProgressFormatted
|
|
54
|
+
*
|
|
55
|
+
* TimeInProgressFormatted
|
|
56
|
+
* @type {TEXT}
|
|
57
|
+
*/
|
|
58
|
+
TimeInProgressFormatted?: string | null;
|
|
52
59
|
/**
|
|
53
60
|
* Time in IVR
|
|
54
61
|
*
|
|
@@ -22,7 +22,7 @@ export interface UpdateCallOnInboundCallAnsweredMessage extends OdinRecordUpdate
|
|
|
22
22
|
* Properties for UpdateCallOnInboundCallAnswered action
|
|
23
23
|
*
|
|
24
24
|
* @property Required fields: 4
|
|
25
|
-
* @property Optional fields:
|
|
25
|
+
* @property Optional fields: 5
|
|
26
26
|
*/
|
|
27
27
|
export interface UpdateCallOnInboundCallAnsweredProperties {
|
|
28
28
|
/**
|
|
@@ -53,10 +53,17 @@ export interface UpdateCallOnInboundCallAnsweredProperties {
|
|
|
53
53
|
* QueueTime
|
|
54
54
|
*
|
|
55
55
|
* Seconds spent in queue (Communications - Call)
|
|
56
|
-
* @type {
|
|
56
|
+
* @type {NUMBER}
|
|
57
57
|
* @required
|
|
58
58
|
*/
|
|
59
|
-
QueueTime:
|
|
59
|
+
QueueTime: number;
|
|
60
|
+
/**
|
|
61
|
+
* QueueTimeFormatted
|
|
62
|
+
*
|
|
63
|
+
* QueueTimeFormatted
|
|
64
|
+
* @type {TEXT}
|
|
65
|
+
*/
|
|
66
|
+
QueueTimeFormatted?: string | null;
|
|
60
67
|
/**
|
|
61
68
|
* RingingDuration
|
|
62
69
|
*
|
|
@@ -22,7 +22,7 @@ export interface UpdateCallOnNumberCallStatusMessage extends OdinRecordUpdatedEv
|
|
|
22
22
|
* Properties for UpdateCallOnNumberCallStatus action
|
|
23
23
|
*
|
|
24
24
|
* @property Required fields: 5
|
|
25
|
-
* @property Optional fields:
|
|
25
|
+
* @property Optional fields: 1
|
|
26
26
|
*/
|
|
27
27
|
export interface UpdateCallOnNumberCallStatusProperties {
|
|
28
28
|
/**
|
|
@@ -61,10 +61,17 @@ export interface UpdateCallOnNumberCallStatusProperties {
|
|
|
61
61
|
* QueueTime
|
|
62
62
|
*
|
|
63
63
|
* Seconds spent in queue (Communications - Call)
|
|
64
|
-
* @type {
|
|
64
|
+
* @type {NUMBER}
|
|
65
65
|
* @required
|
|
66
66
|
*/
|
|
67
|
-
QueueTime:
|
|
67
|
+
QueueTime: number;
|
|
68
|
+
/**
|
|
69
|
+
* QueueTimeFormatted
|
|
70
|
+
*
|
|
71
|
+
* QueueTimeFormatted
|
|
72
|
+
* @type {TEXT}
|
|
73
|
+
*/
|
|
74
|
+
QueueTimeFormatted?: string | null;
|
|
68
75
|
}
|
|
69
76
|
/**
|
|
70
77
|
* UpdateCallOnNumberCallStatus
|
|
@@ -224,6 +224,10 @@ export declare enum CallPropertyKeys {
|
|
|
224
224
|
RecordingDurationFormatted = "RecordingDurationFormatted",
|
|
225
225
|
/** Tracks the agent side call status. Used by Communications team. */
|
|
226
226
|
AgentCallStatus = "AgentCallStatus",
|
|
227
|
+
/** TimeInProgressFormatted */
|
|
228
|
+
TimeInProgressFormatted = "TimeInProgressFormatted",
|
|
229
|
+
/** QueueTimeFormatted */
|
|
230
|
+
QueueTimeFormatted = "QueueTimeFormatted",
|
|
227
231
|
/** Data field for Call records. Used by Communications team. */
|
|
228
232
|
EndTime = "EndTime",
|
|
229
233
|
/** RecordingDuration (Communications - Call) */
|
|
@@ -354,9 +358,9 @@ export interface CallProperties {
|
|
|
354
358
|
WorkOrderId: string;
|
|
355
359
|
/** Seconds spent in queue (Communications - Call)
|
|
356
360
|
*
|
|
357
|
-
* @type {
|
|
361
|
+
* @type {NUMBER}
|
|
358
362
|
*/
|
|
359
|
-
QueueTime:
|
|
363
|
+
QueueTime: number;
|
|
360
364
|
/** TransferCallId (Communications - Call)
|
|
361
365
|
*
|
|
362
366
|
* @type {UUID}
|
|
@@ -389,9 +393,9 @@ export interface CallProperties {
|
|
|
389
393
|
TransferCount: number;
|
|
390
394
|
/** TimeInProgress (Communications - Call)
|
|
391
395
|
*
|
|
392
|
-
* @type {
|
|
396
|
+
* @type {NUMBER}
|
|
393
397
|
*/
|
|
394
|
-
TimeInProgress:
|
|
398
|
+
TimeInProgress: number;
|
|
395
399
|
/** OutboundDialCampaignId
|
|
396
400
|
*
|
|
397
401
|
* @type {LOOKUP}
|
|
@@ -460,6 +464,16 @@ export interface CallProperties {
|
|
|
460
464
|
* @type {TEXT}
|
|
461
465
|
*/
|
|
462
466
|
AgentCallStatus: string;
|
|
467
|
+
/** TimeInProgressFormatted
|
|
468
|
+
*
|
|
469
|
+
* @type {TEXT}
|
|
470
|
+
*/
|
|
471
|
+
TimeInProgressFormatted: string;
|
|
472
|
+
/** QueueTimeFormatted
|
|
473
|
+
*
|
|
474
|
+
* @type {TEXT}
|
|
475
|
+
*/
|
|
476
|
+
QueueTimeFormatted: string;
|
|
463
477
|
/** Data field for Call records. Used by Communications team.
|
|
464
478
|
*
|
|
465
479
|
* @type {DATE_TIME}
|
package/dist/entities-v2/Call.js
CHANGED
|
@@ -224,6 +224,10 @@ var CallPropertyKeys;
|
|
|
224
224
|
CallPropertyKeys["RecordingDurationFormatted"] = "RecordingDurationFormatted";
|
|
225
225
|
/** Tracks the agent side call status. Used by Communications team. */
|
|
226
226
|
CallPropertyKeys["AgentCallStatus"] = "AgentCallStatus";
|
|
227
|
+
/** TimeInProgressFormatted */
|
|
228
|
+
CallPropertyKeys["TimeInProgressFormatted"] = "TimeInProgressFormatted";
|
|
229
|
+
/** QueueTimeFormatted */
|
|
230
|
+
CallPropertyKeys["QueueTimeFormatted"] = "QueueTimeFormatted";
|
|
227
231
|
/** Data field for Call records. Used by Communications team. */
|
|
228
232
|
CallPropertyKeys["EndTime"] = "EndTime";
|
|
229
233
|
/** RecordingDuration (Communications - Call) */
|
|
@@ -120,6 +120,16 @@ export interface KciEffectRow {
|
|
|
120
120
|
* matching row (with supplierCodes) over the generic fallback (no
|
|
121
121
|
* supplierCodes). */
|
|
122
122
|
readonly supplierCodes?: ReadonlyArray<string>;
|
|
123
|
+
/** Optional envelope guard — when set, the row only matches if `when(dto)`
|
|
124
|
+
* returns true. A second discriminator axis alongside `supplierCodes`, for
|
|
125
|
+
* reasonCodes whose YF effect depends on an envelope FIELD rather than a
|
|
126
|
+
* supplier code. Used to split the ambiguous `RESUMED` KCI: the same
|
|
127
|
+
* reasonCode is emitted both for a resume-from-hold (no new slot → log only)
|
|
128
|
+
* and for a reappointment-out-of-remediation (carries a rebooked
|
|
129
|
+
* `appointmentDate` → schedule the WO). The walker prefers a matching
|
|
130
|
+
* guarded row over the generic (no-discriminator) fallback, mirroring the
|
|
131
|
+
* `supplierCodes` precedence. */
|
|
132
|
+
readonly when?: (dto: FibreCafeKciDto) => boolean;
|
|
123
133
|
readonly yfEffect: YfKciEffect;
|
|
124
134
|
/** Always true per rule #4. Field is explicit so future rows that
|
|
125
135
|
* document an intentional skip can mark it false. */
|
|
@@ -143,18 +143,56 @@ exports.KCI_EFFECT_ROWS = [
|
|
|
143
143
|
},
|
|
144
144
|
logAgainstOrder: true,
|
|
145
145
|
},
|
|
146
|
+
{
|
|
147
|
+
// T-20260508-011: NET emits RESUMED (not REAPPOINTED) for a rebook OUT OF
|
|
148
|
+
// RemediationRequired, carrying the rebooked slot on the flat envelope
|
|
149
|
+
// (`appointmentDate` / `appointmentTimeBlock`). `appointmentDate` is only
|
|
150
|
+
// populated on rebooked-slot events (see FibreCafeKciDto.appointmentDate
|
|
151
|
+
// doc) — a plain resume-from-hold RESUMED carries no new slot — so its
|
|
152
|
+
// presence uniquely marks the reappointment case. Route it through the
|
|
153
|
+
// same bundled effect as REAPPOINTED: WO → Scheduled + set appointment
|
|
154
|
+
// date + stamp supplier reservation reference + write Order.CommittedDate.
|
|
155
|
+
// Without this, the reschedule was logged only and the YF WO stayed stuck
|
|
156
|
+
// in RemediationRequired while NET moved to Scheduled (WO2758769 /
|
|
157
|
+
// WO2760529, 2026-07-23).
|
|
158
|
+
id: 'provide-resumed-reappoint',
|
|
159
|
+
reasonCode: 'RESUMED',
|
|
160
|
+
serviceOrderKind: 'PROVIDE',
|
|
161
|
+
when: (dto) => Boolean(dto.appointmentDate),
|
|
162
|
+
yfEffect: {
|
|
163
|
+
kind: 'WO_SCHEDULE_APPOINTMENT',
|
|
164
|
+
scheduleAppointment: {
|
|
165
|
+
dateFrom: 'appointmentDate',
|
|
166
|
+
timeBlockFrom: 'appointmentTimeBlock',
|
|
167
|
+
supplierReferenceFrom: 'provideServiceOrder.appointmentReservationReference',
|
|
168
|
+
rescheduleReason: 'SUPPLIER_RESCHEDULED_APPOINTMENT',
|
|
169
|
+
},
|
|
170
|
+
propertyWrite: {
|
|
171
|
+
entityKey: 'OrderModule:Order',
|
|
172
|
+
properties: ['CommittedDate'],
|
|
173
|
+
},
|
|
174
|
+
notes: 'Reappointment-out-of-remediation delivered via RESUMED (not ' +
|
|
175
|
+
'REAPPOINTED). Same bundled effect as REAPPOINTED: WO → Scheduled + ' +
|
|
176
|
+
'appointment date + supplier reservation reference + new ' +
|
|
177
|
+
'Order.CommittedDate. Guarded on appointmentDate presence so only the ' +
|
|
178
|
+
'rebook variant of RESUMED is scheduled; bare RESUMED falls through to ' +
|
|
179
|
+
'provide-resumed (log only).',
|
|
180
|
+
},
|
|
181
|
+
logAgainstOrder: true,
|
|
182
|
+
},
|
|
146
183
|
{
|
|
147
184
|
id: 'provide-resumed',
|
|
148
185
|
reasonCode: 'RESUMED',
|
|
149
186
|
serviceOrderKind: 'PROVIDE',
|
|
150
187
|
yfEffect: {
|
|
151
188
|
kind: 'LOG_KCI_ONLY',
|
|
152
|
-
notes: "RESUMED is NET-side ambiguous
|
|
153
|
-
"the invalid-in-YF
|
|
154
|
-
"
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"
|
|
189
|
+
notes: "Bare RESUMED (no rebooked appointmentDate) is NET-side ambiguous " +
|
|
190
|
+
"(emitted for both Held→InProgress and the invalid-in-YF " +
|
|
191
|
+
"Pending→InProgress). Per Frank: Pending→InProgress is never a valid " +
|
|
192
|
+
"YF transition (Pending exits to Scheduled only). RESOLVED already " +
|
|
193
|
+
"covers the RemediationRequired→InProgress case. The rebook variant " +
|
|
194
|
+
"(RESUMED carrying appointmentDate) is handled by " +
|
|
195
|
+
"provide-resumed-reappoint above; this row is the log-only fallback.",
|
|
158
196
|
},
|
|
159
197
|
logAgainstOrder: true,
|
|
160
198
|
},
|
|
@@ -618,9 +656,16 @@ function decideKciDispatch(dto) {
|
|
|
618
656
|
// walker resolves each variant to its specific WO target action.
|
|
619
657
|
const candidates = exports.KCI_EFFECT_ROWS.filter((r) => r.reasonCode === dto.reasonCode && r.serviceOrderKind === kind);
|
|
620
658
|
const dtoSupplierCodes = (_a = dto.supplierCodes) !== null && _a !== void 0 ? _a : [];
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
659
|
+
// A row is eligible only if BOTH discriminators it declares pass: the
|
|
660
|
+
// supplierCode gate (intersects the KCI's codes) and the envelope `when`
|
|
661
|
+
// guard. Undeclared gates pass by default.
|
|
662
|
+
const eligible = candidates.filter((r) => (r.supplierCodes === undefined ||
|
|
663
|
+
r.supplierCodes.some((sc) => dtoSupplierCodes.includes(sc))) &&
|
|
664
|
+
(r.when === undefined || r.when(dto)));
|
|
665
|
+
// Prefer a discriminated row (carries a supplierCode or a `when` guard) over
|
|
666
|
+
// the fully-generic fallback for the same reasonCode.
|
|
667
|
+
const specificMatch = eligible.find((r) => r.supplierCodes !== undefined || r.when !== undefined);
|
|
668
|
+
const genericMatch = eligible.find((r) => r.supplierCodes === undefined && r.when === undefined);
|
|
624
669
|
const row = specificMatch !== null && specificMatch !== void 0 ? specificMatch : genericMatch;
|
|
625
670
|
if (!row) {
|
|
626
671
|
return { kind: 'NO_MATCH', reasonCode: dto.reasonCode, serviceOrderKind: kind };
|
|
@@ -48,6 +48,7 @@ function makeProvideDto(reasonCode) {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
function run() {
|
|
51
|
+
var _a;
|
|
51
52
|
console.log('KCI effect bridge — walker tests\n');
|
|
52
53
|
// ── 1. detectKciKind correctly classifies ──────────────────────────
|
|
53
54
|
console.log('Test: detectKciKind on PROVIDE');
|
|
@@ -135,10 +136,29 @@ function run() {
|
|
|
135
136
|
assert("semanticOp=move-to-remediation", decision.semanticOp === 'move-to-remediation');
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
|
-
console.log('Test: RESUMED → LOG_KCI_ONLY (
|
|
139
|
+
console.log('Test: bare RESUMED (no appointmentDate) → LOG_KCI_ONLY (ambiguous resume-from-hold)');
|
|
139
140
|
{
|
|
140
141
|
const decision = (0, kci_effect_bridge_1.decideKciDispatch)(makeProvideDto('RESUMED'));
|
|
141
142
|
assert('kind=LOG_KCI_ONLY', decision.kind === 'LOG_KCI_ONLY');
|
|
143
|
+
if (decision.kind === 'LOG_KCI_ONLY') {
|
|
144
|
+
assert('row=provide-resumed', decision.row.id === 'provide-resumed');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
console.log('Test: RESUMED WITH appointmentDate → WO_SCHEDULE_APPOINTMENT (reappoint-out-of-remediation; T-20260508-011)');
|
|
148
|
+
{
|
|
149
|
+
const dto = makeProvideDto('RESUMED');
|
|
150
|
+
dto.appointmentDate = '2026-07-23';
|
|
151
|
+
dto.appointmentTimeBlock = 'AM';
|
|
152
|
+
const decision = (0, kci_effect_bridge_1.decideKciDispatch)(dto);
|
|
153
|
+
assert('kind=WO_SCHEDULE_APPOINTMENT', decision.kind === 'WO_SCHEDULE_APPOINTMENT');
|
|
154
|
+
if (decision.kind === 'WO_SCHEDULE_APPOINTMENT') {
|
|
155
|
+
assert('row=provide-resumed-reappoint', decision.row.id === 'provide-resumed-reappoint');
|
|
156
|
+
assert('dateFrom=appointmentDate', decision.dateFrom === 'appointmentDate');
|
|
157
|
+
assert('timeBlockFrom=appointmentTimeBlock', decision.timeBlockFrom === 'appointmentTimeBlock');
|
|
158
|
+
assert('rescheduleReason=SUPPLIER_RESCHEDULED_APPOINTMENT', decision.rescheduleReason === 'SUPPLIER_RESCHEDULED_APPOINTMENT');
|
|
159
|
+
assert('co-effect propertyWrite CommittedDate', ((_a = decision.propertyWrite) === null || _a === void 0 ? void 0 : _a.entityKey) === 'OrderModule:Order' &&
|
|
160
|
+
decision.propertyWrite.properties.includes('CommittedDate'));
|
|
161
|
+
}
|
|
142
162
|
}
|
|
143
163
|
console.log('Test: REAPPOINTED → WO_SCHEDULE_APPOINTMENT (reschedule: flat fields + rescheduleReason)');
|
|
144
164
|
{
|