@d19n/youfibre-odin-sdk 2.0.103-beta.7 → 2.0.103-beta.9

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.
@@ -1,4 +1,37 @@
1
+ /**
2
+ * WholesaleProvider — strategy-dispatchable interface for wholesale supplier
3
+ * integrations (Netomnia today, VMO2 from July 2026).
4
+ *
5
+ * **Architecture (T-20260508-009/010 — confirmed with Frank 2026-05-09 as
6
+ * "Path B / first step"):**
7
+ *
8
+ * Methods take YF-context arguments (orderId, principal, etc.) — matching
9
+ * the existing NET module method signatures. Each provider implementation
10
+ * builds the supplier-specific wire request internally from this YF context.
11
+ *
12
+ * Rationale:
13
+ * - Existing NET module methods take YF context, not wire DTOs. The SDK
14
+ * provider abstracts the supplier dispatch layer, not the DTO-building.
15
+ * - Method names align with VMO2 spec where it makes sense (`createOrder`,
16
+ * `cancelOrder`, etc.) so call-site vocabulary doesn't change when the
17
+ * provider switches.
18
+ * - When VMO2 ships, Vmo2Provider implements the same method names with
19
+ * args that may differ slightly. Frank's framing: "we adjust function
20
+ * arguments, nothing beyond that — we map X to Y and get the same result."
21
+ *
22
+ * **Trade-off accepted:** the interface is shaped after NET reality, not
23
+ * VMO2. That keeps Phase 0 a strict drop-in replacement. We can standardize
24
+ * args further once VMO2 ships if a tighter cross-provider abstraction
25
+ * proves valuable.
26
+ *
27
+ * **Type pragmatics:** principal/options/return values use `unknown` /
28
+ * `Record<string, unknown>` so the SDK doesn't need to depend on
29
+ * NestJS-decorated entity types. Modules cast at the wiring boundary (where
30
+ * full types are available).
31
+ */
1
32
  import type { VMO2OrderNotification, VMO2SMRNotification } from './types/vmo2';
33
+ import type { FibreCafeServiceAvailability200, FibreCafeAvailableAppointments200, FibreCafeReserveAppointment201, FibreCafeScheduleServiceWorkOrder201, FibreCafeRescheduleServiceWorkOrder200, FibreCafeOrderCreate202, FibreCafeOrderAmendAccept202, FibreCafeKciDto } from './types/fibrecafe';
34
+ import type { OrderRecord } from '../records-v2/OrderRecord';
2
35
  /**
3
36
  * Reasons a cancellation request can be made (NET wire-level enum value
4
37
  * passed through to the FibreCafe gateway).
@@ -23,6 +56,7 @@ export interface ReserveAppointmentArgs {
23
56
  ServiceCallReason?: string;
24
57
  WorkOrderExternalReference?: string;
25
58
  };
59
+ flow: 'FIRST_TIME_BOOKING' | 'RESCHEDULE';
26
60
  }
27
61
  export interface ListAppointmentsArgs {
28
62
  uprn: number;
@@ -52,8 +86,8 @@ export interface AmendAppointmentArgs {
52
86
  yfWorkOrderId: string;
53
87
  supplierAppointmentReference: string;
54
88
  }
55
- export interface SyncWorkOrderToSupplierArgs {
56
- principal: unknown;
89
+ export interface SyncWorkOrderToSupplierArgs<TPrincipal = unknown> {
90
+ principal: TPrincipal;
57
91
  yfOrderId: string;
58
92
  yfWorkOrderId: string;
59
93
  yfChangeReason: {
@@ -63,6 +97,14 @@ export interface SyncWorkOrderToSupplierArgs {
63
97
  type: unknown;
64
98
  properties: unknown;
65
99
  };
100
+ yfServiceAppointment: {
101
+ id?: string;
102
+ properties: {
103
+ Date?: string;
104
+ TimeBlock?: string;
105
+ [extra: string]: unknown;
106
+ };
107
+ };
66
108
  }
67
109
  export interface UpdateOrderExternalRefArgs {
68
110
  oldYfId: string;
@@ -77,26 +119,41 @@ export interface UpdateOrderExternalRefArgs {
77
119
  * Returns are typed loosely — modules know the return shape via the dep
78
120
  * function they wire in.
79
121
  */
