@final-commerce/command-frame 0.1.73 → 0.1.75

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.
Files changed (100) hide show
  1. package/README.md +50 -28
  2. package/dist/CommonTypes.d.ts +124 -7
  3. package/dist/CommonTypes.js +112 -0
  4. package/dist/actions/add-cart-discount/mock.js +6 -3
  5. package/dist/actions/add-cart-fee/mock.js +6 -6
  6. package/dist/actions/add-custom-sale/mock.js +7 -2
  7. package/dist/actions/add-order-note/mock.js +3 -1
  8. package/dist/actions/add-product-discount/mock.js +10 -3
  9. package/dist/actions/add-product-fee/mock.js +10 -1
  10. package/dist/actions/apply-transition/action.d.ts +2 -0
  11. package/dist/actions/apply-transition/action.js +4 -0
  12. package/dist/actions/apply-transition/mock.d.ts +6 -0
  13. package/dist/actions/apply-transition/mock.js +33 -0
  14. package/dist/actions/apply-transition/types.d.ts +17 -0
  15. package/dist/actions/apply-transition/types.js +1 -0
  16. package/dist/actions/can-transition/action.d.ts +2 -0
  17. package/dist/actions/can-transition/action.js +4 -0
  18. package/dist/actions/can-transition/mock.d.ts +6 -0
  19. package/dist/actions/can-transition/mock.js +28 -0
  20. package/dist/actions/can-transition/types.d.ts +11 -0
  21. package/dist/actions/can-transition/types.js +1 -0
  22. package/dist/actions/cash-payment/mock.js +12 -18
  23. package/dist/actions/cash-payment/types.d.ts +5 -0
  24. package/dist/actions/delete-parked-order/mock.js +3 -1
  25. package/dist/actions/delete-parked-order/types.d.ts +3 -0
  26. package/dist/actions/extension-payment/types.d.ts +5 -0
  27. package/dist/actions/get-available-transitions/action.d.ts +2 -0
  28. package/dist/actions/get-available-transitions/action.js +4 -0
  29. package/dist/actions/get-available-transitions/mock.d.ts +6 -0
  30. package/dist/actions/get-available-transitions/mock.js +26 -0
  31. package/dist/actions/get-available-transitions/types.d.ts +9 -0
  32. package/dist/actions/get-available-transitions/types.js +1 -0
  33. package/dist/actions/get-context/mock.js +1 -1
  34. package/dist/actions/get-orders/mock.js +3 -3
  35. package/dist/actions/initiate-refund/types.d.ts +3 -0
  36. package/dist/actions/open-extension-overlay/mock.d.ts +2 -0
  37. package/dist/actions/open-extension-overlay/mock.js +1 -0
  38. package/dist/actions/open-extension-overlay/types.d.ts +9 -0
  39. package/dist/actions/open-extension-overlay/types.js +1 -0
  40. package/dist/actions/park-order/mock.js +4 -1
  41. package/dist/actions/park-order/types.d.ts +3 -0
  42. package/dist/actions/partial-payment/mock.js +21 -3
  43. package/dist/actions/partial-payment/types.d.ts +5 -0
  44. package/dist/actions/process-partial-refund/types.d.ts +3 -0
  45. package/dist/actions/remove-custom-sale/mock.d.ts +2 -0
  46. package/dist/actions/remove-custom-sale/mock.js +29 -0
  47. package/dist/actions/remove-customer-from-cart/mock.js +3 -1
  48. package/dist/actions/remove-order-note/mock.d.ts +2 -0
  49. package/dist/actions/remove-order-note/mock.js +12 -0
  50. package/dist/actions/remove-product-discount/mock.d.ts +2 -0
  51. package/dist/actions/remove-product-discount/mock.js +17 -0
  52. package/dist/actions/remove-product-fee/mock.d.ts +2 -0
  53. package/dist/actions/remove-product-fee/mock.js +17 -0
  54. package/dist/actions/remove-product-from-cart/mock.js +5 -5
  55. package/dist/actions/resolve-extension-overlay/mock.d.ts +2 -0
  56. package/dist/actions/resolve-extension-overlay/mock.js +1 -0
  57. package/dist/actions/resolve-extension-overlay/types.d.ts +9 -0
  58. package/dist/actions/resolve-extension-overlay/types.js +1 -0
  59. package/dist/actions/resume-parked-order/mock.js +4 -1
  60. package/dist/actions/resume-parked-order/types.d.ts +3 -0
  61. package/dist/actions/tap-to-pay-payment/mock.js +5 -3
  62. package/dist/actions/tap-to-pay-payment/types.d.ts +5 -0
  63. package/dist/actions/terminal-payment/mock.js +4 -5
  64. package/dist/actions/terminal-payment/types.d.ts +5 -0
  65. package/dist/actions/update-cart-item-quantity/mock.js +9 -9
  66. package/dist/actions/vendara-payment/types.d.ts +5 -0
  67. package/dist/common-types/index.d.ts +1 -0
  68. package/dist/common-types/index.js +1 -0
  69. package/dist/common-types/order-state.d.ts +32 -0
  70. package/dist/common-types/order-state.js +6 -0
  71. package/dist/common-types/state-fragment.d.ts +77 -0
  72. package/dist/common-types/state-fragment.js +10 -0
  73. package/dist/demo/database.d.ts +10 -0
  74. package/dist/demo/database.js +169 -3
  75. package/dist/fragments/index.d.ts +1 -0
  76. package/dist/fragments/index.js +1 -0
  77. package/dist/fragments/preorder-no-deposit.d.ts +26 -0
  78. package/dist/fragments/preorder-no-deposit.js +166 -0
  79. package/dist/index.d.ts +14 -4
  80. package/dist/index.js +13 -3
  81. package/dist/interceptors/index.d.ts +21 -0
  82. package/dist/interceptors/index.js +53 -0
  83. package/dist/interceptors/types.d.ts +26 -0
  84. package/dist/interceptors/types.js +1 -0
  85. package/dist/projects/render/mocks.js +16 -4
  86. package/dist/projects/render/types.d.ts +7 -1
  87. package/dist/pubsub/topics/orders/index.js +10 -0
  88. package/dist/pubsub/topics/orders/state-transition-blocked/types.d.ts +16 -0
  89. package/dist/pubsub/topics/orders/state-transition-blocked/types.js +1 -0
  90. package/dist/pubsub/topics/orders/state-transition-completed/types.d.ts +15 -0
  91. package/dist/pubsub/topics/orders/state-transition-completed/types.js +1 -0
  92. package/dist/pubsub/topics/orders/types.d.ts +6 -2
  93. package/dist/pubsub/topics/orders/types.js +2 -0
  94. package/package.json +1 -1
  95. package/dist/actions/extension-refund/constants.d.ts +0 -2
  96. package/dist/actions/extension-refund/constants.js +0 -2
  97. package/dist/actions/extension-refund/extension-refund-listener.d.ts +0 -11
  98. package/dist/actions/extension-refund/extension-refund-listener.js +0 -60
  99. package/dist/actions/extension-refund/types.d.ts +0 -32
  100. package/dist/actions/extension-refund/types.js +0 -5
