@d19n/youfibre-odin-sdk 2.0.151 → 2.0.156

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.
@@ -25,7 +25,7 @@
25
25
  *
26
26
  * Linked: T-20260508-009 / T-20260508-010
27
27
  */
28
- export type { WholesaleProvider, CreateOrderOptions, CancellationReasonCode, ReserveAppointmentArgs, ListAppointmentsArgs, ScheduleServiceWorkOrderArgs, RescheduleServiceWorkOrderArgs, AmendAppointmentArgs, SyncWorkOrderToSupplierArgs, UpdateOrderExternalRefArgs, } from './interface';
28
+ export type { WholesaleProvider, CreateOrderOptions, CancellationReasonCode, CancelServiceWorkOrderArgs, ReserveAppointmentArgs, ListAppointmentsArgs, ScheduleServiceWorkOrderArgs, RescheduleServiceWorkOrderArgs, AmendAppointmentArgs, SyncWorkOrderToSupplierArgs, UpdateOrderExternalRefArgs, } from './interface';
29
29
  export type { WholesaleResult, WholesaleSyncResponse, WholesaleRejectionResponse, WholesaleRejectionMessage, WholesaleBUS, WholesaleSYS, ISO8601DateTime, ISODate, TransactionId, UPRN, UUID, SwitchOrderReference, } from './types/wholesale';
30
30
  export { NetomniaProvider, AggregateNotificationError } from './providers/netomnia-provider';
31
31
  export type { NetomniaProviderDeps } from './providers/netomnia-provider';
@@ -30,7 +30,7 @@
30
30
  * full types are available).
31
31
  */
32
32
  import type { VMO2OrderNotification, VMO2SMRNotification } from './types/vmo2';
33
- import type { FibreCafeServiceAvailability200, FibreCafeAvailableAppointments200, FibreCafeReserveAppointment201, FibreCafeScheduleServiceWorkOrder201, FibreCafeRescheduleServiceWorkOrder200, FibreCafeOrderCreate202, FibreCafeOrderAmendAccept202, FibreCafeKciDto } from './types/fibrecafe';
33
+ import type { FibreCafeServiceAvailability200, FibreCafeAvailableAppointments200, FibreCafeReserveAppointment201, FibreCafeScheduleServiceWorkOrder201, FibreCafeRescheduleServiceWorkOrder200, FibreCafeOrderCreate202, FibreCafeOrderAmendAccept202, FibreCafeKciDto, FibreCafeServiceProblemCancellationReasonCode } from './types/fibrecafe';
34
34
  /**
35
35
  * Reasons a cancellation request can be made (NET wire-level enum value
36
36
  * passed through to the FibreCafe gateway).
@@ -107,6 +107,33 @@ export interface UpdateOrderExternalRefArgs {
107
107
  oldYfId: string;
108
108
  newYfId: string;
109
109
  }
110
+ /**
111
+ * Args for the tenant-initiated cancellation of an inflight SERVICE WO
112
+ * (REPAIR). Structurally identical to `RescheduleServiceWorkOrderArgs` —
113
+ * cancel is the same action shape as reschedule, just a terminal
114
+ * transition instead of a slot move. Same flat primitive fields; only
115
+ * delta is `reasonCode` in place of the timeslot fields (mirrors how
116
+ * `cancelOrder` carries a `reasonCode` while `createOrder` doesn't).
117
+ *
118
+ * The provider impl posts to
119
+ * `POST /WholesaleGateway/v1.0/gateway/service-work-orders/cancel`. NET
120
+ * looks up its SERVICE WO by `externalId = yfWorkOrderId` (NET stamps
121
+ * the YF WO uuid as externalId at create time per
122
+ * `scheduleServiceWorkOrder`), then drives the NET WO to Cancelled via
123
+ * NET field-service `/cancel-action-apply` with a tenant-originated
124
+ * ChangeReason. The existing SERVICE WO stage-update handler emits the
125
+ * UPDATE+`NETOMNIA_SERVICE_WORK_ORDER_CANCELLED` KCI back to YF.
126
+ *
127
+ * YF only commits its own SERVICE WO → Cancelled transition after this
128
+ * call resolves successfully (NET-first semantics — see
129
+ * `CancelServiceWorkOrderTenantService` in field-service module).
130
+ */
131
+ export interface CancelServiceWorkOrderArgs {
132
+ yfWorkOrderId: string;
133
+ uprn: number;
134
+ yfOrderId: string;
135
+ reasonCode: FibreCafeServiceProblemCancellationReasonCode;
136
+ }
110
137
  /**
111
138
  * Capability-shaped interface to a wholesale supplier. Method NAMES align
112
139
  * with VMO2 spec where it makes sense; ARGS match existing NET module
@@ -154,6 +181,20 @@ export interface WholesaleProvider<TPrincipal = unknown> {
154
181
  cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<FibreCafeOrderCreate202>;
155
182
  /** Active-customer cease (post-Active churn / OTS-losing). */
