@final-commerce/command-frame 0.7.0-staging.2 → 0.7.0-staging.4
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/actions/add-booking-to-cart/mock.js +39 -3
- 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/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/remove-booking-from-cart/mock.js +14 -4
- package/dist/demo/database.d.ts +4 -0
- package/dist/demo/database.js +39 -5
- 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
|
@@ -1,8 +1,44 @@
|
|
|
1
|
+
import { ReservationStatus } from '@final-commerce/common';
|
|
2
|
+
import { MOCK_CART, MOCK_PRODUCTS, mockPublishEvent } from '../../demo/database';
|
|
1
3
|
import { mockHoldBooking } from '../hold-booking/mock';
|
|
2
4
|
export const mockAddBookingToCart = async (params) => {
|
|
3
5
|
console.log('[Mock] addBookingToCart called', params);
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
+
// One call, two effects — the window is claimed AND the service is in the cart. The claim goes
|
|
7
|
+
// first and its refusal propagates: a cart line for a window somebody else took is worse than
|
|
8
|
+
// no line at all.
|
|
6
9
|
const { booking } = await mockHoldBooking(params);
|
|
7
|
-
|
|
10
|
+
const product = MOCK_PRODUCTS.find(({ _id }) => _id === params.productId);
|
|
11
|
+
const variant = params.variantId
|
|
12
|
+
? product?.variants?.find(({ _id }) => _id === params.variantId)
|
|
13
|
+
: product?.variants?.[0];
|
|
14
|
+
const price = variant?.price ?? product?.minPrice ?? 0;
|
|
15
|
+
const reservation = {
|
|
16
|
+
internalId: `res_${booking.id}`,
|
|
17
|
+
bookingId: booking.id,
|
|
18
|
+
productId: params.productId,
|
|
19
|
+
variantId: variant?._id ?? null,
|
|
20
|
+
resourceId: params.resourceId,
|
|
21
|
+
name: product?.name ?? 'Booking',
|
|
22
|
+
resourceName: booking.resourceName,
|
|
23
|
+
price,
|
|
24
|
+
quantity: 1,
|
|
25
|
+
total: price,
|
|
26
|
+
taxTableId: product?.taxTable,
|
|
27
|
+
startAt: booking.startAt,
|
|
28
|
+
endAt: booking.endAt,
|
|
29
|
+
bufferEndAt: booking.bufferEndAt,
|
|
30
|
+
status: ReservationStatus.HELD,
|
|
31
|
+
expiresAt: booking.expiresAt,
|
|
32
|
+
};
|
|
33
|
+
if (!MOCK_CART.reservations)
|
|
34
|
+
MOCK_CART.reservations = [];
|
|
35
|
+
MOCK_CART.reservations.push(reservation);
|
|
36
|
+
MOCK_CART.subtotal += price;
|
|
37
|
+
MOCK_CART.total += price;
|
|
38
|
+
MOCK_CART.amountToBeCharged = MOCK_CART.total;
|
|
39
|
+
MOCK_CART.remainingBalance = MOCK_CART.total;
|
|
40
|
+
// The cart topic, not the bookings one: a screen that only watches bookings still has to
|
|
41
|
+
// repaint its cart, and every other cart mutation announces itself the same way.
|
|
42
|
+
mockPublishEvent('cart', 'reservation-added', { reservation });
|
|
43
|
+
return { booking, reservationInternalId: reservation.internalId, timestamp: new Date().toISOString() };
|
|
8
44
|
};
|
|
@@ -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
|
+
};
|
|
@@ -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,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,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 {};
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import { MOCK_BOOKINGS } from '../../demo/database';
|
|
1
|
+
import { MOCK_BOOKINGS, MOCK_CART, mockPublishEvent } from '../../demo/database';
|
|
2
2
|
export const mockRemoveBookingFromCart = async (params) => {
|
|
3
3
|
console.log('[Mock] removeBookingFromCart called', params);
|
|
4
|
+
const reservations = MOCK_CART.reservations ?? [];
|
|
5
|
+
const cartIndex = reservations.findIndex(({ internalId }) => internalId === params.reservationInternalId);
|
|
6
|
+
const [removed] = cartIndex >= 0 ? reservations.splice(cartIndex, 1) : [];
|
|
7
|
+
if (removed) {
|
|
8
|
+
const line = removed.total || removed.price * (removed.quantity ?? 1);
|
|
9
|
+
MOCK_CART.subtotal -= line;
|
|
10
|
+
MOCK_CART.total -= line;
|
|
11
|
+
MOCK_CART.amountToBeCharged = MOCK_CART.total;
|
|
12
|
+
MOCK_CART.remainingBalance = MOCK_CART.total;
|
|
13
|
+
mockPublishEvent('cart', 'reservation-removed', { reservation: removed });
|
|
14
|
+
}
|
|
4
15
|
// The hold has to go back, or the mock is a trap: `addBookingToCart` pushes into the very list
|
|
5
16
|
// availability reads, so add → remove → add the same slot used to refuse forever with "that
|
|
6
|
-
// window has just been taken". The reservation id
|
|
7
|
-
|
|
8
|
-
const bookingId = params.reservationInternalId.replace(/^res_/, '');
|
|
17
|
+
// window has just been taken". The reservation id is `res_<booking.id>`, the link back to the row.
|
|
18
|
+
const bookingId = removed?.bookingId ?? params.reservationInternalId.replace(/^res_/, '');
|
|
9
19
|
const index = MOCK_BOOKINGS.findIndex(({ id }) => id === bookingId);
|
|
10
20
|
const [released] = index >= 0 ? MOCK_BOOKINGS.splice(index, 1) : [];
|
|
11
21
|
return {
|
package/dist/demo/database.d.ts
CHANGED
|
@@ -15,6 +15,10 @@ export interface MockDatabaseConfig {
|
|
|
15
15
|
products?: CFProduct[];
|
|
16
16
|
orders?: CFActiveOrder[];
|
|
17
17
|
parkedOrders?: CFActiveOrder[];
|
|
18
|
+
/** Who is scarce: the staff, rooms or machines the dataset's services are booked against. */
|
|
19
|
+
bookingResources?: CFBookingResource[];
|
|
20
|
+
/** Windows already taken when the dataset loads, so a calendar does not open empty. */
|
|
21
|
+
bookings?: CFBooking[];
|
|
18
22
|
}
|
|
19
23
|
export declare const MOCK_COMPANY: CFActiveCompany;
|
|
20
24
|
export declare const MOCK_OUTLET_MAIN: CFActiveOutlet;
|
package/dist/demo/database.js
CHANGED
|
@@ -688,6 +688,7 @@ export let MOCK_CART = {
|
|
|
688
688
|
remainingBalance: 0,
|
|
689
689
|
products: [],
|
|
690
690
|
customSales: [],
|
|
691
|
+
reservations: [],
|
|
691
692
|
nonRevenueItems: [],
|
|
692
693
|
customer: null,
|
|
693
694
|
};
|
|
@@ -708,6 +709,7 @@ export const resetMockCart = () => {
|
|
|
708
709
|
remainingBalance: 0,
|
|
709
710
|
products: [],
|
|
710
711
|
customSales: [],
|
|
712
|
+
reservations: [],
|
|
711
713
|
nonRevenueItems: [],
|
|
712
714
|
customer: null,
|
|
713
715
|
};
|
|
@@ -752,6 +754,12 @@ export function setMockDatabase(config) {
|
|
|
752
754
|
if (config.parkedOrders !== undefined) {
|
|
753
755
|
MOCK_PARKED_ORDERS.splice(0, MOCK_PARKED_ORDERS.length, ...config.parkedOrders);
|
|
754
756
|
}
|
|
757
|
+
if (config.bookingResources !== undefined) {
|
|
758
|
+
MOCK_BOOKING_RESOURCES.splice(0, MOCK_BOOKING_RESOURCES.length, ...config.bookingResources);
|
|
759
|
+
}
|
|
760
|
+
if (config.bookings !== undefined) {
|
|
761
|
+
MOCK_BOOKINGS.splice(0, MOCK_BOOKINGS.length, ...config.bookings);
|
|
762
|
+
}
|
|
755
763
|
if (MOCK_OUTLETS.length > 0) {
|
|
756
764
|
Object.assign(MOCK_OUTLET_MAIN, MOCK_OUTLETS[0]);
|
|
757
765
|
if (MOCK_OUTLET_MAIN.id === undefined && MOCK_OUTLET_MAIN._id !== undefined) {
|
|
@@ -888,6 +896,30 @@ export const createOrderFromCart = (paymentType, amount, processor = 'cash') =>
|
|
|
888
896
|
billing: MOCK_CART.customer?.billing || null,
|
|
889
897
|
shipping: MOCK_CART.customer?.shipping || null,
|
|
890
898
|
lineItems,
|
|
899
|
+
// A paid booking is a sale: it belongs on the order (and thus the receipt),
|
|
900
|
+
// in its own array rather than among the line items, and CONFIRMED — payment
|
|
901
|
+
// is what turns a held window into a kept appointment.
|
|
902
|
+
reservations: (MOCK_CART.reservations ?? []).map((reservation) => ({
|
|
903
|
+
internalId: reservation.internalId,
|
|
904
|
+
bookingId: reservation.bookingId,
|
|
905
|
+
productId: reservation.productId,
|
|
906
|
+
variantId: reservation.variantId,
|
|
907
|
+
resourceId: reservation.resourceId,
|
|
908
|
+
name: reservation.name,
|
|
909
|
+
resourceName: reservation.resourceName,
|
|
910
|
+
price: reservation.price,
|
|
911
|
+
quantity: reservation.quantity,
|
|
912
|
+
total: reservation.total,
|
|
913
|
+
taxTableId: reservation.taxTableId,
|
|
914
|
+
startAt: reservation.startAt,
|
|
915
|
+
endAt: reservation.endAt,
|
|
916
|
+
bufferEndAt: reservation.bufferEndAt,
|
|
917
|
+
// The mock computes no tax anywhere — line items ship `taxes: []` too.
|
|
918
|
+
taxes: [],
|
|
919
|
+
// `expiresAt` deliberately does not come along: a paid booking has no
|
|
920
|
+
// clock left to run out, which is what CONFIRMED means.
|
|
921
|
+
status: ReservationStatus.CONFIRMED,
|
|
922
|
+
})),
|
|
891
923
|
customSales: [],
|
|
892
924
|
balance: 0,
|
|
893
925
|
user: employeeUser,
|
|
@@ -935,9 +967,11 @@ const SLOT_MINUTES = 30;
|
|
|
935
967
|
const BUFFER_MINUTES = 5;
|
|
936
968
|
const OPEN_HOUR = 9;
|
|
937
969
|
const CLOSE_HOUR = 18;
|
|
970
|
+
// Rooms, not people: a resource is whatever is scarce, and a room is the case that
|
|
971
|
+
// reads the same in every vertical a dataset might describe.
|
|
938
972
|
export const MOCK_BOOKING_RESOURCES = [
|
|
939
|
-
{ id: '
|
|
940
|
-
{ id: '
|
|
973
|
+
{ id: 'res_room_1', name: 'Room 1', kind: BookingResourceKind.ROOM },
|
|
974
|
+
{ id: 'res_room_2', name: 'Room 2', kind: BookingResourceKind.ROOM },
|
|
941
975
|
];
|
|
942
976
|
const at = (dayOffset, hour, minute = 0) => {
|
|
943
977
|
const date = new Date();
|
|
@@ -958,9 +992,9 @@ const booking = (id, resourceId, start, status, customerName) => ({
|
|
|
958
992
|
customerName,
|
|
959
993
|
});
|
|
960
994
|
export const MOCK_BOOKINGS = [
|
|
961
|
-
booking('bk_1', '
|
|
962
|
-
booking('bk_2', '
|
|
963
|
-
booking('bk_3', '
|
|
995
|
+
booking('bk_1', 'res_room_1', at(0, 10), ReservationStatus.CONFIRMED, 'Alex Green'),
|
|
996
|
+
booking('bk_2', 'res_room_2', at(0, 11, 30), ReservationStatus.CONFIRMED, 'Dana White'),
|
|
997
|
+
booking('bk_3', 'res_room_1', at(1, 9, 30), ReservationStatus.CONFIRMED, 'Sam Blue'),
|
|
964
998
|
];
|
|
965
999
|
/** Live = confirmed, or held and not yet expired. An expired hold occupies nothing. */
|
|
966
1000
|
export const mockLiveBookings = () => MOCK_BOOKINGS.filter(({ status, expiresAt }) => status === ReservationStatus.CONFIRMED ||
|
package/dist/index.d.ts
CHANGED
|
@@ -48,6 +48,8 @@ export declare const command: {
|
|
|
48
48
|
readonly extensionPayment: import(".").ExtensionPayment;
|
|
49
49
|
readonly redeemPayment: import(".").RedeemPayment;
|
|
50
50
|
readonly integrationPayment: import(".").IntegrationPayment;
|
|
51
|
+
readonly createPaymentLink: import(".").CreatePaymentLink;
|
|
52
|
+
readonly chargeMoto: import(".").ChargeMoto;
|
|
51
53
|
readonly addCustomerNote: import(".").AddCustomerNote;
|
|
52
54
|
readonly removeCustomerNote: import(".").RemoveCustomerNote;
|
|
53
55
|
readonly removeCustomerFromCart: import(".").RemoveCustomerFromCart;
|
|
@@ -116,6 +118,7 @@ export declare const command: {
|
|
|
116
118
|
readonly uploadMedia: import(".").UploadMedia;
|
|
117
119
|
readonly getTaxTables: import(".").GetTaxTables;
|
|
118
120
|
readonly getBranding: import(".").GetBranding;
|
|
121
|
+
readonly getTimeClockStatus: import(".").GetTimeClockStatus;
|
|
119
122
|
readonly canTransition: import(".").CanTransition;
|
|
120
123
|
readonly getAvailableTransitions: import(".").GetAvailableTransitions;
|
|
121
124
|
readonly applyTransition: import(".").ApplyTransition;
|
|
@@ -187,6 +190,8 @@ export type { TerminalPayment, TerminalPaymentParams, TerminalPaymentResponse }
|
|
|
187
190
|
export type { ExtensionPayment, ExtensionPaymentParams, ExtensionPaymentResponse, } from './actions/extension-payment/types';
|
|
188
191
|
export type { RedeemPayment, RedeemPaymentParams, RedeemPaymentResponse } from './actions/redeem-payment/types';
|
|
189
192
|
export type { IntegrationPayment, IntegrationPaymentParams, IntegrationPaymentResponse, IntegrationEmvData, } from './actions/integration-payment/types';
|
|
193
|
+
export type { CreatePaymentLink, CreatePaymentLinkParams, CreatePaymentLinkResponse, } from './actions/create-payment-link/types';
|
|
194
|
+
export type { ChargeMoto, ChargeMotoParams, ChargeMotoResponse, ChargeMotoCardFields, } from './actions/charge-moto/types';
|
|
190
195
|
export type { AddCustomerNote, AddCustomerNoteParams, AddCustomerNoteResponse, } from './actions/add-customer-note/types';
|
|
191
196
|
export type { RemoveCustomerNote, RemoveCustomerNoteParams, RemoveCustomerNoteResponse, } from './actions/remove-customer-note/types';
|
|
192
197
|
export type { RemoveCustomerFromCart, RemoveCustomerFromCartResponse } from './actions/remove-customer-from-cart/types';
|
|
@@ -200,6 +205,7 @@ export type { GetMedia, GetMediaParams, GetMediaResponse, MediaItemPayload } fro
|
|
|
200
205
|
export type { UploadMedia, UploadMediaParams, UploadMediaResponse } from './actions/upload-media/types';
|
|
201
206
|
export type { GetTaxTables, GetTaxTablesResponse, TaxRatePayload, TaxTablePayload, } from './actions/get-tax-tables/types';
|
|
202
207
|
export type { BorderRadiusPreset, GetBranding, GetBrandingResponse } from './actions/get-branding/types';
|
|
208
|
+
export type { GetTimeClockStatus, GetTimeClockStatusResponse, TimeClockEntry, } from './actions/get-time-clock-status/types';
|
|
203
209
|
export type { ShowConfirmation, ShowConfirmationParams, ShowConfirmationResponse, } from './actions/show-confirmation/types';
|
|
204
210
|
export type { AuthenticateUser, AuthenticateUserParams, AuthenticateUserResponse, } from './actions/authenticate-user/types';
|
|
205
211
|
export type { PartialPayment, PartialPaymentParams, PartialPaymentResponse } from './actions/partial-payment/types';
|
package/dist/index.js
CHANGED
|
@@ -48,6 +48,8 @@ import { terminalPayment } from './actions/terminal-payment/action';
|
|
|
48
48
|
import { extensionPayment } from './actions/extension-payment/action';
|
|
49
49
|
import { redeemPayment } from './actions/redeem-payment/action';
|
|
50
50
|
import { integrationPayment } from './actions/integration-payment/action';
|
|
51
|
+
import { createPaymentLink } from './actions/create-payment-link/action';
|
|
52
|
+
import { chargeMoto } from './actions/charge-moto/action';
|
|
51
53
|
// Customer Actions
|
|
52
54
|
import { addCustomerNote } from './actions/add-customer-note/action';
|
|
53
55
|
import { removeCustomerNote } from './actions/remove-customer-note/action';
|
|
@@ -135,6 +137,8 @@ import { getMedia } from './actions/get-media/action';
|
|
|
135
137
|
import { uploadMedia } from './actions/upload-media/action';
|
|
136
138
|
import { getTaxTables } from './actions/get-tax-tables/action';
|
|
137
139
|
import { getBranding } from './actions/get-branding/action';
|
|
140
|
+
// Time Clock Actions
|
|
141
|
+
import { getTimeClockStatus } from './actions/get-time-clock-status/action';
|
|
138
142
|
// Export actions as command object
|
|
139
143
|
export const command = {
|
|
140
144
|
exampleFunction,
|
|
@@ -188,6 +192,8 @@ export const command = {
|
|
|
188
192
|
extensionPayment,
|
|
189
193
|
redeemPayment,
|
|
190
194
|
integrationPayment,
|
|
195
|
+
createPaymentLink,
|
|
196
|
+
chargeMoto,
|
|
191
197
|
// Customer Actions
|
|
192
198
|
addCustomerNote,
|
|
193
199
|
removeCustomerNote,
|
|
@@ -270,6 +276,8 @@ export const command = {
|
|
|
270
276
|
uploadMedia,
|
|
271
277
|
getTaxTables,
|
|
272
278
|
getBranding,
|
|
279
|
+
// Time Clock Actions
|
|
280
|
+
getTimeClockStatus,
|
|
273
281
|
// State Machine Queries
|
|
274
282
|
canTransition,
|
|
275
283
|
getAvailableTransitions,
|
|
@@ -29,6 +29,9 @@ import { mockAuthenticateUser } from '../../actions/authenticate-user/mock';
|
|
|
29
29
|
import { mockCalculateRefundTotal } from '../../actions/calculate-refund-total/mock';
|
|
30
30
|
import { mockCashPayment } from '../../actions/cash-payment/mock';
|
|
31
31
|
import { mockGetCashRoundingAmount } from '../../actions/get-cash-rounding-amount/mock';
|
|
32
|
+
import { mockCreatePaymentLink } from '../../actions/create-payment-link/mock';
|
|
33
|
+
import { mockChargeMoto } from '../../actions/charge-moto/mock';
|
|
34
|
+
import { mockGetTimeClockStatus } from '../../actions/get-time-clock-status/mock';
|
|
32
35
|
import { mockClearCart } from '../../actions/clear-cart/mock';
|
|
33
36
|
import { mockDeleteParkedOrder } from '../../actions/delete-parked-order/mock';
|
|
34
37
|
import { mockVoidOrder } from '../../actions/void-order/mock';
|
|
@@ -130,6 +133,8 @@ export const RENDER_MOCKS = {
|
|
|
130
133
|
calculateRefundTotal: mockCalculateRefundTotal,
|
|
131
134
|
cashPayment: mockCashPayment,
|
|
132
135
|
getCashRoundingAmount: mockGetCashRoundingAmount,
|
|
136
|
+
createPaymentLink: mockCreatePaymentLink,
|
|
137
|
+
chargeMoto: mockChargeMoto,
|
|
133
138
|
clearCart: mockClearCart,
|
|
134
139
|
deleteParkedOrder: mockDeleteParkedOrder,
|
|
135
140
|
voidOrder: mockVoidOrder,
|
|
@@ -213,4 +218,5 @@ export const RENDER_MOCKS = {
|
|
|
213
218
|
saveSmartGridLayout: mockSaveSmartGridLayout,
|
|
214
219
|
sendEmail: mockSendEmail,
|
|
215
220
|
sendSms: mockSendSms,
|
|
221
|
+
getTimeClockStatus: mockGetTimeClockStatus,
|
|
216
222
|
};
|
|
@@ -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, GetBookingResources, GetBookingAvailability, GetBookings, HoldBooking, AddBookingToCart, RemoveBookingFromCart, CancelBooking } 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, 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, GetBookingResources, GetBookingAvailability, GetBookings, HoldBooking, AddBookingToCart, RemoveBookingFromCart, CancelBooking, CreatePaymentLink, ChargeMoto, GetTimeClockStatus } from '../../index';
|
|
2
2
|
export interface RenderProviderActions {
|
|
3
3
|
exampleFunction: ExampleFunction;
|
|
4
4
|
getProducts: GetProducts;
|
|
@@ -48,6 +48,8 @@ export interface RenderProviderActions {
|
|
|
48
48
|
extensionPayment: ExtensionPayment;
|
|
49
49
|
redeemPayment: RedeemPayment;
|
|
50
50
|
integrationPayment: IntegrationPayment;
|
|
51
|
+
createPaymentLink: CreatePaymentLink;
|
|
52
|
+
chargeMoto: ChargeMoto;
|
|
51
53
|
addNonRevenueItem: AddNonRevenueItem;
|
|
52
54
|
addCustomerNote: AddCustomerNote;
|
|
53
55
|
removeCustomerNote: RemoveCustomerNote;
|
|
@@ -108,4 +110,5 @@ export interface RenderProviderActions {
|
|
|
108
110
|
saveSmartGridLayout: SaveSmartGridLayout;
|
|
109
111
|
sendEmail: SendEmail;
|
|
110
112
|
sendSms: SendSms;
|
|
113
|
+
getTimeClockStatus: GetTimeClockStatus;
|
|
111
114
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@final-commerce/command-frame",
|
|
3
|
-
"version": "0.7.0-staging.
|
|
3
|
+
"version": "0.7.0-staging.4",
|
|
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.3.0-staging.
|
|
60
|
+
"@final-commerce/common": "2.3.0-staging.3"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@commitlint/cli": "^19.0.0",
|