platform-types 1.10.854 → 1.10.855
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/cjs/notification.js +19 -0
- package/dist/cjs/serviceOrder.js +5 -0
- package/dist/cjs/serviceTracking.js +56 -3
- package/dist/notification.d.ts +24 -1
- package/dist/notification.js +19 -0
- package/dist/serviceOrder.d.ts +33 -12
- package/dist/serviceOrder.js +5 -0
- package/dist/serviceTracking.d.ts +111 -4
- package/dist/serviceTracking.js +55 -2
- package/package.json +1 -1
package/dist/cjs/notification.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.WEB_NOTIFICATIONS_MAX_LIMIT = exports.CUSTOMER_NOTIFICATION_TYPES = expo
|
|
|
7
7
|
// Addressed ERROR / ML_FACTURADOR_COLLISION / ML_SELLER_SWITCHED /
|
|
8
8
|
// ML_CONNECTION_BROKEN / MENTION / CUSTOMER_PENDING_APPROVAL /
|
|
9
9
|
// PAYMENT_ATTEMPT_RESOLUTION / CUSTOMER_WHOLESALE_REQUEST / STOREFRONT_QUOTE_REQUEST /
|
|
10
|
+
// SERVICE_QUOTE_DECIDED /
|
|
10
11
|
// REFUND_OBLIGATION_OVERDUE / MESSAGE_CREDITS_LOW / MESSAGE_SKIPPED_NO_CREDITS do not select a
|
|
11
12
|
// producer subscriber audience;
|
|
12
13
|
// the consumer still reads the live USER row for each delivery channel.
|
|
@@ -237,6 +238,24 @@ var NotificationTypeEnum;
|
|
|
237
238
|
* socket).
|
|
238
239
|
*/
|
|
239
240
|
NotificationTypeEnum["STOREFRONT_QUOTE_REQUEST"] = "STOREFRONT_QUOTE_REQUEST";
|
|
241
|
+
/**
|
|
242
|
+
* A customer approved or rejected a presupuesto online — by the tracking link
|
|
243
|
+
* (`POST /services/tracking/quote`) or signed in
|
|
244
|
+
* (`POST /user { mode: 'service-quote-decision' }`). The decision does not
|
|
245
|
+
* move the ticket's stage; the bell is the shop's cue to do it. Names the
|
|
246
|
+
* ticket number and the decision only — never the customer.
|
|
247
|
+
*
|
|
248
|
+
* ⚠️ ADMIN-ONLY, and its own preference key. The producer fans out to every
|
|
249
|
+
* non-disabled ADMIN of the store (`listActiveAdmins`), the same recipients
|
|
250
|
+
* as `STOREFRONT_QUOTE_REQUEST`. No legacy opt-in scan: the consumer enforces
|
|
251
|
+
* explicit type/channel preferences
|
|
252
|
+
* (`notificationPreferences.SERVICE_QUOTE_DECIDED`), default-on when absent.
|
|
253
|
+
* Additive — no backfill or consumer migration owed.
|
|
254
|
+
*
|
|
255
|
+
* ⚠️ Carries `serviceOrderRef` and NEVER a top-level `customerId` (an
|
|
256
|
+
* audience — the bell would reach the customer's own storefront socket).
|
|
257
|
+
*/
|
|
258
|
+
NotificationTypeEnum["SERVICE_QUOTE_DECIDED"] = "SERVICE_QUOTE_DECIDED";
|
|
240
259
|
/**
|
|
241
260
|
* A refund obligation went overdue: its due date passed and the hourly
|
|
242
261
|
* sweep, after reconciling the payment, still found the money owed
|
package/dist/cjs/serviceOrder.js
CHANGED
|
@@ -305,6 +305,11 @@ exports.SERVICE_ORDER_ERROR_CODES = [
|
|
|
305
305
|
'SERVICE_TRACKING_DISABLED',
|
|
306
306
|
'SERVICE_TRACKING_NO_ADDRESS',
|
|
307
307
|
'SERVICE_TRACKING_LINK_LAPSED',
|
|
308
|
+
// The customer's online quote decision (`_customerDecision.ts`, shared by the
|
|
309
|
+
// web routes) — see `SERVICE_TRACKING_DECISION_ERROR_CODES`
|
|
310
|
+
'SERVICE_TRACKING_VERIFICATION_FAILED',
|
|
311
|
+
// A tracking link revoked between the handle check and the decision's commit.
|
|
312
|
+
'SERVICE_TRACKING_TOKEN_INVALID',
|
|
308
313
|
'ADMIN_REQUIRED',
|
|
309
314
|
'CUSTODY_ILLEGAL_TRANSITION',
|
|
310
315
|
'SERVICE_ORDER_BILLABLE_FROZEN',
|
|
@@ -6,18 +6,24 @@
|
|
|
6
6
|
*
|
|
7
7
|
* The HANDLE is 16 base62 characters (~95 bits), stored server-side and a
|
|
8
8
|
* bearer credential over exactly ONE service order of one store. It authorises
|
|
9
|
-
* the read (`GET /services/tracking?handle=`)
|
|
10
|
-
* (`GET /services/tracking?handle=&mode=quote-pdf`)
|
|
9
|
+
* the read (`GET /services/tracking?handle=`), the current presupuesto PDF
|
|
10
|
+
* (`GET /services/tracking?handle=&mode=quote-pdf`) and the online decision on
|
|
11
|
+
* that presupuesto, and nothing else. Every
|
|
11
12
|
* earlier link of an order dies when staff revoke it. A link stops reading
|
|
12
13
|
* `serviceTrackingLinkDaysAfterClose` days (platform global, default 90) after
|
|
13
14
|
* the order reached a terminal status.
|
|
14
15
|
*
|
|
16
|
+
* With the gate on, the customer may also approve (with a 6-digit code sent to
|
|
17
|
+
* the contact on file) or reject the current presupuesto
|
|
18
|
+
* (`POST /services/tracking/quote`, or signed in
|
|
19
|
+
* `POST /user { mode: 'service-quote-decision' }`).
|
|
20
|
+
*
|
|
15
21
|
* The gate is `ecommerce.config.serviceTracking` AND the `workshop` module on.
|
|
16
22
|
* With the gate off, staff cannot mint and the signed-in reads refuse, but a
|
|
17
23
|
* link minted earlier keeps reading (STORE_MODULES rule 4).
|
|
18
24
|
*/
|
|
19
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.SERVICE_TRACKING_HANDLE_RE = exports.SERVICE_TRACKING_PATH_PREFIX = exports.SERVICE_TRACKING_LINK_ACTIONS = exports.SERVICE_TRACKING_OPERATOR_ERROR_CODES = exports.SERVICE_TRACKING_ACCOUNT_ERROR_CODES = exports.SERVICE_TRACKING_ERROR_CODES = void 0;
|
|
26
|
+
exports.SERVICE_TRACKING_HANDLE_RE = exports.SERVICE_TRACKING_PATH_PREFIX = exports.SERVICE_TRACKING_LINK_ACTIONS = exports.SERVICE_TRACKING_CODE_RE = exports.SERVICE_TRACKING_CHALLENGE_HANDLE_RE = exports.SERVICE_TRACKING_QUOTE_DECISIONS = exports.SERVICE_TRACKING_CODE_VIAS = exports.SERVICE_TRACKING_DECISION_ERROR_CODES = exports.SERVICE_TRACKING_OPERATOR_ERROR_CODES = exports.SERVICE_TRACKING_ACCOUNT_ERROR_CODES = exports.SERVICE_TRACKING_ERROR_CODES = void 0;
|
|
21
27
|
/**
|
|
22
28
|
* The refusals the PUBLIC tracking routes answer themselves.
|
|
23
29
|
*
|
|
@@ -56,6 +62,53 @@ exports.SERVICE_TRACKING_OPERATOR_ERROR_CODES = [
|
|
|
56
62
|
'SERVICE_TRACKING_LINK_LAPSED',
|
|
57
63
|
'ADMIN_REQUIRED',
|
|
58
64
|
];
|
|
65
|
+
/**
|
|
66
|
+
* The refusals of the customer's online quote decision — by link
|
|
67
|
+
* (`POST /services/tracking/code`, `POST /services/tracking/quote`) and signed in
|
|
68
|
+
* (`POST /user { mode: 'service-quote-decision' }`).
|
|
69
|
+
*
|
|
70
|
+
* - SERVICE_TRACKING_TOKEN_INVALID (401, link only): the handle cannot be
|
|
71
|
+
* honoured — the same one body as the read.
|
|
72
|
+
* - SERVICE_TRACKING_VERIFICATION_FAILED (401, link approve only): no code, a
|
|
73
|
+
* wrong, used, expired or burnt one, or a challenge minted for another order.
|
|
74
|
+
* One body for every reason.
|
|
75
|
+
* - SERVICE_TRACKING_DISABLED (403): the store's gate is off. The read keeps working.
|
|
76
|
+
* - SERVICE_ORDER_NOT_FOUND (404, signed in only): no such order, a disabled
|
|
77
|
+
* one, or another customer's — one body.
|
|
78
|
+
* - SERVICE_QUOTE_NOT_FOUND (404): no version with that `quoteId`.
|
|
79
|
+
* - SERVICE_QUOTE_SUPERSEDED (409): a newer version replaced it.
|
|
80
|
+
* - SERVICE_QUOTE_ALREADY_RESOLVED (409): it already carries the OTHER decision
|
|
81
|
+
* (the same decision again answers 200 with the current view and writes nothing).
|
|
82
|
+
* - SERVICE_QUOTE_LAPSED (409, approve only): past `expiresAt`. A rejection is
|
|
83
|
+
* still accepted.
|
|
84
|
+
* - SERVICE_ORDER_QUOTE_CHANGED (409): the order moved under the decision — retry.
|
|
85
|
+
* - SERVICE_ORDER_TERMINAL / SERVICE_ORDER_DISABLED (409): the ticket is closed
|
|
86
|
+
* or deleted.
|
|
87
|
+
* - SERVICE_QUOTE_HOLDS_TOO_MANY (400, approve only): the version's parts cannot
|
|
88
|
+
* be reserved in one write; the shop must re-quote.
|
|
89
|
+
*/
|
|
90
|
+
exports.SERVICE_TRACKING_DECISION_ERROR_CODES = [
|
|
91
|
+
'SERVICE_TRACKING_TOKEN_INVALID',
|
|
92
|
+
'SERVICE_TRACKING_VERIFICATION_FAILED',
|
|
93
|
+
'SERVICE_TRACKING_DISABLED',
|
|
94
|
+
'SERVICE_ORDER_NOT_FOUND',
|
|
95
|
+
'SERVICE_QUOTE_NOT_FOUND',
|
|
96
|
+
'SERVICE_QUOTE_SUPERSEDED',
|
|
97
|
+
'SERVICE_QUOTE_ALREADY_RESOLVED',
|
|
98
|
+
'SERVICE_QUOTE_LAPSED',
|
|
99
|
+
'SERVICE_ORDER_QUOTE_CHANGED',
|
|
100
|
+
'SERVICE_ORDER_TERMINAL',
|
|
101
|
+
'SERVICE_ORDER_DISABLED',
|
|
102
|
+
'SERVICE_QUOTE_HOLDS_TOO_MANY',
|
|
103
|
+
];
|
|
104
|
+
/** Where the 6-digit decision code is sent: the phone or the email on file. */
|
|
105
|
+
exports.SERVICE_TRACKING_CODE_VIAS = ['sms', 'email'];
|
|
106
|
+
/** The customer's two online answers to a presupuesto. */
|
|
107
|
+
exports.SERVICE_TRACKING_QUOTE_DECISIONS = ['approve', 'reject'];
|
|
108
|
+
/** A decision challenge handle: 16 random bytes, base64url (22 characters). */
|
|
109
|
+
exports.SERVICE_TRACKING_CHALLENGE_HANDLE_RE = /^[\w-]{22}$/;
|
|
110
|
+
/** The decision code: 6 digits. */
|
|
111
|
+
exports.SERVICE_TRACKING_CODE_RE = /^\d{6}$/;
|
|
59
112
|
/** `POST /services { mode: 'trackingLink' }` actions. */
|
|
60
113
|
exports.SERVICE_TRACKING_LINK_ACTIONS = ['mint', 'revoke'];
|
|
61
114
|
/** The storefront path a tracking link opens: `/servicio/{handle}`. */
|
package/dist/notification.d.ts
CHANGED
|
@@ -214,6 +214,24 @@ export declare enum NotificationTypeEnum {
|
|
|
214
214
|
* socket).
|
|
215
215
|
*/
|
|
216
216
|
STOREFRONT_QUOTE_REQUEST = "STOREFRONT_QUOTE_REQUEST",
|
|
217
|
+
/**
|
|
218
|
+
* A customer approved or rejected a presupuesto online — by the tracking link
|
|
219
|
+
* (`POST /services/tracking/quote`) or signed in
|
|
220
|
+
* (`POST /user { mode: 'service-quote-decision' }`). The decision does not
|
|
221
|
+
* move the ticket's stage; the bell is the shop's cue to do it. Names the
|
|
222
|
+
* ticket number and the decision only — never the customer.
|
|
223
|
+
*
|
|
224
|
+
* ⚠️ ADMIN-ONLY, and its own preference key. The producer fans out to every
|
|
225
|
+
* non-disabled ADMIN of the store (`listActiveAdmins`), the same recipients
|
|
226
|
+
* as `STOREFRONT_QUOTE_REQUEST`. No legacy opt-in scan: the consumer enforces
|
|
227
|
+
* explicit type/channel preferences
|
|
228
|
+
* (`notificationPreferences.SERVICE_QUOTE_DECIDED`), default-on when absent.
|
|
229
|
+
* Additive — no backfill or consumer migration owed.
|
|
230
|
+
*
|
|
231
|
+
* ⚠️ Carries `serviceOrderRef` and NEVER a top-level `customerId` (an
|
|
232
|
+
* audience — the bell would reach the customer's own storefront socket).
|
|
233
|
+
*/
|
|
234
|
+
SERVICE_QUOTE_DECIDED = "SERVICE_QUOTE_DECIDED",
|
|
217
235
|
/**
|
|
218
236
|
* A refund obligation went overdue: its due date passed and the hourly
|
|
219
237
|
* sweep, after reconciling the payment, still found the money owed
|
|
@@ -383,6 +401,11 @@ declare global {
|
|
|
383
401
|
* an audience — see {@link QuoteRequestRef}. Operator surfaces only.
|
|
384
402
|
*/
|
|
385
403
|
quoteRequestRef?: QuoteRequestRef;
|
|
404
|
+
/**
|
|
405
|
+
* `SERVICE_QUOTE_DECIDED` only: which service order to open. A POINTER, not
|
|
406
|
+
* an audience — see {@link ServiceOrderRef}. Operator surfaces only.
|
|
407
|
+
*/
|
|
408
|
+
serviceOrderRef?: ServiceOrderRef;
|
|
386
409
|
/** `MENTION` only: the colleague who wrote the mention (a snapshot). */
|
|
387
410
|
actor?: NotificationActor;
|
|
388
411
|
userId?: string;
|
|
@@ -446,7 +469,7 @@ declare global {
|
|
|
446
469
|
* customer socket carry — the stored row minus the operator-only pointers.
|
|
447
470
|
* A union keyed on `type`; `ORDER` is its only member today.
|
|
448
471
|
*/
|
|
449
|
-
type CustomerNotification = Omit<NotificationInterface, 'cartRef' | 'pendingCustomerRef' | 'wholesaleRequestRef' | 'quoteRequestRef' | 'TableName' | 'type' | 'orderId'> & {
|
|
472
|
+
type CustomerNotification = Omit<NotificationInterface, 'cartRef' | 'pendingCustomerRef' | 'wholesaleRequestRef' | 'quoteRequestRef' | 'serviceOrderRef' | 'TableName' | 'type' | 'orderId'> & {
|
|
450
473
|
type: NotificationTypeEnum.ORDER;
|
|
451
474
|
/** Set on every customer row: the order the bell is about. */
|
|
452
475
|
orderId: string;
|
package/dist/notification.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// Addressed ERROR / ML_FACTURADOR_COLLISION / ML_SELLER_SWITCHED /
|
|
5
5
|
// ML_CONNECTION_BROKEN / MENTION / CUSTOMER_PENDING_APPROVAL /
|
|
6
6
|
// PAYMENT_ATTEMPT_RESOLUTION / CUSTOMER_WHOLESALE_REQUEST / STOREFRONT_QUOTE_REQUEST /
|
|
7
|
+
// SERVICE_QUOTE_DECIDED /
|
|
7
8
|
// REFUND_OBLIGATION_OVERDUE / MESSAGE_CREDITS_LOW / MESSAGE_SKIPPED_NO_CREDITS do not select a
|
|
8
9
|
// producer subscriber audience;
|
|
9
10
|
// the consumer still reads the live USER row for each delivery channel.
|
|
@@ -234,6 +235,24 @@ export var NotificationTypeEnum;
|
|
|
234
235
|
* socket).
|
|
235
236
|
*/
|
|
236
237
|
NotificationTypeEnum["STOREFRONT_QUOTE_REQUEST"] = "STOREFRONT_QUOTE_REQUEST";
|
|
238
|
+
/**
|
|
239
|
+
* A customer approved or rejected a presupuesto online — by the tracking link
|
|
240
|
+
* (`POST /services/tracking/quote`) or signed in
|
|
241
|
+
* (`POST /user { mode: 'service-quote-decision' }`). The decision does not
|
|
242
|
+
* move the ticket's stage; the bell is the shop's cue to do it. Names the
|
|
243
|
+
* ticket number and the decision only — never the customer.
|
|
244
|
+
*
|
|
245
|
+
* ⚠️ ADMIN-ONLY, and its own preference key. The producer fans out to every
|
|
246
|
+
* non-disabled ADMIN of the store (`listActiveAdmins`), the same recipients
|
|
247
|
+
* as `STOREFRONT_QUOTE_REQUEST`. No legacy opt-in scan: the consumer enforces
|
|
248
|
+
* explicit type/channel preferences
|
|
249
|
+
* (`notificationPreferences.SERVICE_QUOTE_DECIDED`), default-on when absent.
|
|
250
|
+
* Additive — no backfill or consumer migration owed.
|
|
251
|
+
*
|
|
252
|
+
* ⚠️ Carries `serviceOrderRef` and NEVER a top-level `customerId` (an
|
|
253
|
+
* audience — the bell would reach the customer's own storefront socket).
|
|
254
|
+
*/
|
|
255
|
+
NotificationTypeEnum["SERVICE_QUOTE_DECIDED"] = "SERVICE_QUOTE_DECIDED";
|
|
237
256
|
/**
|
|
238
257
|
* A refund obligation went overdue: its due date passed and the hourly
|
|
239
258
|
* sweep, after reconciling the payment, still found the money owed
|
package/dist/serviceOrder.d.ts
CHANGED
|
@@ -361,14 +361,13 @@ declare global {
|
|
|
361
361
|
/**
|
|
362
362
|
* How the customer's decision on a quote reached the shop.
|
|
363
363
|
*
|
|
364
|
-
* ⚠️ `'portal'` is
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
* an event that did not happen
|
|
369
|
-
* `POST /services` quote-resolve
|
|
370
|
-
*
|
|
371
|
-
* day a customer portal ships. Filter it out of any channel picker.
|
|
364
|
+
* ⚠️ `'portal'` is stamped ONLY by the customer's own decision — the tracking
|
|
365
|
+
* link (`POST /services/tracking/quote`) or the signed-in account
|
|
366
|
+
* (`POST /user { mode: 'service-quote-decision' }`). The other four are
|
|
367
|
+
* things an operator records second-hand; an operator selecting `'portal'`
|
|
368
|
+
* would be recording an event that did not happen, so staff
|
|
369
|
+
* `POST /services` quote-resolve still refuses it with
|
|
370
|
+
* `400 SERVICE_QUOTE_CHANNEL_RESERVED`. Filter it out of any channel picker.
|
|
372
371
|
*/
|
|
373
372
|
type ServiceQuoteChannel = 'in_person' | 'phone' | 'whatsapp' | 'email' | 'portal';
|
|
374
373
|
/**
|
|
@@ -404,11 +403,14 @@ declare global {
|
|
|
404
403
|
* while an approval recorded all three, so a quote history could show who
|
|
405
404
|
* approved a version and nothing at all for a rejected one.
|
|
406
405
|
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
406
|
+
* `channel` is set ONLY to `'portal'`, when the customer refused the version
|
|
407
|
+
* themselves; absent means an operator recorded it.
|
|
409
408
|
*/
|
|
410
409
|
interface ServiceQuoteRejection {
|
|
411
|
-
/**
|
|
410
|
+
/**
|
|
411
|
+
* userId of the operator who recorded it, or the customerId who refused it
|
|
412
|
+
* online (`channel: 'portal'`). Server-stamped, never from the body.
|
|
413
|
+
*/
|
|
412
414
|
by: string;
|
|
413
415
|
/** Unix ms. Server-stamped. */
|
|
414
416
|
at: number;
|
|
@@ -419,7 +421,19 @@ declare global {
|
|
|
419
421
|
* constancia — those are statutory documents that go to a customer.
|
|
420
422
|
*/
|
|
421
423
|
note?: string;
|
|
424
|
+
/** `'portal'`: the customer refused it online. Absent: an operator recorded it. */
|
|
425
|
+
channel?: 'portal';
|
|
426
|
+
/**
|
|
427
|
+
* The customer's display name when they decided online — a snapshot for the
|
|
428
|
+
* evidence. Cleared by the customer's erasure.
|
|
429
|
+
*/
|
|
430
|
+
byName?: string;
|
|
422
431
|
}
|
|
432
|
+
/**
|
|
433
|
+
* How a customer who decided online proved they were the contact on file:
|
|
434
|
+
* the 6-digit code sent by SMS or by email, or their signed-in session.
|
|
435
|
+
*/
|
|
436
|
+
type ServiceQuoteVerifiedBy = 'sms_code' | 'email_code' | 'session';
|
|
423
437
|
/**
|
|
424
438
|
* A customer decision on a quote version, recorded as an event rather than a
|
|
425
439
|
* boolean so that who/when/how survives a re-quote.
|
|
@@ -430,6 +444,13 @@ declare global {
|
|
|
430
444
|
/** Unix ms. */
|
|
431
445
|
at: number;
|
|
432
446
|
channel: ServiceQuoteChannel;
|
|
447
|
+
/** `channel: 'portal'` only: how the customer proved they were the contact on file. */
|
|
448
|
+
verifiedBy?: ServiceQuoteVerifiedBy;
|
|
449
|
+
/**
|
|
450
|
+
* `channel: 'portal'` only: the customer's display name at decision time — a
|
|
451
|
+
* snapshot for the evidence. Cleared by the customer's erasure.
|
|
452
|
+
*/
|
|
453
|
+
byName?: string;
|
|
433
454
|
}
|
|
434
455
|
/**
|
|
435
456
|
* One version of the presupuesto. `ServiceOrder.quote` is the full ordered
|
|
@@ -1531,6 +1552,6 @@ export declare const MAX_SERVICE_PICTURES = 20;
|
|
|
1531
1552
|
* check `GATING_ERROR_CODES` in addition. The deposit's opt-in idempotency
|
|
1532
1553
|
* refusals are included here.
|
|
1533
1554
|
*/
|
|
1534
|
-
export declare const SERVICE_ORDER_ERROR_CODES: readonly ["UNIT_QUANTITY_NOT_WHOLE", "UNIT_REVISION_STALE", "ROLE_FORBIDDEN", "PARENT_FILTER_CONFLICT", "VALIDATION_FAILED", "INVALID_CURSOR", "INVALID_FROM_DATE", "INVALID_TO_DATE", "INVALID_DATE_RANGE", "REWORK_INDEX_UNAVAILABLE", "STORE_NOT_FOUND", "SERVICE_ORDER_NOT_FOUND", "PRINT_JOB_DOCUMENT_NOT_FOUND", "CUSTOMER_NOT_FOUND", "PARENT_SERVICE_ORDER_NOT_FOUND", "SERVICE_TEMPLATE_NOT_FOUND", "SERVICE_TYPE_REQUIRED", "INVALID_IMAGE_REFERENCE", "MISSING_STORE_CONTEXT", "WARRANTY_BELOW_LEGAL_MINIMUM", "TECHNICIAN_NOT_FOUND", "TECHNICIAN_NOT_ELIGIBLE", "SERVICE_TRACKING_DISABLED", "SERVICE_TRACKING_NO_ADDRESS", "SERVICE_TRACKING_LINK_LAPSED", "ADMIN_REQUIRED", "CUSTODY_ILLEGAL_TRANSITION", "SERVICE_ORDER_BILLABLE_FROZEN", "SERVICE_ORDER_EDIT_CHANGED", "SERVICE_ORDER_DISABLED_STATE_UNCHANGED", "APPOINTMENT_NOT_FOUND", "APPOINTMENT_NOT_LINKABLE", "APPOINTMENT_NOT_OPEN", "APPOINTMENT_CUSTOMER_MISMATCH", "APPOINTMENT_VERSION_CONFLICT", "SERVICE_ORDER_TERMINAL", "SERVICE_ORDER_DISABLED", "SERVICE_ORDER_ILLEGAL_TRANSITION", "SERVICE_ORDER_STATUS_CHANGED", "SERVICE_ORDER_OUTCOME_REQUIRED", "SERVICE_ORDER_OUTCOME_NOT_ALLOWED", "SERVICE_ORDER_CUSTODY_NOT_RECEIVED", "SERVICE_ORDER_BILLABLE_NOT_AT_THIS_TRANSITION", "PAYMENT_METHODS_NOT_CONFIGURED", "PAYMENT_METHOD_NOT_FOUND", "SERVICE_ORDER_DEPOSIT_CHANGED", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSE", "IDEMPOTENCY_IN_PROGRESS", "IDEMPOTENCY_KEY_FAILED", "IDEMPOTENCY_LOCK_CONTENTION", "SERVICE_DEPOSIT_CURRENCY_MISMATCH", "SERVICE_DEPOSIT_INVALID", "INVALID_AMOUNT", "DEFAULT_DRAWER_UNAVAILABLE", "CURRENCY_MISMATCH", "SHIFT_NOT_OPEN", "SERVICE_BILLING_PRODUCTS_UNAVAILABLE", "CATEGORY_NOT_FOUND", "CATEGORY_DISABLED", "CATEGORY_DISABLE_IN_PROGRESS", "SERVICE_BILLING_PRODUCT_IN_BASKET", "SERVICE_DELIVERY_BASKET_DECISION_REQUIRED", "SERVICE_DELIVERY_BASKET_CURRENCY_MISMATCH", "PRODUCT_NOT_AVAILABLE", "PRODUCT_SALE_LIMIT_EXCEEDED", "PRODUCT_SALE_LIMIT_INVALID", "FX_CONVERSION_UNAVAILABLE", "TRANSACT_ITEMS_LIMIT_EXCEEDED", "SERVICE_LINE_PRICE_TOO_LARGE", "SERVICE_PARTS_RESTORE_EXCEEDS_CONSUMED", "SERVICE_PARTS_RESTORE_EXCEEDS_AMOUNT", "SERVICE_PARTS_STOCK_INSUFFICIENT", "SERVICE_PARTS_PRODUCT_NOT_FOUND", "SERVICE_PARTS_LEDGER_FULL", "SERVICE_PARTS_TOO_MANY", "SERVICE_ORDER_PARTS_CHANGED", "SERVICE_QUOTE_NOT_FOUND", "SERVICE_QUOTE_NOT_OFFERABLE", "SERVICE_QUOTE_HOLDS_TOO_MANY", "SERVICE_QUOTE_SUPERSEDED", "SERVICE_QUOTE_ALREADY_RESOLVED", "SERVICE_QUOTE_LAPSED", "SERVICE_QUOTE_CHANNEL_RESERVED", "SERVICE_QUOTE_CHANNEL_NOT_APPLICABLE", "SERVICE_QUOTE_REASON_NOT_APPLICABLE", "SERVICE_ORDER_QUOTE_CHANGED", "SERVICE_ORDER_NOT_READY", "SERVICE_RENOTIFY_IMPERSONATED", "SERVICE_RENOTIFY_TOO_SOON", "SERVICE_RENOTIFY_CONFLICT", "SERVICE_RENOTIFY_NOT_RECORDED", "SERVICE_RENOTIFY_LEDGER_UNREADABLE", "SERVICE_RENOTIFY_NOT_DISPATCHED", "SERVICE_TIMER_ALREADY_RUNNING", "SERVICE_TIMER_NOT_RUNNING", "SERVICE_TIMER_STOP_CONFLICT", "TECHNICIAN_NOT_CLOCKED_IN", "SERVICE_ORDER_GONE", "SERVICE_WORKLOG_NOT_FOUND", "SERVICE_WORKLOG_NOT_REVERSIBLE", "SERVICE_WORKLOG_ALREADY_REVERSED", "SERVICE_ORDER_WORKLOG_CHANGED", "CART_PROMOTION_UNAVAILABLE", "COUPON_NOT_FOUND", "COUPON_NOT_ACTIVE", "COUPON_EXPIRED", "COUPON_EXHAUSTED", "COUPON_EXHAUSTED_AT_CHECKOUT", "COUPON_MIN_SUBTOTAL", "COUPON_REQUIRES_CUSTOMER", "COUPON_BUDGET_EXHAUSTED", "COUPON_CURRENCY_MISMATCH"];
|
|
1555
|
+
export declare const SERVICE_ORDER_ERROR_CODES: readonly ["UNIT_QUANTITY_NOT_WHOLE", "UNIT_REVISION_STALE", "ROLE_FORBIDDEN", "PARENT_FILTER_CONFLICT", "VALIDATION_FAILED", "INVALID_CURSOR", "INVALID_FROM_DATE", "INVALID_TO_DATE", "INVALID_DATE_RANGE", "REWORK_INDEX_UNAVAILABLE", "STORE_NOT_FOUND", "SERVICE_ORDER_NOT_FOUND", "PRINT_JOB_DOCUMENT_NOT_FOUND", "CUSTOMER_NOT_FOUND", "PARENT_SERVICE_ORDER_NOT_FOUND", "SERVICE_TEMPLATE_NOT_FOUND", "SERVICE_TYPE_REQUIRED", "INVALID_IMAGE_REFERENCE", "MISSING_STORE_CONTEXT", "WARRANTY_BELOW_LEGAL_MINIMUM", "TECHNICIAN_NOT_FOUND", "TECHNICIAN_NOT_ELIGIBLE", "SERVICE_TRACKING_DISABLED", "SERVICE_TRACKING_NO_ADDRESS", "SERVICE_TRACKING_LINK_LAPSED", "SERVICE_TRACKING_VERIFICATION_FAILED", "SERVICE_TRACKING_TOKEN_INVALID", "ADMIN_REQUIRED", "CUSTODY_ILLEGAL_TRANSITION", "SERVICE_ORDER_BILLABLE_FROZEN", "SERVICE_ORDER_EDIT_CHANGED", "SERVICE_ORDER_DISABLED_STATE_UNCHANGED", "APPOINTMENT_NOT_FOUND", "APPOINTMENT_NOT_LINKABLE", "APPOINTMENT_NOT_OPEN", "APPOINTMENT_CUSTOMER_MISMATCH", "APPOINTMENT_VERSION_CONFLICT", "SERVICE_ORDER_TERMINAL", "SERVICE_ORDER_DISABLED", "SERVICE_ORDER_ILLEGAL_TRANSITION", "SERVICE_ORDER_STATUS_CHANGED", "SERVICE_ORDER_OUTCOME_REQUIRED", "SERVICE_ORDER_OUTCOME_NOT_ALLOWED", "SERVICE_ORDER_CUSTODY_NOT_RECEIVED", "SERVICE_ORDER_BILLABLE_NOT_AT_THIS_TRANSITION", "PAYMENT_METHODS_NOT_CONFIGURED", "PAYMENT_METHOD_NOT_FOUND", "SERVICE_ORDER_DEPOSIT_CHANGED", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSE", "IDEMPOTENCY_IN_PROGRESS", "IDEMPOTENCY_KEY_FAILED", "IDEMPOTENCY_LOCK_CONTENTION", "SERVICE_DEPOSIT_CURRENCY_MISMATCH", "SERVICE_DEPOSIT_INVALID", "INVALID_AMOUNT", "DEFAULT_DRAWER_UNAVAILABLE", "CURRENCY_MISMATCH", "SHIFT_NOT_OPEN", "SERVICE_BILLING_PRODUCTS_UNAVAILABLE", "CATEGORY_NOT_FOUND", "CATEGORY_DISABLED", "CATEGORY_DISABLE_IN_PROGRESS", "SERVICE_BILLING_PRODUCT_IN_BASKET", "SERVICE_DELIVERY_BASKET_DECISION_REQUIRED", "SERVICE_DELIVERY_BASKET_CURRENCY_MISMATCH", "PRODUCT_NOT_AVAILABLE", "PRODUCT_SALE_LIMIT_EXCEEDED", "PRODUCT_SALE_LIMIT_INVALID", "FX_CONVERSION_UNAVAILABLE", "TRANSACT_ITEMS_LIMIT_EXCEEDED", "SERVICE_LINE_PRICE_TOO_LARGE", "SERVICE_PARTS_RESTORE_EXCEEDS_CONSUMED", "SERVICE_PARTS_RESTORE_EXCEEDS_AMOUNT", "SERVICE_PARTS_STOCK_INSUFFICIENT", "SERVICE_PARTS_PRODUCT_NOT_FOUND", "SERVICE_PARTS_LEDGER_FULL", "SERVICE_PARTS_TOO_MANY", "SERVICE_ORDER_PARTS_CHANGED", "SERVICE_QUOTE_NOT_FOUND", "SERVICE_QUOTE_NOT_OFFERABLE", "SERVICE_QUOTE_HOLDS_TOO_MANY", "SERVICE_QUOTE_SUPERSEDED", "SERVICE_QUOTE_ALREADY_RESOLVED", "SERVICE_QUOTE_LAPSED", "SERVICE_QUOTE_CHANNEL_RESERVED", "SERVICE_QUOTE_CHANNEL_NOT_APPLICABLE", "SERVICE_QUOTE_REASON_NOT_APPLICABLE", "SERVICE_ORDER_QUOTE_CHANGED", "SERVICE_ORDER_NOT_READY", "SERVICE_RENOTIFY_IMPERSONATED", "SERVICE_RENOTIFY_TOO_SOON", "SERVICE_RENOTIFY_CONFLICT", "SERVICE_RENOTIFY_NOT_RECORDED", "SERVICE_RENOTIFY_LEDGER_UNREADABLE", "SERVICE_RENOTIFY_NOT_DISPATCHED", "SERVICE_TIMER_ALREADY_RUNNING", "SERVICE_TIMER_NOT_RUNNING", "SERVICE_TIMER_STOP_CONFLICT", "TECHNICIAN_NOT_CLOCKED_IN", "SERVICE_ORDER_GONE", "SERVICE_WORKLOG_NOT_FOUND", "SERVICE_WORKLOG_NOT_REVERSIBLE", "SERVICE_WORKLOG_ALREADY_REVERSED", "SERVICE_ORDER_WORKLOG_CHANGED", "CART_PROMOTION_UNAVAILABLE", "COUPON_NOT_FOUND", "COUPON_NOT_ACTIVE", "COUPON_EXPIRED", "COUPON_EXHAUSTED", "COUPON_EXHAUSTED_AT_CHECKOUT", "COUPON_MIN_SUBTOTAL", "COUPON_REQUIRES_CUSTOMER", "COUPON_BUDGET_EXHAUSTED", "COUPON_CURRENCY_MISMATCH"];
|
|
1535
1556
|
export type ServiceOrderErrorCode = (typeof SERVICE_ORDER_ERROR_CODES)[number];
|
|
1536
1557
|
export {};
|
package/dist/serviceOrder.js
CHANGED
|
@@ -302,6 +302,11 @@ export const SERVICE_ORDER_ERROR_CODES = [
|
|
|
302
302
|
'SERVICE_TRACKING_DISABLED',
|
|
303
303
|
'SERVICE_TRACKING_NO_ADDRESS',
|
|
304
304
|
'SERVICE_TRACKING_LINK_LAPSED',
|
|
305
|
+
// The customer's online quote decision (`_customerDecision.ts`, shared by the
|
|
306
|
+
// web routes) — see `SERVICE_TRACKING_DECISION_ERROR_CODES`
|
|
307
|
+
'SERVICE_TRACKING_VERIFICATION_FAILED',
|
|
308
|
+
// A tracking link revoked between the handle check and the decision's commit.
|
|
309
|
+
'SERVICE_TRACKING_TOKEN_INVALID',
|
|
305
310
|
'ADMIN_REQUIRED',
|
|
306
311
|
'CUSTODY_ILLEGAL_TRANSITION',
|
|
307
312
|
'SERVICE_ORDER_BILLABLE_FROZEN',
|
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
*
|
|
6
6
|
* The HANDLE is 16 base62 characters (~95 bits), stored server-side and a
|
|
7
7
|
* bearer credential over exactly ONE service order of one store. It authorises
|
|
8
|
-
* the read (`GET /services/tracking?handle=`)
|
|
9
|
-
* (`GET /services/tracking?handle=&mode=quote-pdf`)
|
|
8
|
+
* the read (`GET /services/tracking?handle=`), the current presupuesto PDF
|
|
9
|
+
* (`GET /services/tracking?handle=&mode=quote-pdf`) and the online decision on
|
|
10
|
+
* that presupuesto, and nothing else. Every
|
|
10
11
|
* earlier link of an order dies when staff revoke it. A link stops reading
|
|
11
12
|
* `serviceTrackingLinkDaysAfterClose` days (platform global, default 90) after
|
|
12
13
|
* the order reached a terminal status.
|
|
13
14
|
*
|
|
15
|
+
* With the gate on, the customer may also approve (with a 6-digit code sent to
|
|
16
|
+
* the contact on file) or reject the current presupuesto
|
|
17
|
+
* (`POST /services/tracking/quote`, or signed in
|
|
18
|
+
* `POST /user { mode: 'service-quote-decision' }`).
|
|
19
|
+
*
|
|
14
20
|
* The gate is `ecommerce.config.serviceTracking` AND the `workshop` module on.
|
|
15
21
|
* With the gate off, staff cannot mint and the signed-in reads refuse, but a
|
|
16
22
|
* link minted earlier keeps reading (STORE_MODULES rule 4).
|
|
@@ -51,6 +57,41 @@ export type ServiceTrackingAccountErrorCode = (typeof SERVICE_TRACKING_ACCOUNT_E
|
|
|
51
57
|
*/
|
|
52
58
|
export declare const SERVICE_TRACKING_OPERATOR_ERROR_CODES: readonly ["SERVICE_TRACKING_DISABLED", "SERVICE_TRACKING_NO_ADDRESS", "SERVICE_TRACKING_LINK_LAPSED", "ADMIN_REQUIRED"];
|
|
53
59
|
export type ServiceTrackingOperatorErrorCode = (typeof SERVICE_TRACKING_OPERATOR_ERROR_CODES)[number];
|
|
60
|
+
/**
|
|
61
|
+
* The refusals of the customer's online quote decision — by link
|
|
62
|
+
* (`POST /services/tracking/code`, `POST /services/tracking/quote`) and signed in
|
|
63
|
+
* (`POST /user { mode: 'service-quote-decision' }`).
|
|
64
|
+
*
|
|
65
|
+
* - SERVICE_TRACKING_TOKEN_INVALID (401, link only): the handle cannot be
|
|
66
|
+
* honoured — the same one body as the read.
|
|
67
|
+
* - SERVICE_TRACKING_VERIFICATION_FAILED (401, link approve only): no code, a
|
|
68
|
+
* wrong, used, expired or burnt one, or a challenge minted for another order.
|
|
69
|
+
* One body for every reason.
|
|
70
|
+
* - SERVICE_TRACKING_DISABLED (403): the store's gate is off. The read keeps working.
|
|
71
|
+
* - SERVICE_ORDER_NOT_FOUND (404, signed in only): no such order, a disabled
|
|
72
|
+
* one, or another customer's — one body.
|
|
73
|
+
* - SERVICE_QUOTE_NOT_FOUND (404): no version with that `quoteId`.
|
|
74
|
+
* - SERVICE_QUOTE_SUPERSEDED (409): a newer version replaced it.
|
|
75
|
+
* - SERVICE_QUOTE_ALREADY_RESOLVED (409): it already carries the OTHER decision
|
|
76
|
+
* (the same decision again answers 200 with the current view and writes nothing).
|
|
77
|
+
* - SERVICE_QUOTE_LAPSED (409, approve only): past `expiresAt`. A rejection is
|
|
78
|
+
* still accepted.
|
|
79
|
+
* - SERVICE_ORDER_QUOTE_CHANGED (409): the order moved under the decision — retry.
|
|
80
|
+
* - SERVICE_ORDER_TERMINAL / SERVICE_ORDER_DISABLED (409): the ticket is closed
|
|
81
|
+
* or deleted.
|
|
82
|
+
* - SERVICE_QUOTE_HOLDS_TOO_MANY (400, approve only): the version's parts cannot
|
|
83
|
+
* be reserved in one write; the shop must re-quote.
|
|
84
|
+
*/
|
|
85
|
+
export declare const SERVICE_TRACKING_DECISION_ERROR_CODES: readonly ["SERVICE_TRACKING_TOKEN_INVALID", "SERVICE_TRACKING_VERIFICATION_FAILED", "SERVICE_TRACKING_DISABLED", "SERVICE_ORDER_NOT_FOUND", "SERVICE_QUOTE_NOT_FOUND", "SERVICE_QUOTE_SUPERSEDED", "SERVICE_QUOTE_ALREADY_RESOLVED", "SERVICE_QUOTE_LAPSED", "SERVICE_ORDER_QUOTE_CHANGED", "SERVICE_ORDER_TERMINAL", "SERVICE_ORDER_DISABLED", "SERVICE_QUOTE_HOLDS_TOO_MANY"];
|
|
86
|
+
export type ServiceTrackingDecisionErrorCode = (typeof SERVICE_TRACKING_DECISION_ERROR_CODES)[number];
|
|
87
|
+
/** Where the 6-digit decision code is sent: the phone or the email on file. */
|
|
88
|
+
export declare const SERVICE_TRACKING_CODE_VIAS: readonly ["sms", "email"];
|
|
89
|
+
/** The customer's two online answers to a presupuesto. */
|
|
90
|
+
export declare const SERVICE_TRACKING_QUOTE_DECISIONS: readonly ["approve", "reject"];
|
|
91
|
+
/** A decision challenge handle: 16 random bytes, base64url (22 characters). */
|
|
92
|
+
export declare const SERVICE_TRACKING_CHALLENGE_HANDLE_RE: RegExp;
|
|
93
|
+
/** The decision code: 6 digits. */
|
|
94
|
+
export declare const SERVICE_TRACKING_CODE_RE: RegExp;
|
|
54
95
|
/** `POST /services { mode: 'trackingLink' }` actions. */
|
|
55
96
|
export declare const SERVICE_TRACKING_LINK_ACTIONS: readonly ["mint", "revoke"];
|
|
56
97
|
/** The storefront path a tracking link opens: `/servicio/{handle}`. */
|
|
@@ -148,11 +189,73 @@ declare global {
|
|
|
148
189
|
}
|
|
149
190
|
/** Whether the customer may decide the presupuesto online. */
|
|
150
191
|
interface ServiceTrackingDecision {
|
|
151
|
-
/**
|
|
192
|
+
/**
|
|
193
|
+
* `true` when the store's gate is on, the order is open and not deleted,
|
|
194
|
+
* and the current presupuesto is `sent` (awaiting an answer). A lapsed one
|
|
195
|
+
* stays decidable: it can still be rejected (an approval answers
|
|
196
|
+
* `409 SERVICE_QUOTE_LAPSED`).
|
|
197
|
+
*/
|
|
152
198
|
canDecide: boolean;
|
|
153
|
-
/**
|
|
199
|
+
/**
|
|
200
|
+
* Whether an approval needs the 6-digit code sent to the contact on file:
|
|
201
|
+
* `true` on the link, `false` signed in (the session is the proof).
|
|
202
|
+
*/
|
|
154
203
|
approveNeedsCode: boolean;
|
|
155
204
|
}
|
|
205
|
+
type ServiceTrackingCodeVia = (typeof SERVICE_TRACKING_CODE_VIAS)[number];
|
|
206
|
+
type ServiceTrackingQuoteDecision = (typeof SERVICE_TRACKING_QUOTE_DECISIONS)[number];
|
|
207
|
+
/**
|
|
208
|
+
* `POST /services/tracking/code` body (strict): send the 6-digit decision code
|
|
209
|
+
* to the phone (`sms`) or the email ON FILE for the order's customer — never
|
|
210
|
+
* to an address in the request. Always `202 { challengeHandle }` once the
|
|
211
|
+
* handle is honoured and the gate is on, whether or not a code could be sent
|
|
212
|
+
* (no contact for that channel, a spent daily budget): the page offers only
|
|
213
|
+
* the channels whose masked contact it shows. The code lives
|
|
214
|
+
* `CUSTOMER_OTP_LIMITS.ttlSeconds` and takes `maxAttempts` tries.
|
|
215
|
+
*/
|
|
216
|
+
interface ServiceTrackingCodeRequest {
|
|
217
|
+
handle: string;
|
|
218
|
+
via: ServiceTrackingCodeVia;
|
|
219
|
+
}
|
|
220
|
+
/** `POST /services/tracking/code` `data`. */
|
|
221
|
+
interface ServiceTrackingCodeResult {
|
|
222
|
+
/** 22 characters; sent back with the code on the approval. */
|
|
223
|
+
challengeHandle: string;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* `POST /services/tracking/quote` body (strict): the customer's answer to the
|
|
227
|
+
* current presupuesto, by link. `quoteId` plus the order's own state are the
|
|
228
|
+
* concurrency check — there is no `version`.
|
|
229
|
+
*
|
|
230
|
+
* - `approve` needs `challengeHandle` + `code` (from `POST /services/tracking/code`);
|
|
231
|
+
* `reason` is refused.
|
|
232
|
+
* - `reject` needs `reason` only, and is accepted past `expiresAt`.
|
|
233
|
+
*
|
|
234
|
+
* Answers `200` with the current `ServiceTrackingView`. The same decision again
|
|
235
|
+
* on the same `quoteId` answers `200` with the view and writes nothing. The
|
|
236
|
+
* decision does NOT move the order's stage; the shop does.
|
|
237
|
+
*/
|
|
238
|
+
interface ServiceTrackingQuoteDecisionRequest {
|
|
239
|
+
handle: string;
|
|
240
|
+
quoteId: string;
|
|
241
|
+
decision: ServiceTrackingQuoteDecision;
|
|
242
|
+
reason?: ServiceQuoteRejectionReason;
|
|
243
|
+
challengeHandle?: string;
|
|
244
|
+
code?: string;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* `POST /user { mode: 'service-quote-decision' }` body: the same answer by the
|
|
248
|
+
* signed-in owner of the order (`verifiedBy: 'session'`, no code). Another
|
|
249
|
+
* customer's `serviceOrderId` answers the same `404 SERVICE_ORDER_NOT_FOUND`
|
|
250
|
+
* as a missing one. Answers `200` with the current `ServiceTrackingView`.
|
|
251
|
+
*/
|
|
252
|
+
interface ServiceQuoteDecisionAccountRequest {
|
|
253
|
+
mode: 'service-quote-decision';
|
|
254
|
+
serviceOrderId: string;
|
|
255
|
+
quoteId: string;
|
|
256
|
+
decision: ServiceTrackingQuoteDecision;
|
|
257
|
+
reason?: ServiceQuoteRejectionReason;
|
|
258
|
+
}
|
|
156
259
|
/** The customer's contact as the shop holds it, masked. */
|
|
157
260
|
interface ServiceTrackingContact {
|
|
158
261
|
firstName: string;
|
|
@@ -196,6 +299,10 @@ declare global {
|
|
|
196
299
|
contact: ServiceTrackingContact;
|
|
197
300
|
store: ServiceTrackingStore;
|
|
198
301
|
}
|
|
302
|
+
/** What a `SERVICE_QUOTE_DECIDED` operator bell points at. */
|
|
303
|
+
interface ServiceOrderRef {
|
|
304
|
+
serviceOrderId: string;
|
|
305
|
+
}
|
|
199
306
|
/** One row of `GET /user?mode=service-orders`. */
|
|
200
307
|
interface ServiceTrackingAccountListItem {
|
|
201
308
|
serviceOrderId: string;
|
package/dist/serviceTracking.js
CHANGED
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
*
|
|
6
6
|
* The HANDLE is 16 base62 characters (~95 bits), stored server-side and a
|
|
7
7
|
* bearer credential over exactly ONE service order of one store. It authorises
|
|
8
|
-
* the read (`GET /services/tracking?handle=`)
|
|
9
|
-
* (`GET /services/tracking?handle=&mode=quote-pdf`)
|
|
8
|
+
* the read (`GET /services/tracking?handle=`), the current presupuesto PDF
|
|
9
|
+
* (`GET /services/tracking?handle=&mode=quote-pdf`) and the online decision on
|
|
10
|
+
* that presupuesto, and nothing else. Every
|
|
10
11
|
* earlier link of an order dies when staff revoke it. A link stops reading
|
|
11
12
|
* `serviceTrackingLinkDaysAfterClose` days (platform global, default 90) after
|
|
12
13
|
* the order reached a terminal status.
|
|
13
14
|
*
|
|
15
|
+
* With the gate on, the customer may also approve (with a 6-digit code sent to
|
|
16
|
+
* the contact on file) or reject the current presupuesto
|
|
17
|
+
* (`POST /services/tracking/quote`, or signed in
|
|
18
|
+
* `POST /user { mode: 'service-quote-decision' }`).
|
|
19
|
+
*
|
|
14
20
|
* The gate is `ecommerce.config.serviceTracking` AND the `workshop` module on.
|
|
15
21
|
* With the gate off, staff cannot mint and the signed-in reads refuse, but a
|
|
16
22
|
* link minted earlier keeps reading (STORE_MODULES rule 4).
|
|
@@ -53,6 +59,53 @@ export const SERVICE_TRACKING_OPERATOR_ERROR_CODES = [
|
|
|
53
59
|
'SERVICE_TRACKING_LINK_LAPSED',
|
|
54
60
|
'ADMIN_REQUIRED',
|
|
55
61
|
];
|
|
62
|
+
/**
|
|
63
|
+
* The refusals of the customer's online quote decision — by link
|
|
64
|
+
* (`POST /services/tracking/code`, `POST /services/tracking/quote`) and signed in
|
|
65
|
+
* (`POST /user { mode: 'service-quote-decision' }`).
|
|
66
|
+
*
|
|
67
|
+
* - SERVICE_TRACKING_TOKEN_INVALID (401, link only): the handle cannot be
|
|
68
|
+
* honoured — the same one body as the read.
|
|
69
|
+
* - SERVICE_TRACKING_VERIFICATION_FAILED (401, link approve only): no code, a
|
|
70
|
+
* wrong, used, expired or burnt one, or a challenge minted for another order.
|
|
71
|
+
* One body for every reason.
|
|
72
|
+
* - SERVICE_TRACKING_DISABLED (403): the store's gate is off. The read keeps working.
|
|
73
|
+
* - SERVICE_ORDER_NOT_FOUND (404, signed in only): no such order, a disabled
|
|
74
|
+
* one, or another customer's — one body.
|
|
75
|
+
* - SERVICE_QUOTE_NOT_FOUND (404): no version with that `quoteId`.
|
|
76
|
+
* - SERVICE_QUOTE_SUPERSEDED (409): a newer version replaced it.
|
|
77
|
+
* - SERVICE_QUOTE_ALREADY_RESOLVED (409): it already carries the OTHER decision
|
|
78
|
+
* (the same decision again answers 200 with the current view and writes nothing).
|
|
79
|
+
* - SERVICE_QUOTE_LAPSED (409, approve only): past `expiresAt`. A rejection is
|
|
80
|
+
* still accepted.
|
|
81
|
+
* - SERVICE_ORDER_QUOTE_CHANGED (409): the order moved under the decision — retry.
|
|
82
|
+
* - SERVICE_ORDER_TERMINAL / SERVICE_ORDER_DISABLED (409): the ticket is closed
|
|
83
|
+
* or deleted.
|
|
84
|
+
* - SERVICE_QUOTE_HOLDS_TOO_MANY (400, approve only): the version's parts cannot
|
|
85
|
+
* be reserved in one write; the shop must re-quote.
|
|
86
|
+
*/
|
|
87
|
+
export const SERVICE_TRACKING_DECISION_ERROR_CODES = [
|
|
88
|
+
'SERVICE_TRACKING_TOKEN_INVALID',
|
|
89
|
+
'SERVICE_TRACKING_VERIFICATION_FAILED',
|
|
90
|
+
'SERVICE_TRACKING_DISABLED',
|
|
91
|
+
'SERVICE_ORDER_NOT_FOUND',
|
|
92
|
+
'SERVICE_QUOTE_NOT_FOUND',
|
|
93
|
+
'SERVICE_QUOTE_SUPERSEDED',
|
|
94
|
+
'SERVICE_QUOTE_ALREADY_RESOLVED',
|
|
95
|
+
'SERVICE_QUOTE_LAPSED',
|
|
96
|
+
'SERVICE_ORDER_QUOTE_CHANGED',
|
|
97
|
+
'SERVICE_ORDER_TERMINAL',
|
|
98
|
+
'SERVICE_ORDER_DISABLED',
|
|
99
|
+
'SERVICE_QUOTE_HOLDS_TOO_MANY',
|
|
100
|
+
];
|
|
101
|
+
/** Where the 6-digit decision code is sent: the phone or the email on file. */
|
|
102
|
+
export const SERVICE_TRACKING_CODE_VIAS = ['sms', 'email'];
|
|
103
|
+
/** The customer's two online answers to a presupuesto. */
|
|
104
|
+
export const SERVICE_TRACKING_QUOTE_DECISIONS = ['approve', 'reject'];
|
|
105
|
+
/** A decision challenge handle: 16 random bytes, base64url (22 characters). */
|
|
106
|
+
export const SERVICE_TRACKING_CHALLENGE_HANDLE_RE = /^[\w-]{22}$/;
|
|
107
|
+
/** The decision code: 6 digits. */
|
|
108
|
+
export const SERVICE_TRACKING_CODE_RE = /^\d{6}$/;
|
|
56
109
|
/** `POST /services { mode: 'trackingLink' }` actions. */
|
|
57
110
|
export const SERVICE_TRACKING_LINK_ACTIONS = ['mint', 'revoke'];
|
|
58
111
|
/** The storefront path a tracking link opens: `/servicio/{handle}`. */
|