156
183
  ceaseOrder(yfOrderId: string, serviceId: string): Promise<FibreCafeOrderCreate202>;
184
+ /**
185
+ * Tenant-initiated cancel of an inflight SERVICE WO. Structurally
186
+ * parallel to `rescheduleServiceWorkOrder` — same flat-primitive args,
187
+ * same response envelope shape, just a terminal transition instead of
188
+ * a slot move.
189
+ *
190
+ * **YF must NOT transition its SERVICE WO to Cancelled until this call
191
+ * resolves successfully** — the call site (`CancelServiceWorkOrderTenant
192
+ * Service` in field-service module) is responsible for gating the YF
193
+ * stage change on the response. The inbound UPDATE+CANCELLED KCI from
194
+ * NET (walker row `repair-update-service-cancelled`) is the secondary
195
+ * cure when YF didn't already advance.
196
+ */
197
+ cancelServiceWorkOrder(args: CancelServiceWorkOrderArgs): Promise<FibreCafeRescheduleServiceWorkOrder200>;
157
198
  /**
158
199
  * Inbound supplier KCI handler — the ONE inbound channel from supplier
159
200
  * to YF. KCIs drive (a) OrderKci persistence and (b) WO state changes
@@ -24,7 +24,25 @@
24
24
  * Linked: T-20260508-010 Step 3 followup (receiveKci 100% typed + handled).
25
25
  */
26
26
  import type { FibreCafeKciDto, FibreCafeKciReasonCode } from './types/fibrecafe';
27
- export declare type KciServiceOrderKind = 'PROVIDE' | 'CEASE' | 'MODIFY' | 'UNSOLICITED_CEASE';
27
+ /**
28
+ * Discriminator for the BUSINESS KIND of a KCI — not the wire envelope shape.
29
+ *
30
+ * PROVIDE / CEASE / MODIFY / UNSOLICITED_CEASE — typed by the populated
31
+ * sub-body field on the FibreCafe envelope (`provideServiceOrder` etc.).
32
+ *
33
+ * REPAIR — SERVICE WorkOrder state changes. NET emits these on the parent
34
+ * PROVIDE envelope (the SERVICE WO is a child of the PROVIDE order in
35
+ * the data model) but the semantic kind is REPAIR, discriminated by a
36
+ * `NETOMNIA_SERVICE_WORK_ORDER_*` value in `supplierCodes[]`. The
37
+ * envelope wire shape is a data-model artifact; the BP vocabulary is
38
+ * what consumers reason about (SERVICE WO = repair / service visit).
39
+ *
40
+ * `detectKciKind()` returns REPAIR when ANY supplier code matches the
41
+ * `NETOMNIA_SERVICE_WORK_ORDER_*` prefix, taking precedence over the
42
+ * envelope-field-based PROVIDE detection so service-WO KCIs route to the
43
+ * REPAIR-marked bridge rows.
44
+ */
45
+ export declare type KciServiceOrderKind = 'PROVIDE' | 'CEASE' | 'MODIFY' | 'UNSOLICITED_CEASE' | 'REPAIR';
28
46
  export declare type YfKciEffectKind = 'LOG_KCI_ONLY' | 'WO_FLOW_ACTION' | 'WO_SCHEDULE_APPOINTMENT' | 'PROPERTY_WRITE' | 'NO_OP_DOCUMENTED';