80
- export interface WholesaleProvider {
81
- searchAddress(uprn: number): Promise<unknown>;
82
- listAppointments(args: ListAppointmentsArgs): Promise<unknown>;
83
- reserveAppointment(args: ReserveAppointmentArgs): Promise<unknown>;
84
- scheduleServiceWorkOrder(args: ScheduleServiceWorkOrderArgs): Promise<unknown>;
85
- rescheduleServiceWorkOrder(args: RescheduleServiceWorkOrderArgs): Promise<unknown>;
86
- amendAppointment(args: AmendAppointmentArgs): Promise<unknown>;
87
- createOrder(principal: unknown, orderId: string, options?: CreateOrderOptions): Promise<unknown>;
88
- /** Tier-change CoS — wraps `createModifyOrderForYfOrder` style args. */
89
- amendOrder(principal: unknown, yfOrderId: string): Promise<unknown>;
122
+ /**
123
+ * Generic in `TPrincipal` so call sites get full typing on the user/org
124
+ * principal that NestJS injects via the `@Principal()` decorator. Modules
125
+ * specialize at the wiring boundary:
126
+ *
127
+ * ```ts
128
+ * class WholesaleProviderFactory {
129
+ * constructor(@Inject(...) private netomnia: WholesaleProvider<OrganizationUserEntity>) {}
130
+ * get(...): WholesaleProvider<OrganizationUserEntity> { ... }
131
+ * }
132
+ * ```
133
+ *
134
+ * Default `TPrincipal = unknown` lets the SDK compile standalone without
135
+ * tenant-specific type imports.
136
+ */
137
+ export interface WholesaleProvider<TPrincipal = unknown> {
138
+ searchAddress(uprn: number): Promise<FibreCafeServiceAvailability200>;
139
+ listAppointments(args: ListAppointmentsArgs): Promise<FibreCafeAvailableAppointments200>;
140
+ reserveAppointment(args: ReserveAppointmentArgs): Promise<FibreCafeReserveAppointment201>;
141
+ scheduleServiceWorkOrder(args: ScheduleServiceWorkOrderArgs): Promise<FibreCafeScheduleServiceWorkOrder201>;
142
+ rescheduleServiceWorkOrder(args: RescheduleServiceWorkOrderArgs): Promise<FibreCafeRescheduleServiceWorkOrder200>;
143
+ amendAppointment(args: AmendAppointmentArgs): Promise<FibreCafeOrderAmendAccept202>;
144
+ createOrder(principal: TPrincipal, orderId: string, options?: CreateOrderOptions): Promise<FibreCafeOrderCreate202>;
145
+ /** Tier-change CoS — wraps `createModifyOrderForYfOrder` style args. Returns null when already at target SKU. */
146
+ amendOrder(principal: TPrincipal, yfOrderId: string): Promise<FibreCafeOrderCreate202 | null>;
90
147
  /** Pre-install cancel of an in-flight provide order. */
91
- cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<unknown>;
148
+ cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<FibreCafeOrderCreate202>;
92
149
  /** Active-customer cease (post-Active churn / OTS-losing). */
93
- ceaseOrder(yfOrderId: string, serviceId: string): Promise<unknown>;
150
+ ceaseOrder(yfOrderId: string, serviceId: string): Promise<FibreCafeOrderCreate202>;
94
151
  /** Inbound supplier KCI / notification handler. */
95
- receiveKci(principal: unknown, dto: unknown): Promise<void>;
152
+ receiveKci(principal: TPrincipal, dto: FibreCafeKciDto): Promise<void>;
96
153
  /** Outbound YF→supplier WO sync (cancel propagation, etc.). */
97
- syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs): Promise<unknown>;
154
+ syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs<TPrincipal>): Promise<void>;
98
155
  /** Inbound supplier→YF WO change application (post-receiveKci). */
99
- syncWorkOrderFromSupplier(order: unknown, dto: unknown): Promise<unknown>;
156
+ syncWorkOrderFromSupplier(order: OrderRecord, dto: FibreCafeKciDto): Promise<void>;
100
157
  retrieveServiceIdByUprn(uprn: number): Promise<string | null>;
