@final-commerce/command-frame 0.5.1 → 0.6.0-preprod.2
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 +6 -6
- package/dist/CommonTypes.js +2 -2
- package/dist/actions/add-cart-discount/mock.d.ts +1 -1
- package/dist/actions/add-cart-discount/mock.js +8 -8
- package/dist/actions/add-cart-fee/mock.d.ts +1 -1
- package/dist/actions/add-cart-fee/mock.js +8 -8
- package/dist/actions/add-product-to-cart/mock.d.ts +1 -1
- package/dist/actions/add-product-to-cart/mock.js +24 -10
- package/dist/actions/add-product-to-cart/types.d.ts +15 -3
- package/dist/actions/adjust-inventory/mock.d.ts +1 -1
- package/dist/actions/adjust-inventory/mock.js +6 -6
- package/dist/actions/apply-transition/mock.d.ts +1 -1
- package/dist/actions/apply-transition/mock.js +8 -8
- package/dist/actions/apply-transition/types.d.ts +1 -1
- package/dist/actions/authenticate-user/mock.d.ts +1 -1
- package/dist/actions/authenticate-user/mock.js +3 -3
- package/dist/actions/calculate-refund-total/action.d.ts +1 -1
- package/dist/actions/calculate-refund-total/action.js +2 -2
- package/dist/actions/can-transition/types.d.ts +1 -1
- package/dist/actions/cash-payment/mock.d.ts +1 -1
- package/dist/actions/cash-payment/mock.js +10 -10
- package/dist/actions/cash-payment/types.d.ts +1 -1
- package/dist/actions/charge-moto/action.d.ts +6 -0
- package/dist/actions/charge-moto/action.js +8 -0
- package/dist/actions/charge-moto/mock.d.ts +2 -0
- package/dist/actions/charge-moto/mock.js +47 -0
- package/dist/actions/charge-moto/types.d.ts +53 -0
- package/dist/actions/charge-moto/types.js +1 -0
- package/dist/actions/create-payment-link/action.d.ts +6 -0
- package/dist/actions/create-payment-link/action.js +8 -0
- package/dist/actions/create-payment-link/mock.d.ts +2 -0
- package/dist/actions/create-payment-link/mock.js +35 -0
- package/dist/actions/create-payment-link/types.d.ts +27 -0
- package/dist/actions/create-payment-link/types.js +12 -0
- package/dist/actions/extension-payment/mock.d.ts +1 -1
- package/dist/actions/extension-payment/mock.js +3 -3
- package/dist/actions/extension-payment/types.d.ts +1 -1
- package/dist/actions/get-final-context/mock.d.ts +1 -1
- package/dist/actions/get-final-context/mock.js +2 -2
- package/dist/actions/get-products/mock.d.ts +1 -1
- package/dist/actions/get-products/mock.js +26 -9
- package/dist/actions/get-refunds/types.d.ts +1 -1
- package/dist/actions/get-remaining-refundable-quantities/action.d.ts +1 -1
- package/dist/actions/get-remaining-refundable-quantities/action.js +2 -2
- package/dist/actions/get-remaining-refundable-quantities/mock.d.ts +1 -1
- package/dist/actions/get-remaining-refundable-quantities/mock.js +2 -2
- package/dist/actions/get-time-clock-status/action.d.ts +6 -0
- package/dist/actions/get-time-clock-status/action.js +8 -0
- package/dist/actions/get-time-clock-status/mock.d.ts +2 -0
- package/dist/actions/get-time-clock-status/mock.js +17 -0
- package/dist/actions/get-time-clock-status/types.d.ts +18 -0
- package/dist/actions/get-time-clock-status/types.js +7 -0
- package/dist/actions/integration-payment/types.d.ts +1 -1
- package/dist/actions/park-order/types.d.ts +1 -1
- package/dist/actions/partial-payment/mock.d.ts +1 -1
- package/dist/actions/partial-payment/mock.js +8 -8
- package/dist/actions/partial-payment/types.d.ts +1 -1
- package/dist/actions/print/mock.d.ts +1 -1
- package/dist/actions/print/mock.js +9 -9
- package/dist/actions/print/types.d.ts +6 -6
- package/dist/actions/redeem-payment/types.d.ts +1 -1
- package/dist/actions/remove-cart-fee/mock.d.ts +1 -1
- package/dist/actions/remove-cart-fee/mock.js +7 -7
- package/dist/actions/remove-product-from-cart/mock.d.ts +1 -1
- package/dist/actions/remove-product-from-cart/mock.js +11 -8
- package/dist/actions/resume-parked-order/types.d.ts +1 -1
- package/dist/actions/select-all-refund-items/action.d.ts +1 -1
- package/dist/actions/select-all-refund-items/action.js +2 -2
- package/dist/actions/set-active-product/mock.d.ts +1 -1
- package/dist/actions/set-active-product/mock.js +19 -14
- package/dist/actions/set-active-refund/types.d.ts +1 -1
- package/dist/actions/tap-to-pay-payment/mock.d.ts +1 -1
- package/dist/actions/tap-to-pay-payment/mock.js +6 -6
- package/dist/actions/tap-to-pay-payment/types.d.ts +1 -1
- package/dist/actions/terminal-payment/mock.d.ts +1 -1
- package/dist/actions/terminal-payment/mock.js +6 -6
- package/dist/actions/terminal-payment/types.d.ts +2 -2
- package/dist/actions/update-cart-item-quantity/mock.d.ts +1 -1
- package/dist/actions/update-cart-item-quantity/mock.js +23 -15
- package/dist/actions/update-cart-item-quantity/types.d.ts +8 -1
- package/dist/actions/upsert-custom-table-data/mock.d.ts +1 -1
- package/dist/actions/upsert-custom-table-data/mock.js +3 -3
- package/dist/demo/database.js +30 -4
- package/dist/index.d.ts +6 -0
- package/dist/index.js +8 -0
- package/dist/projects/render/mocks.js +6 -0
- package/dist/projects/render/types.d.ts +4 -1
- package/package.json +2 -2
package/dist/CommonTypes.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * from
|
|
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
|
|
3
|
-
export { CurrencyCode, ProductType as CFProductType, UserTypes as CFUserTypes } from
|
|
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';
|
|
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>;
|
|
6
6
|
export type CFDiscount = Discount;
|
|
@@ -52,7 +52,7 @@ export type CFActiveCart = ActiveCart;
|
|
|
52
52
|
export type CFActivePark = ActivePark;
|
|
53
53
|
export type CFActiveCompany = ActiveCompany;
|
|
54
54
|
/** Tile kinds a SmartGrid cell can represent. Mirrors common's `BaseTile['type']`. */
|
|
55
|
-
export type CFTileType =
|
|
55
|
+
export type CFTileType = 'empty' | 'product' | 'action' | 'category' | 'folder' | 'back';
|
|
56
56
|
/**
|
|
57
57
|
* A single positioned cell in a SmartGrid layout. The wire/extension-facing shape
|
|
58
58
|
* (Render's adapter maps this to/from common's `BaseTile`).
|
|
@@ -96,7 +96,7 @@ export interface CFActiveRefundDetails {
|
|
|
96
96
|
refundProcessingStatus?: CFRefundProcessingStatus | null;
|
|
97
97
|
sessionRefundedTotal?: number;
|
|
98
98
|
}
|
|
99
|
-
export type CFProjectName =
|
|
99
|
+
export type CFProjectName = 'kaching' | 'Manage';
|
|
100
100
|
export interface CFContextRender {
|
|
101
101
|
userId: string | null;
|
|
102
102
|
companyId: string | null;
|
|
@@ -126,7 +126,7 @@ export interface CFContextRender {
|
|
|
126
126
|
thousandSeparator: string | null;
|
|
127
127
|
decimalSeparator: string | null;
|
|
128
128
|
user: Record<string, unknown> | null;
|
|
129
|
-
company: Omit<Record<string, unknown>,
|
|
129
|
+
company: Omit<Record<string, unknown>, 'settings'> | null;
|
|
130
130
|
station: Record<string, unknown> | null;
|
|
131
131
|
outlet: Record<string, unknown> | null;
|
|
132
132
|
timestamp: string;
|
package/dist/CommonTypes.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from './common-types';
|
|
2
2
|
// Enums — re-exported from common (single source). CurrencyCode keeps its name;
|
|
3
3
|
// CFProductType/CFUserTypes alias common's ProductType/UserTypes. Note: common's
|
|
4
4
|
// UserTypes uses `COMPANY_OWNER` where command-frame's enum previously had `OWNER`
|
|
5
5
|
// (same value `'owner'`).
|
|
6
|
-
export { CurrencyCode, ProductType as CFProductType, UserTypes as CFUserTypes } from
|
|
6
|
+
export { CurrencyCode, ProductType as CFProductType, UserTypes as CFUserTypes } from '@final-commerce/common';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AddCartDiscount } from
|
|
1
|
+
import { AddCartDiscount } from './types';
|
|
2
2
|
export declare const mockAddCartDiscount: AddCartDiscount;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { MOCK_CART, mockPublishEvent } from
|
|
2
|
-
import { percentToFraction, requireMinorUnitsInteger } from
|
|
1
|
+
import { MOCK_CART, mockPublishEvent } from '../../demo/database';
|
|
2
|
+
import { percentToFraction, requireMinorUnitsInteger } from '../../demo/units';
|
|
3
3
|
export const mockAddCartDiscount = async (params) => {
|
|
4
|
-
console.log(
|
|
4
|
+
console.log('[Mock] addCartDiscount called', params);
|
|
5
5
|
if (params) {
|
|
6
6
|
// FI-6991: a fixed amount arrives as an INTEGER in MINOR units (500 =
|
|
7
7
|
// $5.00) and is stored directly; a percent arrives raw (50 = 50%) and is
|
|
8
8
|
// stored as a fraction (0.5). Same as the real handler.
|
|
9
9
|
const value = params.isPercent
|
|
10
10
|
? percentToFraction(params.amount)
|
|
11
|
-
: requireMinorUnitsInteger(params.amount,
|
|
11
|
+
: requireMinorUnitsInteger(params.amount, 'Discount amount');
|
|
12
12
|
MOCK_CART.discount = {
|
|
13
13
|
value,
|
|
14
14
|
isPercent: params.isPercent,
|
|
15
|
-
label: params.label
|
|
15
|
+
label: params.label,
|
|
16
16
|
};
|
|
17
17
|
if (params.isPercent) {
|
|
18
18
|
MOCK_CART.total = MOCK_CART.subtotal * (1 - value);
|
|
@@ -24,14 +24,14 @@ export const mockAddCartDiscount = async (params) => {
|
|
|
24
24
|
MOCK_CART.remainingBalance = MOCK_CART.total;
|
|
25
25
|
// Publish cart-discount-added event
|
|
26
26
|
mockPublishEvent('cart', 'cart-discount-added', {
|
|
27
|
-
discount: MOCK_CART.discount
|
|
27
|
+
discount: MOCK_CART.discount,
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
return {
|
|
31
31
|
success: true,
|
|
32
32
|
amount: params?.amount || 0,
|
|
33
33
|
isPercent: params?.isPercent || false,
|
|
34
|
-
label: params?.label ||
|
|
35
|
-
timestamp: new Date().toISOString()
|
|
34
|
+
label: params?.label || '',
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
36
|
};
|
|
37
37
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AddCartFee } from
|
|
1
|
+
import { AddCartFee } from './types';
|
|
2
2
|
export declare const mockAddCartFee: AddCartFee;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { MOCK_CART } from
|
|
2
|
-
import { percentToFraction, requireMinorUnitsInteger } from
|
|
1
|
+
import { MOCK_CART } from '../../demo/database';
|
|
2
|
+
import { percentToFraction, requireMinorUnitsInteger } from '../../demo/units';
|
|
3
3
|
export const mockAddCartFee = async (params) => {
|
|
4
|
-
console.log(
|
|
4
|
+
console.log('[Mock] addCartFee called', params);
|
|
5
5
|
if (params) {
|
|
6
6
|
if (!MOCK_CART.customFee)
|
|
7
7
|
MOCK_CART.customFee = [];
|
|
@@ -10,13 +10,13 @@ export const mockAddCartFee = async (params) => {
|
|
|
10
10
|
// stored as a fraction (0.5). Same as the real handler.
|
|
11
11
|
const storedAmount = params.isPercent
|
|
12
12
|
? percentToFraction(params.amount)
|
|
13
|
-
: requireMinorUnitsInteger(params.amount,
|
|
13
|
+
: requireMinorUnitsInteger(params.amount, 'Fee amount');
|
|
14
14
|
MOCK_CART.customFee.push({
|
|
15
|
-
label: params.label ||
|
|
15
|
+
label: params.label || 'Fee',
|
|
16
16
|
amount: storedAmount,
|
|
17
17
|
isPercent: params.isPercent || false,
|
|
18
18
|
applyTaxes: params.applyTaxes || false,
|
|
19
|
-
taxTableId: params.taxTableId
|
|
19
|
+
taxTableId: params.taxTableId,
|
|
20
20
|
});
|
|
21
21
|
const feeAmount = params.isPercent ? MOCK_CART.subtotal * storedAmount : storedAmount;
|
|
22
22
|
MOCK_CART.total += feeAmount;
|
|
@@ -27,8 +27,8 @@ export const mockAddCartFee = async (params) => {
|
|
|
27
27
|
success: true,
|
|
28
28
|
amount: params?.amount || 0,
|
|
29
29
|
isPercent: params?.isPercent || false,
|
|
30
|
-
label: params?.label ||
|
|
30
|
+
label: params?.label || '',
|
|
31
31
|
applyTaxes: params?.applyTaxes || false,
|
|
32
|
-
timestamp: new Date().toISOString()
|
|
32
|
+
timestamp: new Date().toISOString(),
|
|
33
33
|
};
|
|
34
34
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AddProductToCart } from
|
|
1
|
+
import { AddProductToCart } from './types';
|
|
2
2
|
export declare const mockAddProductToCart: AddProductToCart;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { MOCK_CART, MOCK_PRODUCTS, mockPublishEvent } from
|
|
1
|
+
import { MOCK_CART, MOCK_PRODUCTS, mockPublishEvent } from '../../demo/database';
|
|
2
|
+
import { extendPrice, isValidQuantity, resolveUnit } from '@final-commerce/common';
|
|
2
3
|
export const mockAddProductToCart = async (params) => {
|
|
3
|
-
console.log(
|
|
4
|
+
console.log('[Mock] addProductToCart called', params);
|
|
4
5
|
const variantId = params?.variantId;
|
|
5
6
|
const quantity = params?.quantity || 1;
|
|
7
|
+
if (!Number.isFinite(quantity) || quantity <= 0) {
|
|
8
|
+
throw new Error(`${quantity} is not a quantity`);
|
|
9
|
+
}
|
|
6
10
|
let product = MOCK_PRODUCTS[0]; // Default fallback
|
|
7
11
|
let variant = product.variants[0];
|
|
8
12
|
// Determine variant and product from variantId
|
|
9
13
|
if (variantId) {
|
|
10
14
|
// Search through all products to find the one containing this variant
|
|
11
15
|
for (const p of MOCK_PRODUCTS) {
|
|
12
|
-
const foundVariant = p.variants.find(v => v._id === variantId);
|
|
16
|
+
const foundVariant = p.variants.find((v) => v._id === variantId);
|
|
13
17
|
if (foundVariant) {
|
|
14
18
|
product = p;
|
|
15
19
|
variant = foundVariant;
|
|
@@ -17,12 +21,20 @@ export const mockAddProductToCart = async (params) => {
|
|
|
17
21
|
}
|
|
18
22
|
}
|
|
19
23
|
}
|
|
24
|
+
// A measured variant carries a resolved unit; the host does the same. What the unit can
|
|
25
|
+
// express is its precision — 1.5 of a kilogram is a sale, 1.5 of a piece is a typo.
|
|
26
|
+
const unit = variant.unitId ? resolveUnit(variant.unitId) : undefined;
|
|
27
|
+
if (!isValidQuantity(quantity, unit ?? { unitId: 'piece', ratioToBase: 1, precision: 0 })) {
|
|
28
|
+
throw new Error(unit
|
|
29
|
+
? `${quantity} is finer than ${unit.abbreviation} can express (${unit.precision} decimals)`
|
|
30
|
+
: `${product.name} is sold by the piece, so ${quantity} is not a quantity it can sell in`);
|
|
31
|
+
}
|
|
20
32
|
// Add to MOCK_CART
|
|
21
|
-
const internalId = product._id +
|
|
33
|
+
const internalId = product._id + '_' + Date.now();
|
|
22
34
|
// Process optional fields for mock
|
|
23
35
|
let note = undefined;
|
|
24
36
|
if (params?.notes) {
|
|
25
|
-
note = Array.isArray(params.notes) ? params.notes.join(
|
|
37
|
+
note = Array.isArray(params.notes) ? params.notes.join(', ') : params.notes;
|
|
26
38
|
}
|
|
27
39
|
const activeProduct = {
|
|
28
40
|
...product,
|
|
@@ -33,16 +45,18 @@ export const mockAddProductToCart = async (params) => {
|
|
|
33
45
|
price: Number(variant.price),
|
|
34
46
|
taxTableId: product.taxTable,
|
|
35
47
|
stock: variant.inventory?.[0]?.stock || 0,
|
|
48
|
+
...(unit ? { unit } : {}),
|
|
36
49
|
images: product.images || [],
|
|
37
50
|
localQuantity: quantity,
|
|
38
51
|
sku: variant.sku,
|
|
39
|
-
attributes: variant.attributes.map(a => `${a.name}: ${a.value}`).join(
|
|
40
|
-
note: note
|
|
52
|
+
attributes: variant.attributes.map((a) => `${a.name}: ${a.value}`).join(', '),
|
|
53
|
+
note: note,
|
|
41
54
|
// discount/fee could be added here to mock object if CFActiveProduct supports it
|
|
42
55
|
};
|
|
43
56
|
MOCK_CART.products.push(activeProduct);
|
|
44
|
-
// Recalculate totals
|
|
45
|
-
|
|
57
|
+
// Recalculate totals. extendPrice, not a raw multiply: a fractional quantity times an
|
|
58
|
+
// integer-minor price leaves fractional cents, which no order may carry.
|
|
59
|
+
const lineTotal = extendPrice(activeProduct.price, quantity);
|
|
46
60
|
MOCK_CART.subtotal += lineTotal;
|
|
47
61
|
// Simple total calculation (ignoring taxes/fees for mock simplicity unless needed)
|
|
48
62
|
MOCK_CART.total += lineTotal;
|
|
@@ -57,6 +71,6 @@ export const mockAddProductToCart = async (params) => {
|
|
|
57
71
|
internalId: activeProduct.internalId,
|
|
58
72
|
name: activeProduct.name,
|
|
59
73
|
quantity: quantity,
|
|
60
|
-
timestamp: new Date().toISOString()
|
|
74
|
+
timestamp: new Date().toISOString(),
|
|
61
75
|
};
|
|
62
76
|
};
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import type { AddProductDiscountParams } from
|
|
2
|
-
import type { AddProductFeeParams } from
|
|
1
|
+
import type { AddProductDiscountParams } from '../add-product-discount/types';
|
|
2
|
+
import type { AddProductFeeParams } from '../add-product-fee/types';
|
|
3
3
|
export interface AddProductToCartParams {
|
|
4
4
|
/** ID of the variant to add. */
|
|
5
5
|
variantId: string;
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Defaults to 1. **May be fractional** — a variant sold by weight, volume or length is priced
|
|
8
|
+
* per its own unit and keyed in that unit, so `0.456` kg is a quantity, not a typo.
|
|
9
|
+
*
|
|
10
|
+
* How many decimals are allowed is the variant's own business: `variant.unit.precision` —
|
|
11
|
+
* `3` for a litre, `0` for anything sold by the piece. The engine refuses a quantity finer
|
|
12
|
+
* than that and says which unit it was measured against; do not round, floor or clamp before
|
|
13
|
+
* sending, and never key a quantity in base units to work around it (a per-100g price with a
|
|
14
|
+
* gram count is explicitly not supported).
|
|
15
|
+
*
|
|
16
|
+
* A quantity field should take its step and its decimal count from `variant.unit.precision`,
|
|
17
|
+
* not from a constant.
|
|
18
|
+
*/
|
|
7
19
|
quantity?: number;
|
|
8
20
|
/** Array of discounts to apply immediately. */
|
|
9
21
|
discounts?: AddProductDiscountParams[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AdjustInventory } from
|
|
1
|
+
import { AdjustInventory } from './types';
|
|
2
2
|
export declare const mockAdjustInventory: AdjustInventory;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { MOCK_PRODUCTS } from
|
|
1
|
+
import { MOCK_PRODUCTS } from '../../demo/database';
|
|
2
2
|
export const mockAdjustInventory = async (params) => {
|
|
3
|
-
console.log(
|
|
3
|
+
console.log('[Mock] adjustInventory called', params);
|
|
4
4
|
let newStock = 0;
|
|
5
5
|
// Real handler keys off variantId (falling back to the active product's
|
|
6
6
|
// variant); mirror that here by finding the owning product for the variant.
|
|
@@ -8,7 +8,7 @@ export const mockAdjustInventory = async (params) => {
|
|
|
8
8
|
const variantId = params.variantId;
|
|
9
9
|
let variant;
|
|
10
10
|
for (const product of MOCK_PRODUCTS) {
|
|
11
|
-
const match = product.variants.find(v => v._id === variantId);
|
|
11
|
+
const match = product.variants.find((v) => v._id === variantId);
|
|
12
12
|
if (match) {
|
|
13
13
|
variant = match;
|
|
14
14
|
break;
|
|
@@ -32,9 +32,9 @@ export const mockAdjustInventory = async (params) => {
|
|
|
32
32
|
}
|
|
33
33
|
return {
|
|
34
34
|
success: true,
|
|
35
|
-
amount: params?.amount ||
|
|
36
|
-
stockType: params?.stockType ||
|
|
35
|
+
amount: params?.amount || '0',
|
|
36
|
+
stockType: params?.stockType || 'set',
|
|
37
37
|
newStock,
|
|
38
|
-
timestamp: new Date().toISOString()
|
|
38
|
+
timestamp: new Date().toISOString(),
|
|
39
39
|
};
|
|
40
40
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApplyTransitionParams, ApplyTransitionResponse } from
|
|
1
|
+
import type { ApplyTransitionParams, ApplyTransitionResponse } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Mock implementation: applies all transitions except a known-invalid one
|
|
4
4
|
* so the demo app can show both successful and blocked responses.
|
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export const applyTransitionMock = (params) => {
|
|
6
6
|
const { targetFulfillmentState } = params;
|
|
7
|
-
if (targetFulfillmentState ===
|
|
7
|
+
if (targetFulfillmentState === 'cancelled') {
|
|
8
8
|
return Promise.resolve({
|
|
9
9
|
result: {
|
|
10
10
|
allowed: false,
|
|
11
|
-
blockedBy:
|
|
12
|
-
guard:
|
|
13
|
-
reason:
|
|
14
|
-
}
|
|
11
|
+
blockedBy: 'condition',
|
|
12
|
+
guard: 'no-cancel-open-order',
|
|
13
|
+
reason: 'Cannot cancel an order with open items',
|
|
14
|
+
},
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
return Promise.resolve({
|
|
18
18
|
result: { allowed: true },
|
|
19
|
-
from: { payment:
|
|
20
|
-
to: { payment:
|
|
21
|
-
displayState: `unpaid / ${targetFulfillmentState}
|
|
19
|
+
from: { payment: 'unpaid', fulfillment: 'draft' },
|
|
20
|
+
to: { payment: 'unpaid', fulfillment: targetFulfillmentState },
|
|
21
|
+
displayState: `unpaid / ${targetFulfillmentState}`,
|
|
22
22
|
});
|
|
23
23
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CFStatePair, CFTransitionResult } from
|
|
1
|
+
import type { CFStatePair, CFTransitionResult } from '../../common-types/order-state';
|
|
2
2
|
export interface ApplyTransitionParams {
|
|
3
3
|
/** Order to transition. Omit to target the active order — materializing one from the live cart if none exists. */
|
|
4
4
|
orderId?: string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AuthenticateUser } from
|
|
1
|
+
import { AuthenticateUser } from './types';
|
|
2
2
|
export declare const mockAuthenticateUser: AuthenticateUser;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export const mockAuthenticateUser = async (params) => {
|
|
2
|
-
console.log(
|
|
2
|
+
console.log('[Mock] authenticateUser called', params);
|
|
3
3
|
// Headless path requires both userId and pin together (mirrors the handler guard).
|
|
4
4
|
if ((params?.userId || params?.pin) && !(params?.userId && params?.pin)) {
|
|
5
|
-
throw new Error(
|
|
5
|
+
throw new Error('userId and pin must be provided together');
|
|
6
6
|
}
|
|
7
7
|
return {
|
|
8
8
|
success: true,
|
|
9
9
|
roleIds: params?.roleIds || [],
|
|
10
|
-
timestamp: new Date().toISOString()
|
|
10
|
+
timestamp: new Date().toISOString(),
|
|
11
11
|
};
|
|
12
12
|
};
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* Calculate refund total action
|
|
3
3
|
* Calls the calculateRefundTotal action on the parent window
|
|
4
4
|
*/
|
|
5
|
-
import type { CalculateRefundTotal } from
|
|
5
|
+
import type { CalculateRefundTotal } from './types';
|
|
6
6
|
export declare const calculateRefundTotal: CalculateRefundTotal;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Calculate refund total action
|
|
3
3
|
* Calls the calculateRefundTotal action on the parent window
|
|
4
4
|
*/
|
|
5
|
-
import { commandFrameClient } from
|
|
5
|
+
import { commandFrameClient } from '../../client';
|
|
6
6
|
export const calculateRefundTotal = async (params) => {
|
|
7
|
-
return await commandFrameClient.call(
|
|
7
|
+
return await commandFrameClient.call('calculateRefundTotal', params);
|
|
8
8
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CFStatePair, CFTransitionResult } from
|
|
1
|
+
import type { CFStatePair, CFTransitionResult } from '../../common-types/order-state';
|
|
2
2
|
export interface CanTransitionParams {
|
|
3
3
|
/** Order to evaluate. Defaults to the active order; if there is no active order (or none matches), evaluates as a brand-new order (from = null). */
|
|
4
4
|
orderId?: string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CashPayment } from
|
|
1
|
+
import { CashPayment } from './types';
|
|
2
2
|
export declare const mockCashPayment: CashPayment;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { applyMockPayment, MOCK_CART } from
|
|
1
|
+
import { applyMockPayment, MOCK_CART } from '../../demo/database';
|
|
2
2
|
export const mockCashPayment = async (params) => {
|
|
3
|
-
console.log(
|
|
3
|
+
console.log('[Mock] cashPayment called', params);
|
|
4
4
|
const fail = (reason) => {
|
|
5
5
|
console.warn(`[Mock] cashPayment rejected: ${reason}`);
|
|
6
6
|
return {
|
|
@@ -9,17 +9,17 @@ export const mockCashPayment = async (params) => {
|
|
|
9
9
|
openChangeCalculator: params?.openChangeCalculator ?? false,
|
|
10
10
|
change: 0,
|
|
11
11
|
cashRounding: 0,
|
|
12
|
-
paymentType:
|
|
12
|
+
paymentType: 'cash',
|
|
13
13
|
order: null,
|
|
14
14
|
timestamp: new Date().toISOString(),
|
|
15
15
|
saleFinalized: false,
|
|
16
|
-
remainingBalance: 0
|
|
16
|
+
remainingBalance: 0,
|
|
17
17
|
};
|
|
18
18
|
};
|
|
19
19
|
// Contract: `amount` is required, integer MINOR currency units — same
|
|
20
20
|
// scale as the mock cart's totals, so comparisons are direct.
|
|
21
21
|
if (params?.amount === undefined || params.amount === null) {
|
|
22
|
-
return fail(
|
|
22
|
+
return fail('amount is required (integer minor currency units)');
|
|
23
23
|
}
|
|
24
24
|
const balanceDue = MOCK_CART.amountToBeCharged ?? MOCK_CART.total;
|
|
25
25
|
if (params.amount > balanceDue) {
|
|
@@ -38,16 +38,16 @@ export const mockCashPayment = async (params) => {
|
|
|
38
38
|
try {
|
|
39
39
|
const input = window.prompt(`Amount due: $${(params.amount / 100).toFixed(2)}\nEnter amount tendered (dollars):`, (params.amount / 100).toFixed(2));
|
|
40
40
|
if (input === null)
|
|
41
|
-
return fail(
|
|
41
|
+
return fail('cancelled');
|
|
42
42
|
const tendered = parseFloat(input);
|
|
43
43
|
if (!isNaN(tendered))
|
|
44
44
|
change = Math.round(tendered * 100) - params.amount;
|
|
45
45
|
}
|
|
46
46
|
catch (e) {
|
|
47
|
-
console.warn(
|
|
47
|
+
console.warn('Could not open prompt/alert (possibly in non-interactive environment)', e);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
const order = applyMockPayment(params.amount,
|
|
50
|
+
const order = applyMockPayment(params.amount, 'cash', 'cash');
|
|
51
51
|
return {
|
|
52
52
|
success: true,
|
|
53
53
|
amount: params.amount,
|
|
@@ -55,10 +55,10 @@ export const mockCashPayment = async (params) => {
|
|
|
55
55
|
change,
|
|
56
56
|
tenderedAmount: params.tenderedAmount,
|
|
57
57
|
cashRounding: 0,
|
|
58
|
-
paymentType:
|
|
58
|
+
paymentType: 'cash',
|
|
59
59
|
order,
|
|
60
60
|
timestamp: new Date().toISOString(),
|
|
61
61
|
saleFinalized: true,
|
|
62
|
-
remainingBalance: 0
|
|
62
|
+
remainingBalance: 0,
|
|
63
63
|
};
|
|
64
64
|
};
|
|
@@ -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,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
|
+
};
|