@final-commerce/command-frame 0.1.63 → 0.2.0-beta.1
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/README.md +26 -28
- package/dist/CommonTypes.d.ts +21 -17
- package/dist/actions/add-customer-note/mock.js +3 -3
- package/dist/actions/can-transition/action.d.ts +2 -0
- package/dist/actions/can-transition/action.js +4 -0
- package/dist/actions/can-transition/mock.d.ts +6 -0
- package/dist/actions/can-transition/mock.js +28 -0
- package/dist/actions/can-transition/types.d.ts +11 -0
- package/dist/actions/can-transition/types.js +1 -0
- package/dist/actions/cash-payment/types.d.ts +5 -0
- package/dist/actions/delete-parked-order/types.d.ts +3 -0
- package/dist/actions/extension-payment/types.d.ts +5 -0
- package/dist/actions/get-available-transitions/action.d.ts +2 -0
- package/dist/actions/get-available-transitions/action.js +4 -0
- package/dist/actions/get-available-transitions/mock.d.ts +6 -0
- package/dist/actions/get-available-transitions/mock.js +26 -0
- package/dist/actions/get-available-transitions/types.d.ts +9 -0
- package/dist/actions/get-available-transitions/types.js +1 -0
- package/dist/actions/initiate-refund/types.d.ts +3 -0
- package/dist/actions/park-order/types.d.ts +3 -0
- package/dist/actions/partial-payment/types.d.ts +5 -0
- package/dist/actions/process-partial-refund/types.d.ts +3 -0
- package/dist/actions/remove-customer-note/mock.js +4 -4
- package/dist/actions/resume-parked-order/types.d.ts +3 -0
- package/dist/actions/tap-to-pay-payment/types.d.ts +5 -0
- package/dist/actions/terminal-payment/types.d.ts +5 -0
- package/dist/actions/vendara-payment/types.d.ts +5 -0
- package/dist/common-types/index.d.ts +1 -0
- package/dist/common-types/index.js +1 -0
- package/dist/common-types/order-state.d.ts +32 -0
- package/dist/common-types/order-state.js +6 -0
- package/dist/common-types/state-fragment.d.ts +77 -0
- package/dist/common-types/state-fragment.js +10 -0
- package/dist/demo/database.js +9 -0
- package/dist/fragments/index.d.ts +1 -0
- package/dist/fragments/index.js +1 -0
- package/dist/fragments/preorder-no-deposit.d.ts +26 -0
- package/dist/fragments/preorder-no-deposit.js +166 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +8 -0
- package/dist/projects/render/mocks.js +5 -1
- package/dist/projects/render/types.d.ts +3 -1
- package/dist/pubsub/topics/orders/index.js +10 -0
- package/dist/pubsub/topics/orders/state-transition-blocked/types.d.ts +16 -0
- package/dist/pubsub/topics/orders/state-transition-blocked/types.js +1 -0
- package/dist/pubsub/topics/orders/state-transition-completed/types.d.ts +15 -0
- package/dist/pubsub/topics/orders/state-transition-completed/types.js +1 -0
- package/dist/pubsub/topics/orders/types.d.ts +6 -2
- package/dist/pubsub/topics/orders/types.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,11 +10,11 @@ Command Frame provides a structured way to build integrations that run inside Fi
|
|
|
10
10
|
|
|
11
11
|
The library provides three main capabilities:
|
|
12
12
|
|
|
13
|
-
| Capability
|
|
14
|
-
|
|
15
|
-
| **Commands**
|
|
16
|
-
| **Pub/Sub**
|
|
17
|
-
| **Hooks**
|
|
13
|
+
| Capability | Purpose | Scope |
|
|
14
|
+
| ------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------- |
|
|
15
|
+
| **Commands** | Call host functions from the iframe (e.g. get products, open cash drawer) | Request/response per call |
|
|
16
|
+
| **Pub/Sub** | Subscribe to real-time events from the host (e.g. cart changes, payments) | Page-scoped (while iframe is mounted) |
|
|
17
|
+
| **Hooks** | Register business-logic callbacks that persist across all pages | Session-scoped (survives page navigation) |
|
|
18
18
|
| **Host → iframe refunds** | Render asks the extension to reverse redeem / gift-card payments before completing a POS refund | Parent `postMessage` + `requestId` (see below) |
|
|
19
19
|
|
|
20
20
|
Domain models (orders, cart, customers, products, and related types) are documented in **[Types reference](./src/types/README.md)**.
|
|
@@ -54,7 +54,7 @@ For building applications that run inside the Render Point of Sale interface.
|
|
|
54
54
|
- **Features:** Order management, Product catalog, Customer management, Payments, Hardware integration (Cash drawer, Printer), Custom tables, Secrets storage.
|
|
55
55
|
|
|
56
56
|
```typescript
|
|
57
|
-
import { RenderClient } from
|
|
57
|
+
import { RenderClient } from "@final-commerce/command-frame";
|
|
58
58
|
|
|
59
59
|
const client = new RenderClient();
|
|
60
60
|
const products = await client.getProducts();
|
|
@@ -68,7 +68,7 @@ For building applications that run inside the Final Commerce Management Dashboar
|
|
|
68
68
|
- **Features:** Context, catalog, entities, custom tables, secrets, and optional host-specific commands (navigation, media, tax, branding, notifications) when the dashboard implements them.
|
|
69
69
|
|
|
70
70
|
```typescript
|
|
71
|
-
import { ManageClient } from
|
|
71
|
+
import { ManageClient } from "@final-commerce/command-frame";
|
|
72
72
|
|
|
73
73
|
const client = new ManageClient();
|
|
74
74
|
const context = await client.getContext();
|
|
@@ -82,14 +82,14 @@ The pub/sub system allows iframe extensions to subscribe to topics and receive r
|
|
|
82
82
|
- **Topics:** Cart (16), Customers (8), Orders (4), Payments (2), Products (4), Refunds (4), Print (3), Custom Tables (3), Outlet (2), Station (2), Session (2), Users (2).
|
|
83
83
|
|
|
84
84
|
```typescript
|
|
85
|
-
import { topics } from
|
|
85
|
+
import { topics } from "@final-commerce/command-frame";
|
|
86
86
|
|
|
87
|
-
const subscriptionId = topics.subscribe(
|
|
88
|
-
console.log(
|
|
87
|
+
const subscriptionId = topics.subscribe("cart", event => {
|
|
88
|
+
console.log("Cart event:", event.type, event.data);
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
// Unsubscribe when done
|
|
92
|
-
topics.unsubscribe(
|
|
92
|
+
topics.unsubscribe("cart", subscriptionId);
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
## Hooks
|
|
@@ -101,17 +101,21 @@ Hooks are **session-scoped** event callbacks that run in the host (Render) conte
|
|
|
101
101
|
- A stable `hookId` is required for deduplication (safe on iframe reload).
|
|
102
102
|
|
|
103
103
|
```typescript
|
|
104
|
-
import { hooks } from
|
|
105
|
-
|
|
106
|
-
hooks.register(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
104
|
+
import { hooks } from "@final-commerce/command-frame";
|
|
105
|
+
|
|
106
|
+
hooks.register(
|
|
107
|
+
"cart",
|
|
108
|
+
async (event, hostCommands) => {
|
|
109
|
+
await hostCommands.upsertCustomTableData({
|
|
110
|
+
tableName: "cart-events-log",
|
|
111
|
+
data: { eventType: event.type, payload: event.data, timestamp: event.timestamp }
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
{ hookId: "my-extension:cart-log" }
|
|
115
|
+
);
|
|
112
116
|
|
|
113
117
|
// Unregister when no longer needed
|
|
114
|
-
hooks.unregister(
|
|
118
|
+
hooks.unregister("my-extension:cart-log");
|
|
115
119
|
```
|
|
116
120
|
|
|
117
121
|
## Host-initiated extension refunds (redeem / gift card)
|
|
@@ -127,18 +131,12 @@ hooks.unregister('my-extension:cart-log');
|
|
|
127
131
|
Exported APIs: `installExtensionRefundListener`, `EXTENSION_REFUND_REQUEST_ACTION`, types **`ExtensionRefundParams`** / **`ExtensionRefundResponse`**.
|
|
128
132
|
|
|
129
133
|
```typescript
|
|
130
|
-
import {
|
|
131
|
-
installExtensionRefundListener,
|
|
132
|
-
type ExtensionRefundParams,
|
|
133
|
-
type ExtensionRefundResponse
|
|
134
|
-
} from '@final-commerce/command-frame';
|
|
134
|
+
import { installExtensionRefundListener, type ExtensionRefundParams, type ExtensionRefundResponse } from "@final-commerce/command-frame";
|
|
135
135
|
|
|
136
136
|
const unsubscribe = installExtensionRefundListener(async (params: ExtensionRefundParams): Promise<ExtensionRefundResponse> => {
|
|
137
137
|
// params.paymentType === "redeem", params.amount in major currency units, params.saleId, params.processor, etc.
|
|
138
138
|
const ok = await myGiftCardProvider.refund(params);
|
|
139
|
-
return ok
|
|
140
|
-
? { success: true, extensionTransactionId: ok.providerRefundId }
|
|
141
|
-
: { success: false, error: 'Refund declined' };
|
|
139
|
+
return ok ? { success: true, extensionTransactionId: ok.providerRefundId } : { success: false, error: "Refund declined" };
|
|
142
140
|
});
|
|
143
141
|
|
|
144
142
|
// on teardown (optional)
|
package/dist/CommonTypes.d.ts
CHANGED
|
@@ -60,8 +60,7 @@ export declare enum CFUserTypes {
|
|
|
60
60
|
CASHIER = "cashier",
|
|
61
61
|
RESELLER = "reseller"
|
|
62
62
|
}
|
|
63
|
-
export
|
|
64
|
-
}
|
|
63
|
+
export type CFActiveEntity = object;
|
|
65
64
|
export interface CFDiscount {
|
|
66
65
|
value: number;
|
|
67
66
|
label?: string;
|
|
@@ -199,7 +198,7 @@ export interface CFActiveProduct extends CFActiveEntity {
|
|
|
199
198
|
}
|
|
200
199
|
export interface CFCustomer {
|
|
201
200
|
_id: string;
|
|
202
|
-
companyId:
|
|
201
|
+
companyId: string;
|
|
203
202
|
externalId?: string;
|
|
204
203
|
email: string;
|
|
205
204
|
firstName: string;
|
|
@@ -358,8 +357,7 @@ export interface CFRefundedLineItem {
|
|
|
358
357
|
images?: string[];
|
|
359
358
|
fee: CFFeeLineItem;
|
|
360
359
|
}
|
|
361
|
-
export
|
|
362
|
-
}
|
|
360
|
+
export type CFRefundedCustomSale = CFCustomSale;
|
|
363
361
|
export interface CFRefundItem {
|
|
364
362
|
lineItems: CFRefundedLineItem[];
|
|
365
363
|
customSales: CFRefundedCustomSale[];
|
|
@@ -382,6 +380,12 @@ export interface CFOrder {
|
|
|
382
380
|
companyId: string;
|
|
383
381
|
externalId: string | null;
|
|
384
382
|
status: string;
|
|
383
|
+
/** Financial state (state machine). Undefined on legacy orders pre-backfill. */
|
|
384
|
+
paymentState?: string;
|
|
385
|
+
/** Operational state (state machine). Undefined on legacy orders pre-backfill. */
|
|
386
|
+
fulfillmentState?: string;
|
|
387
|
+
/** Computed display label from paymentState + fulfillmentState matrix. */
|
|
388
|
+
displayState?: string;
|
|
385
389
|
customer: Partial<CFActiveCustomer | null>;
|
|
386
390
|
customerNote?: string;
|
|
387
391
|
summary: CFSummary;
|
|
@@ -435,7 +439,7 @@ export interface CFActiveUser extends CFActiveEntity {
|
|
|
435
439
|
_id: string;
|
|
436
440
|
}[];
|
|
437
441
|
type?: CFUserTypes;
|
|
438
|
-
companies?:
|
|
442
|
+
companies?: unknown;
|
|
439
443
|
}
|
|
440
444
|
export interface CFActiveOutlet extends CFActiveEntity {
|
|
441
445
|
address: string;
|
|
@@ -515,8 +519,8 @@ export interface CFActiveCustomSales {
|
|
|
515
519
|
taxTableId?: string;
|
|
516
520
|
quantity: number;
|
|
517
521
|
price: number;
|
|
518
|
-
discount?:
|
|
519
|
-
fee?:
|
|
522
|
+
discount?: Record<string, unknown>;
|
|
523
|
+
fee?: Record<string, unknown>;
|
|
520
524
|
}
|
|
521
525
|
/** Non-revenue cart line (e.g. gift card load) — aligned with Render `NonRevenueItem.externalId` (order line id). */
|
|
522
526
|
export interface CFNonRevenueItem {
|
|
@@ -550,7 +554,7 @@ export interface CFActiveCompany extends CFActiveEntity {
|
|
|
550
554
|
id?: string;
|
|
551
555
|
name?: string;
|
|
552
556
|
logo?: string;
|
|
553
|
-
settings?:
|
|
557
|
+
settings?: Record<string, unknown>;
|
|
554
558
|
}
|
|
555
559
|
export type CFProjectName = "Render" | "Manage";
|
|
556
560
|
export interface CFContextRender {
|
|
@@ -568,10 +572,10 @@ export interface CFContextRender {
|
|
|
568
572
|
buildSourceId: string | null;
|
|
569
573
|
buildIsPremium: boolean;
|
|
570
574
|
isOffline: boolean;
|
|
571
|
-
user: Record<string,
|
|
572
|
-
company: Omit<Record<string,
|
|
573
|
-
station: Record<string,
|
|
574
|
-
outlet: Record<string,
|
|
575
|
+
user: Record<string, unknown> | null;
|
|
576
|
+
company: Omit<Record<string, unknown>, "settings"> | null;
|
|
577
|
+
station: Record<string, unknown> | null;
|
|
578
|
+
outlet: Record<string, unknown> | null;
|
|
575
579
|
timestamp: string;
|
|
576
580
|
}
|
|
577
581
|
export interface CFOutletInfo {
|
|
@@ -591,11 +595,11 @@ export interface CFOutletInfo {
|
|
|
591
595
|
country?: string;
|
|
592
596
|
}
|
|
593
597
|
export interface CFContextManage {
|
|
594
|
-
user:
|
|
595
|
-
company:
|
|
596
|
-
menuItem?:
|
|
598
|
+
user: unknown;
|
|
599
|
+
company: unknown;
|
|
600
|
+
menuItem?: unknown;
|
|
597
601
|
extensionId: string;
|
|
598
|
-
outlets?:
|
|
602
|
+
outlets?: unknown[];
|
|
599
603
|
timestamp: string;
|
|
600
604
|
}
|
|
601
605
|
export type CFContext = CFContextRender;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MOCK_CUSTOMERS } from "../../demo/database";
|
|
2
|
-
export const mockAddCustomerNote =
|
|
2
|
+
export const mockAddCustomerNote = (params) => {
|
|
3
3
|
console.log("[Mock] addCustomerNote called", params);
|
|
4
4
|
if (params?.customerId && params.note) {
|
|
5
5
|
const customer = MOCK_CUSTOMERS.find(c => c._id === params.customerId);
|
|
@@ -14,10 +14,10 @@ export const mockAddCustomerNote = async (params) => {
|
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
return {
|
|
17
|
+
return Promise.resolve({
|
|
18
18
|
success: true,
|
|
19
19
|
customerId: params?.customerId || "",
|
|
20
20
|
note: params?.note || "",
|
|
21
21
|
timestamp: new Date().toISOString()
|
|
22
|
-
};
|
|
22
|
+
});
|
|
23
23
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CanTransitionParams, CanTransitionResponse } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Mock implementation: allows all transitions except a few known-invalid ones
|
|
4
|
+
* so the demo app can show both allowed and blocked responses.
|
|
5
|
+
*/
|
|
6
|
+
export declare const canTransitionMock: (params: CanTransitionParams) => Promise<CanTransitionResponse>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock implementation: allows all transitions except a few known-invalid ones
|
|
3
|
+
* so the demo app can show both allowed and blocked responses.
|
|
4
|
+
*/
|
|
5
|
+
export const canTransitionMock = async (params) => {
|
|
6
|
+
const to = params.to;
|
|
7
|
+
if (to.payment === "refunded" && to.fulfillment === "draft") {
|
|
8
|
+
return {
|
|
9
|
+
result: {
|
|
10
|
+
allowed: false,
|
|
11
|
+
blockedBy: "financial_invariant",
|
|
12
|
+
guard: "no-refund-in-draft",
|
|
13
|
+
reason: "Cannot refund an order that is still in draft",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (to.payment === "paid" && to.fulfillment === "cancelled") {
|
|
18
|
+
return {
|
|
19
|
+
result: {
|
|
20
|
+
allowed: false,
|
|
21
|
+
blockedBy: "cross_axis_rule",
|
|
22
|
+
guard: "no-pay-cancelled",
|
|
23
|
+
reason: "Cannot mark a cancelled order as paid",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return { result: { allowed: true } };
|
|
28
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CFStatePair, CFTransitionResult } from "../../common-types/order-state";
|
|
2
|
+
export interface CanTransitionParams {
|
|
3
|
+
/** Order to evaluate. If omitted, evaluates against a new order (from = null). */
|
|
4
|
+
orderId?: string;
|
|
5
|
+
/** Target state pair to transition to. */
|
|
6
|
+
to: CFStatePair;
|
|
7
|
+
}
|
|
8
|
+
export interface CanTransitionResponse {
|
|
9
|
+
result: CFTransitionResult;
|
|
10
|
+
}
|
|
11
|
+
export type CanTransition = (params: CanTransitionParams) => Promise<CanTransitionResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { CFOrder } from "../../CommonTypes";
|
|
2
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
2
3
|
export interface CashPaymentParams {
|
|
3
4
|
/** If not provided, uses the cart total. */
|
|
4
5
|
amount?: number;
|
|
5
6
|
/** Defaults to false. */
|
|
6
7
|
openChangeCalculator?: boolean;
|
|
8
|
+
/** Override the fulfillment state after full payment. Render resolves the cascade. */
|
|
9
|
+
checkoutFulfillmentTarget?: string;
|
|
7
10
|
}
|
|
8
11
|
export interface CashPaymentResponse {
|
|
9
12
|
success: boolean;
|
|
@@ -12,5 +15,7 @@ export interface CashPaymentResponse {
|
|
|
12
15
|
paymentType: string;
|
|
13
16
|
order: CFOrder | null;
|
|
14
17
|
timestamp: string;
|
|
18
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
19
|
+
transitionResult?: CFTransitionResult;
|
|
15
20
|
}
|
|
16
21
|
export type CashPayment = (params?: CashPaymentParams) => Promise<CashPaymentResponse>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
1
2
|
export interface DeleteParkedOrderParams {
|
|
2
3
|
orderId: string;
|
|
3
4
|
}
|
|
@@ -5,5 +6,7 @@ export interface DeleteParkedOrderResponse {
|
|
|
5
6
|
success: boolean;
|
|
6
7
|
orderId: string;
|
|
7
8
|
timestamp: string;
|
|
9
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
10
|
+
transitionResult?: CFTransitionResult;
|
|
8
11
|
}
|
|
9
12
|
export type DeleteParkedOrder = (params?: DeleteParkedOrderParams) => Promise<DeleteParkedOrderResponse>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CFOrder } from "../../CommonTypes";
|
|
2
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
2
3
|
/** Params for extension-initiated payments; host routes by `paymentType`. */
|
|
3
4
|
export interface ExtensionPaymentParams {
|
|
4
5
|
paymentType: string;
|
|
@@ -8,6 +9,8 @@ export interface ExtensionPaymentParams {
|
|
|
8
9
|
referenceId?: string;
|
|
9
10
|
extensionId?: string;
|
|
10
11
|
metadata?: Record<string, unknown>;
|
|
12
|
+
/** Override the fulfillment state after full payment. Render resolves the cascade. */
|
|
13
|
+
checkoutFulfillmentTarget?: string;
|
|
11
14
|
}
|
|
12
15
|
export interface ExtensionPaymentResponse {
|
|
13
16
|
success: boolean;
|
|
@@ -15,5 +18,7 @@ export interface ExtensionPaymentResponse {
|
|
|
15
18
|
paymentType: string;
|
|
16
19
|
order: CFOrder | null;
|
|
17
20
|
timestamp: string;
|
|
21
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
22
|
+
transitionResult?: CFTransitionResult;
|
|
18
23
|
}
|
|
19
24
|
export type ExtensionPayment = (params?: ExtensionPaymentParams) => Promise<ExtensionPaymentResponse>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GetAvailableTransitionsParams, GetAvailableTransitionsResponse } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Mock implementation: returns a fixed set of plausible transitions
|
|
4
|
+
* so the demo app has data to render.
|
|
5
|
+
*/
|
|
6
|
+
export declare const getAvailableTransitionsMock: (_params: GetAvailableTransitionsParams) => Promise<GetAvailableTransitionsResponse>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock implementation: returns a fixed set of plausible transitions
|
|
3
|
+
* so the demo app has data to render.
|
|
4
|
+
*/
|
|
5
|
+
export const getAvailableTransitionsMock = async (_params) => ({
|
|
6
|
+
transitions: [
|
|
7
|
+
{
|
|
8
|
+
to: { payment: "refunded", fulfillment: "returned" },
|
|
9
|
+
displayLabel: "Full Refund",
|
|
10
|
+
conditions: [{ met: true, description: "Order is paid" }],
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
to: { payment: "partially_refunded", fulfillment: "partially_returned" },
|
|
14
|
+
displayLabel: "Partial Refund",
|
|
15
|
+
conditions: [{ met: true, description: "Order is paid" }],
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
to: { payment: "voided", fulfillment: "cancelled" },
|
|
19
|
+
displayLabel: "Void Order",
|
|
20
|
+
conditions: [
|
|
21
|
+
{ met: true, description: "Order exists" },
|
|
22
|
+
{ met: false, description: "No payments captured (mock: skipped)" },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CFAvailableTransition } from "../../common-types/order-state";
|
|
2
|
+
export interface GetAvailableTransitionsParams {
|
|
3
|
+
/** Order to evaluate. Required — we need the current state. */
|
|
4
|
+
orderId: string;
|
|
5
|
+
}
|
|
6
|
+
export interface GetAvailableTransitionsResponse {
|
|
7
|
+
transitions: CFAvailableTransition[];
|
|
8
|
+
}
|
|
9
|
+
export type GetAvailableTransitions = (params: GetAvailableTransitionsParams) => Promise<GetAvailableTransitionsResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
1
2
|
export interface InitiateRefundParams {
|
|
2
3
|
/** The ID of the order to refund. If not provided, uses the currently active order. */
|
|
3
4
|
orderId?: string;
|
|
@@ -6,5 +7,7 @@ export interface InitiateRefundResponse {
|
|
|
6
7
|
success: boolean;
|
|
7
8
|
orderId: string;
|
|
8
9
|
timestamp: string;
|
|
10
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
11
|
+
transitionResult?: CFTransitionResult;
|
|
9
12
|
}
|
|
10
13
|
export type InitiateRefund = (params?: InitiateRefundParams) => Promise<InitiateRefundResponse>;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { CFOrder } from "../../CommonTypes";
|
|
2
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
2
3
|
export interface ParkOrderResponse {
|
|
3
4
|
success: boolean;
|
|
4
5
|
order: CFOrder;
|
|
5
6
|
timestamp: string;
|
|
7
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
8
|
+
transitionResult?: CFTransitionResult;
|
|
6
9
|
}
|
|
7
10
|
export type ParkOrder = () => Promise<ParkOrderResponse>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CFOrder } from "../../CommonTypes";
|
|
2
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
2
3
|
export interface PartialPaymentParams {
|
|
3
4
|
/** The payment amount (required if openUI is false). */
|
|
4
5
|
amount?: number;
|
|
@@ -6,6 +7,8 @@ export interface PartialPaymentParams {
|
|
|
6
7
|
isPercent?: boolean;
|
|
7
8
|
/** If true, opens the split payment UI. */
|
|
8
9
|
openUI?: boolean;
|
|
10
|
+
/** Override the fulfillment state after full payment. Render resolves the cascade. */
|
|
11
|
+
checkoutFulfillmentTarget?: string;
|
|
9
12
|
}
|
|
10
13
|
export interface PartialPaymentResponse {
|
|
11
14
|
success: boolean;
|
|
@@ -14,5 +17,7 @@ export interface PartialPaymentResponse {
|
|
|
14
17
|
openUI: boolean;
|
|
15
18
|
order: CFOrder | null;
|
|
16
19
|
timestamp: string;
|
|
20
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
21
|
+
transitionResult?: CFTransitionResult;
|
|
17
22
|
}
|
|
18
23
|
export type PartialPayment = (params?: PartialPaymentParams) => Promise<PartialPaymentResponse>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
1
2
|
export interface ProcessPartialRefundParams {
|
|
2
3
|
/** Optional refund reason. */
|
|
3
4
|
reason?: string;
|
|
@@ -15,5 +16,7 @@ export interface ProcessPartialRefundResponse {
|
|
|
15
16
|
success: boolean;
|
|
16
17
|
refundId: string;
|
|
17
18
|
timestamp: string;
|
|
19
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
20
|
+
transitionResult?: CFTransitionResult;
|
|
18
21
|
}
|
|
19
22
|
export type ProcessPartialRefund = (params?: ProcessPartialRefundParams) => Promise<ProcessPartialRefundResponse>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export const mockRemoveCustomerNote =
|
|
1
|
+
export const mockRemoveCustomerNote = (params) => {
|
|
2
2
|
console.log("[Mock] removeCustomerNote called", params);
|
|
3
3
|
const noteId = params?.noteId ?? "";
|
|
4
4
|
const timestamp = new Date().toISOString();
|
|
5
5
|
if (!noteId) {
|
|
6
|
-
return { success: false, noteId, timestamp };
|
|
6
|
+
return Promise.resolve({ success: false, noteId, timestamp });
|
|
7
7
|
}
|
|
8
|
-
return {
|
|
8
|
+
return Promise.resolve({
|
|
9
9
|
success: true,
|
|
10
10
|
noteId,
|
|
11
11
|
timestamp
|
|
12
|
-
};
|
|
12
|
+
});
|
|
13
13
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CFOrder } from "../../CommonTypes";
|
|
2
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
2
3
|
export interface ResumeParkedOrderParams {
|
|
3
4
|
orderId: string;
|
|
4
5
|
}
|
|
@@ -6,5 +7,7 @@ export interface ResumeParkedOrderResponse {
|
|
|
6
7
|
success: boolean;
|
|
7
8
|
order: CFOrder;
|
|
8
9
|
timestamp: string;
|
|
10
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
11
|
+
transitionResult?: CFTransitionResult;
|
|
9
12
|
}
|
|
10
13
|
export type ResumeParkedOrder = (params?: ResumeParkedOrderParams) => Promise<ResumeParkedOrderResponse>;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { CFOrder } from "../../CommonTypes";
|
|
2
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
2
3
|
export interface TapToPayPaymentParams {
|
|
3
4
|
/** If not provided, uses the cart total. */
|
|
4
5
|
amount?: number;
|
|
6
|
+
/** Override the fulfillment state after full payment. Render resolves the cascade. */
|
|
7
|
+
checkoutFulfillmentTarget?: string;
|
|
5
8
|
}
|
|
6
9
|
export interface TapToPayPaymentResponse {
|
|
7
10
|
success: boolean;
|
|
@@ -9,5 +12,7 @@ export interface TapToPayPaymentResponse {
|
|
|
9
12
|
paymentType: string;
|
|
10
13
|
order: CFOrder | null;
|
|
11
14
|
timestamp: string;
|
|
15
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
16
|
+
transitionResult?: CFTransitionResult;
|
|
12
17
|
}
|
|
13
18
|
export type TapToPayPayment = (params?: TapToPayPaymentParams) => Promise<TapToPayPaymentResponse>;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { CFOrder } from "../../CommonTypes";
|
|
2
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
2
3
|
export interface TerminalPaymentParams {
|
|
3
4
|
/** If not provided, uses the cart total. */
|
|
4
5
|
amount?: number;
|
|
5
6
|
/** "Bluetooth" or "Cloud". Defaults to "Cloud". */
|
|
6
7
|
paymentType?: 'Bluetooth' | 'Cloud';
|
|
8
|
+
/** Override the fulfillment state after full payment. Render resolves the cascade. */
|
|
9
|
+
checkoutFulfillmentTarget?: string;
|
|
7
10
|
}
|
|
8
11
|
export interface TerminalPaymentResponse {
|
|
9
12
|
success: boolean;
|
|
@@ -11,5 +14,7 @@ export interface TerminalPaymentResponse {
|
|
|
11
14
|
paymentType: string;
|
|
12
15
|
order: CFOrder | null;
|
|
13
16
|
timestamp: string;
|
|
17
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
18
|
+
transitionResult?: CFTransitionResult;
|
|
14
19
|
}
|
|
15
20
|
export type TerminalPayment = (params?: TerminalPaymentParams) => Promise<TerminalPaymentResponse>;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { CFOrder } from "../../CommonTypes";
|
|
2
|
+
import type { CFTransitionResult } from "../../common-types/order-state";
|
|
2
3
|
export interface VendaraPaymentParams {
|
|
3
4
|
/** If not provided, uses the cart total. */
|
|
4
5
|
amount?: number;
|
|
6
|
+
/** Override the fulfillment state after full payment. Render resolves the cascade. */
|
|
7
|
+
checkoutFulfillmentTarget?: string;
|
|
5
8
|
}
|
|
6
9
|
export interface VendaraPaymentResponse {
|
|
7
10
|
success: boolean;
|
|
@@ -9,5 +12,7 @@ export interface VendaraPaymentResponse {
|
|
|
9
12
|
paymentType: string;
|
|
10
13
|
order: CFOrder | null;
|
|
11
14
|
timestamp: string;
|
|
15
|
+
/** Present when the state machine blocked or forced the transition. */
|
|
16
|
+
transitionResult?: CFTransitionResult;
|
|
12
17
|
}
|
|
13
18
|
export type VendaraPayment = (params?: VendaraPaymentParams) => Promise<VendaraPaymentResponse>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State machine protocol types for the command-frame boundary.
|
|
3
|
+
* These mirror @final-commerce/common shapes but carry no logic.
|
|
4
|
+
* command-frame must NOT depend on common.
|
|
5
|
+
*/
|
|
6
|
+
export interface CFStatePair {
|
|
7
|
+
payment: string;
|
|
8
|
+
fulfillment: string;
|
|
9
|
+
}
|
|
10
|
+
export type CFBlockedBy = "financial_invariant" | "cross_axis_rule" | "path" | "condition";
|
|
11
|
+
export interface CFTransitionResult {
|
|
12
|
+
allowed: boolean;
|
|
13
|
+
blockedBy?: CFBlockedBy;
|
|
14
|
+
guard?: string;
|
|
15
|
+
reason?: string;
|
|
16
|
+
failedConditions?: CFFailedCondition[];
|
|
17
|
+
}
|
|
18
|
+
export interface CFFailedCondition {
|
|
19
|
+
field: string;
|
|
20
|
+
operator: string;
|
|
21
|
+
value: unknown;
|
|
22
|
+
reason?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CFConditionStatus {
|
|
25
|
+
met: boolean;
|
|
26
|
+
description: string;
|
|
27
|
+
}
|
|
28
|
+
export interface CFAvailableTransition {
|
|
29
|
+
to: CFStatePair;
|
|
30
|
+
displayLabel: string;
|
|
31
|
+
conditions: CFConditionStatus[];
|
|
32
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State config fragment types for the command-frame boundary.
|
|
3
|
+
* These mirror @final-commerce/common's StateConfigFragment shapes but carry no logic.
|
|
4
|
+
* command-frame must NOT depend on common.
|
|
5
|
+
*
|
|
6
|
+
* Extensions pass a CFStateConfigFragment to Render, which merges it into the
|
|
7
|
+
* running StoredStateConfig via mergeFragment(). All contributed entries are tagged
|
|
8
|
+
* with the extension's id (_sourceExtensionId) so they can be cleaned up on unregister.
|
|
9
|
+
*/
|
|
10
|
+
import type { CFStatePair } from "./order-state";
|
|
11
|
+
export type CFConditionOperator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "notIn" | "isEmpty" | "isNotEmpty";
|
|
12
|
+
export interface CFCondition {
|
|
13
|
+
/** Dot-path into the OrderContext, e.g. "order.balance" or "computed.totalPaid". */
|
|
14
|
+
field: string;
|
|
15
|
+
operator: CFConditionOperator;
|
|
16
|
+
value: unknown;
|
|
17
|
+
}
|
|
18
|
+
/** All conditions in a group are AND'd together. */
|
|
19
|
+
export interface CFConditionGroup {
|
|
20
|
+
conditions: CFCondition[];
|
|
21
|
+
}
|
|
22
|
+
/** Groups are OR'd — the first passing group satisfies the set. */
|
|
23
|
+
export interface CFTransitionConditionSet {
|
|
24
|
+
id: string;
|
|
25
|
+
label: string;
|
|
26
|
+
groups: CFConditionGroup[];
|
|
27
|
+
}
|
|
28
|
+
export interface CFPaymentTransitionPath {
|
|
29
|
+
from: string;
|
|
30
|
+
to: string;
|
|
31
|
+
/** References a CFTransitionConditionSet.id in the same fragment. */
|
|
32
|
+
conditionSetId?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface CFFulfillmentTransitionPath {
|
|
35
|
+
from: string;
|
|
36
|
+
to: string;
|
|
37
|
+
conditionSetId?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface CFCrossAxisRule {
|
|
40
|
+
id: string;
|
|
41
|
+
label: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
enabled: boolean;
|
|
44
|
+
/** Which axis + target state(s) activate this rule. */
|
|
45
|
+
trigger: {
|
|
46
|
+
axis: "payment" | "fulfillment";
|
|
47
|
+
to: string[];
|
|
48
|
+
};
|
|
49
|
+
/** Which axis + states must be present when the trigger fires. */
|
|
50
|
+
requires: {
|
|
51
|
+
axis: "payment" | "fulfillment";
|
|
52
|
+
states: string[];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export interface CFDisplayStateRule {
|
|
56
|
+
paymentState?: string[];
|
|
57
|
+
fulfillmentState?: string[];
|
|
58
|
+
label: string;
|
|
59
|
+
color?: string;
|
|
60
|
+
icon?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Partial config contributed by an extension. Render merges this into the
|
|
64
|
+
* running StoredStateConfig. All arrays are appended; display rules replace
|
|
65
|
+
* on key collision.
|
|
66
|
+
*
|
|
67
|
+
* IDs (crossAxisRules, transitionConditions) must be unique across the merged
|
|
68
|
+
* config — prefix them with your extension id to avoid collisions.
|
|
69
|
+
*/
|
|
70
|
+
export interface CFStateConfigFragment {
|
|
71
|
+
paymentPaths?: CFPaymentTransitionPath[];
|
|
72
|
+
fulfillmentPaths?: CFFulfillmentTransitionPath[];
|
|
73
|
+
crossAxisRules?: CFCrossAxisRule[];
|
|
74
|
+
transitionConditions?: CFTransitionConditionSet[];
|
|
75
|
+
validInitialStates?: CFStatePair[];
|
|
76
|
+
displayStateMap?: CFDisplayStateRule[];
|
|
77
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State config fragment types for the command-frame boundary.
|
|
3
|
+
* These mirror @final-commerce/common's StateConfigFragment shapes but carry no logic.
|
|
4
|
+
* command-frame must NOT depend on common.
|
|
5
|
+
*
|
|
6
|
+
* Extensions pass a CFStateConfigFragment to Render, which merges it into the
|
|
7
|
+
* running StoredStateConfig via mergeFragment(). All contributed entries are tagged
|
|
8
|
+
* with the extension's id (_sourceExtensionId) so they can be cleaned up on unregister.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/dist/demo/database.js
CHANGED
|
@@ -335,6 +335,9 @@ export const MOCK_ORDER_1 = {
|
|
|
335
335
|
companyId: MOCK_COMPANY.id,
|
|
336
336
|
externalId: null,
|
|
337
337
|
status: "completed",
|
|
338
|
+
paymentState: "paid",
|
|
339
|
+
fulfillmentState: "fulfilled",
|
|
340
|
+
displayState: "Completed",
|
|
338
341
|
customer: MOCK_CUSTOMER_1,
|
|
339
342
|
summary: {
|
|
340
343
|
total: 2100,
|
|
@@ -385,6 +388,9 @@ export const MOCK_ORDER_2 = {
|
|
|
385
388
|
companyId: MOCK_COMPANY.id,
|
|
386
389
|
externalId: null,
|
|
387
390
|
status: "completed",
|
|
391
|
+
paymentState: "paid",
|
|
392
|
+
fulfillmentState: "fulfilled",
|
|
393
|
+
displayState: "Completed",
|
|
388
394
|
customer: MOCK_CUSTOMER_2,
|
|
389
395
|
summary: {
|
|
390
396
|
total: 3000,
|
|
@@ -618,6 +624,9 @@ export const createOrderFromCart = (paymentType, amount, processor = "cash") =>
|
|
|
618
624
|
companyId: MOCK_COMPANY.id,
|
|
619
625
|
externalId: null,
|
|
620
626
|
status: "completed",
|
|
627
|
+
paymentState: "paid",
|
|
628
|
+
fulfillmentState: "fulfilled",
|
|
629
|
+
displayState: "Completed",
|
|
621
630
|
customer: MOCK_CART.customer ? MOCK_CART.customer : null,
|
|
622
631
|
summary: {
|
|
623
632
|
total: totalNum,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { preorderNoDepositFragment } from "./preorder-no-deposit";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { preorderNoDepositFragment } from "./preorder-no-deposit";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CFStateConfigFragment } from "../common-types/state-fragment";
|
|
2
|
+
/**
|
|
3
|
+
* Pre-order fragment — no deposit required.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors preorder-with-deposit but removes the cross-axis rule that gates
|
|
6
|
+
* `on_hold` on payment state. An order can be placed on hold while still
|
|
7
|
+
* `unpaid` — payment is collected later (pay-at-pickup, BOPIS, COD, etc.).
|
|
8
|
+
*
|
|
9
|
+
* State flow:
|
|
10
|
+
*
|
|
11
|
+
* unpaid|on_hold → (cashier collects payment) → paid|on_hold
|
|
12
|
+
* ↓
|
|
13
|
+
* (progress fulfillment)
|
|
14
|
+
* ↓
|
|
15
|
+
* paid|in_progress
|
|
16
|
+
*
|
|
17
|
+
* Key difference from preorder-with-deposit:
|
|
18
|
+
* - No cross-axis rule blocking `on_hold` when unpaid. The cashier can
|
|
19
|
+
* confirm the pre-order without collecting a deposit first.
|
|
20
|
+
* - `unpaid|on_hold` is whitelisted as a valid first-save state pair.
|
|
21
|
+
* - `in_progress` still requires at least a partial payment (you must pay
|
|
22
|
+
* before the merchant starts fulfilling).
|
|
23
|
+
*
|
|
24
|
+
* Prefix all IDs with your extension id before merging to avoid collisions.
|
|
25
|
+
*/
|
|
26
|
+
export declare const preorderNoDepositFragment: CFStateConfigFragment;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-order fragment — no deposit required.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors preorder-with-deposit but removes the cross-axis rule that gates
|
|
5
|
+
* `on_hold` on payment state. An order can be placed on hold while still
|
|
6
|
+
* `unpaid` — payment is collected later (pay-at-pickup, BOPIS, COD, etc.).
|
|
7
|
+
*
|
|
8
|
+
* State flow:
|
|
9
|
+
*
|
|
10
|
+
* unpaid|on_hold → (cashier collects payment) → paid|on_hold
|
|
11
|
+
* ↓
|
|
12
|
+
* (progress fulfillment)
|
|
13
|
+
* ↓
|
|
14
|
+
* paid|in_progress
|
|
15
|
+
*
|
|
16
|
+
* Key difference from preorder-with-deposit:
|
|
17
|
+
* - No cross-axis rule blocking `on_hold` when unpaid. The cashier can
|
|
18
|
+
* confirm the pre-order without collecting a deposit first.
|
|
19
|
+
* - `unpaid|on_hold` is whitelisted as a valid first-save state pair.
|
|
20
|
+
* - `in_progress` still requires at least a partial payment (you must pay
|
|
21
|
+
* before the merchant starts fulfilling).
|
|
22
|
+
*
|
|
23
|
+
* Prefix all IDs with your extension id before merging to avoid collisions.
|
|
24
|
+
*/
|
|
25
|
+
export const preorderNoDepositFragment = {
|
|
26
|
+
paymentPaths: [
|
|
27
|
+
{
|
|
28
|
+
from: "unpaid",
|
|
29
|
+
to: "partially_paid",
|
|
30
|
+
conditionSetId: "require-payment-recorded"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
from: "unpaid",
|
|
34
|
+
to: "paid",
|
|
35
|
+
conditionSetId: "require-full-payment"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
from: "partially_paid",
|
|
39
|
+
to: "paid",
|
|
40
|
+
conditionSetId: "require-full-payment"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
fulfillmentPaths: [
|
|
44
|
+
{
|
|
45
|
+
from: "draft",
|
|
46
|
+
to: "on_hold",
|
|
47
|
+
conditionSetId: "preorder-no-deposit-require-items"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
from: "on_hold",
|
|
51
|
+
to: "cancelled",
|
|
52
|
+
conditionSetId: "preorder-no-deposit-cancel-before-progress"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
transitionConditions: [
|
|
56
|
+
{
|
|
57
|
+
id: "preorder-no-deposit-require-items",
|
|
58
|
+
label: "Require at least one item to place pre-order",
|
|
59
|
+
groups: [
|
|
60
|
+
{
|
|
61
|
+
conditions: [
|
|
62
|
+
{
|
|
63
|
+
field: "computed.itemCount",
|
|
64
|
+
operator: "gte",
|
|
65
|
+
value: 1
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "preorder-no-deposit-cancel-before-progress",
|
|
73
|
+
label: "Can only cancel pre-order before fulfillment begins",
|
|
74
|
+
groups: [
|
|
75
|
+
{
|
|
76
|
+
conditions: [
|
|
77
|
+
{
|
|
78
|
+
field: "computed.fulfilledItemCount",
|
|
79
|
+
operator: "eq",
|
|
80
|
+
value: 0
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "require-payment-recorded",
|
|
88
|
+
label: "Require at least one payment before marking partially paid",
|
|
89
|
+
groups: [
|
|
90
|
+
{
|
|
91
|
+
conditions: [
|
|
92
|
+
{
|
|
93
|
+
field: "computed.totalPaid",
|
|
94
|
+
operator: "gt",
|
|
95
|
+
value: 0
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "require-full-payment",
|
|
103
|
+
label: "Require full payment before marking as paid",
|
|
104
|
+
groups: [
|
|
105
|
+
{
|
|
106
|
+
conditions: [
|
|
107
|
+
{
|
|
108
|
+
field: "computed.totalPaid",
|
|
109
|
+
operator: "gte",
|
|
110
|
+
value: 1
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
field: "computed.balance",
|
|
114
|
+
operator: "lte",
|
|
115
|
+
value: 0
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
crossAxisRules: [
|
|
123
|
+
{
|
|
124
|
+
id: "preorder-no-deposit-require-payment-before-confirm",
|
|
125
|
+
label: "Require payment before starting fulfillment",
|
|
126
|
+
description: "Fulfillment cannot move to in_progress unless at least a partial payment has been made",
|
|
127
|
+
enabled: true,
|
|
128
|
+
trigger: {
|
|
129
|
+
axis: "fulfillment",
|
|
130
|
+
to: ["in_progress"]
|
|
131
|
+
},
|
|
132
|
+
requires: {
|
|
133
|
+
axis: "payment",
|
|
134
|
+
states: ["partially_paid", "paid"]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
validInitialStates: [
|
|
139
|
+
{ payment: "unpaid", fulfillment: "on_hold" },
|
|
140
|
+
{ payment: "partially_paid", fulfillment: "on_hold" },
|
|
141
|
+
{ payment: "paid", fulfillment: "on_hold" }
|
|
142
|
+
],
|
|
143
|
+
displayStateMap: [
|
|
144
|
+
{
|
|
145
|
+
paymentState: ["unpaid"],
|
|
146
|
+
fulfillmentState: ["on_hold"],
|
|
147
|
+
label: "Pre-Order",
|
|
148
|
+
color: "#6366f1",
|
|
149
|
+
icon: "clock"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
paymentState: ["partially_paid"],
|
|
153
|
+
fulfillmentState: ["on_hold"],
|
|
154
|
+
label: "Pre-Order (Deposit Received)",
|
|
155
|
+
color: "#f59e0b",
|
|
156
|
+
icon: "clock"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
paymentState: ["paid"],
|
|
160
|
+
fulfillmentState: ["on_hold"],
|
|
161
|
+
label: "Pre-Order (Fully Paid)",
|
|
162
|
+
color: "#22c55e",
|
|
163
|
+
icon: "check-circle"
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -104,6 +104,8 @@ export declare const command: {
|
|
|
104
104
|
readonly uploadMedia: import("./actions/upload-media/types").UploadMedia;
|
|
105
105
|
readonly getTaxTables: import("./actions/get-tax-tables/types").GetTaxTables;
|
|
106
106
|
readonly getBranding: import("./actions/get-branding/types").GetBranding;
|
|
107
|
+
readonly canTransition: import("./actions/can-transition/types").CanTransition;
|
|
108
|
+
readonly getAvailableTransitions: import("./actions/get-available-transitions/types").GetAvailableTransitions;
|
|
107
109
|
};
|
|
108
110
|
export type { ExampleFunction, ExampleFunctionParams, ExampleFunctionResponse } from "./actions/example-function/types";
|
|
109
111
|
export type { GenerateAPIKey, GenerateAPIKeyParams, GenerateAPIKeyResponse } from "./actions/generate-api-key/types";
|
|
@@ -227,7 +229,7 @@ export { stationTopic } from "./pubsub/topics/station";
|
|
|
227
229
|
export { sessionTopic } from "./pubsub/topics/session";
|
|
228
230
|
export { usersTopic } from "./pubsub/topics/users";
|
|
229
231
|
export type { CustomerCreatedPayload, CustomerUpdatedPayload, CustomerNoteAddedPayload, CustomerNoteDeletedPayload, CustomerAssignedPayload, CustomerUnassignedPayload, CustomerActiveSetPayload, CustomerActiveGetPayload, CustomerCreatedEvent, CustomerUpdatedEvent, CustomerNoteAddedEvent, CustomerNoteDeletedEvent, CustomerAssignedEvent, CustomerUnassignedEvent, CustomerActiveSetEvent, CustomerActiveGetEvent, CustomersEventType, CustomersEventPayload } from "./pubsub/topics/customers/types";
|
|
230
|
-
export type { OrderCreatedPayload, OrderUpdatedPayload, OrderActiveSetPayload, OrderActiveGetPayload, OrderCreatedEvent, OrderUpdatedEvent, OrderActiveSetEvent, OrderActiveGetEvent, OrdersEventType, OrdersEventPayload } from "./pubsub/topics/orders/types";
|
|
232
|
+
export type { OrderCreatedPayload, OrderUpdatedPayload, OrderActiveSetPayload, OrderActiveGetPayload, OrderCreatedEvent, OrderUpdatedEvent, OrderActiveSetEvent, OrderActiveGetEvent, OrderStateTransitionCompletedPayload, OrderStateTransitionBlockedPayload, OrderStateTransitionCompletedEvent, OrderStateTransitionBlockedEvent, OrdersEventType, OrdersEventPayload } from "./pubsub/topics/orders/types";
|
|
231
233
|
export type { RefundCreatedPayload, RefundUpdatedPayload, RefundActiveSetPayload, RefundActiveGetPayload, RefundCreatedEvent, RefundUpdatedEvent, RefundActiveSetEvent, RefundActiveGetEvent, RefundsEventType, RefundsEventPayload } from "./pubsub/topics/refunds/types";
|
|
232
234
|
export type { ProductCreatedPayload, ProductUpdatedPayload, ProductSetActivePayload, ProductGetActivePayload, ProductCreatedEvent, ProductUpdatedEvent, ProductSetActiveEvent, ProductGetActiveEvent, ProductsEventType, ProductsEventPayload } from "./pubsub/topics/products/types";
|
|
233
235
|
export type { OutletActiveSetPayload, OutletActiveGetPayload, OutletActiveSetEvent, OutletActiveGetEvent, OutletEventType, OutletEventPayload } from "./pubsub/topics/outlet/types";
|
|
@@ -253,3 +255,8 @@ export type { GetSecretVal, GetSecretValParams, GetSecretValResponse } from "./a
|
|
|
253
255
|
export type { SetSecretVal, SetSecretValParams, SetSecretValResponse } from "./actions/set-secret-val/types";
|
|
254
256
|
export type { GetUsers, GetUsersParams, GetUsersResponse } from "./actions/get-users/types";
|
|
255
257
|
export type { GetRoles, GetRolesParams, GetRolesResponse } from "./actions/get-roles/types";
|
|
258
|
+
export type { CFBlockedBy, CFStatePair, CFTransitionResult, CFFailedCondition, CFConditionStatus, CFAvailableTransition } from "./common-types/order-state";
|
|
259
|
+
export type { CFConditionOperator, CFCondition, CFConditionGroup, CFTransitionConditionSet, CFPaymentTransitionPath, CFFulfillmentTransitionPath, CFCrossAxisRule, CFDisplayStateRule, CFStateConfigFragment } from "./common-types/state-fragment";
|
|
260
|
+
export type { CanTransition, CanTransitionParams, CanTransitionResponse } from "./actions/can-transition/types";
|
|
261
|
+
export type { GetAvailableTransitions, GetAvailableTransitionsParams, GetAvailableTransitionsResponse } from "./actions/get-available-transitions/types";
|
|
262
|
+
export { preorderNoDepositFragment } from "./fragments";
|
package/dist/index.js
CHANGED
|
@@ -102,6 +102,9 @@ import { getSecretsKeys } from "./actions/get-secrets-keys/action";
|
|
|
102
102
|
import { getSecretVal } from "./actions/get-secret-val/action";
|
|
103
103
|
import { setSecretVal } from "./actions/set-secret-val/action";
|
|
104
104
|
import { generateAPIKey } from "./actions/generate-api-key/action";
|
|
105
|
+
// State Machine Query Actions
|
|
106
|
+
import { canTransition } from "./actions/can-transition/action";
|
|
107
|
+
import { getAvailableTransitions } from "./actions/get-available-transitions/action";
|
|
105
108
|
// Product CRUD Actions
|
|
106
109
|
import { addProduct } from "./actions/add-product/action";
|
|
107
110
|
import { editProduct } from "./actions/edit-product/action";
|
|
@@ -238,6 +241,9 @@ export const command = {
|
|
|
238
241
|
uploadMedia,
|
|
239
242
|
getTaxTables,
|
|
240
243
|
getBranding,
|
|
244
|
+
// State Machine Queries
|
|
245
|
+
canTransition,
|
|
246
|
+
getAvailableTransitions
|
|
241
247
|
};
|
|
242
248
|
export { EXTENSION_REFUND_REQUEST_ACTION } from "./actions/extension-refund/constants";
|
|
243
249
|
export { installExtensionRefundListener } from "./actions/extension-refund/extension-refund-listener";
|
|
@@ -272,3 +278,5 @@ export { sessionTopic } from "./pubsub/topics/session";
|
|
|
272
278
|
export { usersTopic } from "./pubsub/topics/users";
|
|
273
279
|
// Export Custom Tables Types
|
|
274
280
|
export { AttributeType } from "./common-types/attribute-type";
|
|
281
|
+
// State Machine Fragments
|
|
282
|
+
export { preorderNoDepositFragment } from "./fragments";
|
|
@@ -85,6 +85,8 @@ import { mockSetActiveSession } from "../../actions/set-active-session/mock";
|
|
|
85
85
|
import { mockGetActiveUser } from "../../actions/get-active-user/mock";
|
|
86
86
|
import { mockSetActiveUser } from "../../actions/set-active-user/mock";
|
|
87
87
|
import { mockSetActiveRefund } from "../../actions/set-active-refund/mock";
|
|
88
|
+
import { canTransitionMock } from "../../actions/can-transition/mock";
|
|
89
|
+
import { getAvailableTransitionsMock } from "../../actions/get-available-transitions/mock";
|
|
88
90
|
export const RENDER_MOCKS = {
|
|
89
91
|
addCartDiscount: mockAddCartDiscount,
|
|
90
92
|
addCartFee: mockAddCartFee,
|
|
@@ -178,5 +180,7 @@ export const RENDER_MOCKS = {
|
|
|
178
180
|
removeCartFee: mockRemoveCartFee,
|
|
179
181
|
removeOrderNote: () => Promise.resolve({ success: true, timestamp: new Date().toISOString() }),
|
|
180
182
|
removeCustomSale: params => Promise.resolve({ success: true, id: params.id, timestamp: new Date().toISOString() }),
|
|
181
|
-
removeNonRevenueItem: params => Promise.resolve({ success: true, externalId: params.externalId, timestamp: new Date().toISOString() })
|
|
183
|
+
removeNonRevenueItem: params => Promise.resolve({ success: true, externalId: params.externalId, timestamp: new Date().toISOString() }),
|
|
184
|
+
canTransition: canTransitionMock,
|
|
185
|
+
getAvailableTransitions: getAvailableTransitionsMock
|
|
182
186
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExampleFunction, GetProducts, AddCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, InitiateRefund, CashPayment, TapToPayPayment, TerminalPayment, VendaraPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, TriggerWebhook, TriggerZapierWebhook, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, SetActiveOutlet, GetActiveStation, SetActiveStation, GetActiveSession, SetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem } from "../../index";
|
|
1
|
+
import type { ExampleFunction, GetProducts, AddCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, InitiateRefund, CashPayment, TapToPayPayment, TerminalPayment, VendaraPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, TriggerWebhook, TriggerZapierWebhook, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, SetActiveOutlet, GetActiveStation, SetActiveStation, GetActiveSession, SetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem, CanTransition, GetAvailableTransitions } from "../../index";
|
|
2
2
|
export interface RenderProviderActions {
|
|
3
3
|
exampleFunction: ExampleFunction;
|
|
4
4
|
getProducts: GetProducts;
|
|
@@ -93,4 +93,6 @@ export interface RenderProviderActions {
|
|
|
93
93
|
removeOrderNote: RemoveOrderNote;
|
|
94
94
|
removeCustomSale: RemoveCustomSale;
|
|
95
95
|
removeNonRevenueItem: RemoveNonRevenueItem;
|
|
96
|
+
canTransition: CanTransition;
|
|
97
|
+
getAvailableTransitions: GetAvailableTransitions;
|
|
96
98
|
}
|
|
@@ -26,6 +26,16 @@ export const ordersTopic = {
|
|
|
26
26
|
id: "get-active-order",
|
|
27
27
|
name: "Get Active Order",
|
|
28
28
|
description: "Published when the active order is retrieved for listeners"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "state-transition-completed",
|
|
32
|
+
name: "State Transition Completed",
|
|
33
|
+
description: "Published when an order state transition completes successfully"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "state-transition-blocked",
|
|
37
|
+
name: "State Transition Blocked",
|
|
38
|
+
description: "Published when an order state transition is blocked by the state machine"
|
|
29
39
|
}
|
|
30
40
|
]
|
|
31
41
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TopicEvent } from "../../../types";
|
|
2
|
+
import type { CFBlockedBy } from "../../../../common-types/order-state";
|
|
3
|
+
export interface OrderStateTransitionBlockedPayload {
|
|
4
|
+
orderId: string;
|
|
5
|
+
from: {
|
|
6
|
+
payment: string;
|
|
7
|
+
fulfillment: string;
|
|
8
|
+
};
|
|
9
|
+
to: {
|
|
10
|
+
payment: string;
|
|
11
|
+
fulfillment: string;
|
|
12
|
+
};
|
|
13
|
+
blockedBy: CFBlockedBy;
|
|
14
|
+
reason?: string;
|
|
15
|
+
}
|
|
16
|
+
export type OrderStateTransitionBlockedEvent = TopicEvent<OrderStateTransitionBlockedPayload>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TopicEvent } from "../../../types";
|
|
2
|
+
export interface OrderStateTransitionCompletedPayload {
|
|
3
|
+
orderId: string;
|
|
4
|
+
from: {
|
|
5
|
+
payment: string;
|
|
6
|
+
fulfillment: string;
|
|
7
|
+
};
|
|
8
|
+
to: {
|
|
9
|
+
payment: string;
|
|
10
|
+
fulfillment: string;
|
|
11
|
+
};
|
|
12
|
+
displayState: string;
|
|
13
|
+
forced?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export type OrderStateTransitionCompletedEvent = TopicEvent<OrderStateTransitionCompletedPayload>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -6,9 +6,13 @@ export * from "./order-created/types";
|
|
|
6
6
|
export * from "./order-updated/types";
|
|
7
7
|
export * from "./set-active-order/types";
|
|
8
8
|
export * from "./get-active-order/types";
|
|
9
|
+
export * from "./state-transition-completed/types";
|
|
10
|
+
export * from "./state-transition-blocked/types";
|
|
9
11
|
import type { OrderCreatedPayload } from "./order-created/types";
|
|
10
12
|
import type { OrderUpdatedPayload } from "./order-updated/types";
|
|
11
13
|
import type { OrderActiveSetPayload } from "./set-active-order/types";
|
|
12
14
|
import type { OrderActiveGetPayload } from "./get-active-order/types";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
import type { OrderStateTransitionCompletedPayload } from "./state-transition-completed/types";
|
|
16
|
+
import type { OrderStateTransitionBlockedPayload } from "./state-transition-blocked/types";
|
|
17
|
+
export type OrdersEventPayload = OrderCreatedPayload | OrderUpdatedPayload | OrderActiveSetPayload | OrderActiveGetPayload | OrderStateTransitionCompletedPayload | OrderStateTransitionBlockedPayload;
|
|
18
|
+
export type OrdersEventType = "order-created" | "order-updated" | "set-active-order" | "get-active-order" | "state-transition-completed" | "state-transition-blocked";
|
|
@@ -7,3 +7,5 @@ export * from "./order-created/types";
|
|
|
7
7
|
export * from "./order-updated/types";
|
|
8
8
|
export * from "./set-active-order/types";
|
|
9
9
|
export * from "./get-active-order/types";
|
|
10
|
+
export * from "./state-transition-completed/types";
|
|
11
|
+
export * from "./state-transition-blocked/types";
|