29
47
  export declare type YfKciTargetEntity = 'OrderModule:Order' | 'CrmModule:Address' | 'FieldServiceModule:WorkOrder';
30
48
  /**
@@ -321,11 +321,19 @@ exports.KCI_EFFECT_ROWS = [
321
321
  },
322
322
  logAgainstOrder: true,
323
323
  },
324
- // SERVICE WO
325
- {
326
- id: 'provide-update-service-scheduled',
324
+ // ═══════════════════════════════════════════════════════════════════
325
+ // REPAIR — SERVICE WO state changes (post-install fault / service visit)
326
+ //
327
+ // NET emits these on the parent PROVIDE envelope (data-model artifact:
328
+ // SERVICE WO is a child of the PROVIDE order). The semantic kind is
329
+ // REPAIR — discriminated by a `NETOMNIA_SERVICE_WORK_ORDER_*` value in
330
+ // `supplierCodes[]`. `detectKciKind()` recognises that prefix and
331
+ // returns REPAIR ahead of the envelope-field-based PROVIDE detection.
332
+ // ═══════════════════════════════════════════════════════════════════
333
+ {
334
+ id: 'repair-update-service-scheduled',
327
335
  reasonCode: 'UPDATE',
328
- serviceOrderKind: 'PROVIDE',
336
+ serviceOrderKind: 'REPAIR',
329
337
  supplierCodes: ['NETOMNIA_SERVICE_WORK_ORDER_SCHEDULED'],
330
338
  yfEffect: {
331
339
  kind: 'LOG_KCI_ONLY',
@@ -334,9 +342,9 @@ exports.KCI_EFFECT_ROWS = [
334
342
  logAgainstOrder: true,
335
343
  },
336
344
  {
337
- id: 'provide-update-service-en-route',
345
+ id: 'repair-update-service-en-route',
338
346
  reasonCode: 'UPDATE',
339
- serviceOrderKind: 'PROVIDE',
347
+ serviceOrderKind: 'REPAIR',
340
348
  supplierCodes: ['NETOMNIA_SERVICE_WORK_ORDER_EN_ROUTE'],
341
349
  yfEffect: {
342
350
  kind: 'WO_FLOW_ACTION',
@@ -346,9 +354,9 @@ exports.KCI_EFFECT_ROWS = [
346
354
  logAgainstOrder: true,
347
355
  },
348
356
  {
349
- id: 'provide-update-service-in-progress',
357
+ id: 'repair-update-service-in-progress',
350
358
  reasonCode: 'UPDATE',
351
- serviceOrderKind: 'PROVIDE',
359
+ serviceOrderKind: 'REPAIR',
352
360
  supplierCodes: ['NETOMNIA_SERVICE_WORK_ORDER_IN_PROGRESS'],
353
361
  yfEffect: {
354
362
  kind: 'WO_FLOW_ACTION',
@@ -448,12 +456,38 @@ exports.KCI_EFFECT_ROWS = [
448
456
  },
449
457
  logAgainstOrder: true,
450
458
  },
459
+ // SERVICE-WO cancel — same REPAIR-kind UPDATE-with-supplierCode pattern
460
+ // as the scheduled / en-route / in-progress rows above. NET enum value:
461
+ // NETOMNIA_SERVICE_WORK_ORDER_CANCELLED (netomnia-supplier-code.enum.ts).
462
+ // Dispatcher resolves the cancel semanticOp per WO.type —
463
+ // SERVICE → CancelServiceWorkOrderFlowSupplier.
464
+ {
465
+ id: 'repair-update-service-cancelled',
466
+ reasonCode: 'UPDATE',
467
+ serviceOrderKind: 'REPAIR',
468
+ supplierCodes: ['NETOMNIA_SERVICE_WORK_ORDER_CANCELLED'],
469
+ yfEffect: {
470
+ kind: 'WO_FLOW_ACTION',
471
+ semanticOp: 'cancel',
472
+ notes: 'SERVICE WO cancelled by supplier — advance YF SERVICE WO Accepted/Scheduled/EnRoute/InProgress → Cancelled via CancelServiceWorkOrderFlowSupplier. Also the inbound confirmation when YF tenant-initiated the cancel via /service-problem-cancellations; YF WO usually already at Cancelled, action is idempotent on the WO stage.',
473
+ },
474
+ logAgainstOrder: true,
475
+ },
451
476
  ];
452
477
  /**
453
478
  * Determine which serviceOrder kind the KCI is about by inspecting which
454
479
  * sub-body field is populated.
455
480
  */
