@final-commerce/command-frame 0.8.0-online.1 → 0.8.0-online.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CommonTypes.d.ts +14 -1
- package/dist/actions/add-product-to-cart/mock.js +18 -1
- package/dist/actions/add-product-to-cart/types.d.ts +10 -0
- package/dist/actions/get-product-modifier-selections/action.d.ts +6 -0
- package/dist/actions/get-product-modifier-selections/action.js +8 -0
- package/dist/actions/get-product-modifier-selections/mock.d.ts +2 -0
- package/dist/actions/get-product-modifier-selections/mock.js +30 -0
- package/dist/actions/get-product-modifier-selections/types.d.ts +23 -0
- package/dist/actions/get-product-modifier-selections/types.js +1 -0
- package/dist/actions/resume-checkout/action.d.ts +6 -0
- package/dist/actions/resume-checkout/action.js +8 -0
- package/dist/actions/resume-checkout/mock.d.ts +16 -0
- package/dist/actions/resume-checkout/mock.js +51 -0
- package/dist/actions/resume-checkout/types.d.ts +59 -0
- package/dist/actions/resume-checkout/types.js +13 -0
- package/dist/actions/set-product-modifier-selections/action.d.ts +6 -0
- package/dist/actions/set-product-modifier-selections/action.js +8 -0
- package/dist/actions/set-product-modifier-selections/mock.d.ts +2 -0
- package/dist/actions/set-product-modifier-selections/mock.js +49 -0
- package/dist/actions/set-product-modifier-selections/types.d.ts +27 -0
- package/dist/actions/set-product-modifier-selections/types.js +1 -0
- package/dist/demo/database.d.ts +24 -1
- package/dist/demo/database.js +67 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/projects/render/mocks.js +6 -0
- package/dist/projects/render/types.d.ts +9 -1
- package/dist/pubsub/topics/cart/product-modifiers-updated/types.d.ts +16 -0
- package/dist/pubsub/topics/cart/product-modifiers-updated/types.js +1 -0
- package/dist/pubsub/topics/cart/types.d.ts +4 -2
- package/dist/pubsub/topics/cart/types.js +1 -0
- package/dist/pubsub/topics/checkout/payment-completed/types.d.ts +12 -0
- package/package.json +2 -2
package/dist/CommonTypes.d.ts
CHANGED
|
@@ -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
|
+
* 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,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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resume Checkout action
|
|
3
|
+
* Calls the resumeCheckout action on the parent window
|
|
4
|
+
*/
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
|
+
export const resumeCheckout = async (params) => {
|
|
7
|
+
return await commandFrameClient.call('resumeCheckout', params ?? {});
|
|
8
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ResumeCheckout } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone mock of the checkout return leg.
|
|
4
|
+
*
|
|
5
|
+
* It reads the SAME query parameters the real provider sends a shopper back
|
|
6
|
+
* with, so a builder can exercise the redirect path in preview by appending
|
|
7
|
+
* them by hand:
|
|
8
|
+
*
|
|
9
|
+
* ?sessionId=CS_MOCK&redirectResult=MOCK
|
|
10
|
+
*
|
|
11
|
+
* Without them it answers `{ resumed: false }` and does nothing, which is what
|
|
12
|
+
* the real command does on an ordinary page load. That is the branch a
|
|
13
|
+
* checkout screen hits on every normal visit, so it is the one worth getting
|
|
14
|
+
* right in a mock.
|
|
15
|
+
*/
|
|
16
|
+
export declare const mockResumeCheckout: ResumeCheckout;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mockPublishEvent } from '../../demo/database';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone mock of the checkout return leg.
|
|
4
|
+
*
|
|
5
|
+
* It reads the SAME query parameters the real provider sends a shopper back
|
|
6
|
+
* with, so a builder can exercise the redirect path in preview by appending
|
|
7
|
+
* them by hand:
|
|
8
|
+
*
|
|
9
|
+
* ?sessionId=CS_MOCK&redirectResult=MOCK
|
|
10
|
+
*
|
|
11
|
+
* Without them it answers `{ resumed: false }` and does nothing, which is what
|
|
12
|
+
* the real command does on an ordinary page load. That is the branch a
|
|
13
|
+
* checkout screen hits on every normal visit, so it is the one worth getting
|
|
14
|
+
* right in a mock.
|
|
15
|
+
*/
|
|
16
|
+
export const mockResumeCheckout = async (params) => {
|
|
17
|
+
console.log('[Mock] resumeCheckout called', params);
|
|
18
|
+
const href = params?.url ?? (typeof window === 'undefined' ? '' : window.location.href);
|
|
19
|
+
let redirectResult = null;
|
|
20
|
+
try {
|
|
21
|
+
redirectResult = new URL(href).searchParams.get('redirectResult');
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
redirectResult = null;
|
|
25
|
+
}
|
|
26
|
+
if (!redirectResult) {
|
|
27
|
+
return {
|
|
28
|
+
success: true,
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
checkout: { resumed: false },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const suffix = String(Date.now()).slice(-6);
|
|
34
|
+
const checkout = {
|
|
35
|
+
resumed: true,
|
|
36
|
+
orderId: `mock-order-${suffix}`,
|
|
37
|
+
receiptId: `ON-001-${suffix}`,
|
|
38
|
+
// `Authorised` is what the real provider reports for a completed redirect.
|
|
39
|
+
// It is NOT settlement — see the checkout topic docs.
|
|
40
|
+
resultCode: 'Authorised',
|
|
41
|
+
sessionResult: `mock-session-result-${suffix}`,
|
|
42
|
+
orderPassword: `mock-pw-${suffix}`,
|
|
43
|
+
};
|
|
44
|
+
mockPublishEvent('checkout', 'payment-completed', {
|
|
45
|
+
orderId: checkout.orderId,
|
|
46
|
+
receiptId: checkout.receiptId,
|
|
47
|
+
resultCode: checkout.resultCode,
|
|
48
|
+
sessionResult: checkout.sessionResult,
|
|
49
|
+
});
|
|
50
|
+
return { success: true, timestamp: new Date().toISOString(), checkout };
|
|
51
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface ResumeCheckoutParams {
|
|
2
|
+
/**
|
|
3
|
+
* Where to read the provider's return data from. Defaults to the current
|
|
4
|
+
* page URL, which is where the shopper has just landed.
|
|
5
|
+
*
|
|
6
|
+
* Only override this if you moved the query string somewhere else before
|
|
7
|
+
* calling — for example if your router strips it on mount.
|
|
8
|
+
*/
|
|
9
|
+
url?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* What the return leg produced. Every field past `resumed` is absent when
|
|
13
|
+
* `resumed` is `false`.
|
|
14
|
+
*/
|
|
15
|
+
export interface ResumeCheckoutResult {
|
|
16
|
+
/**
|
|
17
|
+
* `false` means THIS WAS AN ORDINARY PAGE LOAD — no provider return data was
|
|
18
|
+
* in the URL, so there was nothing to finish and nothing happened. It is not
|
|
19
|
+
* an error, and it is the answer on every normal visit to the page. Call this
|
|
20
|
+
* unconditionally when your checkout or confirmation screen mounts and branch
|
|
21
|
+
* on this field.
|
|
22
|
+
*/
|
|
23
|
+
resumed: boolean;
|
|
24
|
+
orderId?: string;
|
|
25
|
+
receiptId?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The provider's result code for the completed attempt, e.g. `Authorised` or
|
|
28
|
+
* `Refused`. As everywhere else in this topic, `Authorised` is an
|
|
29
|
+
* authorisation and NOT a settlement.
|
|
30
|
+
*/
|
|
31
|
+
resultCode?: string;
|
|
32
|
+
/** Opaque proof of the payment — see `PaymentCompletedPayload.sessionResult`. */
|
|
33
|
+
sessionResult?: string;
|
|
34
|
+
/**
|
|
35
|
+
* The one-time order password from the original checkout, recovered from
|
|
36
|
+
* browser storage. The page that held it in memory is gone, so this is the
|
|
37
|
+
* only way back to the order's status after a redirect.
|
|
38
|
+
*/
|
|
39
|
+
orderPassword?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ResumeCheckoutResponse {
|
|
42
|
+
success: boolean;
|
|
43
|
+
timestamp: string;
|
|
44
|
+
checkout: ResumeCheckoutResult;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Finish a checkout the shopper was redirected away from.
|
|
48
|
+
*
|
|
49
|
+
* SAFE TO CALL ON EVERY PAGE LOAD, and that is how it is meant to be used: on
|
|
50
|
+
* an ordinary visit it finds no return data and answers `{ resumed: false }`
|
|
51
|
+
* without touching the network. Calling it twice for one return is also safe —
|
|
52
|
+
* the return data is consumed the first time.
|
|
53
|
+
*
|
|
54
|
+
* The outcome is ALSO published on the `checkout` topic (`payment-completed` or
|
|
55
|
+
* `payment-failed`), exactly as it would have been had the shopper never left,
|
|
56
|
+
* so a page that already subscribes needs no second code path. Subscribe first,
|
|
57
|
+
* then call this, or the event fires before you are listening.
|
|
58
|
+
*/
|
|
59
|
+
export type ResumeCheckout = (params?: ResumeCheckoutParams) => Promise<ResumeCheckoutResponse>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Resume Checkout Types
|
|
2
|
+
//
|
|
3
|
+
// The RETURN LEG of an online checkout. Some payment methods — and 3-D Secure
|
|
4
|
+
// on a card — take the shopper away to their bank or wallet and send them back
|
|
5
|
+
// to `returnUrl` on a FRESH PAGE LOAD. The payment is not finished at that
|
|
6
|
+
// point: the provider hands the browser a one-time result in the URL, and it
|
|
7
|
+
// has to be handed back to the provider to complete the charge.
|
|
8
|
+
//
|
|
9
|
+
// `startCheckout` cannot do this. It ran in the page that is now gone.
|
|
10
|
+
//
|
|
11
|
+
// Served ONLY by the storefront runtime (a published website), like
|
|
12
|
+
// `startCheckout`. On a register it does not exist.
|
|
13
|
+
export {};
|
|
@@ -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,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>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/demo/database.d.ts
CHANGED
|
@@ -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[];
|
package/dist/demo/database.js
CHANGED
|
@@ -914,3 +914,70 @@ 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
|
+
}));
|
|
951
|
+
return { rows, modifiersTotal: rows.reduce((sum, row) => sum + row.amount, 0) };
|
|
952
|
+
};
|
|
953
|
+
export const buildCartLineModifiers = (menu, selections, productTaxTableId) => {
|
|
954
|
+
if (!menu?.length || !selections?.length)
|
|
955
|
+
return [];
|
|
956
|
+
const byModifier = new Map(menu.map((modifier) => [modifier._id, modifier]));
|
|
957
|
+
const rows = [];
|
|
958
|
+
for (const selection of selections) {
|
|
959
|
+
const modifier = byModifier.get(selection.modifierId);
|
|
960
|
+
if (!modifier)
|
|
961
|
+
continue;
|
|
962
|
+
const byChoice = new Map(modifier.choices.map((choice) => [choice._id, choice]));
|
|
963
|
+
for (const picked of selection.choices ?? []) {
|
|
964
|
+
const choice = byChoice.get(picked.choiceId);
|
|
965
|
+
// `quantity` is units per ONE line unit; 0 means "not chosen" and carries no row.
|
|
966
|
+
if (!choice || !(picked.quantity > 0))
|
|
967
|
+
continue;
|
|
968
|
+
rows.push({
|
|
969
|
+
modifierId: modifier._id,
|
|
970
|
+
modifierName: modifier.name,
|
|
971
|
+
choiceId: choice._id,
|
|
972
|
+
choiceName: choice.name,
|
|
973
|
+
// The cart's display label, like CustomFee.label — "Toppings - Avocado".
|
|
974
|
+
label: `${modifier.name} - ${choice.name}`,
|
|
975
|
+
unitPrice: choice.price,
|
|
976
|
+
quantity: picked.quantity,
|
|
977
|
+
// Modifier tax follows the parent product's table, as a fee's does.
|
|
978
|
+
...(productTaxTableId ? { taxTableId: productTaxTableId } : {}),
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
return rows;
|
|
983
|
+
};
|
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;
|
|
@@ -44,6 +46,7 @@ export declare const command: {
|
|
|
44
46
|
readonly createPaymentLink: import(".").CreatePaymentLink;
|
|
45
47
|
readonly chargeMoto: import(".").ChargeMoto;
|
|
46
48
|
readonly startCheckout: import(".").StartCheckout;
|
|
49
|
+
readonly resumeCheckout: import(".").ResumeCheckout;
|
|
47
50
|
readonly addCustomerNote: import(".").AddCustomerNote;
|
|
48
51
|
readonly removeCustomerNote: import(".").RemoveCustomerNote;
|
|
49
52
|
readonly removeCustomerFromCart: import(".").RemoveCustomerFromCart;
|
|
@@ -158,6 +161,8 @@ export type { GetContext, GetContextResponse, GetContextResponseManage, GetConte
|
|
|
158
161
|
export type { GetFinalContext, GetFinalContextResponse } from './actions/get-final-context/types';
|
|
159
162
|
export type { AddProductNote, AddProductNoteParams, AddProductNoteResponse } from './actions/add-product-note/types';
|
|
160
163
|
export type { AddProductFee, AddProductFeeParams, AddProductFeeResponse } from './actions/add-product-fee/types';
|
|
164
|
+
export type { GetProductModifierSelections, GetProductModifierSelectionsParams, GetProductModifierSelectionsResponse, ModifierSelection, ModifierChoiceSelection, } from './actions/get-product-modifier-selections/types';
|
|
165
|
+
export type { SetProductModifierSelections, SetProductModifierSelectionsParams, SetProductModifierSelectionsResponse, } from './actions/set-product-modifier-selections/types';
|
|
161
166
|
export type { SetActiveProductFee, SetActiveProductFeeParams, SetActiveProductFeeResponse, } from './actions/set-active-product-fee/types';
|
|
162
167
|
export type { SetActiveProductDiscount, SetActiveProductDiscountParams, SetActiveProductDiscountResponse, } from './actions/set-active-product-discount/types';
|
|
163
168
|
export type { GetActiveProduct, GetActiveProductResponse } from './actions/get-active-product/types';
|
|
@@ -180,6 +185,7 @@ export type { IntegrationPayment, IntegrationPaymentParams, IntegrationPaymentRe
|
|
|
180
185
|
export type { CreatePaymentLink, CreatePaymentLinkParams, CreatePaymentLinkResponse, } from './actions/create-payment-link/types';
|
|
181
186
|
export type { ChargeMoto, ChargeMotoParams, ChargeMotoResponse, ChargeMotoCardFields, } from './actions/charge-moto/types';
|
|
182
187
|
export type { StartCheckout, StartCheckoutParams, StartCheckoutResponse, StartCheckoutContact, StartCheckoutOrder, } from './actions/start-checkout/types';
|
|
188
|
+
export type { ResumeCheckout, ResumeCheckoutParams, ResumeCheckoutResponse, ResumeCheckoutResult, } from './actions/resume-checkout/types';
|
|
183
189
|
export type { AddCustomerNote, AddCustomerNoteParams, AddCustomerNoteResponse, } from './actions/add-customer-note/types';
|
|
184
190
|
export type { RemoveCustomerNote, RemoveCustomerNoteParams, RemoveCustomerNoteResponse, } from './actions/remove-customer-note/types';
|
|
185
191
|
export type { RemoveCustomerFromCart, RemoveCustomerFromCartResponse } from './actions/remove-customer-from-cart/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';
|
|
@@ -44,6 +46,7 @@ import { integrationPayment } from './actions/integration-payment/action';
|
|
|
44
46
|
import { createPaymentLink } from './actions/create-payment-link/action';
|
|
45
47
|
import { chargeMoto } from './actions/charge-moto/action';
|
|
46
48
|
import { startCheckout } from './actions/start-checkout/action';
|
|
49
|
+
import { resumeCheckout } from './actions/resume-checkout/action';
|
|
47
50
|
// Customer Actions
|
|
48
51
|
import { addCustomerNote } from './actions/add-customer-note/action';
|
|
49
52
|
import { removeCustomerNote } from './actions/remove-customer-note/action';
|
|
@@ -158,6 +161,8 @@ export const command = {
|
|
|
158
161
|
// Product Actions
|
|
159
162
|
addProductNote,
|
|
160
163
|
addProductFee,
|
|
164
|
+
getProductModifierSelections,
|
|
165
|
+
setProductModifierSelections,
|
|
161
166
|
setActiveProductFee,
|
|
162
167
|
setActiveProductDiscount,
|
|
163
168
|
getActiveProduct,
|
|
@@ -182,6 +187,7 @@ export const command = {
|
|
|
182
187
|
createPaymentLink,
|
|
183
188
|
chargeMoto,
|
|
184
189
|
startCheckout,
|
|
190
|
+
resumeCheckout,
|
|
185
191
|
// Customer Actions
|
|
186
192
|
addCustomerNote,
|
|
187
193
|
removeCustomerNote,
|
|
@@ -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';
|
|
@@ -32,6 +34,7 @@ import { mockGetCashRoundingAmount } from '../../actions/get-cash-rounding-amoun
|
|
|
32
34
|
import { mockCreatePaymentLink } from '../../actions/create-payment-link/mock';
|
|
33
35
|
import { mockChargeMoto } from '../../actions/charge-moto/mock';
|
|
34
36
|
import { mockStartCheckout } from '../../actions/start-checkout/mock';
|
|
37
|
+
import { mockResumeCheckout } from '../../actions/resume-checkout/mock';
|
|
35
38
|
import { mockGetTimeClockStatus } from '../../actions/get-time-clock-status/mock';
|
|
36
39
|
import { mockClearCart } from '../../actions/clear-cart/mock';
|
|
37
40
|
import { mockDeleteParkedOrder } from '../../actions/delete-parked-order/mock';
|
|
@@ -113,6 +116,8 @@ export const RENDER_MOCKS = {
|
|
|
113
116
|
addOrderNote: mockAddOrderNote,
|
|
114
117
|
addProductDiscount: mockAddProductDiscount,
|
|
115
118
|
addProductFee: mockAddProductFee,
|
|
119
|
+
getProductModifierSelections: mockGetProductModifierSelections,
|
|
120
|
+
setProductModifierSelections: mockSetProductModifierSelections,
|
|
116
121
|
setActiveProductFee: mockSetActiveProductFee,
|
|
117
122
|
setActiveProductDiscount: mockSetActiveProductDiscount,
|
|
118
123
|
getActiveProduct: mockGetActiveProduct,
|
|
@@ -130,6 +135,7 @@ export const RENDER_MOCKS = {
|
|
|
130
135
|
createPaymentLink: mockCreatePaymentLink,
|
|
131
136
|
chargeMoto: mockChargeMoto,
|
|
132
137
|
startCheckout: mockStartCheckout,
|
|
138
|
+
resumeCheckout: mockResumeCheckout,
|
|
133
139
|
clearCart: mockClearCart,
|
|
134
140
|
deleteParkedOrder: mockDeleteParkedOrder,
|
|
135
141
|
voidOrder: mockVoidOrder,
|
|
@@ -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, CreatePaymentLink, ChargeMoto, StartCheckout, GetTimeClockStatus } 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, StartCheckout, ResumeCheckout, 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;
|
|
@@ -49,6 +51,12 @@ export interface RenderProviderActions {
|
|
|
49
51
|
* a register, because the two declare different things to the card networks.
|
|
50
52
|
*/
|
|
51
53
|
startCheckout: StartCheckout;
|
|
54
|
+
/**
|
|
55
|
+
* The RETURN LEG of an online checkout, after a redirect payment method or
|
|
56
|
+
* 3-D Secure sent the shopper away and back. Storefront-only, like
|
|
57
|
+
* `startCheckout`, and safe to call on every page load.
|
|
58
|
+
*/
|
|
59
|
+
resumeCheckout: ResumeCheckout;
|
|
52
60
|
addNonRevenueItem: AddNonRevenueItem;
|
|
53
61
|
addCustomerNote: AddCustomerNote;
|
|
54
62
|
removeCustomerNote: RemoveCustomerNote;
|
|
@@ -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>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
-
|
|
36
|
-
export type
|
|
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";
|
|
@@ -12,6 +12,18 @@ export interface PaymentCompletedPayload {
|
|
|
12
12
|
receiptId: string;
|
|
13
13
|
/** The provider's result code, e.g. `Authorised`. */
|
|
14
14
|
resultCode: string;
|
|
15
|
+
/**
|
|
16
|
+
* OPAQUE PROOF OF THIS PAYMENT, handed to the browser by the provider and to
|
|
17
|
+
* nobody else. Pass it to the order-status read and the server verifies it
|
|
18
|
+
* with the provider before believing a word of it.
|
|
19
|
+
*
|
|
20
|
+
* It is the only evidence of an outcome that exists outside the provider's
|
|
21
|
+
* webhook, so it is what recovers an order whose confirmation never arrived.
|
|
22
|
+
* Treat it as a credential: do not log it, do not put it in a URL you share.
|
|
23
|
+
*
|
|
24
|
+
* Absent when the provider reported no payment (see `resultCode`).
|
|
25
|
+
*/
|
|
26
|
+
sessionResult?: string;
|
|
15
27
|
}
|
|
16
28
|
/**
|
|
17
29
|
* Typed event for payment-completed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@final-commerce/command-frame",
|
|
3
|
-
"version": "0.8.0-online.
|
|
3
|
+
"version": "0.8.0-online.3",
|
|
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.
|
|
60
|
+
"@final-commerce/common": "2.2.4-preprod.1"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@commitlint/cli": "^19.0.0",
|