@d19n/youfibre-odin-sdk 2.0.340 → 2.0.341
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/SendTlosNinjaCaseEmailDto.d.ts +15 -1
- package/dist/entities-v2/Case.d.ts +5 -1
- package/dist/entities-v2/Case.js +5 -1
- package/dist/process-model/order-process.projection.js +23 -0
- package/dist/process-model/order-process.projection.spec.d.ts +1 -0
- package/dist/process-model/order-process.projection.spec.js +121 -0
- package/package.json +6 -1
|
@@ -23,7 +23,7 @@ export interface SendTlosNinjaCaseEmailMessage extends OdinRecordCreatedEvent<Se
|
|
|
23
23
|
* Properties for SendTlosNinjaCaseEmail action
|
|
24
24
|
*
|
|
25
25
|
* @property Required fields: 1
|
|
26
|
-
* @property Optional fields:
|
|
26
|
+
* @property Optional fields: 9
|
|
27
27
|
*/
|
|
28
28
|
export interface SendTlosNinjaCaseEmailProperties {
|
|
29
29
|
/**
|
|
@@ -85,6 +85,20 @@ export interface SendTlosNinjaCaseEmailProperties {
|
|
|
85
85
|
* @type {TEXT}
|
|
86
86
|
*/
|
|
87
87
|
MessageId?: string | null;
|
|
88
|
+
/**
|
|
89
|
+
* Title
|
|
90
|
+
*
|
|
91
|
+
* Used for title
|
|
92
|
+
* @type {TEXT}
|
|
93
|
+
*/
|
|
94
|
+
Title?: string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Subject
|
|
97
|
+
*
|
|
98
|
+
* Data field for Email records. Used by Communications team.
|
|
99
|
+
* @type {TEXT}
|
|
100
|
+
*/
|
|
101
|
+
Subject?: string | null;
|
|
88
102
|
}
|
|
89
103
|
/**
|
|
90
104
|
* SendTlosNinjaCaseEmail
|
|
@@ -355,9 +355,11 @@ export declare enum CaseProblem {
|
|
|
355
355
|
* - `TLOS_APP` - TLOS App
|
|
356
356
|
* - `TLOS_PORTAL` - TLOS Portal
|
|
357
357
|
* - `TRUSTPILOT_SERVICE_REVIEW` - Trustpilot review
|
|
358
|
+
* - `TWITTER_X_DM` - Twitter/X DM
|
|
359
|
+
* - `TWITTER_X_MENTION` - Twitter/X Mention
|
|
358
360
|
* - `WEB_CHAT` - Web chat
|
|
359
361
|
* - `WEB_FORM_BUSINESS` - Web form - Business
|
|
360
|
-
* -
|
|
362
|
+
* - ... and 1 more
|
|
361
363
|
*/
|
|
362
364
|
export declare enum CaseChannel {
|
|
363
365
|
CALL = "CALL",
|
|
@@ -371,6 +373,8 @@ export declare enum CaseChannel {
|
|
|
371
373
|
TLOS_APP = "TLOS_APP",
|
|
372
374
|
TLOS_PORTAL = "TLOS_PORTAL",
|
|
373
375
|
TRUSTPILOT_REVIEW = "TRUSTPILOT_SERVICE_REVIEW",
|
|
376
|
+
TWITTER_X_DM = "TWITTER_X_DM",
|
|
377
|
+
TWITTER_X_MENTION = "TWITTER_X_MENTION",
|
|
374
378
|
WEB_CHAT = "WEB_CHAT",
|
|
375
379
|
WEB_FORM_BUSINESS = "WEB_FORM_BUSINESS",
|
|
376
380
|
WHATSAPP = "WHATSAPP"
|
package/dist/entities-v2/Case.js
CHANGED
|
@@ -347,9 +347,11 @@ var CaseProblem;
|
|
|
347
347
|
* - `TLOS_APP` - TLOS App
|
|
348
348
|
* - `TLOS_PORTAL` - TLOS Portal
|
|
349
349
|
* - `TRUSTPILOT_SERVICE_REVIEW` - Trustpilot review
|
|
350
|
+
* - `TWITTER_X_DM` - Twitter/X DM
|
|
351
|
+
* - `TWITTER_X_MENTION` - Twitter/X Mention
|
|
350
352
|
* - `WEB_CHAT` - Web chat
|
|
351
353
|
* - `WEB_FORM_BUSINESS` - Web form - Business
|
|
352
|
-
* -
|
|
354
|
+
* - ... and 1 more
|
|
353
355
|
*/
|
|
354
356
|
var CaseChannel;
|
|
355
357
|
(function (CaseChannel) {
|
|
@@ -364,6 +366,8 @@ var CaseChannel;
|
|
|
364
366
|
CaseChannel["TLOS_APP"] = "TLOS_APP";
|
|
365
367
|
CaseChannel["TLOS_PORTAL"] = "TLOS_PORTAL";
|
|
366
368
|
CaseChannel["TRUSTPILOT_REVIEW"] = "TRUSTPILOT_SERVICE_REVIEW";
|
|
369
|
+
CaseChannel["TWITTER_X_DM"] = "TWITTER_X_DM";
|
|
370
|
+
CaseChannel["TWITTER_X_MENTION"] = "TWITTER_X_MENTION";
|
|
367
371
|
CaseChannel["WEB_CHAT"] = "WEB_CHAT";
|
|
368
372
|
CaseChannel["WEB_FORM_BUSINESS"] = "WEB_FORM_BUSINESS";
|
|
369
373
|
CaseChannel["WHATSAPP"] = "WHATSAPP";
|
|
@@ -130,6 +130,29 @@ exports.ORDER_PROCESS_PROJECTION = {
|
|
|
130
130
|
{
|
|
131
131
|
kind: 'related-record',
|
|
132
132
|
id: 'supply-must-have-active-progressing-install',
|
|
133
|
+
/**
|
|
134
|
+
* NOT START ORDERS. A START order takes over a line that is already
|
|
135
|
+
* provisioned, so no engineer attends: the supplier cancels its INSTALL
|
|
136
|
+
* and says so with
|
|
137
|
+
* `NETOMNIA_INSTALL_WORK_ORDER_CANCELLED_ENGINEER_NOT_REQUIRED`. A
|
|
138
|
+
* Cancelled INSTALL is therefore the normal, correct state for these
|
|
139
|
+
* orders — they activate on the COMPLETED-OrderKci gate instead.
|
|
140
|
+
*
|
|
141
|
+
* Without this scope the dep fires BLOCKER on every healthy START order
|
|
142
|
+
* and the Cancelled-INSTALL clause below prescribes `cancel-parent`,
|
|
143
|
+
* which would deny a customer a service that is physically ready.
|
|
144
|
+
* Measured in production 2026-09-16: 98 of the 122 orders off-track on
|
|
145
|
+
* this dep were START, 58 of them with a COMPLETED confirmation.
|
|
146
|
+
*
|
|
147
|
+
* `notEq` is plain `!==` in the engine, so a NULL SupplierOrderType —
|
|
148
|
+
* the ordinary NEW-provide case — still matches and stays in scope.
|
|
149
|
+
*
|
|
150
|
+
* Ported from the BP, which has declared this since 2026-08-13:
|
|
151
|
+
* agent-os/knowledge/business-processes/order.ts:740
|
|
152
|
+
*/
|
|
153
|
+
parentFilter: [
|
|
154
|
+
{ column: 'SupplierOrderType', op: 'notEq', value: 'START' },
|
|
155
|
+
],
|
|
133
156
|
relation: {
|
|
134
157
|
entity: 'FieldServiceModule:WorkOrder',
|
|
135
158
|
filter: [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* ORDER projection — scope of `supply-must-have-active-progressing-install`.
|
|
5
|
+
*
|
|
6
|
+
* A START order takes over a line that is already provisioned, so no engineer
|
|
7
|
+
* attends: the supplier cancels its INSTALL and says so with
|
|
8
|
+
* `NETOMNIA_INSTALL_WORK_ORDER_CANCELLED_ENGINEER_NOT_REQUIRED`. A Cancelled
|
|
9
|
+
* INSTALL is therefore the normal, correct state for these orders — they
|
|
10
|
+
* activate on the COMPLETED-OrderKci gate instead.
|
|
11
|
+
*
|
|
12
|
+
* The BP has excluded START from this rule since 2026-08-13
|
|
13
|
+
* (agent-os/knowledge/business-processes/order.ts:740). The projection is a
|
|
14
|
+
* hand-maintained copy and never received it, so the engine kept scoring healthy
|
|
15
|
+
* START orders BLOCKER and prescribing `cancel-parent` — which would deny a
|
|
16
|
+
* customer a service that is physically ready. Production 2026-09-16: 98 of the
|
|
17
|
+
* 122 orders off-track on this dep were START, 58 with a COMPLETED confirmation.
|
|
18
|
+
*
|
|
19
|
+
* These tests pin the scope so the drift cannot return silently.
|
|
20
|
+
*
|
|
21
|
+
* Run: npm run test:process-model
|
|
22
|
+
*/
|
|
23
|
+
const order_process_projection_1 = require("./order-process.projection");
|
|
24
|
+
const process_validator_1 = require("./process-validator");
|
|
25
|
+
const DEP_ID = 'supply-must-have-active-progressing-install';
|
|
26
|
+
const WO_ENTITY = 'FieldServiceModule:WorkOrder';
|
|
27
|
+
let passed = 0;
|
|
28
|
+
let failed = 0;
|
|
29
|
+
const failures = [];
|
|
30
|
+
function assert(label, cond, hint) {
|
|
31
|
+
if (cond) {
|
|
32
|
+
passed += 1;
|
|
33
|
+
console.log(` PASS ${label}`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
failed += 1;
|
|
37
|
+
const msg = ` FAIL ${label}${hint ? ' — ' + hint : ''}`;
|
|
38
|
+
failures.push(msg);
|
|
39
|
+
console.log(msg);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** A Supply order, optionally carrying a SupplierOrderType discriminator. */
|
|
43
|
+
function supplyOrder(supplierOrderType) {
|
|
44
|
+
return {
|
|
45
|
+
id: 'ord-1',
|
|
46
|
+
recordNumber: 'OR0000001',
|
|
47
|
+
stage: { key: 'OrderStageSupply', name: 'Supply' },
|
|
48
|
+
createdAt: new Date(Date.now() - 3 * 86400000).toISOString(),
|
|
49
|
+
properties: supplierOrderType === undefined
|
|
50
|
+
? { Provider: 'NETOMNIA' }
|
|
51
|
+
: { Provider: 'NETOMNIA', SupplierOrderType: supplierOrderType },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const CANCELLED_INSTALL = {
|
|
55
|
+
id: 'wo-1',
|
|
56
|
+
recordNumber: 'WO-1',
|
|
57
|
+
type: 'INSTALL',
|
|
58
|
+
stage: { key: 'WorkOrderStageCancelled', name: 'Cancelled' },
|
|
59
|
+
};
|
|
60
|
+
const SCHEDULED_INSTALL = {
|
|
61
|
+
id: 'wo-2',
|
|
62
|
+
recordNumber: 'WO-2',
|
|
63
|
+
type: 'INSTALL',
|
|
64
|
+
stage: { key: 'WorkOrderStageScheduled', name: 'Scheduled' },
|
|
65
|
+
};
|
|
66
|
+
function score(order, workOrders) {
|
|
67
|
+
return (0, process_validator_1.validateAgainstProcess)({
|
|
68
|
+
record: order,
|
|
69
|
+
process: order_process_projection_1.ORDER_PROCESS_PROJECTION,
|
|
70
|
+
relations: { [WO_ENTITY]: workOrders },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function verdictFor(report, depId) {
|
|
74
|
+
return report.verdicts.find((v) => v.depId === depId);
|
|
75
|
+
}
|
|
76
|
+
function run() {
|
|
77
|
+
var _a, _b, _c, _d, _e;
|
|
78
|
+
console.log(`\n${DEP_ID} — START orders are out of scope\n`);
|
|
79
|
+
{
|
|
80
|
+
const v = verdictFor(score(supplyOrder('START'), [CANCELLED_INSTALL]), DEP_ID);
|
|
81
|
+
assert('START + Cancelled INSTALL scores correct (parentFilter excludes it)', (v === null || v === void 0 ? void 0 : v.status) === 'correct', `got ${v === null || v === void 0 ? void 0 : v.status} / ${(_b = (_a = v === null || v === void 0 ? void 0 : v.prescribedAction) === null || _a === void 0 ? void 0 : _a.kind) !== null && _b !== void 0 ? _b : 'no action'}`);
|
|
82
|
+
}
|
|
83
|
+
{
|
|
84
|
+
const v = verdictFor(score(supplyOrder('NEW'), [CANCELLED_INSTALL]), DEP_ID);
|
|
85
|
+
assert('NEW + Cancelled INSTALL still scores variance', (v === null || v === void 0 ? void 0 : v.status) === 'variance', `got ${v === null || v === void 0 ? void 0 : v.status}`);
|
|
86
|
+
assert('NEW + Cancelled INSTALL still prescribes cancel-parent', ((_c = v === null || v === void 0 ? void 0 : v.prescribedAction) === null || _c === void 0 ? void 0 : _c.kind) === 'cancel-parent', `got ${(_d = v === null || v === void 0 ? void 0 : v.prescribedAction) === null || _d === void 0 ? void 0 : _d.kind}`);
|
|
87
|
+
}
|
|
88
|
+
{
|
|
89
|
+
// `notEq` is plain `!==`, so the ordinary NEW-provide case (no discriminator
|
|
90
|
+
// written) must stay fully in scope. This is the rule's blast radius.
|
|
91
|
+
const v = verdictFor(score(supplyOrder(undefined), [CANCELLED_INSTALL]), DEP_ID);
|
|
92
|
+
assert('absent SupplierOrderType stays in scope (variance)', (v === null || v === void 0 ? void 0 : v.status) === 'variance', `got ${v === null || v === void 0 ? void 0 : v.status}`);
|
|
93
|
+
}
|
|
94
|
+
{
|
|
95
|
+
const v = verdictFor(score(supplyOrder('START'), [SCHEDULED_INSTALL]), DEP_ID);
|
|
96
|
+
assert('START + Scheduled INSTALL scores correct', (v === null || v === void 0 ? void 0 : v.status) === 'correct', `got ${v === null || v === void 0 ? void 0 : v.status}`);
|
|
97
|
+
}
|
|
98
|
+
{
|
|
99
|
+
const v = verdictFor(score(supplyOrder('NEW'), [SCHEDULED_INSTALL]), DEP_ID);
|
|
100
|
+
assert('NEW + Scheduled INSTALL scores correct', (v === null || v === void 0 ? void 0 : v.status) === 'correct', `got ${v === null || v === void 0 ? void 0 : v.status}`);
|
|
101
|
+
}
|
|
102
|
+
{
|
|
103
|
+
// The projection must carry the BP's filter verbatim, not merely behave
|
|
104
|
+
// correctly for the cases above.
|
|
105
|
+
const supply = order_process_projection_1.ORDER_PROCESS_PROJECTION.stages.find((s) => s.key === 'OrderStageSupply');
|
|
106
|
+
const dep = (_e = supply === null || supply === void 0 ? void 0 : supply.dependencies) === null || _e === void 0 ? void 0 : _e.find((d) => d.id === DEP_ID);
|
|
107
|
+
const pf = dep && 'parentFilter' in dep ? dep.parentFilter : undefined;
|
|
108
|
+
assert('dep declares parentFilter SupplierOrderType notEq START', (pf === null || pf === void 0 ? void 0 : pf.length) === 1 &&
|
|
109
|
+
pf[0].column === 'SupplierOrderType' &&
|
|
110
|
+
pf[0].op === 'notEq' &&
|
|
111
|
+
pf[0].value === 'START', `got ${JSON.stringify(pf)}`);
|
|
112
|
+
}
|
|
113
|
+
console.log('\n────────────────────────────────────────');
|
|
114
|
+
console.log(`Tests: ${passed} passed, ${failed} failed`);
|
|
115
|
+
if (failed > 0) {
|
|
116
|
+
console.log('');
|
|
117
|
+
failures.forEach((f) => console.log(f));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
run();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@d19n/youfibre-odin-sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.341",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "@d19n",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -42,12 +42,17 @@
|
|
|
42
42
|
"ts-node": "^10.9.2",
|
|
43
43
|
"typescript": "4.8.4"
|
|
44
44
|
},
|
|
45
|
+
"overrides": {
|
|
46
|
+
"uuid": "^11.1.1",
|
|
47
|
+
"diff": "^4.0.4"
|
|
48
|
+
},
|
|
45
49
|
"dependencies": {
|
|
46
50
|
"@d19n/odin-sdk-generator": "^3.0.244",
|
|
47
51
|
"@d19n/odin-types": "^3.6.8"
|
|
48
52
|
},
|
|
49
53
|
"scripts": {
|
|
50
54
|
"test:wholesale-provider": "npx --yes tsx src/wholesale-provider/test/netomnia-provider.spec.ts && npx --yes tsx src/wholesale-provider/test/kci-effect-bridge.spec.ts",
|
|
55
|
+
"test:process-model": "npx --yes tsx src/process-model/order-process.projection.spec.ts",
|
|
51
56
|
"release:beta": "echo Local publish disabled. Use GitHub Actions workflow_dispatch on .github/workflows/release.yml with target=beta && exit 1",
|
|
52
57
|
"release:prod": "echo Local publish disabled. Use GitHub Actions workflow_dispatch on .github/workflows/release.yml with target=prod && exit 1"
|
|
53
58
|
},
|