456
481
  function detectKciKind(dto) {
482
+ var _a;
483
+ // REPAIR check first — SERVICE WO state changes use the NETOMNIA_SERVICE_
484
+ // WORK_ORDER_* supplier-code discriminator, regardless of which sub-body
485
+ // field is populated (it's typically `provideServiceOrder` because the
486
+ // SERVICE WO is a child of a PROVIDE order in the data model).
487
+ const codes = (_a = dto.supplierCodes) !== null && _a !== void 0 ? _a : [];
488
+ if (codes.some((c) => c.startsWith('NETOMNIA_SERVICE_WORK_ORDER_'))) {
489
+ return 'REPAIR';
490
+ }
457
491
  if (dto.provideServiceOrder)
458
492
  return 'PROVIDE';
459
493
  if (dto.ceaseServiceOrder)
@@ -27,7 +27,7 @@
27
27
  * invokes `dispatchOrderNotification` / `dispatchSMRNotification` after
28
28
  * envelope-mapping; registered handlers are fanned out.
29
29
  */
30
- import type { WholesaleProvider, CreateOrderOptions, CancellationReasonCode, ReserveAppointmentArgs, ListAppointmentsArgs, ScheduleServiceWorkOrderArgs, RescheduleServiceWorkOrderArgs, AmendAppointmentArgs, SyncWorkOrderToSupplierArgs, UpdateOrderExternalRefArgs } from '../interface';
30
+ import type { WholesaleProvider, CreateOrderOptions, CancellationReasonCode, CancelServiceWorkOrderArgs, ReserveAppointmentArgs, ListAppointmentsArgs, ScheduleServiceWorkOrderArgs, RescheduleServiceWorkOrderArgs, AmendAppointmentArgs, SyncWorkOrderToSupplierArgs, UpdateOrderExternalRefArgs } from '../interface';
31
31
  import type { VMO2OrderNotification, VMO2SMRNotification } from '../types/vmo2';
32
32
  import type { FibreCafeServiceAvailability200, FibreCafeAvailableAppointments200, FibreCafeReserveAppointment201, FibreCafeScheduleServiceWorkOrder201, FibreCafeRescheduleServiceWorkOrder200, FibreCafeOrderCreate202, FibreCafeOrderAmendAccept202, FibreCafeKciDto } from '../types/fibrecafe';
33
33
  /**
@@ -60,6 +60,7 @@ export interface NetomniaProviderDeps<TPrincipal = unknown, TKci = FibreCafeKciD
60
60
  amendOrder: (principal: TPrincipal, yfOrderId: string) => Promise<FibreCafeOrderCreate202 | null>;
61
61
  cancelOrder: (yfOrderId: string, reasonCode: CancellationReasonCode) => Promise<FibreCafeOrderCreate202>;
62
62
  ceaseOrder: (yfOrderId: string, serviceId: string) => Promise<FibreCafeOrderCreate202>;
63
+ cancelServiceWorkOrder: (args: CancelServiceWorkOrderArgs) => Promise<FibreCafeRescheduleServiceWorkOrder200>;
63
64
  receiveKci: (principal: TPrincipal, dto: FibreCafeKciDto) => Promise<void>;
64
65
  syncWorkOrderToSupplier: (args: SyncWorkOrderToSupplierArgs<TPrincipal>) => Promise<void>;
65
66
  retrieveServiceIdByUprn: (uprn: number) => Promise<string | null>;
@@ -82,6 +83,7 @@ export declare class NetomniaProvider<TPrincipal = unknown> implements Wholesale
82
83
  amendOrder(principal: TPrincipal, yfOrderId: string): Promise<FibreCafeOrderCreate202>;
83
84
  cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<FibreCafeOrderCreate202>;
84
85
  ceaseOrder(yfOrderId: string, serviceId: string): Promise<FibreCafeOrderCreate202>;
86
+ cancelServiceWorkOrder(args: CancelServiceWorkOrderArgs): Promise<FibreCafeRescheduleServiceWorkOrder200>;
85
87
  receiveKci(principal: TPrincipal, dto: FibreCafeKciDto): Promise<void>;
86
88
  syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs<TPrincipal>): Promise<void>;
87
89
  retrieveServiceIdByUprn(uprn: number): Promise<string>;
@@ -47,6 +47,9 @@ class NetomniaProvider {
47
47
  ceaseOrder(yfOrderId, serviceId) {
48
48
  return this.deps.ceaseOrder(yfOrderId, serviceId);
49
49
  }
50
+ cancelServiceWorkOrder(args) {
51
+ return this.deps.cancelServiceWorkOrder(args);
52
+ }
50
53
  receiveKci(principal, dto) {
51
54
  return this.deps.receiveKci(principal, dto);
52
55
  }
@@ -71,6 +71,7 @@ function buildDeps() {
71
71
  amendOrder: {},
72
72
  cancelOrder: {},
73
73
  ceaseOrder: {},
74
+ cancelServiceWorkOrder: { workOrderId: 'WO-CANC-1', appointmentId: '' },
74
75
  };
75
76
  const deps = {
76
77
  searchAddress: (uprn) => {
@@ -113,6 +114,10 @@ function buildDeps() {
113
114
  log.push({ method: 'ceaseOrder', args: [yfOrderId, serviceId] });
114
115
  return Promise.resolve(stubs.ceaseOrder);
115
116
  },
117
+ cancelServiceWorkOrder: (args) => {
118
+ log.push({ method: 'cancelServiceWorkOrder', args: [args] });
119
+ return Promise.resolve(stubs.cancelServiceWorkOrder);
120
+ },
116
121
  receiveKci: (principal, dto) => {
117
122
  log.push({ method: 'receiveKci', args: [principal, dto] });
118
123
  return Promise.resolve();
@@ -430,6 +430,21 @@ export interface FibreCafeModifyServiceOrder {
430
430
  serviceOrderCancellationId?: string | number;
431
431
  serviceOrderCancellationReference?: string;
432
432
  }
433
+ /**
434
+ * Codes representing the reason for cancelling an inflight service problem
435
+ * (FibreCafe `POST /service-problem-cancellations` request body). Used on
436
+ * the OUTBOUND (YF → NET) side for the tenant-initiated SERVICE WO cancel.
437
+ *
438
+ * The corresponding INBOUND state-change (NET → YF) confirming the
439
+ * cancellation is emitted by NET as an `UPDATE` KCI with
440
+ * `supplierCodes: [NETOMNIA_SERVICE_WORK_ORDER_CANCELLED]` — see the
441
+ * `provide-update-service-cancelled` row in kci-effect-bridge.ts.
442
+ *
443
+ * Mirrors NET's `ServiceProblemCancellationReasonCode` enum verbatim.
444
+ *
445
+ * @see {@link https://docs.thefibrecafe.net/openapi/supplier/operation/handleCancelProblem/#!path=reasonCode&t=request | FibreCafe ServiceProblemCancellationReasonCode}
446
+ */
447
+ export declare type FibreCafeServiceProblemCancellationReasonCode = 'ISSUE_RESOLVED' | 'CUSTOMER_NETWORK_ISSUE' | 'NO_LONGER_REQUIRED' | 'OTHER';
433
448
  /** Unsolicited cease — supplier-initiated, surfaced via KCI. */
434
449
  export interface FibreCafeUnsolicitedCeaseServiceOrder {
435
450
  orderType?: FibreCafeCeaseOrderType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d19n/youfibre-odin-sdk",
3
- "version": "2.0.151",
3
+ "version": "2.0.156",
4
4
  "description": "",
5
5
  "author": "@d19n",
6
6
  "license": "UNLICENSED",