101
158
  updateOrderExternalRef(args: UpdateOrderExternalRefArgs): Promise<void>;
102
159
  /**
@@ -29,6 +29,8 @@
29
29
  */
30
30
  import type { WholesaleProvider, CreateOrderOptions, CancellationReasonCode, ReserveAppointmentArgs, ListAppointmentsArgs, ScheduleServiceWorkOrderArgs, RescheduleServiceWorkOrderArgs, AmendAppointmentArgs, SyncWorkOrderToSupplierArgs, UpdateOrderExternalRefArgs } from '../interface';
31
31
  import type { VMO2OrderNotification, VMO2SMRNotification } from '../types/vmo2';
32
+ import type { FibreCafeServiceAvailability200, FibreCafeAvailableAppointments200, FibreCafeReserveAppointment201, FibreCafeScheduleServiceWorkOrder201, FibreCafeRescheduleServiceWorkOrder200, FibreCafeOrderCreate202, FibreCafeOrderAmendAccept202, FibreCafeKciDto } from '../types/fibrecafe';
33
+ import type { OrderRecord } from '../../records-v2/OrderRecord';
32
34
  /**
33
35
  * Function-bag of dependencies the NetomniaProvider delegates to.
34
36
  *
@@ -48,43 +50,43 @@ import type { VMO2OrderNotification, VMO2SMRNotification } from '../types/vmo2';
48
50
  * }
49
51
  * ```
50
52
  */