@@ -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,15 +1,14 @@
1
- import { createOrderFromCart, MOCK_CART } from "../../demo/database";
1
+ import { applyMockPayment, MOCK_CART } from "../../demo/database";
2
2
  export const mockTerminalPayment = async (params) => {
3
3
  console.log("[Mock] terminalPayment called", params);
4
4
  const connectionType = params?.paymentType || "Cloud";
5
5
  // Simulate terminal interaction
6
6
  window.alert(`Demo: Processing ${connectionType} Terminal Payment...\n(Please tap, insert, or swipe card on terminal)`);
7
- const amount = params?.amount || MOCK_CART.total;
8
- // Mocking terminal payment success immediately
9
- const order = createOrderFromCart("card", amount, "stripe_terminal");
7
+ const due = params?.amount ?? MOCK_CART.amountToBeCharged ?? MOCK_CART.total;
8
+ const order = applyMockPayment(due, "card", "stripe_terminal");
10
9
  return {
11
10
  success: true,
12
- amount: amount,
11
+ amount: due,
13
12
  paymentType: "terminal",
14
13
  order,
15
14
  timestamp: new Date().toISOString()
@@ -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,11 +1,11 @@
1
1
  import { MOCK_CART, mockPublishEvent } from "../../demo/database";
2
- export const mockUpdateCartItemQuantity = async (params) => {
2
+ export const mockUpdateCartItemQuantity = (params) => {
3
3
  console.log("[Mock] updateCartItemQuantity called", params);
4
4
  if (!params?.internalId) {
5
- throw new Error('internalId is required');
5
+ throw new Error("internalId is required");
6
6
  }
7
7
  if (params.quantity === undefined || params.quantity === null) {
8
- throw new Error('quantity is required');
8
+ throw new Error("quantity is required");
9
9
  }
10
10
  const { internalId, quantity } = params;
11
11
  // Find the product in the cart
@@ -25,16 +25,16 @@ export const mockUpdateCartItemQuantity = async (params) => {
25
25
  MOCK_CART.amountToBeCharged = MOCK_CART.total;
26
26
  MOCK_CART.remainingBalance = MOCK_CART.total;
27
27
  // Publish product-deleted event
28
- mockPublishEvent('cart', 'product-deleted', {
28
+ mockPublishEvent("cart", "product-deleted", {
29
29
  product: product,
30
30
  internalId: internalId
31
31
  });
32
- return {
32
+ return Promise.resolve({
33
33
  success: true,
34
34
  internalId: internalId,
35
35
  quantity: 0,
36
36
  timestamp: new Date().toISOString()
37
- };
37
+ });
38
38
  }
39
39
  // Update quantity
40
40
  const quantityDelta = quantity - previousQuantity;
@@ -46,15 +46,15 @@ export const mockUpdateCartItemQuantity = async (params) => {
46
46
  MOCK_CART.amountToBeCharged = MOCK_CART.total;
47
47
  MOCK_CART.remainingBalance = MOCK_CART.total;
48
48
  // Publish product-updated event
49
- mockPublishEvent('cart', 'product-updated', {
49
+ mockPublishEvent("cart", "product-updated", {
50
50
  product: product,
51
51
  previousQuantity: previousQuantity,
52
52
  newQuantity: quantity
53
53
  });
54
- return {
54
+ return Promise.resolve({
55
55
  success: true,
56
56
  internalId: internalId,
57
57
  quantity: quantity,
58
58
  timestamp: new Date().toISOString()
59
- };
59
+ });
60
60
  };
@@ -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>;
@@ -1,2 +1,3 @@
1
1
  export * from "./custom-tables";
2
2
  export * from "./custom-extensions";
3
+ export * from "./order-state";
@@ -1,2 +1,3 @@
1
1
  export * from "./custom-tables";
2
2
  export * from "./custom-extensions";
3
+ export * from "./order-state";
@@ -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,6 @@
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 {};
@@ -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 {};
@@ -50,6 +50,9 @@ export declare const MOCK_PRODUCT_HABANERO: CFProduct;
50
50
  export declare const MOCK_PRODUCT_BLACK_GARLIC: CFProduct;
51
51
  export declare const MOCK_ORDER_1: CFActiveOrder;
52
52
  export declare const MOCK_ORDER_2: CFActiveOrder;
53
+ export declare const MOCK_ORDER_3: CFActiveOrder;
54
+ export declare const MOCK_PARKED_ORDER_1: CFActiveOrder;
55
+ export declare const MOCK_PARKED_ORDER_2: CFActiveOrder;
53
56
  export declare const MOCK_USERS: CFActiveUser[];
54
57
  export declare const MOCK_STATIONS: CFActiveStation[];
55
58
  export declare const MOCK_OUTLETS: CFActiveOutlet[];
@@ -76,3 +79,10 @@ type MockEventCallback = (event: any) => void;
76
79
  export declare const mockPublishEvent: (topic: string, eventType: string, data: any) => void;
77
80
  export declare const mockSubscribeToTopic: (topic: string, callback: MockEventCallback) => void;
78
81
  export declare const createOrderFromCart: (paymentType: string, amount: number, processor?: string) => CFActiveOrder;
82
+ /**
83
+ * Apply a (possibly partial) payment of `amount` minor units to the active cart.
84
+ * Decrements the remaining balance; when it reaches zero the sale completes
85
+ * (creates the order, resets the cart, returns it). Otherwise the cart stays
86
+ * open with `amountToBeCharged` reset to what's left, and returns null.
87
+ */
88
+ export declare const applyMockPayment: (amount: number, paymentType: string, processor?: string) => CFActiveOrder | null;
@@ -6,7 +6,7 @@ import { CFProductType, CFUserTypes, CurrencyCode } from "../CommonTypes";
6
6
  export * from "./mocks";
7
7
  // Asset Imports - Using Remote URLs to avoid build complexity with asset copying
8
8
  const ASSETS_BASE_URL = "https://raw.githubusercontent.com/Final-Commerce/command-frame/refs/heads/main/src/demo/assets";
9
- const logo = `${ASSETS_BASE_URL}/logo.png`;
9
+ const logo = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA5OCAxMDQiIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik01NS4yOTk4IDUwLjE2NjJMMzcuNzQyMiA0MC4wMjkzTDMxLjcyNzQgNTAuNDQ3MkwzOC45MjQyIDU0LjYwMjJMNTUuMjk5OCA1MC4xNjYyWiIgZmlsbD0iI0E1NjhCQyIvPjxwYXRoIGQ9Ik0zMS45OTYzIDcwLjQ1MDZMNDkuNTU2OCA4MC41ODkxTDU1LjU3MTUgNzAuMTcxM0w0OC4zNzE4IDY2LjAxNDVMMzEuOTk2MyA3MC40NTA2WiIgZmlsbD0iI0E1NjhCQyIvPjxwYXRoIGQ9Ik0zNi4zNTQyIDM0LjI1NzJDMzYuMzU0MiAzNC4yNTcyIDM2LjM1ODMgMzQuMjYzNSAzNi4zNTY3IDM0LjI2NjRDMzcuMTg2OSAzNC4wNjk3IDM4LjA5MTkgMzQuMTU5NiAzOC44ODUxIDM0LjYxNzVMNjIuNjQ4NiA0OC4zMzc0TDU0LjE3MjkgMjkuOTA0OUwzNi4zNDk2IDM0LjI1ODRMMzYuMzU0MiAzNC4yNTcyWiIgZmlsbD0iI0ZDODc1MyIvPjxwYXRoIGQ9Ik0zMy4wOTg4IDE4Ljk1MDdMMzYuMzc3MiAzNC4yNDczTDE4Ljc1MjUgMzkuMzM3M0wyOC45MzcxIDIxLjY5N0MyOS44NDUzIDIwLjEyMzkgMzEuNDE1MiAxOS4xNzYxIDMzLjA5ODggMTguOTUwN1oiIGZpbGw9IiM0MkQzQTkiLz48cGF0aCBkPSJNMTguNzUzMiAzOS4zNDA2TDIwLjY4MjQgNTkuNTE3MUw0LjU3NjA3IDYzLjg5NjJMMTguNzUzMiAzOS4zNDA2WiIgZmlsbD0iIzI3OTdFOCIvPjxwYXRoIGQ9Ik0zNi4zNzc5IDM0LjI0MDNDMzYuMzc3OSAzNC4yNDAzIDM2LjM3NzQgMzQuMjQ3OCAzNi4zODAzIDM0LjI0OTVDMzUuNTYyNCAzNC40OTE5IDM0LjgyMzkgMzUuMDE5NiAzNC4zNjczIDM1LjgxMDVMMjAuNjg2OSA1OS41MDU2TDE4Ljc1NzcgMzkuMzI5MUwzNi4zODI0IDM0LjIzOTFMMzYuMzc3OSAzNC4yNDAzWiIgZmlsbD0iIzFEQkFDQiIvPjxwYXRoIGQ9Ik01NS45MzUyIDEwMS41NDNMNTEuMDgxOSA4Ni42NjU5TDMzLjI1ODcgOTEuMDE5NEw1MC45NDk5IDEwMS4yMzNDNTIuNTI3NiAxMDIuMTQ0IDU0LjM2MzggMTAyLjE4NSA1NS45MzUyIDEwMS41NDNaIiBmaWxsPSIjRkQ1MjYzIi8+PHBhdGggZD0iTTMzLjI1NDkgOTEuMDE0TDI0Ljc4MjEgNzIuNTgzMkw4LjYzMTM1IDc2Ljc5NzVMMzMuMjU0OSA5MS4wMTRaIiBmaWxsPSIjRkZDRjIzIi8+PHBhdGggZD0iTTUxLjA3OSA4Ni42NjM5QzUxLjA3OSA4Ni42NjM5IDUxLjA3NDkgODYuNjU3NiA1MS4wNzY2IDg2LjY1NDdDNTAuMjQ2NCA4Ni44NTE0IDQ5LjM0MTQgODYuNzYxNSA0OC41NDgyIDg2LjMwMzZMMjQuNzg0NiA3Mi41ODM3TDMzLjI2MDQgOTEuMDE2MUw1MS4wODM2IDg2LjY2MjdMNTEuMDc5IDg2LjY2MzlaIiBmaWxsPSIjRkM4NzUzIi8+PHBhdGggZD0iTTU0LjMzNDIgMTAxLjk2OUw1MS4wNTU4IDg2LjY3MjRMNjguNjgwNSA4MS41ODI0TDU4LjQ5NTkgOTkuMjIyN0M1Ny41ODc3IDEwMC43OTYgNTYuMDE3OCAxMDEuNzQ0IDU0LjMzNDIgMTAxLjk2OVoiIGZpbGw9IiM0MkQzQTkiLz48cGF0aCBkPSJNNjguNjgwMSA4MS41ODk5TDY2Ljc0OTMgNjEuNDE2Mkw4Mi44NTU2IDU3LjAzNzJMNjguNjgwMSA4MS41ODk5WiIgZmlsbD0iIzI3OTdFOCIvPjxwYXRoIGQ9Ik01MS4wNTU3IDg2LjY3ODZDNTEuMDU1NyA4Ni42Nzg2IDUxLjA1NjIgODYuNjcxMSA1MS4wNTMyIDg2LjY2OTVDNTEuODcxMSA4Ni40MjcxIDUyLjYwOTcgODUuODk5NCA1My4wNjYzIDg1LjEwODVMNjYuNzQ2NyA2MS40MTMzTDY4LjY3NTkgODEuNTg5OEw1MS4wNTExIDg2LjY3OThMNTEuMDU1NyA4Ni42Nzg2WiIgZmlsbD0iIzFEQkFDQiIvPjxwYXRoIGQ9Ik00Ny44OTQ5IDI2LjI3NjhDNDcuODc4IDI2LjM1OTcgNDcuODUyIDI2LjQ0NTEgNDcuODA2OCAyNi41MjM0TDQ3LjYyNzggMjYuODMzM0M0Ni43ODY1IDI4LjI5MDYgNDQuOTIzMiAyOC43ODY5IDQzLjQ2MTggMjcuOTQzMkM0Mi4wMDA0IDI3LjA5OTQgNDEuNDk4NiAyNS4yMzc2IDQyLjMzOTkgMjMuNzgwNEw0Mi41MTg5IDIzLjQ3MDRDNDIuNTY0MSAyMy4zOTIyIDQyLjYyNSAyMy4zMjY5IDQyLjY4ODQgMjMuMjcwOEwzNi40ODUzIDE5LjY4OTRDMzQuOTA3NiAxOC43Nzg2IDMzLjA3MTQgMTguNzM4MiAzMS41IDE5LjM3OTlMMzYuMzUzMyAzNC4yNTY5TDU0LjE3NjUgMjkuOTAzNUw0Ny44OTQ5IDI2LjI3NjhaIiBmaWxsPSIjRkQ1MjYzIi8+PHBhdGggZD0iTTc1LjYzODYgNDIuMzAyN0M3NS42MjE3IDQyLjM4NTcgNzUuNTk1NiA0Mi40NzExIDc1LjU1MDUgNDIuNTQ5M0w3NS4zNzE1IDQyLjg1OTNDNzQuNTMwMiA0NC4zMTY1IDcyLjY2NjkgNDQuODEyOSA3MS4yMDU0IDQzLjk2OTFDNjkuNzQ0IDQzLjEyNTQgNjkuMjQyMyA0MS4yNjM2IDcwLjA4MzYgMzkuODA2M0w3MC4yNjI2IDM5LjQ5NjNDNzAuMzA3NyAzOS40MTgxIDcwLjM2ODYgMzkuMzUyOSA3MC40MzIgMzkuMjk2N0w1NC4xNzYxIDI5LjkxMTRMNjIuNjUxOSA0OC4zNDM5TDc4LjgwMjYgNDQuMTI5NUw3NS42NDE1IDQyLjMwNDRMNzUuNjM4NiA0Mi4zMDI3WiIgZmlsbD0iI0ZGQ0YyMyIvPjxwYXRoIGQ9Ik03NS42MDU4IDQwLjgyMTlMNzUuNjQ0MiA0MC43NTUyQzc4LjE0ODEgMzYuNDE4MyA3OC42NDY1IDMxLjQ0ODggNzcuNDMwOSAyNi45MzgzQzc2LjIxNTMgMjIuNDI3OCA3My4yODQ3IDE4LjM3MTggNjguOTM1MiAxNS44NjA3QzY0LjU4NTggMTMuMzQ5NSA1OS42MDc5IDEyLjgzOTUgNTUuMDkzOSAxNC4wNDJDNTAuNTc5OSAxNS4yNDQ2IDQ2LjUyNTMgMTguMTYwOSA0NC4wMjE0IDIyLjQ5NzhMNDIuOTkxMSAyNC4yODI0QzQyLjM3MzkgMjUuMzUxNCA0Mi43NDQyIDI2LjcxNjMgNDMuODEzNCAyNy4zMzM2QzQ0Ljg4MjYgMjcuOTUwOSA0Ni4yNTE0IDI3LjU4NjIgNDYuODY2OSAyNi41MjAxTDQ3Ljg5NzMgMjQuNzM1NUM0OS43ODU3IDIxLjQ2NDcgNTIuODQ0MSAxOS4yNjczIDU2LjI1NDggMTguMzU4N0M1OS42NjU0IDE3LjQ1MDEgNjMuNDE5MSAxNy44MzI3IDY2LjY5OTQgMTkuNzI2NkM2OS45Nzk2IDIxLjYyMDQgNzIuMTg3OSAyNC42Nzk5IDczLjEwNjMgMjguMDg3OUM3NC4wMjQ4IDMxLjQ5NiA3My42NTEgMzUuMjQzNCA3MS43NjI2IDM4LjUxNDFMNzEuNzI0MSAzOC41ODA4TDcwLjczMzkgNDAuMjk1OEM3MC4xMTY3IDQxLjM2NDkgNzAuNDg3IDQyLjcyOTcgNzEuNTU2MiA0My4zNDdDNzIuNjI1NCA0My45NjQ0IDczLjk5NDIgNDMuNTk5NyA3NC42MDk4IDQyLjUzMzZMNzUuNTk5OSA0MC44MTg1TDc1LjYwNTggNDAuODIxOVoiIGZpbGw9IiMzRDRDNjYiLz48L3N2Zz4="; // crisp vector mark (was logo.png — pixelated when scaled)
10
10
  const basilAlmondImg = `${ASSETS_BASE_URL}/basil-almond-paste.png`;
11
11
  const beerImg = `${ASSETS_BASE_URL}/beer-paste.png`;
12
12
  const beetImg = `${ASSETS_BASE_URL}/beet-paste.png`;
@@ -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,
@@ -382,6 +385,9 @@ export const MOCK_ORDER_2 = {
382
385
  companyId: MOCK_COMPANY.id,
383
386
  externalId: null,
384
387
  status: "completed",
388
+ paymentState: "paid",
389
+ fulfillmentState: "fulfilled",
390
+ displayState: "Completed",
385
391
  customer: MOCK_CUSTOMER_2,
386
392
  summary: {
387
393
  total: 3000,
@@ -421,6 +427,140 @@ export const MOCK_ORDER_2 = {
421
427
  station: MOCK_STATION_2,
422
428
  createdAt: new Date(Date.now() - 3600000).toISOString()
423
429
  };
430
+ export const MOCK_ORDER_3 = {
431
+ _id: "order_1003",
432
+ currency: CurrencyCode.USD,
433
+ minorUnits: 2,
434
+ receiptId: "1001-0003",
435
+ companyId: MOCK_COMPANY.id,
436
+ externalId: null,
437
+ status: "refunded",
438
+ paymentState: "refunded",
439
+ fulfillmentState: "fulfilled",
440
+ displayState: "Refunded",
441
+ customer: MOCK_CUSTOMER_3,
442
+ summary: {
443
+ total: 1500,
444
+ subTotal: 1500,
445
+ discountTotal: 0,
446
+ shippingTotal: 0,
447
+ totalTaxes: 0,
448
+ taxes: [],
449
+ isTaxInclusive: false
450
+ },
451
+ cartDiscount: null,
452
+ cartFees: [],
453
+ paymentMethods: [
454
+ {
455
+ transactionId: "trans_card_2",
456
+ paymentType: "credit_card",
457
+ amount: 1500,
458
+ timestamp: new Date(Date.now() - 7200000).toISOString(),
459
+ processor: "stripe"
460
+ }
461
+ ],
462
+ source: "pos",
463
+ posData: {
464
+ outlet: MOCK_OUTLET_MAIN.id,
465
+ station: MOCK_STATION_1._id,
466
+ employee: MOCK_USER_MARIO.id
467
+ },
468
+ sessionId: "sess_3",
469
+ metadata: [],
470
+ billing: null,
471
+ shipping: null,
472
+ lineItems: [createLineItem(MOCK_PRODUCT_BEET, 0, 1)],
473
+ customSales: [],
474
+ balance: 0,
475
+ user: MOCK_USER_MARIO,
476
+ outlet: MOCK_OUTLET_MAIN,
477
+ station: MOCK_STATION_1,
478
+ createdAt: new Date(Date.now() - 7200000).toISOString()
479
+ };
480
+ export const MOCK_PARKED_ORDER_1 = {
481
+ _id: "parked_2001",
482
+ currency: CurrencyCode.USD,
483
+ minorUnits: 2,
484
+ receiptId: "PARK-0001",
485
+ companyId: MOCK_COMPANY.id,
486
+ externalId: null,
487
+ status: "parked",
488
+ paymentState: "unpaid",
489
+ fulfillmentState: "unfulfilled",
490
+ displayState: "Parked",
491
+ customer: MOCK_CUSTOMER_4,
492
+ summary: {
493
+ total: 2100,
494
+ subTotal: 2100,
495
+ discountTotal: 0,
496
+ shippingTotal: 0,
497
+ totalTaxes: 0,
498
+ taxes: [],
499
+ isTaxInclusive: false
500
+ },
501
+ cartDiscount: null,
502
+ cartFees: [],
503
+ paymentMethods: [],
504
+ source: "pos",
505
+ posData: {
506
+ outlet: MOCK_OUTLET_MAIN.id,
507
+ station: MOCK_STATION_1._id,
508
+ employee: MOCK_USER_LUIGI.id
509
+ },
510
+ sessionId: "sess_park_1",
511
+ metadata: [],
512
+ billing: null,
513
+ shipping: null,
514
+ lineItems: [createLineItem(MOCK_PRODUCT_LEMON, 0, 2)],
515
+ customSales: [],
516
+ balance: 2100,
517
+ user: MOCK_USER_LUIGI,
518
+ outlet: MOCK_OUTLET_MAIN,
519
+ station: MOCK_STATION_1,
520
+ createdAt: new Date(Date.now() - 1800000).toISOString()
521
+ };
522
+ export const MOCK_PARKED_ORDER_2 = {
523
+ _id: "parked_2002",
524
+ currency: CurrencyCode.USD,
525
+ minorUnits: 2,
526
+ receiptId: "PARK-0002",
527
+ companyId: MOCK_COMPANY.id,
528
+ externalId: null,
529
+ status: "parked",
530
+ paymentState: "unpaid",
531
+ fulfillmentState: "unfulfilled",
532
+ displayState: "Parked",
533
+ customer: null,
534
+ summary: {
535
+ total: 2500,
536
+ subTotal: 2500,
537
+ discountTotal: 0,
538
+ shippingTotal: 0,
539
+ totalTaxes: 0,
540
+ taxes: [],
541
+ isTaxInclusive: false
542
+ },
543
+ cartDiscount: null,
544
+ cartFees: [],
545
+ paymentMethods: [],
546
+ source: "pos",
547
+ posData: {
548
+ outlet: MOCK_OUTLET_MAIN.id,
549
+ station: MOCK_STATION_2._id,
550
+ employee: MOCK_USER_MARIO.id
551
+ },
552
+ sessionId: "sess_park_2",
553
+ metadata: [],
554
+ billing: null,
555
+ shipping: null,
556
+ lineItems: [createLineItem(MOCK_PRODUCT_CARAMELIZED, 0, 1), createLineItem(MOCK_PRODUCT_GINGER_LIME, 0, 1)],
557
+ customSales: [],
558
+ balance: 2500,
559
+ user: MOCK_USER_MARIO,
560
+ outlet: MOCK_OUTLET_MAIN,
561
+ station: MOCK_STATION_2,
562
+ createdAt: new Date(Date.now() - 900000).toISOString()
563
+ };
424
564
  // --- EXPORT COLLECTIONS ---
425
565
  export const MOCK_USERS = [MOCK_USER_MARIO, MOCK_USER_LUIGI];
426
566
  export const MOCK_STATIONS = [MOCK_STATION_1, MOCK_STATION_2];
@@ -443,8 +583,8 @@ export const MOCK_PRODUCTS = [
443
583
  MOCK_PRODUCT_HABANERO,
444
584
  MOCK_PRODUCT_BLACK_GARLIC
445
585
  ];
446
- export const MOCK_ORDERS = [MOCK_ORDER_1, MOCK_ORDER_2];
447
- export const MOCK_PARKED_ORDERS = [];
586
+ export const MOCK_ORDERS = [MOCK_ORDER_1, MOCK_ORDER_2, MOCK_ORDER_3];
587
+ export const MOCK_PARKED_ORDERS = [MOCK_PARKED_ORDER_1, MOCK_PARKED_ORDER_2];
448
588
  // Compatibility Exports (reassigned by setMockDatabase)
449
589
  export let MOCK_USER = MOCK_USERS[0];
450
590
  export let MOCK_STATION = MOCK_STATIONS[0];
@@ -607,6 +747,9 @@ export const createOrderFromCart = (paymentType, amount, processor = "cash") =>
607
747
  companyId: MOCK_COMPANY.id,
608
748
  externalId: null,
609
749
  status: "completed",
750
+ paymentState: "paid",
751
+ fulfillmentState: "fulfilled",
752
+ displayState: "Completed",
610
753
  customer: MOCK_CART.customer ? MOCK_CART.customer : null,
611
754
  summary: {
612
755
  total: totalNum,
@@ -652,3 +795,26 @@ export const createOrderFromCart = (paymentType, amount, processor = "cash") =>
652
795
  mockPublishEvent("cart", "cart-created", {});
653
796
  return newOrder;
654
797
  };
798
+ /**
799
+ * Apply a (possibly partial) payment of `amount` minor units to the active cart.
800
+ * Decrements the remaining balance; when it reaches zero the sale completes
801
+ * (creates the order, resets the cart, returns it). Otherwise the cart stays
802
+ * open with `amountToBeCharged` reset to what's left, and returns null.
803
+ */
804
+ export const applyMockPayment = (amount, paymentType, processor = "cash") => {
805
+ const remainingBefore = MOCK_CART.remainingBalance ?? MOCK_CART.total;
806
+ const charge = Math.min(Math.max(0, amount || remainingBefore), remainingBefore);
807
+ const remainingAfter = Math.max(0, remainingBefore - charge);
808
+ if (remainingAfter > 0) {
809
+ // Partial payment — keep the cart open, queue the rest for the next tender.
810
+ MOCK_CART.remainingBalance = remainingAfter;
811
+ MOCK_CART.amountToBeCharged = remainingAfter;
812
+ mockPublishEvent("cart", "partial-payment-applied", { charged: charge, remaining: remainingAfter });
813
+ return null;
814
+ }
815
+ // Fully paid — create the completed order (this also resets the cart).
816
+ const orderTotal = MOCK_CART.total;
817
+ const order = createOrderFromCart(paymentType, orderTotal, processor);
818
+ mockPublishEvent("payments", "payment-done", { order });
819
+ return order;
820
+ };
@@ -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;