@final-commerce/command-frame 0.6.0 → 0.6.2-staging3.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.
Files changed (44) hide show
  1. package/dist/CommonTypes.d.ts +14 -1
  2. package/dist/actions/add-product-to-cart/mock.js +18 -1
  3. package/dist/actions/add-product-to-cart/types.d.ts +10 -0
  4. package/dist/actions/charge-moto/action.d.ts +6 -0
  5. package/dist/actions/charge-moto/action.js +8 -0
  6. package/dist/actions/charge-moto/mock.d.ts +2 -0
  7. package/dist/actions/charge-moto/mock.js +47 -0
  8. package/dist/actions/charge-moto/types.d.ts +53 -0
  9. package/dist/actions/charge-moto/types.js +1 -0
  10. package/dist/actions/create-payment-link/action.d.ts +6 -0
  11. package/dist/actions/create-payment-link/action.js +8 -0
  12. package/dist/actions/create-payment-link/mock.d.ts +2 -0
  13. package/dist/actions/create-payment-link/mock.js +35 -0
  14. package/dist/actions/create-payment-link/types.d.ts +27 -0
  15. package/dist/actions/create-payment-link/types.js +12 -0
  16. package/dist/actions/get-product-modifier-selections/action.d.ts +6 -0
  17. package/dist/actions/get-product-modifier-selections/action.js +8 -0
  18. package/dist/actions/get-product-modifier-selections/mock.d.ts +2 -0
  19. package/dist/actions/get-product-modifier-selections/mock.js +30 -0
  20. package/dist/actions/get-product-modifier-selections/types.d.ts +23 -0
  21. package/dist/actions/get-product-modifier-selections/types.js +1 -0
  22. package/dist/actions/get-time-clock-status/action.d.ts +6 -0
  23. package/dist/actions/get-time-clock-status/action.js +8 -0
  24. package/dist/actions/get-time-clock-status/mock.d.ts +2 -0
  25. package/dist/actions/get-time-clock-status/mock.js +17 -0
  26. package/dist/actions/get-time-clock-status/types.d.ts +18 -0
  27. package/dist/actions/get-time-clock-status/types.js +7 -0
  28. package/dist/actions/set-product-modifier-selections/action.d.ts +6 -0
  29. package/dist/actions/set-product-modifier-selections/action.js +8 -0
  30. package/dist/actions/set-product-modifier-selections/mock.d.ts +2 -0
  31. package/dist/actions/set-product-modifier-selections/mock.js +49 -0
  32. package/dist/actions/set-product-modifier-selections/types.d.ts +27 -0
  33. package/dist/actions/set-product-modifier-selections/types.js +1 -0
  34. package/dist/demo/database.d.ts +24 -1
  35. package/dist/demo/database.js +68 -0
  36. package/dist/index.d.ts +10 -0
  37. package/dist/index.js +12 -0
  38. package/dist/projects/render/mocks.js +10 -0
  39. package/dist/projects/render/types.d.ts +6 -1
  40. package/dist/pubsub/topics/cart/product-modifiers-updated/types.d.ts +16 -0
  41. package/dist/pubsub/topics/cart/product-modifiers-updated/types.js +1 -0
  42. package/dist/pubsub/topics/cart/types.d.ts +4 -2
  43. package/dist/pubsub/topics/cart/types.js +1 -0
  44. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  export * from './common-types';
2
- import type { Tax, Tip, Address, MetadataItem, PosDataItem, CartFeeTaxEntry, CartFeeItem, CartDiscountItem, OrderNote, NonRevenueItem, DiscountDetail, FeeDetail, DiscountLineItem, FeeLineItem, LineItem, TipPayment, RefundedTipPayment, PaymentMethod, Discount, CustomFee, Summary, CustomSale, RefundedCustomSale, RefundItem, RefundedLineItem, ActiveStation, ActiveSession, ActiveOutlet, ActiveUser, ActiveUserRole, ActiveOrder, ActiveCart, ActivePark, ActiveCompany, ActiveCustomSales, ActiveProduct, ActiveCustomer, FullProduct, ProductVariant, Inventory, CustomerNote, Attribute, AttributeOption, Category, Transaction, ActiveSplitPayment } from '@final-commerce/common/pos-types';
2
+ import type { Tax, Tip, Address, MetadataItem, PosDataItem, CartFeeTaxEntry, CartFeeItem, CartDiscountItem, OrderNote, NonRevenueItem, DiscountDetail, FeeDetail, DiscountLineItem, FeeLineItem, LineItem, TipPayment, RefundedTipPayment, PaymentMethod, Discount, CustomFee, Summary, CustomSale, RefundedCustomSale, RefundItem, RefundedLineItem, ActiveStation, ActiveSession, ActiveOutlet, ActiveUser, ActiveUserRole, ActiveOrder, ActiveCart, ActivePark, ActiveCompany, ActiveCustomSales, ActiveProduct, ActiveCustomer, FullProduct, ProductVariant, Inventory, ResolvedModifier, ResolvedModifierChoice, ModifierSelectionType, ModifierSelection, ModifierChoiceSelection, CartLineModifier, ProdModifierBreakdown, OrderLineItemModifier, CustomerNote, Attribute, AttributeOption, Category, Transaction, ActiveSplitPayment } from '@final-commerce/common/pos-types';
3
3
  export { CurrencyCode, ProductType as CFProductType, UserTypes as CFUserTypes } from '@final-commerce/common';
4
4
  /** Open record used as a base for active entities. */
5
5
  export type CFActiveEntity = Record<string, unknown>;
@@ -18,6 +18,19 @@ export type CFTransaction = Transaction;
18
18
  export type CFCategory = Category;
19
19
  export type CFProductVariant = ProductVariant;
20
20
  export type CFProduct = FullProduct;
21
+ /** Modifier (the till question) resolved onto a product — `CFProduct.modifiers`. */
22
+ export type CFResolvedModifier = ResolvedModifier;
23
+ export type CFResolvedModifierChoice = ResolvedModifierChoice;
24
+ export type CFModifierSelectionType = ModifierSelectionType;
25
+ /** One selected modifier choice on an order line item (wire shape). */
26
+ export type CFOrderLineItemModifier = OrderLineItemModifier;
27
+ /** Cart-line modifier answer (pre-pricing) — `CFActiveProduct.modifierSelections`. */
28
+ export type CFModifierSelection = ModifierSelection;
29
+ export type CFModifierChoiceSelection = ModifierChoiceSelection;
30
+ /** One selected choice PRICED onto a cart line — `CFActiveProduct.modifiers` (not the resolved stack). */
31
+ export type CFCartLineModifier = CartLineModifier;
32
+ /** A priced, line-extended modifier row: "Toppings - Avocado" x2, `amount` 1000. */
33
+ export type CFProdModifierBreakdown = ProdModifierBreakdown;
21
34
  export type CFActiveProduct = ActiveProduct;
22
35
  export type CFCustomer = ActiveCustomer;
23
36
  export type CFActiveCustomer = ActiveCustomer;
@@ -1,4 +1,4 @@
1
- import { MOCK_CART, MOCK_PRODUCTS, mockPublishEvent } from '../../demo/database';
1
+ import { MOCK_CART, MOCK_PRODUCTS, buildCartLineModifiers, buildModifierRows, mockPublishEvent, } from '../../demo/database';
2
2
  import { extendPrice, isValidQuantity, resolveUnit } from '@final-commerce/common';