51
- export interface NetomniaProviderDeps {
52
- searchAddress: (uprn: number) => Promise<unknown>;
53
- listAppointments: (args: ListAppointmentsArgs) => Promise<unknown>;
54
- reserveAppointment: (args: ReserveAppointmentArgs) => Promise<unknown>;
55
- scheduleServiceWorkOrder: (args: ScheduleServiceWorkOrderArgs) => Promise<unknown>;
56
- rescheduleServiceWorkOrder: (args: RescheduleServiceWorkOrderArgs) => Promise<unknown>;
57
- amendAppointment: (args: AmendAppointmentArgs) => Promise<unknown>;
58
- createOrder: (principal: unknown, orderId: string, options?: CreateOrderOptions) => Promise<unknown>;
59
- amendOrder: (principal: unknown, yfOrderId: string) => Promise<unknown>;
60
- cancelOrder: (yfOrderId: string, reasonCode: CancellationReasonCode) => Promise<unknown>;
61
- ceaseOrder: (yfOrderId: string, serviceId: string) => Promise<unknown>;
62
- receiveKci: (principal: unknown, dto: unknown) => Promise<void>;
63
- syncWorkOrderToSupplier: (args: SyncWorkOrderToSupplierArgs) => Promise<unknown>;
64
- syncWorkOrderFromSupplier: (order: unknown, dto: unknown) => Promise<unknown>;
53
+ export interface NetomniaProviderDeps<TPrincipal = unknown> {
54
+ searchAddress: (uprn: number) => Promise<FibreCafeServiceAvailability200>;
55
+ listAppointments: (args: ListAppointmentsArgs) => Promise<FibreCafeAvailableAppointments200>;
56
+ reserveAppointment: (args: ReserveAppointmentArgs) => Promise<FibreCafeReserveAppointment201>;
57
+ scheduleServiceWorkOrder: (args: ScheduleServiceWorkOrderArgs) => Promise<FibreCafeScheduleServiceWorkOrder201>;
58
+ rescheduleServiceWorkOrder: (args: RescheduleServiceWorkOrderArgs) => Promise<FibreCafeRescheduleServiceWorkOrder200>;
59
+ amendAppointment: (args: AmendAppointmentArgs) => Promise<FibreCafeOrderAmendAccept202>;
60
+ createOrder: (principal: TPrincipal, orderId: string, options?: CreateOrderOptions) => Promise<FibreCafeOrderCreate202>;
61
+ amendOrder: (principal: TPrincipal, yfOrderId: string) => Promise<FibreCafeOrderCreate202 | null>;
62
+ cancelOrder: (yfOrderId: string, reasonCode: CancellationReasonCode) => Promise<FibreCafeOrderCreate202>;
63
+ ceaseOrder: (yfOrderId: string, serviceId: string) => Promise<FibreCafeOrderCreate202>;
64
+ receiveKci: (principal: TPrincipal, dto: FibreCafeKciDto) => Promise<void>;
65
+ syncWorkOrderToSupplier: (args: SyncWorkOrderToSupplierArgs<TPrincipal>) => Promise<void>;
66
+ syncWorkOrderFromSupplier: (order: OrderRecord, dto: FibreCafeKciDto) => Promise<void>;
65
67
  retrieveServiceIdByUprn: (uprn: number) => Promise<string | null>;
66
68
  updateOrderExternalRef: (args: UpdateOrderExternalRefArgs) => Promise<void>;
67
69
  }
68
70
  declare type OrderNotificationHandler = (n: VMO2OrderNotification) => Promise<void>;
69
71
  declare type SMRNotificationHandler = (n: VMO2SMRNotification) => Promise<void>;
70
- export declare class NetomniaProvider implements WholesaleProvider {
72
+ export declare class NetomniaProvider<TPrincipal = unknown> implements WholesaleProvider<TPrincipal> {
71
73
  private readonly deps;
72
74
  private readonly orderHandlers;
73
75
  private readonly smrHandlers;
74
- constructor(deps: NetomniaProviderDeps);
75
- searchAddress(uprn: number): Promise<unknown>;
76
- listAppointments(args: ListAppointmentsArgs): Promise<unknown>;
77
- reserveAppointment(args: ReserveAppointmentArgs): Promise<unknown>;
78
- scheduleServiceWorkOrder(args: ScheduleServiceWorkOrderArgs): Promise<unknown>;
79
- rescheduleServiceWorkOrder(args: RescheduleServiceWorkOrderArgs): Promise<unknown>;
80
- amendAppointment(args: AmendAppointmentArgs): Promise<unknown>;
81
- createOrder(principal: unknown, orderId: string, options?: CreateOrderOptions): Promise<unknown>;
82
- amendOrder(principal: unknown, yfOrderId: string): Promise<unknown>;
83
- cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<unknown>;
84
- ceaseOrder(yfOrderId: string, serviceId: string): Promise<unknown>;
85
- receiveKci(principal: unknown, dto: unknown): Promise<void>;
86
- syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs): Promise<unknown>;
87
- syncWorkOrderFromSupplier(order: unknown, dto: unknown): Promise<unknown>;
76
+ constructor(deps: NetomniaProviderDeps<TPrincipal>);
77
+ searchAddress(uprn: number): Promise<FibreCafeServiceAvailability200>;
78
+ listAppointments(args: ListAppointmentsArgs): Promise<FibreCafeAvailableAppointments200>;
79
+ reserveAppointment(args: ReserveAppointmentArgs): Promise<FibreCafeReserveAppointment201>;
80
+ scheduleServiceWorkOrder(args: ScheduleServiceWorkOrderArgs): Promise<FibreCafeScheduleServiceWorkOrder201>;
81
+ rescheduleServiceWorkOrder(args: RescheduleServiceWorkOrderArgs): Promise<FibreCafeRescheduleServiceWorkOrder200>;
82
+ amendAppointment(args: AmendAppointmentArgs): Promise<FibreCafeOrderAmendAccept202>;
83
+ createOrder(principal: TPrincipal, orderId: string, options?: CreateOrderOptions): Promise<FibreCafeOrderCreate202>;
84
+ amendOrder(principal: TPrincipal, yfOrderId: string): Promise<FibreCafeOrderCreate202>;
85
+ cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<FibreCafeOrderCreate202>;
86
+ ceaseOrder(yfOrderId: string, serviceId: string): Promise<FibreCafeOrderCreate202>;
87
+ receiveKci(principal: TPrincipal, dto: FibreCafeKciDto): Promise<void>;
88
+ syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs<TPrincipal>): Promise<void>;
89
+ syncWorkOrderFromSupplier(order: OrderRecord, dto: FibreCafeKciDto): Promise<void>;
88
90
  retrieveServiceIdByUprn(uprn: number): Promise<string>;
89
91
  updateOrderExternalRef(args: UpdateOrderExternalRefArgs): Promise<void>;
90
92
  onOrderNotification(handler: OrderNotificationHandler): void;
@@ -43,69 +43,127 @@ function brand(s) {
43
43
  return s;
44
44
  }
45
45
  /**
46
- * Build deps that record their call args + return a sentinel value. Each
47
- * dep is identical-shape to the corresponding WholesaleProvider method so
48
- * the pass-through pattern can be tested.
46
+ * Stubs each dep with a typed canned return so the pass-through pattern
47
+ * can be tested via identity equality (`result === stubs.searchAddress`)
48
+ * without resorting to `as unknown` casts.
49
49
  */
50
50
  function buildDeps() {
51
51
  const log = [];
52
- const sentinel = Symbol('NET-RETURN');
53
- const record = (method) => (...args) => {
54
- log.push({ method, args });
55
- return Promise.resolve(sentinel);
56
- };
57
- const recordVoid = (method) => (...args) => {
58
- log.push({ method, args });
59
- return Promise.resolve(undefined);
52
+ const stubs = {
53
+ searchAddress: {
54
+ address: { identifier: { id: 'UPRN-1', type: 'UPRN' } },
55
+ serviceSpecifications: [],
56
+ },
57
+ listAppointments: { availableTimeslots: [] },
58
+ reserveAppointment: {
59
+ id: 'A1',
60
+ supplierReference: 'NET-A1',
61
+ address: { id: 'X', type: 'UPRN' },
62
+ serviceSpecification: { id: 'fttp' },
63
+ timeslot: {
64
+ timeslotStartDateTime: '2026-06-01T08:00:00Z',
65
+ timeslotEndDateTime: '2026-06-01T13:00:00Z',
66
+ },
67
+ },
68
+ scheduleServiceWorkOrder: { workOrderId: 'WO-1', appointmentId: 'APT-1' },
69
+ rescheduleServiceWorkOrder: { workOrderId: 'WO-1', appointmentId: 'APT-2' },
70
+ amendAppointment: {},
71
+ createOrder: {},
72
+ amendOrder: {},
73
+ cancelOrder: {},
74
+ ceaseOrder: {},
60
75
  };
61
76
  const deps = {
62
- searchAddress: record('searchAddress'),
63
- listAppointments: record('listAppointments'),
64
- reserveAppointment: record('reserveAppointment'),
65
- scheduleServiceWorkOrder: record('scheduleServiceWorkOrder'),
66
- rescheduleServiceWorkOrder: record('rescheduleServiceWorkOrder'),
67
- amendAppointment: record('amendAppointment'),
68
- createOrder: record('createOrder'),
69
- amendOrder: record('amendOrder'),
70
- cancelOrder: record('cancelOrder'),
71
- ceaseOrder: record('ceaseOrder'),
72
- receiveKci: recordVoid('receiveKci'),
73
- syncWorkOrderToSupplier: record('syncWorkOrderToSupplier'),
74
- syncWorkOrderFromSupplier: record('syncWorkOrderFromSupplier'),
77
+ searchAddress: (uprn) => {
78
+ log.push({ method: 'searchAddress', args: [uprn] });
79
+ return Promise.resolve(stubs.searchAddress);
80
+ },
81
+ listAppointments: (args) => {
82
+ log.push({ method: 'listAppointments', args: [args] });
83
+ return Promise.resolve(stubs.listAppointments);
84
+ },
85
+ reserveAppointment: (args) => {
86
+ log.push({ method: 'reserveAppointment', args: [args] });
87
+ return Promise.resolve(stubs.reserveAppointment);
88
+ },
89
+ scheduleServiceWorkOrder: (args) => {
90
+ log.push({ method: 'scheduleServiceWorkOrder', args: [args] });
91
+ return Promise.resolve(stubs.scheduleServiceWorkOrder);
92
+ },
93
+ rescheduleServiceWorkOrder: (args) => {
94
+ log.push({ method: 'rescheduleServiceWorkOrder', args: [args] });
95
+ return Promise.resolve(stubs.rescheduleServiceWorkOrder);
96
+ },
97
+ amendAppointment: (args) => {
98
+ log.push({ method: 'amendAppointment', args: [args] });
99
+ return Promise.resolve(stubs.amendAppointment);
100
+ },
101
+ createOrder: (principal, orderId, options) => {
102
+ log.push({ method: 'createOrder', args: [principal, orderId, options] });
103
+ return Promise.resolve(stubs.createOrder);
104
+ },
105
+ amendOrder: (principal, yfOrderId) => {
106
+ log.push({ method: 'amendOrder', args: [principal, yfOrderId] });
107
+ return Promise.resolve(stubs.amendOrder);
108
+ },
109
+ cancelOrder: (yfOrderId, reasonCode) => {
110
+ log.push({ method: 'cancelOrder', args: [yfOrderId, reasonCode] });
111
+ return Promise.resolve(stubs.cancelOrder);
112
+ },
113
+ ceaseOrder: (yfOrderId, serviceId) => {
114
+ log.push({ method: 'ceaseOrder', args: [yfOrderId, serviceId] });
115
+ return Promise.resolve(stubs.ceaseOrder);
116
+ },
117
+ receiveKci: (principal, dto) => {
118
+ log.push({ method: 'receiveKci', args: [principal, dto] });
119
+ return Promise.resolve();
120
+ },
121
+ syncWorkOrderToSupplier: (args) => {
122
+ log.push({ method: 'syncWorkOrderToSupplier', args: [args] });
123
+ return Promise.resolve();
124
+ },
125
+ syncWorkOrderFromSupplier: (order, dto) => {
126
+ log.push({ method: 'syncWorkOrderFromSupplier', args: [order, dto] });
127
+ return Promise.resolve();
128
+ },
75
129
  retrieveServiceIdByUprn: (uprn) => {
76
130
  log.push({ method: 'retrieveServiceIdByUprn', args: [uprn] });
77
131
  return Promise.resolve('NET-SVC-1');
78
132
  },
79
- updateOrderExternalRef: recordVoid('updateOrderExternalRef'),
133
+ updateOrderExternalRef: (args) => {
134
+ log.push({ method: 'updateOrderExternalRef', args: [args] });
135
+ return Promise.resolve();
136
+ },
80
137
  };
81
- return { deps, log, sentinel };
138
+ return { deps, log, stubs };
82
139
  }
83
140
  function run() {
84
141
  return __awaiter(this, void 0, void 0, function* () {
85
142
  console.log('NetomniaProvider — pass-through delegation tests\n');
86
143
  console.log('Test: searchAddress delegates to deps');
87
144
  {
88
- const { deps, log, sentinel } = buildDeps();
145
+ const { deps, log, stubs } = buildDeps();
89
146
  const p = new netomnia_provider_1.NetomniaProvider(deps);
90
147
  const result = yield p.searchAddress(123);
91
148
  assert('called once', log.length === 1);
92
149
  assert('correct method', log[0].method === 'searchAddress');
93
150
  assert('args forwarded', JSON.stringify(log[0].args) === JSON.stringify([123]));
94
- assert('return forwarded', result === sentinel);
151
+ assert('return forwarded', result === stubs.searchAddress);
95
152
  }
96
153
  console.log('Test: listAppointments delegates to deps');
97
154
  {
98
- const { deps, log } = buildDeps();
155
+ const { deps, log, stubs } = buildDeps();
99
156
  const p = new netomnia_provider_1.NetomniaProvider(deps);
100
157
  const args = { uprn: 123, start: '2026-06-01', appointmentPurpose: 'PROVIDE' };
101
- yield p.listAppointments(args);
158
+ const result = yield p.listAppointments(args);
102
159
  assert('called once', log.length === 1);
103
160
  assert('correct method', log[0].method === 'listAppointments');
104
161
  assert('args forwarded', log[0].args[0] === args);
162
+ assert('return forwarded', result === stubs.listAppointments);
105
163
  }
106
164
  console.log('Test: reserveAppointment delegates to deps');
107
165
  {
108
- const { deps, log } = buildDeps();
166
+ const { deps, log, stubs } = buildDeps();
109
167
  const p = new netomnia_provider_1.NetomniaProvider(deps);
110
168
  const args = {
111
169
  yfAppointmentId: 'APT-1',
@@ -115,11 +173,13 @@ function run() {
115
173
  purpose: 'PROVIDE',
116
174
  yfOrderId: 'ORD-1',
117
175
  woProperties: {},
176
+ flow: 'FIRST_TIME_BOOKING',
118
177
  };
119
- yield p.reserveAppointment(args);
178
+ const result = yield p.reserveAppointment(args);
120
179
  assert('called once', log.length === 1);
121
180
  assert('correct method', log[0].method === 'reserveAppointment');
122
181
  assert('args forwarded', log[0].args[0] === args);
182
+ assert('return forwarded', result === stubs.reserveAppointment);
123
183
  }
124
184
  console.log('Test: scheduleServiceWorkOrder delegates to deps');
125
185
  {
@@ -163,7 +223,7 @@ function run() {
163
223
  }
164
224
  console.log('Test: createOrder delegates to deps with all args (principal, orderId, options)');
165
225
  {
166
- const { deps, log, sentinel } = buildDeps();
226
+ const { deps, log, stubs } = buildDeps();
167
227
  const p = new netomnia_provider_1.NetomniaProvider(deps);
168
228
  const principal = { id: 'user-1' };
169
229
  const opts = { doNotCreateServiceAppointment: true };
@@ -173,7 +233,7 @@ function run() {
173
233
  assert('principal forwarded', log[0].args[0] === principal);
174
234
  assert('orderId forwarded', log[0].args[1] === 'ORD-1');
175
235
  assert('options forwarded', log[0].args[2] === opts);
176
- assert('return forwarded', result === sentinel);
236
+ assert('return forwarded', result === stubs.createOrder);
177
237
  }
178
238
  console.log('Test: createOrder forwards undefined options correctly');
179
239
  {
@@ -217,7 +277,13 @@ function run() {
217
277
  const { deps, log } = buildDeps();
218
278
  const p = new netomnia_provider_1.NetomniaProvider(deps);
219
279
  const principal = { id: 'user-1' };
220
- const dto = { id: 'kci-1', sequenceNumber: 1 };
280
+ // Minimal valid FibreCafeKciDto fixture.
281
+ const dto = {
282
+ id: 'kci-1',
283
+ sequenceNumber: 1,
284
+ issuedOn: '2026-05-09T00:00:00Z',
285
+ reasonCode: 'ACKNOWLEDGED',
286
+ };
221
287
  yield p.receiveKci(principal, dto);
222
288
  assert('called once', log.length === 1);
223
289
  assert('correct method', log[0].method === 'receiveKci');
@@ -232,6 +298,7 @@ function run() {
232
298
  yfOrderId: 'ORD-1',
233
299
  yfWorkOrderId: 'WO-1',
234
300
  yfChangeReason: { externalId: 'CR-1', entity: 'ChangeReason', type: 'CANCEL', properties: {} },
301
+ yfServiceAppointment: { properties: {} },
235
302
  };
236
303
  yield p.syncWorkOrderToSupplier(args);
237
304
  assert('called once', log.length === 1);
@@ -241,11 +308,22 @@ function run() {
241
308
  {
242
309
  const { deps, log } = buildDeps();
243
310
  const p = new netomnia_provider_1.NetomniaProvider(deps);
311
+ // Test fixtures: pass-through tests treat these values as opaque; we
312
+ // assert reference equality, not structural equality. Casting a minimal
313
+ // shape to the expected types is acceptable here — the alternative is
314
+ // constructing 80+ OrderRecord properties for no semantic value.
244
315
  const order = { id: 'ORD-1' };
245
- const dto = { id: 'kci-1' };
316
+ const dto = {
317
+ id: 'kci-1',
318
+ sequenceNumber: 1,
319
+ issuedOn: '2026-05-09T00:00:00Z',
320
+ reasonCode: 'UPDATE',
321
+ };
246
322
  yield p.syncWorkOrderFromSupplier(order, dto);
247
323
  assert('called once', log.length === 1);
248
324
  assert('correct method', log[0].method === 'syncWorkOrderFromSupplier');
325
+ assert('order reference forwarded', log[0].args[0] === order);
326
+ assert('dto reference forwarded', log[0].args[1] === dto);
249
327
  }
250
328
  console.log('Test: retrieveServiceIdByUprn delegates to deps');
251
329
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d19n/youfibre-odin-sdk",
3
- "version": "2.0.103-beta.7",
3
+ "version": "2.0.103-beta.9",
4
4
  "description": "",
5
5
  "author": "@d19n",
6
6
  "license": "UNLICENSED",