3
3
  export const mockAddProductToCart = async (params) => {
4
4
  console.log('[Mock] addProductToCart called', params);
@@ -53,6 +53,18 @@ export const mockAddProductToCart = async (params) => {
53
53
  note: note,
54
54
  // discount/fee could be added here to mock object if CFActiveProduct supports it
55
55
  };
56
+ // The spread above copied the PRODUCT's `modifiers` — a ResolvedModifier[] menu — into a
57
+ // field that means "the choices this line carries" (CartLineModifier[]). Two different
58
+ // shapes, one name. Drop it before anything reads the line, or every surface renders the
59
+ // whole catalogue as if the cashier had picked all of it.
60
+ delete activeProduct.modifiers;
61
+ // Keep the raw answers AND the priced rows, the same pair the real host writes. The cart
62
+ // TOTAL is deliberately left alone: like a product fee (see add-product-fee), per-line
63
+ // money is not accumulated by this mock — `total` stays Σ extendPrice(price, quantity).
64
+ if (params?.modifiers?.length) {
65
+ activeProduct.modifierSelections = params.modifiers;
66
+ activeProduct.modifiers = buildCartLineModifiers(product.modifiers, params.modifiers, product.taxTable);
67
+ }
56
68
  MOCK_CART.products.push(activeProduct);
57
69
  // Recalculate totals. extendPrice, not a raw multiply: a fractional quantity times an
58
70
  // integer-minor price leaves fractional cents, which no order may carry.
@@ -64,6 +76,9 @@ export const mockAddProductToCart = async (params) => {
64
76
  MOCK_CART.remainingBalance = MOCK_CART.total;
65
77
  // Publish cart event to simulate real behavior
66
78
  mockPublishEvent('cart', 'product-added', { product: activeProduct });
79
+ // Display-ready modifier rows for the line that was just created, so a flow can
80
+ // show what the modifiers added without re-reading the cart or multiplying.
81
+ const { rows, modifiersTotal } = buildModifierRows(activeProduct.modifiers, quantity);
67
82
  return {
68
83
  success: true,
69
84
  productId: activeProduct.id,
@@ -71,6 +86,8 @@ export const mockAddProductToCart = async (params) => {
71
86
  internalId: activeProduct.internalId,
72
87
  name: activeProduct.name,
73
88
  quantity: quantity,
89
+ rows,
90
+ modifiersTotal,
74
91
  timestamp: new Date().toISOString(),
75
92
  };
76
93
  };
@@ -1,5 +1,7 @@
1
1
  import type { AddProductDiscountParams } from '../add-product-discount/types';
2
2
  import type { AddProductFeeParams } from '../add-product-fee/types';
3
+ import type { ModifierSelection } from '../get-product-modifier-selections/types';
4
+ import type { CFProdModifierBreakdown } from '../../CommonTypes';
3
5
  export interface AddProductToCartParams {
4
6
  /** ID of the variant to add. */
5
7
  variantId: string;
@@ -21,17 +23,25 @@ export interface AddProductToCartParams {
21
23
  discounts?: AddProductDiscountParams[];
22
24
  /** Array of fees to apply immediately. */
23
25
  fees?: AddProductFeeParams[];
26
+ /** Modifier selections to apply immediately. */
27
+ modifiers?: ModifierSelection[];
24
28
  /** Note or array of notes to add immediately. */
25
29
  notes?: string | string[];
26
30
  }
27
31
  export interface AddProductToCartResponse {
28
32
  success: boolean;
33
+ /** Set when the add was rejected (e.g. a required modifier is unanswered). */
34
+ reason?: string;
29
35
  productId: string;
30
36
  variantId: string;
31
37
  /** The unique identifier for the specific item instance added to the cart. */
32
38
  internalId: string;
33
39
  name: string;
34
40
  quantity: number;
41
+ /** The modifiers the line was created with, display-ready. Empty when there are none. */
42
+ rows: CFProdModifierBreakdown[];
43
+ /** Sum of `rows[].amount` — what the modifiers added to this line, in minor units. */
44
+ modifiersTotal: number;
35
45
  timestamp: string;
36
46
  }
37
47
  export type AddProductToCart = (params?: AddProductToCartParams) => Promise<AddProductToCartResponse>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Charge MOTO action
3
+ * Calls the chargeMoto action on the parent window
4
+ */
5
+ import type { ChargeMoto } from './types';
6
+ export declare const chargeMoto: ChargeMoto;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Charge MOTO action
3
+ * Calls the chargeMoto action on the parent window
4
+ */
5
+ import { commandFrameClient } from '../../client';
6
+ export const chargeMoto = async (params) => {
7
+ return await commandFrameClient.call('chargeMoto', params);
8
+ };
@@ -0,0 +1,2 @@
1
+ import { ChargeMoto } from './types';
2
+ export declare const mockChargeMoto: ChargeMoto;
@@ -0,0 +1,47 @@
1
+ import { applyMockPayment, MOCK_CART } from '../../demo/database';
2
+ /** Mirrors kaching's `MOTO_MIN_AMOUNT_MINOR` — the processors' floor. */
3
+ const MOTO_MIN_AMOUNT_MINOR = 50;
4
+ /** The card fields that must be present and non-empty on every keyed charge. */
5
+ const REQUIRED_CARD_FIELDS = [
6
+ 'encryptedCardNumber',
7
+ 'encryptedExpiryMonth',
8
+ 'encryptedExpiryYear',
9
+ 'encryptedSecurityCode',
10
+ ];
11
+ export const mockChargeMoto = async (params) => {
12
+ console.log('[Mock] chargeMoto called', params);
13
+ if (!params)
14
+ throw new Error('Params required');
15
+ if (!Number.isInteger(params.amount)) {
16
+ throw new Error('amount must be an integer number of minor units');
17
+ }
18
+ if (params.amount < MOTO_MIN_AMOUNT_MINOR) {
19
+ throw new Error(`amount must be at least ${MOTO_MIN_AMOUNT_MINOR} minor units`);
20
+ }
21
+ for (const field of REQUIRED_CARD_FIELDS) {
22
+ const value = params.card?.[field];
23
+ if (!value || !value.trim()) {
24
+ throw new Error(`card.${field} is required`);
25
+ }
26
+ }
27
+ if (!params.idempotencyKey || !params.idempotencyKey.trim()) {
28
+ throw new Error('idempotencyKey is required so a retry replays instead of recharging');
29
+ }
30
+ const balanceDue = MOCK_CART.amountToBeCharged ?? MOCK_CART.total;
31
+ if (params.amount > balanceDue) {
32
+ throw new Error(`amount ${params.amount} exceeds balance due ${balanceDue}`);
33
+ }
34
+ // The mock always completes the sale in one leg — it does not model
35
+ // kaching's real partial-leg (`saleFinalized: false`) path.
36
+ const order = applyMockPayment(balanceDue, 'card', 'adyen');
37
+ if (!order) {
38
+ throw new Error('MOCK_CHARGE_MOTO_PARTIAL_UNSUPPORTED: the mock only supports charging the full balance due');
39
+ }
40
+ return {
41
+ success: true,
42
+ timestamp: new Date().toISOString(),
43
+ order,
44
+ saleFinalized: true,
45
+ remainingBalance: 0,
46
+ };
47
+ };
@@ -0,0 +1,53 @@
1
+ import { CFOrder } from '../../CommonTypes';
2
+ /**
3
+ * Provider-encrypted card fields, opaque to this library — mirrors kaching's
4
+ * `MotoCardFields` field for field. Never a raw PAN: these are the CSE blobs
5
+ * the provider's own client-side encrypter produces.
6
+ */
7
+ export interface ChargeMotoCardFields {
8
+ encryptedCardNumber: string;
9
+ encryptedExpiryMonth: string;
10
+ encryptedExpiryYear: string;
11
+ encryptedSecurityCode: string;
12
+ /** Optional AVS postal code the operator entered. */
13
+ postalCode?: string;
14
+ /** Provider-side `reference` for this payment. Defaults to the idempotency key server-side. */
15
+ reference?: string;
16
+ }
17
+ export interface ChargeMotoParams {
18
+ /**
19
+ * Total to charge, base + tip, in integer MINOR currency units. Minimum 50
20
+ * minor units (the processors' floor). This is the leg amount: a value
21
+ * below the cart's balance due records a partial payment.
22
+ */
23
+ amount: number;
24
+ /** Tip portion of `amount`, already collected by the caller's own UI. Defaults to 0. */
25
+ tipAmount?: number;
26
+ /**
27
+ * ISO 4217 code. Optional — the active company's currency is authoritative.
28
+ * When supplied it must match, or the charge is rejected before anything is sent.
29
+ */
30
+ currency?: string;
31
+ card: ChargeMotoCardFields;
32
+ /**
33
+ * Idempotency key for this charge, HELD BY THE CALLER across retries. A
34
+ * retried charge must carry the SAME key so the charge is replayed instead
35
+ * of doubled; use a NEW key after changing the amount.
36
+ */
37
+ idempotencyKey: string;
38
+ /** Optional tender label recorded on the payment method. */
39
+ paymentName?: string;
40
+ /** Fulfillment state to land on after full payment (default: auto-fulfill). */
41
+ targetFulfillmentState?: string;
42
+ }
43
+ export interface ChargeMotoResponse {
44
+ success: boolean;
45
+ timestamp: string;
46
+ /** The persisted order the charge landed on. */
47
+ order: CFOrder;
48
+ /** True only when this charge completed the sale (`paymentState === 'paid'`). */
49
+ saleFinalized: boolean;
50
+ /** Balance still due after this charge, in integer minor units. 0 when finalized. */
51
+ remainingBalance: number;
52
+ }
53
+ export type ChargeMoto = (params: ChargeMotoParams) => Promise<ChargeMotoResponse>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Create payment link action
3
+ * Calls the createPaymentLink action on the parent window
4
+ */
5
+ import type { CreatePaymentLink } from './types';
6
+ export declare const createPaymentLink: CreatePaymentLink;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Create payment link action
3
+ * Calls the createPaymentLink action on the parent window
4
+ */
5
+ import { commandFrameClient } from '../../client';
6
+ export const createPaymentLink = async (params) => {
7
+ return await commandFrameClient.call('createPaymentLink', params);
8
+ };
@@ -0,0 +1,2 @@
1
+ import { CreatePaymentLink } from './types';
2
+ export declare const mockCreatePaymentLink: CreatePaymentLink;
@@ -0,0 +1,35 @@
1
+ import { MOCK_CART, mockPublishEvent, resetMockCart } from '../../demo/database';
2
+ export const mockCreatePaymentLink = async (params) => {
3
+ console.log('[Mock] createPaymentLink called', params);
4
+ if (!params)
5
+ throw new Error('Params required');
6
+ const email = params.email?.trim() ? params.email : undefined;
7
+ const phone = params.phone?.trim() ? params.phone : undefined;
8
+ if (email && phone) {
9
+ throw new Error('exactly one of email or phone delivers a payment link, not both');
10
+ }
11
+ if (!email && !phone) {
12
+ throw new Error('exactly one of email or phone is required to deliver the payment link');
13
+ }
14
+ const hasCart = MOCK_CART.products.length > 0 ||
15
+ (MOCK_CART.customSales?.length ?? 0) > 0 ||
16
+ (MOCK_CART.nonRevenueItems?.length ?? 0) > 0;
17
+ if (!hasCart || MOCK_CART.total <= 0) {
18
+ throw new Error('ring the sale up before creating a payment link: the cart is empty');
19
+ }
20
+ const mockOrderId = 'order_' + Date.now();
21
+ const mockLinkId = 'link_' + Math.random().toString(36).substr(2, 9);
22
+ // Marks the cart consumed, same as a successful send in kaching — the order
23
+ // now owns the line.
24
+ resetMockCart();
25
+ mockPublishEvent('cart', 'cart-created', {});
26
+ return {
27
+ success: true,
28
+ timestamp: new Date().toISOString(),
29
+ orderId: mockOrderId,
30
+ url: `https://mock.finalpos.dev/pay/${mockLinkId}`,
31
+ id: mockLinkId,
32
+ expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
33
+ delivery: email ? { email: 'sent' } : { sms: 'sent' },
34
+ };
35
+ };
@@ -0,0 +1,27 @@
1
+ export interface CreatePaymentLinkParams {
2
+ /** Address to email the link to. Exactly one of `email`/`phone` is required. */
3
+ email?: string;
4
+ /** Phone number to text the link to, E.164 (e.g. "+15555550123"). Exactly one of `email`/`phone` is required. */
5
+ phone?: string;
6
+ }
7
+ export interface CreatePaymentLinkResponse {
8
+ success: boolean;
9
+ timestamp: string;
10
+ /** Client `_id` of the order the station created for this link (charges the CURRENT CART). */
11
+ orderId: string;
12
+ /** Hosted payment page to send the shopper. */
13
+ url: string;
14
+ /** The provider's payment-link id. */
15
+ id: string;
16
+ /** When the link stops accepting payment (ISO date string). */
17
+ expiresAt: string;
18
+ /**
19
+ * Per-channel delivery outcome. OPTIONAL: an older hub omits it entirely,
20
+ * which reads as "delivered" — treat absence as success, not as a failure.
21
+ */
22
+ delivery?: {
23
+ email?: 'sent' | 'failed';
24
+ sms?: 'sent' | 'failed';
25
+ };
26
+ }
27
+ export type CreatePaymentLink = (params: CreatePaymentLinkParams) => Promise<CreatePaymentLinkResponse>;
@@ -0,0 +1,12 @@
1
+ // Create Payment Link Types
2
+ //
3
+ // Creates a hosted payment link FOR THE CURRENT CART (station-v2 D-decisions,
4
+ // payment-link addendum). Mirrors kaching's `createPaymentLinkFromCart` input
5
+ // (`CreatePaymentLinkFromCartInput`) and result (`PaymentLinkFromCartResult`,
6
+ // which extends the provider's `PaymentLinkResponse` with the local `orderId`).
7
+ //
8
+ // The station creates the order (client `_id`, `unpaid × in_progress`) BEFORE
9
+ // the link is requested — only that id travels on the wire. On success the
10
+ // cart is cleared; on a failed send the order is voided and the cart is kept
11
+ // so the cashier can retry. Adyen-only today.
12
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Get product modifier selections action (read-only)
3
+ * Calls the getProductModifierSelections action on the parent window
4
+ */
5
+ import type { GetProductModifierSelections } from "./types";
6
+ export declare const getProductModifierSelections: GetProductModifierSelections;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Get product modifier selections action (read-only)
3
+ * Calls the getProductModifierSelections action on the parent window
4
+ */
5
+ import { commandFrameClient } from "../../client";
6
+ export const getProductModifierSelections = async (params) => {
7
+ return await commandFrameClient.call("getProductModifierSelections", params);
8
+ };
@@ -0,0 +1,2 @@
1
+ import { GetProductModifierSelections } from "./types";
2
+ export declare const mockGetProductModifierSelections: GetProductModifierSelections;
@@ -0,0 +1,30 @@
1
+ import { MOCK_CART, buildModifierRows } from "../../demo/database";
2
+ export const mockGetProductModifierSelections = async (params) => {
3
+ console.log("[Mock] getProductModifierSelections called", params);
4
+ // Read the line's stored selections from the mock cart (last line when no internalId).
5
+ const line = params?.internalId
6
+ ? MOCK_CART.products.find((p) => p.internalId === params.internalId)
7
+ : MOCK_CART.products[MOCK_CART.products.length - 1];
8
+ if (!line) {
9
+ return {
10
+ success: false,
11
+ reason: "No matching cart line",
12
+ internalId: params?.internalId,
13
+ selections: [],
14
+ rows: [],
15
+ modifiersTotal: 0,
16
+ timestamp: new Date().toISOString()
17
+ };
18
+ }
19
+ // The priced rows already sit on the line; extending them by the line quantity is
20
+ // the host's job, not the flow's.
21
+ const { rows, modifiersTotal } = buildModifierRows(line.modifiers, line.quantity);
22
+ return {
23
+ success: true,
24
+ internalId: line.internalId,
25
+ selections: line.modifierSelections ?? [],
26
+ rows,
27
+ modifiersTotal,
28
+ timestamp: new Date().toISOString()
29
+ };
30
+ };
@@ -0,0 +1,23 @@
1
+ import type { CFModifierSelection, CFModifierChoiceSelection, CFProdModifierBreakdown } from "../../CommonTypes";
2
+ /** Alias of common's — `quantity` is units PER LINE-ITEM UNIT. */
3
+ export type ModifierChoiceSelection = CFModifierChoiceSelection;
4
+ /** Alias of common's — the cashier's answer to one modifier. */
5
+ export type ModifierSelection = CFModifierSelection;
6
+ export interface GetProductModifierSelectionsParams {
7
+ /** The cart line to read. Defaults to the active product's line. */
8
+ internalId?: string;
9
+ }
10
+ export interface GetProductModifierSelectionsResponse {
11
+ success: boolean;
12
+ /** Set when the read failed (e.g. no such line, no active product). */
13
+ reason?: string;
14
+ internalId?: string;
15
+ /** Raw ids — round-trips back into the setter. No names, no money: use `rows` to display. */
16
+ selections: ModifierSelection[];
17
+ /** Display-ready: "Toppings - Avocado" x2, `amount` 1000. One row per chosen choice. */
18
+ rows: CFProdModifierBreakdown[];
19
+ /** Sum of `rows[].amount` for THIS line, in minor units. */
20
+ modifiersTotal: number;
21
+ timestamp: string;
22
+ }
23
+ export type GetProductModifierSelections = (params?: GetProductModifierSelectionsParams) => Promise<GetProductModifierSelectionsResponse>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Get time clock status action
3
+ * Calls the getTimeClockStatus action on the parent window
4
+ */
5
+ import type { GetTimeClockStatus } from './types';
6
+ export declare const getTimeClockStatus: GetTimeClockStatus;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Get time clock status action
3
+ * Calls the getTimeClockStatus action on the parent window
4
+ */
5
+ import { commandFrameClient } from '../../client';
6
+ export const getTimeClockStatus = async (params) => {
7
+ return await commandFrameClient.call('getTimeClockStatus', params);
8
+ };
@@ -0,0 +1,2 @@
1
+ import { GetTimeClockStatus } from './types';
2
+ export declare const mockGetTimeClockStatus: GetTimeClockStatus;
@@ -0,0 +1,17 @@
1
+ /** Fixed shift start used by the mock — a clocked-in snapshot with no break taken. */
2
+ const MOCK_CLOCK_IN_TIME = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
3
+ export const mockGetTimeClockStatus = async (params) => {
4
+ console.log('[Mock] getTimeClockStatus called', params);
5
+ const workedMs = Date.now() - new Date(MOCK_CLOCK_IN_TIME).getTime();
6
+ return {
7
+ success: true,
8
+ timestamp: new Date().toISOString(),
9
+ status: 'clocked-in',
10
+ entry: {
11
+ clockInTime: MOCK_CLOCK_IN_TIME,
12
+ breakStartedAt: null,
13
+ totalBreakMinutes: 0,
14
+ workedMs,
15
+ },
16
+ };
17
+ };
@@ -0,0 +1,18 @@
1
+ export interface TimeClockEntry {
2
+ /** ISO date string of when the current shift started. */
3
+ clockInTime: string;
4
+ /** ISO date string of when the current break started, or `null` when not on break. */
5
+ breakStartedAt: string | null;
6
+ /** Total break time accumulated on this shift so far, in minutes. */
7
+ totalBreakMinutes: number;
8
+ /** Milliseconds worked so far on this shift, as of when this response was generated. */
9
+ workedMs: number;
10
+ }
11
+ export interface GetTimeClockStatusResponse {
12
+ success: boolean;
13
+ timestamp: string;
14
+ status: 'out' | 'clocked-in' | 'on-break';
15
+ /** `null` when `status` is `'out'`. */
16
+ entry: TimeClockEntry | null;
17
+ }
18
+ export type GetTimeClockStatus = (params?: Record<string, never>) => Promise<GetTimeClockStatusResponse>;
@@ -0,0 +1,7 @@
1
+ // Get Time Clock Status Types
2
+ //
3
+ // READ-ONLY query: is the active employee clocked out, clocked in, or on
4
+ // break, and since when? There are no clock-in/clock-out commands — this
5
+ // action only reports the current state; `workedMs` is a snapshot at call
6
+ // time and flows are expected to tick it client-side from `clockInTime`.
7
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Set product modifier selections action
3
+ * Calls the setProductModifierSelections action on the parent window
4
+ */
5
+ import type { SetProductModifierSelections } from "./types";
6
+ export declare const setProductModifierSelections: SetProductModifierSelections;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Set product modifier selections action
3
+ * Calls the setProductModifierSelections action on the parent window
4
+ */
5
+ import { commandFrameClient } from "../../client";
6
+ export const setProductModifierSelections = async (params) => {
7
+ return await commandFrameClient.call("setProductModifierSelections", params);
8
+ };
@@ -0,0 +1,2 @@
1
+ import { SetProductModifierSelections } from "./types";
2
+ export declare const mockSetProductModifierSelections: SetProductModifierSelections;
@@ -0,0 +1,49 @@
1
+ import { MOCK_CART, MOCK_PRODUCTS, buildCartLineModifiers, buildModifierRows } from "../../demo/database";
2
+ export const mockSetProductModifierSelections = async (params) => {
3
+ console.log("[Mock] setProductModifierSelections called", params);
4
+ if (!params?.selections) {
5
+ return {
6
+ success: false,
7
+ reason: "selections is required (pass [] to clear)",
8
+ internalId: params?.internalId,
9
+ selections: [],
10
+ rows: [],
11
+ modifiersTotal: 0,
12
+ timestamp: new Date().toISOString()
13
+ };
14
+ }
15
+ // Write the selections onto the mock cart line (last line when no internalId).
16
+ // The mock does not re-run rule VALIDATION — the real host does — but it does reprice,
17
+ // because a replacement that left the old rows and the old money behind would make the
18
+ // cart disagree with itself the moment a cashier edits a line.
19
+ const line = params.internalId
20
+ ? MOCK_CART.products.find((p) => p.internalId === params.internalId)
21
+ : MOCK_CART.products[MOCK_CART.products.length - 1];
22
+ if (!line) {
23
+ return {
24
+ success: false,
25
+ reason: "No matching cart line",
26
+ internalId: params.internalId,
27
+ selections: [],
28
+ rows: [],
29
+ modifiersTotal: 0,
30
+ timestamp: new Date().toISOString()
31
+ };
32
+ }
33
+ // Full replacement — passing [] clears the rows. The line drops the product's resolved
34
+ // stack when it is built, so the menu is re-read from the catalogue to price against.
35
+ // Cart totals are not touched, for the same reason addProductToCart does not move them
36
+ // for modifiers: this mock does not accumulate per-line money.
37
+ const source = MOCK_PRODUCTS.find((product) => product._id === line.id);
38
+ line.modifierSelections = params.selections;
39
+ line.modifiers = buildCartLineModifiers(source?.modifiers, params.selections, source?.taxTable);
40
+ const { rows, modifiersTotal } = buildModifierRows(line.modifiers, line.quantity);
41
+ return {
42
+ success: true,
43
+ internalId: line.internalId,
44
+ selections: params.selections,
45
+ rows,
46
+ modifiersTotal,
47
+ timestamp: new Date().toISOString()
48
+ };
49
+ };
@@ -0,0 +1,27 @@
1
+ import type { ModifierSelection } from "../get-product-modifier-selections/types";
2
+ import type { CFProdModifierBreakdown } from "../../CommonTypes";
3
+ export interface SetProductModifierSelectionsParams {
4
+ /** The cart line to edit. Defaults to the active product's line. */
5
+ internalId?: string;
6
+ /**
7
+ * The line's COMPLETE new answers — replaces every existing selection on the
8
+ * line (send [] to clear all optional modifiers). Validated against the
9
+ * product's category-modifier rules exactly like addProductToCart({ modifiers }):
10
+ * a selection that breaks a rule rejects the edit and the line is unchanged.
11
+ */
12
+ selections: ModifierSelection[];
13
+ }
14
+ export interface SetProductModifierSelectionsResponse {
15
+ success: boolean;
16
+ /** Set when the edit was rejected (rule violation) — the line is unchanged. */
17
+ reason?: string;
18
+ internalId?: string;
19
+ /** The selections now on the line (the new ones on success, the old ones on rejection). */
20
+ selections: ModifierSelection[];
21
+ /** Display-ready — repaint from this instead of re-reading after an edit. */
22
+ rows: CFProdModifierBreakdown[];
23
+ /** Sum of `rows[].amount` for THIS line, in minor units. */
24
+ modifiersTotal: number;
25
+ timestamp: string;
26
+ }
27
+ export type SetProductModifierSelections = (params?: SetProductModifierSelectionsParams) => Promise<SetProductModifierSelectionsResponse>;
@@ -2,7 +2,7 @@
2
2
  * Mock Database for Standalone/Demo Mode
3
3
  * Stores mock data that mimics the Render environment
4
4
  */
5
- import { CFActiveCompany, CFActiveUser, CFActiveStation, CFActiveOutlet, CFActiveOrder, CFCustomer, CFProduct, CFActiveCart, CFCategory, CFActiveProduct, CFSession, CFActiveRefundDetails, CFSmartGridLayout } from '../CommonTypes';
5
+ import { CFActiveCompany, CFActiveUser, CFActiveStation, CFActiveOutlet, CFActiveOrder, CFCustomer, CFProduct, CFActiveCart, CFCategory, CFActiveProduct, CFSession, CFActiveRefundDetails, CFSmartGridLayout, CFCartLineModifier, CFProdModifierBreakdown, CFModifierSelection, CFResolvedModifier } from '../CommonTypes';
6
6
  export * from './mocks';
7
7
  /** Replace mock catalog / context data in place (same array references mock handlers use). */
8
8
  export interface MockDatabaseConfig {
@@ -87,3 +87,26 @@ export declare const createOrderFromCart: (paymentType: string, amount: number,
87
87
  * open with `amountToBeCharged` reset to what's left, and returns null.
88
88
  */
89
89
  export declare const applyMockPayment: (amount: number, paymentType: string, processor?: string) => CFActiveOrder | null;
90
+ /**
91
+ * Price the cashier's raw modifier answers into the rows a CART line carries (FT-0010).
92
+ *
93
+ * Here rather than in an action folder because two mocks need it — addProductToCart and
94
+ * setProductModifierSelections — and this module is already where the shared mock helpers
95
+ * live (`safeSerialize`, `resetMockCart`, `mockPublishEvent`).
96
+ *
97
+ * Note the two shapes that share the name `modifiers` and are NOT interchangeable:
98
+ * `FullProduct.modifiers` is `ResolvedModifier[]` (the menu — question + every choice),
99
+ * `ActiveProduct.modifiers` is `CartLineModifier[]` (one row per CHOSEN choice). The mock
100
+ * builds a cart line by spreading the product, so the menu must be stripped off the line
101
+ * before anything reads it, or every surface renders the catalogue as if it were picked.
102
+ *
103
+ * Names and unitPrice are snapshotted, as the real host does — a later rename never
104
+ * rewrites an open cart. No `total`: a cart row is per line UNIT, and the host extends it.
105
+ * Rule validation (required/min/max) is deliberately not mocked; the real host owns it.
106
+ */
107
+ /** Extend a line's priced modifier rows by the line quantity. Mirrors kaching's `buildProdModifiers`. */
108
+ export declare const buildModifierRows: (modifiers: CFCartLineModifier[] | undefined, lineQuantity: number | undefined) => {
109
+ rows: CFProdModifierBreakdown[];
110
+ modifiersTotal: number;
111
+ };
112
+ export declare const buildCartLineModifiers: (menu: CFResolvedModifier[] | undefined, selections: CFModifierSelection[] | undefined, productTaxTableId?: string) => CFCartLineModifier[];
@@ -914,3 +914,71 @@ export const applyMockPayment = (amount, paymentType, processor = 'cash') => {
914
914
  mockPublishEvent('payments', 'payment-done', { order });
915
915
  return order;
916
916
  };
917
+ /**
918
+ * Price the cashier's raw modifier answers into the rows a CART line carries (FT-0010).
919
+ *
920
+ * Here rather than in an action folder because two mocks need it — addProductToCart and
921
+ * setProductModifierSelections — and this module is already where the shared mock helpers
922
+ * live (`safeSerialize`, `resetMockCart`, `mockPublishEvent`).
923
+ *
924
+ * Note the two shapes that share the name `modifiers` and are NOT interchangeable:
925
+ * `FullProduct.modifiers` is `ResolvedModifier[]` (the menu — question + every choice),
926
+ * `ActiveProduct.modifiers` is `CartLineModifier[]` (one row per CHOSEN choice). The mock
927
+ * builds a cart line by spreading the product, so the menu must be stripped off the line
928
+ * before anything reads it, or every surface renders the catalogue as if it were picked.
929
+ *
930
+ * Names and unitPrice are snapshotted, as the real host does — a later rename never
931
+ * rewrites an open cart. No `total`: a cart row is per line UNIT, and the host extends it.
932
+ * Rule validation (required/min/max) is deliberately not mocked; the real host owns it.
933
+ */
934
+ /** Extend a line's priced modifier rows by the line quantity. Mirrors kaching's `buildProdModifiers`. */
935
+ export const buildModifierRows = (modifiers, lineQuantity) => {
936
+ const quantity = lineQuantity ?? 1;
937
+ const rows = (modifiers ?? []).map((modifier) => ({
938
+ modifierId: modifier.modifierId,
939
+ modifierName: modifier.modifierName,
940
+ choiceId: modifier.choiceId,
941
+ choiceName: modifier.choiceName,
942
+ label: modifier.label ?? `${modifier.modifierName} - ${modifier.choiceName}`,
943
+ unitPrice: modifier.unitPrice,
944
+ quantity: modifier.quantity,
945
+ // extendPrice, not a raw multiply: a fractional line quantity (1.5 kg) must not
946
+ // leave fractional minor units on the row.
947
+ amount: extendPrice(modifier.unitPrice * modifier.quantity, quantity),
948
+ tax: 0,
949
+ ...(modifier.taxTableId ? { taxTableId: modifier.taxTableId } : {}),
950
+ ...(modifier.taxRateId ? { taxRateId: modifier.taxRateId } : {})
951
+ }));
952
+ return { rows, modifiersTotal: rows.reduce((sum, row) => sum + row.amount, 0) };
953
+ };
954
+ export const buildCartLineModifiers = (menu, selections, productTaxTableId) => {
955
+ if (!menu?.length || !selections?.length)
956
+ return [];
957
+ const byModifier = new Map(menu.map((modifier) => [modifier._id, modifier]));
958
+ const rows = [];
959
+ for (const selection of selections) {
960
+ const modifier = byModifier.get(selection.modifierId);
961
+ if (!modifier)
962
+ continue;
963
+ const byChoice = new Map(modifier.choices.map((choice) => [choice._id, choice]));
964
+ for (const picked of selection.choices ?? []) {
965
+ const choice = byChoice.get(picked.choiceId);
966
+ // `quantity` is units per ONE line unit; 0 means "not chosen" and carries no row.
967
+ if (!choice || !(picked.quantity > 0))
968
+ continue;
969
+ rows.push({
970
+ modifierId: modifier._id,
971
+ modifierName: modifier.name,
972
+ choiceId: choice._id,
973
+ choiceName: choice.name,
974
+ // The cart's display label, like CustomFee.label — "Toppings - Avocado".
975
+ label: `${modifier.name} - ${choice.name}`,
976
+ unitPrice: choice.price,
977
+ quantity: picked.quantity,
978
+ // Modifier tax follows the parent product's table, as a fee's does.
979
+ ...(productTaxTableId ? { taxTableId: productTaxTableId } : {}),
980
+ });
981
+ }
982
+ }
983
+ return rows;
984
+ };
package/dist/index.d.ts CHANGED
@@ -21,6 +21,8 @@ export declare const command: {
21
21
  readonly getFinalContext: import(".").GetFinalContext;
22
22
  readonly addProductNote: import(".").AddProductNote;
23
23
  readonly addProductFee: import(".").AddProductFee;
24
+ readonly getProductModifierSelections: import(".").GetProductModifierSelections;
25
+ readonly setProductModifierSelections: import(".").SetProductModifierSelections;
24
26
  readonly setActiveProductFee: import(".").SetActiveProductFee;
25
27
  readonly setActiveProductDiscount: import(".").SetActiveProductDiscount;
26
28
  readonly getActiveProduct: import(".").GetActiveProduct;
@@ -41,6 +43,8 @@ export declare const command: {
41
43
  readonly extensionPayment: import(".").ExtensionPayment;
42
44
  readonly redeemPayment: import(".").RedeemPayment;
43
45
  readonly integrationPayment: import(".").IntegrationPayment;
46
+ readonly createPaymentLink: import(".").CreatePaymentLink;
47
+ readonly chargeMoto: import(".").ChargeMoto;
44
48
  readonly addCustomerNote: import(".").AddCustomerNote;
45
49
  readonly removeCustomerNote: import(".").RemoveCustomerNote;
46
50
  readonly removeCustomerFromCart: import(".").RemoveCustomerFromCart;
@@ -109,6 +113,7 @@ export declare const command: {
109
113
  readonly uploadMedia: import(".").UploadMedia;
110
114
  readonly getTaxTables: import(".").GetTaxTables;
111
115
  readonly getBranding: import(".").GetBranding;
116
+ readonly getTimeClockStatus: import(".").GetTimeClockStatus;
112
117
  readonly canTransition: import(".").CanTransition;
113
118
  readonly getAvailableTransitions: import(".").GetAvailableTransitions;
114
119
  readonly applyTransition: import(".").ApplyTransition;
@@ -154,6 +159,8 @@ export type { GetContext, GetContextResponse, GetContextResponseManage, GetConte
154
159
  export type { GetFinalContext, GetFinalContextResponse } from './actions/get-final-context/types';
155
160
  export type { AddProductNote, AddProductNoteParams, AddProductNoteResponse } from './actions/add-product-note/types';
156
161
  export type { AddProductFee, AddProductFeeParams, AddProductFeeResponse } from './actions/add-product-fee/types';
162
+ export type { GetProductModifierSelections, GetProductModifierSelectionsParams, GetProductModifierSelectionsResponse, ModifierSelection, ModifierChoiceSelection, } from './actions/get-product-modifier-selections/types';
163
+ export type { SetProductModifierSelections, SetProductModifierSelectionsParams, SetProductModifierSelectionsResponse, } from './actions/set-product-modifier-selections/types';
157
164
  export type { SetActiveProductFee, SetActiveProductFeeParams, SetActiveProductFeeResponse, } from './actions/set-active-product-fee/types';
158
165
  export type { SetActiveProductDiscount, SetActiveProductDiscountParams, SetActiveProductDiscountResponse, } from './actions/set-active-product-discount/types';
159
166
  export type { GetActiveProduct, GetActiveProductResponse } from './actions/get-active-product/types';
@@ -173,6 +180,8 @@ export type { TerminalPayment, TerminalPaymentParams, TerminalPaymentResponse }
173
180
  export type { ExtensionPayment, ExtensionPaymentParams, ExtensionPaymentResponse, } from './actions/extension-payment/types';
174
181
  export type { RedeemPayment, RedeemPaymentParams, RedeemPaymentResponse } from './actions/redeem-payment/types';
175
182
  export type { IntegrationPayment, IntegrationPaymentParams, IntegrationPaymentResponse, IntegrationEmvData, } from './actions/integration-payment/types';
183
+ export type { CreatePaymentLink, CreatePaymentLinkParams, CreatePaymentLinkResponse, } from './actions/create-payment-link/types';
184
+ export type { ChargeMoto, ChargeMotoParams, ChargeMotoResponse, ChargeMotoCardFields, } from './actions/charge-moto/types';
176
185
  export type { AddCustomerNote, AddCustomerNoteParams, AddCustomerNoteResponse, } from './actions/add-customer-note/types';
177
186
  export type { RemoveCustomerNote, RemoveCustomerNoteParams, RemoveCustomerNoteResponse, } from './actions/remove-customer-note/types';
178
187
  export type { RemoveCustomerFromCart, RemoveCustomerFromCartResponse } from './actions/remove-customer-from-cart/types';
@@ -186,6 +195,7 @@ export type { GetMedia, GetMediaParams, GetMediaResponse, MediaItemPayload } fro
186
195
  export type { UploadMedia, UploadMediaParams, UploadMediaResponse } from './actions/upload-media/types';
187
196
  export type { GetTaxTables, GetTaxTablesResponse, TaxRatePayload, TaxTablePayload, } from './actions/get-tax-tables/types';
188
197
  export type { BorderRadiusPreset, GetBranding, GetBrandingResponse } from './actions/get-branding/types';
198
+ export type { GetTimeClockStatus, GetTimeClockStatusResponse, TimeClockEntry, } from './actions/get-time-clock-status/types';
189
199
  export type { ShowConfirmation, ShowConfirmationParams, ShowConfirmationResponse, } from './actions/show-confirmation/types';
190
200
  export type { AuthenticateUser, AuthenticateUserParams, AuthenticateUserResponse, } from './actions/authenticate-user/types';
191
201
  export type { PartialPayment, PartialPaymentParams, PartialPaymentResponse } from './actions/partial-payment/types';
package/dist/index.js CHANGED
@@ -20,6 +20,8 @@ import { updateCartItemQuantity } from './actions/update-cart-item-quantity/acti
20
20
  // Product Actions
21
21
  import { addProductNote } from './actions/add-product-note/action';
22
22
  import { addProductFee } from './actions/add-product-fee/action';
23
+ import { getProductModifierSelections } from './actions/get-product-modifier-selections/action';
24
+ import { setProductModifierSelections } from './actions/set-product-modifier-selections/action';
23
25
  import { setActiveProductFee } from './actions/set-active-product-fee/action';
24
26
  import { setActiveProductDiscount } from './actions/set-active-product-discount/action';
25
27
  import { getActiveProduct } from './actions/get-active-product/action';
@@ -41,6 +43,8 @@ import { terminalPayment } from './actions/terminal-payment/action';
41
43
  import { extensionPayment } from './actions/extension-payment/action';
42
44
  import { redeemPayment } from './actions/redeem-payment/action';
43
45
  import { integrationPayment } from './actions/integration-payment/action';
46
+ import { createPaymentLink } from './actions/create-payment-link/action';
47
+ import { chargeMoto } from './actions/charge-moto/action';
44
48
  // Customer Actions
45
49
  import { addCustomerNote } from './actions/add-customer-note/action';
46
50
  import { removeCustomerNote } from './actions/remove-customer-note/action';
@@ -128,6 +132,8 @@ import { getMedia } from './actions/get-media/action';
128
132
  import { uploadMedia } from './actions/upload-media/action';
129
133
  import { getTaxTables } from './actions/get-tax-tables/action';
130
134
  import { getBranding } from './actions/get-branding/action';
135
+ // Time Clock Actions
136
+ import { getTimeClockStatus } from './actions/get-time-clock-status/action';
131
137
  // Export actions as command object
132
138
  export const command = {
133
139
  exampleFunction,
@@ -153,6 +159,8 @@ export const command = {
153
159
  // Product Actions
154
160
  addProductNote,
155
161
  addProductFee,
162
+ getProductModifierSelections,
163
+ setProductModifierSelections,
156
164
  setActiveProductFee,
157
165
  setActiveProductDiscount,
158
166
  getActiveProduct,
@@ -174,6 +182,8 @@ export const command = {
174
182
  extensionPayment,
175
183
  redeemPayment,
176
184
  integrationPayment,
185
+ createPaymentLink,
186
+ chargeMoto,
177
187
  // Customer Actions
178
188
  addCustomerNote,
179
189
  removeCustomerNote,
@@ -256,6 +266,8 @@ export const command = {
256
266
  uploadMedia,
257
267
  getTaxTables,
258
268
  getBranding,
269
+ // Time Clock Actions
270
+ getTimeClockStatus,
259
271
  // State Machine Queries
260
272
  canTransition,
261
273
  getAvailableTransitions,
@@ -12,6 +12,8 @@ import { mockAddOrderNote } from '../../actions/add-order-note/mock';
12
12
  import { mockRemoveOrderNote } from '../../actions/remove-order-note/mock';
13
13
  import { mockAddProductDiscount } from '../../actions/add-product-discount/mock';
14
14
  import { mockAddProductFee } from '../../actions/add-product-fee/mock';
15
+ import { mockGetProductModifierSelections } from '../../actions/get-product-modifier-selections/mock';
16
+ import { mockSetProductModifierSelections } from '../../actions/set-product-modifier-selections/mock';
15
17
  import { mockRemoveProductDiscount } from '../../actions/remove-product-discount/mock';
16
18
  import { mockRemoveProductFee } from '../../actions/remove-product-fee/mock';
17
19
  import { mockSetActiveProductFee } from '../../actions/set-active-product-fee/mock';
@@ -29,6 +31,9 @@ import { mockAuthenticateUser } from '../../actions/authenticate-user/mock';
29
31
  import { mockCalculateRefundTotal } from '../../actions/calculate-refund-total/mock';
30
32
  import { mockCashPayment } from '../../actions/cash-payment/mock';
31
33
  import { mockGetCashRoundingAmount } from '../../actions/get-cash-rounding-amount/mock';
34
+ import { mockCreatePaymentLink } from '../../actions/create-payment-link/mock';
35
+ import { mockChargeMoto } from '../../actions/charge-moto/mock';
36
+ import { mockGetTimeClockStatus } from '../../actions/get-time-clock-status/mock';
32
37
  import { mockClearCart } from '../../actions/clear-cart/mock';
33
38
  import { mockDeleteParkedOrder } from '../../actions/delete-parked-order/mock';
34
39
  import { mockVoidOrder } from '../../actions/void-order/mock';
@@ -109,6 +114,8 @@ export const RENDER_MOCKS = {
109
114
  addOrderNote: mockAddOrderNote,
110
115
  addProductDiscount: mockAddProductDiscount,
111
116
  addProductFee: mockAddProductFee,
117
+ getProductModifierSelections: mockGetProductModifierSelections,
118
+ setProductModifierSelections: mockSetProductModifierSelections,
112
119
  setActiveProductFee: mockSetActiveProductFee,
113
120
  setActiveProductDiscount: mockSetActiveProductDiscount,
114
121
  getActiveProduct: mockGetActiveProduct,
@@ -123,6 +130,8 @@ export const RENDER_MOCKS = {
123
130
  calculateRefundTotal: mockCalculateRefundTotal,
124
131
  cashPayment: mockCashPayment,
125
132
  getCashRoundingAmount: mockGetCashRoundingAmount,
133
+ createPaymentLink: mockCreatePaymentLink,
134
+ chargeMoto: mockChargeMoto,
126
135
  clearCart: mockClearCart,
127
136
  deleteParkedOrder: mockDeleteParkedOrder,
128
137
  voidOrder: mockVoidOrder,
@@ -199,4 +208,5 @@ export const RENDER_MOCKS = {
199
208
  saveSmartGridLayout: mockSaveSmartGridLayout,
200
209
  sendEmail: mockSendEmail,
201
210
  sendSms: mockSendSms,
211
+ getTimeClockStatus: mockGetTimeClockStatus,
202
212
  };
@@ -1,4 +1,4 @@
1
- import type { ExampleFunction, GetProducts, AddCustomSale, EditCustomSale, 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, VoidOrder, InitiateRefund, CashPayment, GetCashRoundingAmount, TapToPayPayment, TerminalPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, RedeemRefund, GetRefundPlan, CheckPermission, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, GetActiveStation, GetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem, CanTransition, GetAvailableTransitions, ApplyTransition, IntegrationPayment, GetSmartGridLayout, SaveSmartGridLayout, SendEmail, SendSms } from '../../index';
1
+ import type { ExampleFunction, GetProducts, AddCustomSale, EditCustomSale, GetCustomers, AssignCustomer, AddCustomer, EditCustomer, GetCategories, GetOrders, GetRefunds, GetTaxTables, AddProductDiscount, AddProductToCart, RemoveProductFromCart, UpdateCartItemQuantity, AddCartDiscount, GetContext, GetFinalContext, AddProductNote, AddProductFee, GetProductModifierSelections, SetProductModifierSelections, SetActiveProductFee, SetActiveProductDiscount, GetActiveProduct, SetActiveProduct, AdjustInventory, AddOrderNote, AddCartFee, ClearCart, ParkOrder, ResumeParkedOrder, DeleteParkedOrder, VoidOrder, InitiateRefund, CashPayment, GetCashRoundingAmount, TapToPayPayment, TerminalPayment, ExtensionPayment, RedeemPayment, AddNonRevenueItem, AddCustomerNote, RemoveCustomerNote, RemoveCustomerFromCart, GoToStationHome, OpenCashDrawer, ShowNotification, ShowConfirmation, AuthenticateUser, PartialPayment, SwitchUser, SetRefundStockAction, SelectAllRefundItems, ResetRefundDetails, CalculateRefundTotal, GetRemainingRefundableQuantities, ProcessPartialRefund, RedeemRefund, GetRefundPlan, CheckPermission, GetCurrentCart, Print, SetActiveOrder, GetCustomTables, GetCustomTableData, UpsertCustomTableData, DeleteCustomTableData, GetCustomExtensions, GetCurrentCompanyCustomExtensions, GetCustomExtensionCustomTables, GetCustomTableFields, GetSecretsKeys, GetSecretVal, SetSecretVal, GetUsers, GetRoles, RemoveCartDiscount, GetActiveOrder, GetActiveCustomer, SetActiveCustomer, GetActiveOutlet, GetActiveStation, GetActiveSession, GetActiveUser, SetActiveUser, SetActiveRefund, RemoveProductDiscount, RemoveProductFee, RemoveProductNote, RemoveCartFee, RemoveOrderNote, RemoveCustomSale, RemoveNonRevenueItem, CanTransition, GetAvailableTransitions, ApplyTransition, IntegrationPayment, GetSmartGridLayout, SaveSmartGridLayout, SendEmail, SendSms, CreatePaymentLink, ChargeMoto, GetTimeClockStatus } from '../../index';
2
2
  export interface RenderProviderActions {
3
3
  exampleFunction: ExampleFunction;
4
4
  getProducts: GetProducts;
@@ -21,6 +21,8 @@ export interface RenderProviderActions {
21
21
  getFinalContext: GetFinalContext;
22
22
  addProductNote: AddProductNote;
23
23
  addProductFee: AddProductFee;
24
+ getProductModifierSelections: GetProductModifierSelections;
25
+ setProductModifierSelections: SetProductModifierSelections;
24
26
  setActiveProductFee: SetActiveProductFee;
25
27
  setActiveProductDiscount: SetActiveProductDiscount;
26
28
  getActiveProduct: GetActiveProduct;
@@ -41,6 +43,8 @@ export interface RenderProviderActions {
41
43
  extensionPayment: ExtensionPayment;
42
44
  redeemPayment: RedeemPayment;
43
45
  integrationPayment: IntegrationPayment;
46
+ createPaymentLink: CreatePaymentLink;
47
+ chargeMoto: ChargeMoto;
44
48
  addNonRevenueItem: AddNonRevenueItem;
45
49
  addCustomerNote: AddCustomerNote;
46
50
  removeCustomerNote: RemoveCustomerNote;
@@ -101,4 +105,5 @@ export interface RenderProviderActions {
101
105
  saveSmartGridLayout: SaveSmartGridLayout;
102
106
  sendEmail: SendEmail;
103
107
  sendSms: SendSms;
108
+ getTimeClockStatus: GetTimeClockStatus;
104
109
  }
@@ -0,0 +1,16 @@
1
+ import type { CFActiveProduct, CFModifierSelection } from "../../../../CommonTypes";
2
+ import type { TopicEvent } from "../../../types";
3
+ /**
4
+ * Payload for product-modifiers-updated event (FT-0010) — fired when a cart
5
+ * line's modifier selections are replaced via setProductModifierSelections.
6
+ */
7
+ export interface ProductModifiersUpdatedPayload {
8
+ product: CFActiveProduct;
9
+ internalId?: string;
10
+ /** The line's complete new answers (full replacement). */
11
+ selections: CFModifierSelection[];
12
+ }
13
+ /**
14
+ * Typed event for product-modifiers-updated
15
+ */
16
+ export type ProductModifiersUpdatedEvent = TopicEvent<ProductModifiersUpdatedPayload>;
@@ -17,6 +17,7 @@ export * from "./product-fee-added/types";
17
17
  export * from "./product-fee-removed/types";
18
18
  export * from "./product-note-added/types";
19
19
  export * from "./product-note-removed/types";
20
+ export * from "./product-modifiers-updated/types";
20
21
  import type { CartCreatedPayload } from "./cart-created/types";
21
22
  import type { CartCustomerAssignedPayload } from "./customer-assigned/types";
22
23
  import type { ProductAddedPayload } from "./product-added/types";
@@ -32,5 +33,6 @@ import type { ProductFeeAddedPayload } from "./product-fee-added/types";
32
33
  import type { ProductFeeRemovedPayload } from "./product-fee-removed/types";
33
34
  import type { ProductNoteAddedPayload } from "./product-note-added/types";
34
35
  import type { ProductNoteRemovedPayload } from "./product-note-removed/types";
35
- export type CartEventPayload = CartCreatedPayload | CartCustomerAssignedPayload | ProductAddedPayload | ProductDeletedPayload | CartProductUpdatedPayload | CartDiscountAddedPayload | CartDiscountRemovedPayload | CartFeeAddedPayload | CartFeeRemovedPayload | ProductDiscountAddedPayload | ProductDiscountRemovedPayload | ProductFeeAddedPayload | ProductFeeRemovedPayload | ProductNoteAddedPayload | ProductNoteRemovedPayload;
36
- export type CartEventType = "cart-created" | "customer-assigned" | "customer-unassigned" | "product-added" | "product-deleted" | "product-updated" | "cart-discount-added" | "cart-discount-removed" | "cart-fee-added" | "cart-fee-removed" | "product-discount-added" | "product-discount-removed" | "product-fee-added" | "product-fee-removed" | "product-note-added" | "product-note-removed";
36
+ import type { ProductModifiersUpdatedPayload } from "./product-modifiers-updated/types";
37
+ export type CartEventPayload = CartCreatedPayload | CartCustomerAssignedPayload | ProductAddedPayload | ProductDeletedPayload | CartProductUpdatedPayload | CartDiscountAddedPayload | CartDiscountRemovedPayload | CartFeeAddedPayload | CartFeeRemovedPayload | ProductDiscountAddedPayload | ProductDiscountRemovedPayload | ProductFeeAddedPayload | ProductFeeRemovedPayload | ProductNoteAddedPayload | ProductNoteRemovedPayload | ProductModifiersUpdatedPayload;
38
+ export type CartEventType = "cart-created" | "customer-assigned" | "customer-unassigned" | "product-added" | "product-deleted" | "product-updated" | "cart-discount-added" | "cart-discount-removed" | "cart-fee-added" | "cart-fee-removed" | "product-discount-added" | "product-discount-removed" | "product-fee-added" | "product-fee-removed" | "product-note-added" | "product-note-removed" | "product-modifiers-updated";
@@ -18,3 +18,4 @@ export * from "./product-fee-added/types";
18
18
  export * from "./product-fee-removed/types";
19
19
  export * from "./product-note-added/types";
20
20
  export * from "./product-note-removed/types";
21
+ export * from "./product-modifiers-updated/types";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@final-commerce/command-frame",
3
- "version": "0.6.0",
3
+ "version": "0.6.2-staging3.1",
4
4
  "description": "Commands Frame library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -57,7 +57,7 @@
57
57
  "ignoreBranchesMissingTickets": true
58
58
  },
59
59
  "dependencies": {
60
- "@final-commerce/common": "2.2.0"
60
+ "@final-commerce/common": "2.2.4-staging3.1"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@commitlint/cli": "^19.0.0",