@aglyn/plugins-bookings 1.0.0-beta.230 → 1.0.0-beta.232
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/README.md +3 -1
- package/package.json +11 -11
- package/src/lib/components/bookings-console-page.js +30 -4
- package/src/lib/components/bookings-console-page.js.map +1 -1
- package/src/lib/declarations.console-server.d.ts +3 -0
- package/src/lib/declarations.console-server.js +20 -0
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/model/booking-events.d.ts +52 -0
- package/src/lib/model/booking-events.js +53 -0
- package/src/lib/model/booking-events.js.map +1 -0
- package/src/lib/model/booking-in-person.d.ts +72 -0
- package/src/lib/model/booking-in-person.js +71 -0
- package/src/lib/model/booking-in-person.js.map +1 -0
- package/src/lib/model/booking-manage.d.ts +73 -0
- package/src/lib/model/booking-manage.js +104 -0
- package/src/lib/model/booking-manage.js.map +1 -0
- package/src/lib/model/booking-view.d.ts +63 -0
- package/src/lib/model/booking-view.js +70 -0
- package/src/lib/model/booking-view.js.map +1 -0
- package/src/lib/server/api-v1/bookings.d.ts +33 -0
- package/src/lib/server/api-v1/bookings.js +103 -0
- package/src/lib/server/api-v1/bookings.js.map +1 -0
- package/src/lib/server/api-v1/openapi.d.ts +22 -0
- package/src/lib/server/api-v1/openapi.js +107 -0
- package/src/lib/server/api-v1/openapi.js.map +1 -0
- package/src/lib/server/billing-webhook.js +17 -0
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/booking-events.d.ts +40 -0
- package/src/lib/server/booking-events.js +50 -0
- package/src/lib/server/booking-events.js.map +1 -0
- package/src/lib/server/booking-manage.d.ts +93 -0
- package/src/lib/server/booking-manage.js +380 -0
- package/src/lib/server/booking-manage.js.map +1 -0
- package/src/lib/server/in-person-payment.d.ts +20 -0
- package/src/lib/server/in-person-payment.js +469 -0
- package/src/lib/server/in-person-payment.js.map +1 -0
- package/src/lib/server/refund.js +17 -1
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server.js +30 -2
- package/src/lib/server.js.map +1 -1
- package/src/lib/tenant-emails.js +59 -0
- package/src/lib/tenant-emails.js.map +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { definePluginDomainEvent } from "@aglyn/aglyn/plugin-manager/plugin-domain-events";
|
|
17
|
+
export const BOOKING_CREATED_EVENT = definePluginDomainEvent('booking.created');
|
|
18
|
+
export const BOOKING_RESCHEDULED_EVENT = definePluginDomainEvent('booking.rescheduled');
|
|
19
|
+
export const BOOKING_CANCELED_EVENT = definePluginDomainEvent('booking.canceled');
|
|
20
|
+
export const BOOKING_EVENT_DECLARATIONS = [
|
|
21
|
+
{
|
|
22
|
+
event: BOOKING_CREATED_EVENT,
|
|
23
|
+
label: 'Booking confirmed',
|
|
24
|
+
description: 'A booking was confirmed: a free one when it was made, a paid one when its payment landed.',
|
|
25
|
+
payloadKeys: [
|
|
26
|
+
'booking'
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
event: BOOKING_RESCHEDULED_EVENT,
|
|
31
|
+
label: 'Booking rescheduled',
|
|
32
|
+
description: 'The site’s team moved a booking to another time, once per move.',
|
|
33
|
+
payloadKeys: [
|
|
34
|
+
'booking'
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
event: BOOKING_CANCELED_EVENT,
|
|
39
|
+
label: 'Booking canceled',
|
|
40
|
+
description: 'A booking was canceled: a free one by the site’s team, a paid one by a full refund.',
|
|
41
|
+
payloadKeys: [
|
|
42
|
+
'booking'
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
];
|
|
46
|
+
/**
|
|
47
|
+
* The key naming one occurrence, so the same fact raised twice is one event:
|
|
48
|
+
* a booking is confirmed once, canceled once, and moved once to each time.
|
|
49
|
+
*/ export function bookingEventKey(event, bookingId, startsAtMs) {
|
|
50
|
+
return event === 'booking.rescheduled' ? `${bookingId}@${Number(startsAtMs != null ? startsAtMs : 0)}` : bookingId;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=booking-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/bookings/src/lib/model/booking-events.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n definePluginDomainEvent,\n type PluginDomainEvent,\n type PluginDomainEventDeclaration,\n} from '@aglyn/aglyn/plugin-manager/plugin-domain-events'\nimport type { BookingView } from './booking-view'\n\n/**\n * The booking events the bookings plugin raises (AGL-3643), for other\n * plugins — the merchant's Zapier connections first — through core's\n * domain-event outbox: written when the fact is, delivered at least once,\n * retried with backoff. A subscriber names the event and never imports this\n * plugin.\n *\n * Each payload is `{ booking }` in the public API's shape\n * (`GET /v1/sites/{siteId}/bookings/{bookingId}`), as it stood once the fact\n * was written.\n *\n * - `booking.created`: a booking is confirmed — a free one when it is made,\n * a paid one when its payment lands. A checkout the guest never finished\n * raises nothing.\n * - `booking.rescheduled`: the site's team moved it to another time; one\n * event per move.\n * - `booking.canceled`: it was canceled — a free one by the site's team, a\n * paid one by the refund that gave all of the money back.\n */\nexport interface BookingEventPayload {\n booking: BookingView\n}\n\nexport const BOOKING_CREATED_EVENT = definePluginDomainEvent<BookingEventPayload>('booking.created')\nexport const BOOKING_RESCHEDULED_EVENT = definePluginDomainEvent<BookingEventPayload>('booking.rescheduled')\nexport const BOOKING_CANCELED_EVENT = definePluginDomainEvent<BookingEventPayload>('booking.canceled')\n\nexport type BookingEventName = 'booking.created' | 'booking.rescheduled' | 'booking.canceled'\n\nexport const BOOKING_EVENT_DECLARATIONS: ReadonlyArray<PluginDomainEventDeclaration & { event: PluginDomainEvent<unknown> }> = [\n {\n event: BOOKING_CREATED_EVENT,\n label: 'Booking confirmed',\n description: 'A booking was confirmed: a free one when it was made, a paid one when its payment landed.',\n payloadKeys: ['booking'],\n },\n {\n event: BOOKING_RESCHEDULED_EVENT,\n label: 'Booking rescheduled',\n description: 'The site’s team moved a booking to another time, once per move.',\n payloadKeys: ['booking'],\n },\n {\n event: BOOKING_CANCELED_EVENT,\n label: 'Booking canceled',\n description: 'A booking was canceled: a free one by the site’s team, a paid one by a full refund.',\n payloadKeys: ['booking'],\n },\n]\n\n/**\n * The key naming one occurrence, so the same fact raised twice is one event:\n * a booking is confirmed once, canceled once, and moved once to each time.\n */\nexport function bookingEventKey(event: BookingEventName, bookingId: string, startsAtMs?: number): string {\n return event === 'booking.rescheduled' ? `${bookingId}@${Number(startsAtMs ?? 0)}` : bookingId\n}\n"],"names":["definePluginDomainEvent","BOOKING_CREATED_EVENT","BOOKING_RESCHEDULED_EVENT","BOOKING_CANCELED_EVENT","BOOKING_EVENT_DECLARATIONS","event","label","description","payloadKeys","bookingEventKey","bookingId","startsAtMs","Number"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,uBAAuB,QAGlB,mDAAkD;AA0BzD,OAAO,MAAMC,wBAAwBD,wBAA6C,mBAAkB;AACpG,OAAO,MAAME,4BAA4BF,wBAA6C,uBAAsB;AAC5G,OAAO,MAAMG,yBAAyBH,wBAA6C,oBAAmB;AAItG,OAAO,MAAMI,6BAAkH;IAC7H;QACEC,OAAOJ;QACPK,OAAO;QACPC,aAAa;QACbC,aAAa;YAAC;SAAU;IAC1B;IACA;QACEH,OAAOH;QACPI,OAAO;QACPC,aAAa;QACbC,aAAa;YAAC;SAAU;IAC1B;IACA;QACEH,OAAOF;QACPG,OAAO;QACPC,aAAa;QACbC,aAAa;YAAC;SAAU;IAC1B;CACD,CAAA;AAED;;;CAGC,GACD,OAAO,SAASC,gBAAgBJ,KAAuB,EAAEK,SAAiB,EAAEC,UAAmB;IAC7F,OAAON,UAAU,wBAAwB,GAAGK,UAAU,CAAC,EAAEE,OAAOD,qBAAAA,aAAc,IAAI,GAAGD;AACvF"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/** The in-person payment a booking carries while it is being taken. */
|
|
18
|
+
export interface BookingInPersonPayment {
|
|
19
|
+
paymentIntentId: string;
|
|
20
|
+
/** The whole charge: service plus tax. */
|
|
21
|
+
amountCents: number;
|
|
22
|
+
/** The service amount staff typed, before tax. */
|
|
23
|
+
serviceCents: number;
|
|
24
|
+
taxCents: number;
|
|
25
|
+
feeCents: number;
|
|
26
|
+
status: 'pending' | 'paid' | 'canceled';
|
|
27
|
+
startedAtMs: number;
|
|
28
|
+
startedBy: string;
|
|
29
|
+
paidAtMs?: number;
|
|
30
|
+
/** Which start request made it, so a retried start returns it again. */
|
|
31
|
+
attempt?: string;
|
|
32
|
+
}
|
|
33
|
+
/** The fields of a booking this reads; any booking document has them. */
|
|
34
|
+
export interface BookingPaymentFields {
|
|
35
|
+
status?: string;
|
|
36
|
+
expiresAtMs?: number;
|
|
37
|
+
paidAmountCents?: number;
|
|
38
|
+
refundedCents?: number;
|
|
39
|
+
paymentIntentId?: string;
|
|
40
|
+
inPersonPayment?: Partial<BookingInPersonPayment> | null;
|
|
41
|
+
}
|
|
42
|
+
export type BookingInPersonState =
|
|
43
|
+
/** Paid already, online or in person. */
|
|
44
|
+
'paid'
|
|
45
|
+
/** Canceled: nothing to pay. */
|
|
46
|
+
| 'canceled'
|
|
47
|
+
/** Still holding for its online Checkout: the customer is paying there. */
|
|
48
|
+
| 'awaiting-online'
|
|
49
|
+
/** A card is being taken for it now. */
|
|
50
|
+
| 'collecting'
|
|
51
|
+
/** Can be paid at the counter. */
|
|
52
|
+
| 'payable';
|
|
53
|
+
export declare function bookingInPersonState(booking: BookingPaymentFields, nowMs?: number): BookingInPersonState;
|
|
54
|
+
/** The most one in-person booking charge may be: $10,000. */
|
|
55
|
+
export declare const BOOKING_IN_PERSON_MAX_CENTS = 1000000;
|
|
56
|
+
/** Stripe's card minimum in USD. */
|
|
57
|
+
export declare const BOOKING_IN_PERSON_MIN_CENTS = 50;
|
|
58
|
+
/** Null when `serviceCents` is an amount staff may charge; otherwise why not. */
|
|
59
|
+
export declare function bookingInPersonAmountProblem(serviceCents: unknown): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* The amount to suggest: the service's fixed price, when it has one. A
|
|
62
|
+
* `varies`, `estimate` or `contact` price suggests nothing; staff type it.
|
|
63
|
+
*/
|
|
64
|
+
export declare function bookingSuggestedCents(service: {
|
|
65
|
+
priceUsd?: number;
|
|
66
|
+
priceDisplay?: string;
|
|
67
|
+
} | null | undefined): number | null;
|
|
68
|
+
/** The start and end of the local day that holds `atMs`, in milliseconds. */
|
|
69
|
+
export declare function bookingDayRange(atMs: number, offsetMinutes?: number): {
|
|
70
|
+
startMs: number;
|
|
71
|
+
endMs: number;
|
|
72
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /*==========================================
|
|
17
|
+
* TAKING PAYMENT FOR A BOOKING IN PERSON (AGL-3618).
|
|
18
|
+
*
|
|
19
|
+
* A booking whose price was not charged online (a free booking, or one whose
|
|
20
|
+
* price `varies`, is an `estimate` or is `contact`) is paid at the counter
|
|
21
|
+
* once the work is done: staff type the amount and the customer taps or
|
|
22
|
+
* inserts a card on the register's reader. Pure: the server route, the
|
|
23
|
+
* console and the native Aglyn POS app all read a booking through these.
|
|
24
|
+
*
|
|
25
|
+
* Imports nothing, so client and server code alike may import it.
|
|
26
|
+
*=========================================*/ /** The in-person payment a booking carries while it is being taken. */ export function bookingInPersonState(booking, nowMs = Date.now()) {
|
|
27
|
+
var _booking_paidAmountCents;
|
|
28
|
+
var _booking_inPersonPayment, _booking_inPersonPayment1;
|
|
29
|
+
if (Math.round(Number((_booking_paidAmountCents = booking.paidAmountCents) != null ? _booking_paidAmountCents : 0)) > 0 || Boolean(booking.paymentIntentId) || ((_booking_inPersonPayment = booking.inPersonPayment) == null ? void 0 : _booking_inPersonPayment.status) === 'paid') {
|
|
30
|
+
return 'paid';
|
|
31
|
+
}
|
|
32
|
+
if (booking.status === 'canceled') return 'canceled';
|
|
33
|
+
if (booking.status === 'pendingPayment') {
|
|
34
|
+
var _booking_expiresAtMs;
|
|
35
|
+
// A lapsed online hold released its slot; it is not an appointment.
|
|
36
|
+
return Number((_booking_expiresAtMs = booking.expiresAtMs) != null ? _booking_expiresAtMs : 0) < nowMs ? 'canceled' : 'awaiting-online';
|
|
37
|
+
}
|
|
38
|
+
if (((_booking_inPersonPayment1 = booking.inPersonPayment) == null ? void 0 : _booking_inPersonPayment1.status) === 'pending') return 'collecting';
|
|
39
|
+
return 'payable';
|
|
40
|
+
}
|
|
41
|
+
/** The most one in-person booking charge may be: $10,000. */ export const BOOKING_IN_PERSON_MAX_CENTS = 1000000;
|
|
42
|
+
/** Stripe's card minimum in USD. */ export const BOOKING_IN_PERSON_MIN_CENTS = 50;
|
|
43
|
+
/** Null when `serviceCents` is an amount staff may charge; otherwise why not. */ export function bookingInPersonAmountProblem(serviceCents) {
|
|
44
|
+
if (typeof serviceCents !== 'number' || !Number.isInteger(serviceCents)) {
|
|
45
|
+
return 'Enter the amount to charge.';
|
|
46
|
+
}
|
|
47
|
+
if (serviceCents < BOOKING_IN_PERSON_MIN_CENTS) return 'Card payments start at $0.50.';
|
|
48
|
+
if (serviceCents > BOOKING_IN_PERSON_MAX_CENTS) return 'Charge at most $10,000 at a time.';
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The amount to suggest: the service's fixed price, when it has one. A
|
|
53
|
+
* `varies`, `estimate` or `contact` price suggests nothing; staff type it.
|
|
54
|
+
*/ export function bookingSuggestedCents(service) {
|
|
55
|
+
var _service_priceUsd;
|
|
56
|
+
if (!service) return null;
|
|
57
|
+
if (service.priceDisplay && service.priceDisplay !== 'fixed') return null;
|
|
58
|
+
const cents = Math.round(Number((_service_priceUsd = service.priceUsd) != null ? _service_priceUsd : 0) * 100);
|
|
59
|
+
return Number.isFinite(cents) && cents >= BOOKING_IN_PERSON_MIN_CENTS ? cents : null;
|
|
60
|
+
}
|
|
61
|
+
/** The start and end of the local day that holds `atMs`, in milliseconds. */ export function bookingDayRange(atMs, offsetMinutes = new Date(atMs).getTimezoneOffset()) {
|
|
62
|
+
const local = atMs - offsetMinutes * 60000;
|
|
63
|
+
const dayStart = Math.floor(local / 86400000) * 86400000;
|
|
64
|
+
const startMs = dayStart + offsetMinutes * 60000;
|
|
65
|
+
return {
|
|
66
|
+
startMs,
|
|
67
|
+
endMs: startMs + 86400000
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=booking-in-person.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/bookings/src/lib/model/booking-in-person.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * TAKING PAYMENT FOR A BOOKING IN PERSON (AGL-3618).\n *\n * A booking whose price was not charged online (a free booking, or one whose\n * price `varies`, is an `estimate` or is `contact`) is paid at the counter\n * once the work is done: staff type the amount and the customer taps or\n * inserts a card on the register's reader. Pure: the server route, the\n * console and the native Aglyn POS app all read a booking through these.\n *\n * Imports nothing, so client and server code alike may import it.\n *=========================================*/\n\n/** The in-person payment a booking carries while it is being taken. */\nexport interface BookingInPersonPayment {\n paymentIntentId: string\n /** The whole charge: service plus tax. */\n amountCents: number\n /** The service amount staff typed, before tax. */\n serviceCents: number\n taxCents: number\n feeCents: number\n status: 'pending' | 'paid' | 'canceled'\n startedAtMs: number\n startedBy: string\n paidAtMs?: number\n /** Which start request made it, so a retried start returns it again. */\n attempt?: string\n}\n\n/** The fields of a booking this reads; any booking document has them. */\nexport interface BookingPaymentFields {\n status?: string\n expiresAtMs?: number\n paidAmountCents?: number\n refundedCents?: number\n paymentIntentId?: string\n inPersonPayment?: Partial<BookingInPersonPayment> | null\n}\n\nexport type BookingInPersonState =\n /** Paid already, online or in person. */\n | 'paid'\n /** Canceled: nothing to pay. */\n | 'canceled'\n /** Still holding for its online Checkout: the customer is paying there. */\n | 'awaiting-online'\n /** A card is being taken for it now. */\n | 'collecting'\n /** Can be paid at the counter. */\n | 'payable'\n\nexport function bookingInPersonState(\n booking: BookingPaymentFields,\n nowMs = Date.now(),\n): BookingInPersonState {\n if (\n Math.round(Number(booking.paidAmountCents ?? 0)) > 0 ||\n Boolean(booking.paymentIntentId) ||\n booking.inPersonPayment?.status === 'paid'\n ) {\n return 'paid'\n }\n if (booking.status === 'canceled') return 'canceled'\n if (booking.status === 'pendingPayment') {\n // A lapsed online hold released its slot; it is not an appointment.\n return Number(booking.expiresAtMs ?? 0) < nowMs ? 'canceled' : 'awaiting-online'\n }\n if (booking.inPersonPayment?.status === 'pending') return 'collecting'\n return 'payable'\n}\n\n/** The most one in-person booking charge may be: $10,000. */\nexport const BOOKING_IN_PERSON_MAX_CENTS = 1_000_000\n/** Stripe's card minimum in USD. */\nexport const BOOKING_IN_PERSON_MIN_CENTS = 50\n\n/** Null when `serviceCents` is an amount staff may charge; otherwise why not. */\nexport function bookingInPersonAmountProblem(serviceCents: unknown): string | null {\n if (typeof serviceCents !== 'number' || !Number.isInteger(serviceCents)) {\n return 'Enter the amount to charge.'\n }\n if (serviceCents < BOOKING_IN_PERSON_MIN_CENTS) return 'Card payments start at $0.50.'\n if (serviceCents > BOOKING_IN_PERSON_MAX_CENTS) return 'Charge at most $10,000 at a time.'\n return null\n}\n\n/**\n * The amount to suggest: the service's fixed price, when it has one. A\n * `varies`, `estimate` or `contact` price suggests nothing; staff type it.\n */\nexport function bookingSuggestedCents(service: {\n priceUsd?: number\n priceDisplay?: string\n} | null | undefined): number | null {\n if (!service) return null\n if (service.priceDisplay && service.priceDisplay !== 'fixed') return null\n const cents = Math.round(Number(service.priceUsd ?? 0) * 100)\n return Number.isFinite(cents) && cents >= BOOKING_IN_PERSON_MIN_CENTS ? cents : null\n}\n\n/** The start and end of the local day that holds `atMs`, in milliseconds. */\nexport function bookingDayRange(atMs: number, offsetMinutes = new Date(atMs).getTimezoneOffset()): {\n startMs: number\n endMs: number\n} {\n const local = atMs - offsetMinutes * 60_000\n const dayStart = Math.floor(local / 86_400_000) * 86_400_000\n const startMs = dayStart + offsetMinutes * 60_000\n return { startMs, endMs: startMs + 86_400_000 }\n}\n"],"names":["bookingInPersonState","booking","nowMs","Date","now","Math","round","Number","paidAmountCents","Boolean","paymentIntentId","inPersonPayment","status","expiresAtMs","BOOKING_IN_PERSON_MAX_CENTS","BOOKING_IN_PERSON_MIN_CENTS","bookingInPersonAmountProblem","serviceCents","isInteger","bookingSuggestedCents","service","priceDisplay","cents","priceUsd","isFinite","bookingDayRange","atMs","offsetMinutes","getTimezoneOffset","local","dayStart","floor","startMs","endMs"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;2CAU2C,GAE3C,qEAAqE,GAuCrE,OAAO,SAASA,qBACdC,OAA6B,EAC7BC,QAAQC,KAAKC,GAAG,EAAE;QAGEH;QAElBA,0BASEA;IAZJ,IACEI,KAAKC,KAAK,CAACC,QAAON,2BAAAA,QAAQO,eAAe,YAAvBP,2BAA2B,MAAM,KACnDQ,QAAQR,QAAQS,eAAe,KAC/BT,EAAAA,2BAAAA,QAAQU,eAAe,qBAAvBV,yBAAyBW,MAAM,MAAK,QACpC;QACA,OAAO;IACT;IACA,IAAIX,QAAQW,MAAM,KAAK,YAAY,OAAO;IAC1C,IAAIX,QAAQW,MAAM,KAAK,kBAAkB;YAEzBX;QADd,oEAAoE;QACpE,OAAOM,QAAON,uBAAAA,QAAQY,WAAW,YAAnBZ,uBAAuB,KAAKC,QAAQ,aAAa;IACjE;IACA,IAAID,EAAAA,4BAAAA,QAAQU,eAAe,qBAAvBV,0BAAyBW,MAAM,MAAK,WAAW,OAAO;IAC1D,OAAO;AACT;AAEA,2DAA2D,GAC3D,OAAO,MAAME,8BAA8B,QAAS;AACpD,kCAAkC,GAClC,OAAO,MAAMC,8BAA8B,GAAE;AAE7C,+EAA+E,GAC/E,OAAO,SAASC,6BAA6BC,YAAqB;IAChE,IAAI,OAAOA,iBAAiB,YAAY,CAACV,OAAOW,SAAS,CAACD,eAAe;QACvE,OAAO;IACT;IACA,IAAIA,eAAeF,6BAA6B,OAAO;IACvD,IAAIE,eAAeH,6BAA6B,OAAO;IACvD,OAAO;AACT;AAEA;;;CAGC,GACD,OAAO,SAASK,sBAAsBC,OAGlB;QAGcA;IAFhC,IAAI,CAACA,SAAS,OAAO;IACrB,IAAIA,QAAQC,YAAY,IAAID,QAAQC,YAAY,KAAK,SAAS,OAAO;IACrE,MAAMC,QAAQjB,KAAKC,KAAK,CAACC,QAAOa,oBAAAA,QAAQG,QAAQ,YAAhBH,oBAAoB,KAAK;IACzD,OAAOb,OAAOiB,QAAQ,CAACF,UAAUA,SAASP,8BAA8BO,QAAQ;AAClF;AAEA,2EAA2E,GAC3E,OAAO,SAASG,gBAAgBC,IAAY,EAAEC,gBAAgB,IAAIxB,KAAKuB,MAAME,iBAAiB,EAAE;IAI9F,MAAMC,QAAQH,OAAOC,gBAAgB;IACrC,MAAMG,WAAWzB,KAAK0B,KAAK,CAACF,QAAQ,YAAc;IAClD,MAAMG,UAAUF,WAAWH,gBAAgB;IAC3C,OAAO;QAAEK;QAASC,OAAOD,UAAU;IAAW;AAChD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* What a site's team may do to a booking once it exists (AGL-3621): check
|
|
19
|
+
* the guest in, move it, cancel it. One pure rule set, read by the routes
|
|
20
|
+
* that do it (`bookings/check-in`, `bookings/reschedule`) under their own
|
|
21
|
+
* transaction, and the rules a client mirrors to decide what to OFFER (the
|
|
22
|
+
* native Aglyn app's bookings screens port them, cased by this file's spec),
|
|
23
|
+
* so a screen can only offer what the route would then do.
|
|
24
|
+
*
|
|
25
|
+
* No I/O and no React, so client and server code alike may import it.
|
|
26
|
+
*/
|
|
27
|
+
/** `hosts/{hostId}/bookings/{id}`, the fields these rules read. */
|
|
28
|
+
export interface ManagedBooking {
|
|
29
|
+
status?: string;
|
|
30
|
+
startsAtMs?: number;
|
|
31
|
+
endsAtMs?: number;
|
|
32
|
+
expiresAtMs?: number;
|
|
33
|
+
paidAmountCents?: number;
|
|
34
|
+
refundedCents?: number;
|
|
35
|
+
checkedInAtMs?: number;
|
|
36
|
+
}
|
|
37
|
+
export type BookingState = 'confirmed' | 'pendingPayment' | 'expired' | 'canceled';
|
|
38
|
+
/**
|
|
39
|
+
* The booking's state as a reader should treat it. A payment hold whose
|
|
40
|
+
* fifteen minutes ran out released its slot (AGL-170) even while the
|
|
41
|
+
* document still says `pendingPayment`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function bookingState(booking: ManagedBooking, nowMs: number): BookingState;
|
|
44
|
+
export declare const BOOKING_STATE_LABELS: Readonly<Record<BookingState, string>>;
|
|
45
|
+
/** The paid amount not yet refunded, in cents. */
|
|
46
|
+
export declare function bookingOutstandingCents(booking: ManagedBooking): number;
|
|
47
|
+
export interface BookingActions {
|
|
48
|
+
checkIn: boolean;
|
|
49
|
+
undoCheckIn: boolean;
|
|
50
|
+
reschedule: boolean;
|
|
51
|
+
/** Cancel; with `refundCents` above zero the cancel goes through the refund route. */
|
|
52
|
+
cancel: boolean;
|
|
53
|
+
refundCents: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* What may be done to a booking now. A guest is checked in only on a
|
|
57
|
+
* confirmed booking; a booking moves only while it is confirmed and has not
|
|
58
|
+
* ended; and nothing is done to a canceled one.
|
|
59
|
+
*/
|
|
60
|
+
export declare function bookingActions(booking: ManagedBooking, nowMs: number): BookingActions;
|
|
61
|
+
/** Why a check-in (or its undo) cannot be written, or null. The route's own refusal. */
|
|
62
|
+
export declare function checkInRefusal(booking: ManagedBooking, checkedIn: boolean, nowMs: number): string | null;
|
|
63
|
+
/** Why a booking cannot move to `startsAtMs`, or null. The slot itself is checked apart. */
|
|
64
|
+
export declare function rescheduleRefusal(booking: ManagedBooking, startsAtMs: number, nowMs: number): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* Why a booking cannot be canceled by the cancel route, or null (AGL-3643).
|
|
67
|
+
* A canceled booking is not refused: canceling it again changes nothing. A
|
|
68
|
+
* booking with money still paid on it cancels through the refund instead, so
|
|
69
|
+
* a guest is never left canceled and charged.
|
|
70
|
+
*/
|
|
71
|
+
export declare function cancelRefusal(booking: ManagedBooking, nowMs: number): string | null;
|
|
72
|
+
/** The booking's length, kept when it moves: what the guest booked, not what the service says today. */
|
|
73
|
+
export declare function bookingDurationMs(booking: ManagedBooking, fallbackMinutes: number): number;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* What a site's team may do to a booking once it exists (AGL-3621): check
|
|
18
|
+
* the guest in, move it, cancel it. One pure rule set, read by the routes
|
|
19
|
+
* that do it (`bookings/check-in`, `bookings/reschedule`) under their own
|
|
20
|
+
* transaction, and the rules a client mirrors to decide what to OFFER (the
|
|
21
|
+
* native Aglyn app's bookings screens port them, cased by this file's spec),
|
|
22
|
+
* so a screen can only offer what the route would then do.
|
|
23
|
+
*
|
|
24
|
+
* No I/O and no React, so client and server code alike may import it.
|
|
25
|
+
*/ /** `hosts/{hostId}/bookings/{id}`, the fields these rules read. */ /**
|
|
26
|
+
* The booking's state as a reader should treat it. A payment hold whose
|
|
27
|
+
* fifteen minutes ran out released its slot (AGL-170) even while the
|
|
28
|
+
* document still says `pendingPayment`.
|
|
29
|
+
*/ export function bookingState(booking, nowMs) {
|
|
30
|
+
if (booking.status === 'canceled') return 'canceled';
|
|
31
|
+
if (booking.status === 'pendingPayment') {
|
|
32
|
+
var _booking_expiresAtMs;
|
|
33
|
+
return Number((_booking_expiresAtMs = booking.expiresAtMs) != null ? _booking_expiresAtMs : 0) < nowMs ? 'expired' : 'pendingPayment';
|
|
34
|
+
}
|
|
35
|
+
return 'confirmed';
|
|
36
|
+
}
|
|
37
|
+
export const BOOKING_STATE_LABELS = {
|
|
38
|
+
confirmed: 'Confirmed',
|
|
39
|
+
pendingPayment: 'Awaiting payment',
|
|
40
|
+
expired: 'Payment not finished',
|
|
41
|
+
canceled: 'Canceled'
|
|
42
|
+
};
|
|
43
|
+
/** The paid amount not yet refunded, in cents. */ export function bookingOutstandingCents(booking) {
|
|
44
|
+
var _booking_paidAmountCents, _booking_refundedCents;
|
|
45
|
+
const paid = Math.max(0, Math.round(Number((_booking_paidAmountCents = booking.paidAmountCents) != null ? _booking_paidAmountCents : 0)));
|
|
46
|
+
const refunded = Math.max(0, Math.round(Number((_booking_refundedCents = booking.refundedCents) != null ? _booking_refundedCents : 0)));
|
|
47
|
+
return Math.max(0, paid - refunded);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* What may be done to a booking now. A guest is checked in only on a
|
|
51
|
+
* confirmed booking; a booking moves only while it is confirmed and has not
|
|
52
|
+
* ended; and nothing is done to a canceled one.
|
|
53
|
+
*/ export function bookingActions(booking, nowMs) {
|
|
54
|
+
var _booking_checkedInAtMs, _booking_endsAtMs;
|
|
55
|
+
const state = bookingState(booking, nowMs);
|
|
56
|
+
const confirmed = state === 'confirmed';
|
|
57
|
+
const checkedIn = Number((_booking_checkedInAtMs = booking.checkedInAtMs) != null ? _booking_checkedInAtMs : 0) > 0;
|
|
58
|
+
const ended = Number((_booking_endsAtMs = booking.endsAtMs) != null ? _booking_endsAtMs : 0) <= nowMs;
|
|
59
|
+
return {
|
|
60
|
+
checkIn: confirmed && !checkedIn,
|
|
61
|
+
undoCheckIn: confirmed && checkedIn,
|
|
62
|
+
reschedule: confirmed && !checkedIn && !ended,
|
|
63
|
+
cancel: state !== 'canceled' && !checkedIn,
|
|
64
|
+
refundCents: bookingOutstandingCents(booking)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Why a check-in (or its undo) cannot be written, or null. The route's own refusal. */ export function checkInRefusal(booking, checkedIn, nowMs) {
|
|
68
|
+
var _booking_checkedInAtMs;
|
|
69
|
+
const state = bookingState(booking, nowMs);
|
|
70
|
+
if (state === 'canceled') return 'This booking was canceled';
|
|
71
|
+
if (state !== 'confirmed') return 'This booking is not paid yet';
|
|
72
|
+
return checkedIn || Number((_booking_checkedInAtMs = booking.checkedInAtMs) != null ? _booking_checkedInAtMs : 0) > 0 ? null : 'This guest is not checked in';
|
|
73
|
+
}
|
|
74
|
+
/** Why a booking cannot move to `startsAtMs`, or null. The slot itself is checked apart. */ export function rescheduleRefusal(booking, startsAtMs, nowMs) {
|
|
75
|
+
var _booking_checkedInAtMs, _booking_endsAtMs;
|
|
76
|
+
const state = bookingState(booking, nowMs);
|
|
77
|
+
if (state === 'canceled') return 'This booking was canceled';
|
|
78
|
+
if (state !== 'confirmed') return 'This booking is not paid yet';
|
|
79
|
+
if (Number((_booking_checkedInAtMs = booking.checkedInAtMs) != null ? _booking_checkedInAtMs : 0) > 0) return 'This guest is already checked in';
|
|
80
|
+
if (Number((_booking_endsAtMs = booking.endsAtMs) != null ? _booking_endsAtMs : 0) <= nowMs) return 'This booking has already ended';
|
|
81
|
+
if (!Number.isFinite(startsAtMs) || startsAtMs <= nowMs) return 'Pick a time that has not passed';
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Why a booking cannot be canceled by the cancel route, or null (AGL-3643).
|
|
86
|
+
* A canceled booking is not refused: canceling it again changes nothing. A
|
|
87
|
+
* booking with money still paid on it cancels through the refund instead, so
|
|
88
|
+
* a guest is never left canceled and charged.
|
|
89
|
+
*/ export function cancelRefusal(booking, nowMs) {
|
|
90
|
+
var _booking_checkedInAtMs;
|
|
91
|
+
if (bookingState(booking, nowMs) === 'canceled') return null;
|
|
92
|
+
if (Number((_booking_checkedInAtMs = booking.checkedInAtMs) != null ? _booking_checkedInAtMs : 0) > 0) return 'This guest is already checked in';
|
|
93
|
+
if (bookingOutstandingCents(booking) > 0) {
|
|
94
|
+
return 'This booking was paid. Cancel it with a refund so the guest gets their money back';
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/** The booking's length, kept when it moves: what the guest booked, not what the service says today. */ export function bookingDurationMs(booking, fallbackMinutes) {
|
|
99
|
+
var _booking_endsAtMs, _booking_startsAtMs;
|
|
100
|
+
const stored = Number((_booking_endsAtMs = booking.endsAtMs) != null ? _booking_endsAtMs : 0) - Number((_booking_startsAtMs = booking.startsAtMs) != null ? _booking_startsAtMs : 0);
|
|
101
|
+
return stored > 0 ? stored : Math.max(5, Math.round(fallbackMinutes || 30)) * 60000;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//# sourceMappingURL=booking-manage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/bookings/src/lib/model/booking-manage.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * What a site's team may do to a booking once it exists (AGL-3621): check\n * the guest in, move it, cancel it. One pure rule set, read by the routes\n * that do it (`bookings/check-in`, `bookings/reschedule`) under their own\n * transaction, and the rules a client mirrors to decide what to OFFER (the\n * native Aglyn app's bookings screens port them, cased by this file's spec),\n * so a screen can only offer what the route would then do.\n *\n * No I/O and no React, so client and server code alike may import it.\n */\n\n/** `hosts/{hostId}/bookings/{id}`, the fields these rules read. */\nexport interface ManagedBooking {\n status?: string\n startsAtMs?: number\n endsAtMs?: number\n expiresAtMs?: number\n paidAmountCents?: number\n refundedCents?: number\n checkedInAtMs?: number\n}\n\nexport type BookingState = 'confirmed' | 'pendingPayment' | 'expired' | 'canceled'\n\n/**\n * The booking's state as a reader should treat it. A payment hold whose\n * fifteen minutes ran out released its slot (AGL-170) even while the\n * document still says `pendingPayment`.\n */\nexport function bookingState(booking: ManagedBooking, nowMs: number): BookingState {\n if (booking.status === 'canceled') return 'canceled'\n if (booking.status === 'pendingPayment') {\n return Number(booking.expiresAtMs ?? 0) < nowMs ? 'expired' : 'pendingPayment'\n }\n return 'confirmed'\n}\n\nexport const BOOKING_STATE_LABELS: Readonly<Record<BookingState, string>> = {\n confirmed: 'Confirmed',\n pendingPayment: 'Awaiting payment',\n expired: 'Payment not finished',\n canceled: 'Canceled',\n}\n\n/** The paid amount not yet refunded, in cents. */\nexport function bookingOutstandingCents(booking: ManagedBooking): number {\n const paid = Math.max(0, Math.round(Number(booking.paidAmountCents ?? 0)))\n const refunded = Math.max(0, Math.round(Number(booking.refundedCents ?? 0)))\n return Math.max(0, paid - refunded)\n}\n\nexport interface BookingActions {\n checkIn: boolean\n undoCheckIn: boolean\n reschedule: boolean\n /** Cancel; with `refundCents` above zero the cancel goes through the refund route. */\n cancel: boolean\n refundCents: number\n}\n\n/**\n * What may be done to a booking now. A guest is checked in only on a\n * confirmed booking; a booking moves only while it is confirmed and has not\n * ended; and nothing is done to a canceled one.\n */\nexport function bookingActions(booking: ManagedBooking, nowMs: number): BookingActions {\n const state = bookingState(booking, nowMs)\n const confirmed = state === 'confirmed'\n const checkedIn = Number(booking.checkedInAtMs ?? 0) > 0\n const ended = Number(booking.endsAtMs ?? 0) <= nowMs\n return {\n checkIn: confirmed && !checkedIn,\n undoCheckIn: confirmed && checkedIn,\n reschedule: confirmed && !checkedIn && !ended,\n cancel: state !== 'canceled' && !checkedIn,\n refundCents: bookingOutstandingCents(booking),\n }\n}\n\n/** Why a check-in (or its undo) cannot be written, or null. The route's own refusal. */\nexport function checkInRefusal(booking: ManagedBooking, checkedIn: boolean, nowMs: number): string | null {\n const state = bookingState(booking, nowMs)\n if (state === 'canceled') return 'This booking was canceled'\n if (state !== 'confirmed') return 'This booking is not paid yet'\n return checkedIn || Number(booking.checkedInAtMs ?? 0) > 0 ? null : 'This guest is not checked in'\n}\n\n/** Why a booking cannot move to `startsAtMs`, or null. The slot itself is checked apart. */\nexport function rescheduleRefusal(booking: ManagedBooking, startsAtMs: number, nowMs: number): string | null {\n const state = bookingState(booking, nowMs)\n if (state === 'canceled') return 'This booking was canceled'\n if (state !== 'confirmed') return 'This booking is not paid yet'\n if (Number(booking.checkedInAtMs ?? 0) > 0) return 'This guest is already checked in'\n if (Number(booking.endsAtMs ?? 0) <= nowMs) return 'This booking has already ended'\n if (!Number.isFinite(startsAtMs) || startsAtMs <= nowMs) return 'Pick a time that has not passed'\n return null\n}\n\n/**\n * Why a booking cannot be canceled by the cancel route, or null (AGL-3643).\n * A canceled booking is not refused: canceling it again changes nothing. A\n * booking with money still paid on it cancels through the refund instead, so\n * a guest is never left canceled and charged.\n */\nexport function cancelRefusal(booking: ManagedBooking, nowMs: number): string | null {\n if (bookingState(booking, nowMs) === 'canceled') return null\n if (Number(booking.checkedInAtMs ?? 0) > 0) return 'This guest is already checked in'\n if (bookingOutstandingCents(booking) > 0) {\n return 'This booking was paid. Cancel it with a refund so the guest gets their money back'\n }\n return null\n}\n\n/** The booking's length, kept when it moves: what the guest booked, not what the service says today. */\nexport function bookingDurationMs(booking: ManagedBooking, fallbackMinutes: number): number {\n const stored = Number(booking.endsAtMs ?? 0) - Number(booking.startsAtMs ?? 0)\n return stored > 0 ? stored : Math.max(5, Math.round(fallbackMinutes || 30)) * 60_000\n}\n"],"names":["bookingState","booking","nowMs","status","Number","expiresAtMs","BOOKING_STATE_LABELS","confirmed","pendingPayment","expired","canceled","bookingOutstandingCents","paid","Math","max","round","paidAmountCents","refunded","refundedCents","bookingActions","state","checkedIn","checkedInAtMs","ended","endsAtMs","checkIn","undoCheckIn","reschedule","cancel","refundCents","checkInRefusal","rescheduleRefusal","startsAtMs","isFinite","cancelRefusal","bookingDurationMs","fallbackMinutes","stored"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;CASC,GAED,iEAAiE,GAajE;;;;CAIC,GACD,OAAO,SAASA,aAAaC,OAAuB,EAAEC,KAAa;IACjE,IAAID,QAAQE,MAAM,KAAK,YAAY,OAAO;IAC1C,IAAIF,QAAQE,MAAM,KAAK,kBAAkB;YACzBF;QAAd,OAAOG,QAAOH,uBAAAA,QAAQI,WAAW,YAAnBJ,uBAAuB,KAAKC,QAAQ,YAAY;IAChE;IACA,OAAO;AACT;AAEA,OAAO,MAAMI,uBAA+D;IAC1EC,WAAW;IACXC,gBAAgB;IAChBC,SAAS;IACTC,UAAU;AACZ,EAAC;AAED,gDAAgD,GAChD,OAAO,SAASC,wBAAwBV,OAAuB;QAClBA,0BACIA;IAD/C,MAAMW,OAAOC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAACX,QAAOH,2BAAAA,QAAQe,eAAe,YAAvBf,2BAA2B;IACtE,MAAMgB,WAAWJ,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAACX,QAAOH,yBAAAA,QAAQiB,aAAa,YAArBjB,yBAAyB;IACxE,OAAOY,KAAKC,GAAG,CAAC,GAAGF,OAAOK;AAC5B;AAWA;;;;CAIC,GACD,OAAO,SAASE,eAAelB,OAAuB,EAAEC,KAAa;QAG1CD,wBACJA;IAHrB,MAAMmB,QAAQpB,aAAaC,SAASC;IACpC,MAAMK,YAAYa,UAAU;IAC5B,MAAMC,YAAYjB,QAAOH,yBAAAA,QAAQqB,aAAa,YAArBrB,yBAAyB,KAAK;IACvD,MAAMsB,QAAQnB,QAAOH,oBAAAA,QAAQuB,QAAQ,YAAhBvB,oBAAoB,MAAMC;IAC/C,OAAO;QACLuB,SAASlB,aAAa,CAACc;QACvBK,aAAanB,aAAac;QAC1BM,YAAYpB,aAAa,CAACc,aAAa,CAACE;QACxCK,QAAQR,UAAU,cAAc,CAACC;QACjCQ,aAAalB,wBAAwBV;IACvC;AACF;AAEA,sFAAsF,GACtF,OAAO,SAAS6B,eAAe7B,OAAuB,EAAEoB,SAAkB,EAAEnB,KAAa;QAI5DD;IAH3B,MAAMmB,QAAQpB,aAAaC,SAASC;IACpC,IAAIkB,UAAU,YAAY,OAAO;IACjC,IAAIA,UAAU,aAAa,OAAO;IAClC,OAAOC,aAAajB,QAAOH,yBAAAA,QAAQqB,aAAa,YAArBrB,yBAAyB,KAAK,IAAI,OAAO;AACtE;AAEA,0FAA0F,GAC1F,OAAO,SAAS8B,kBAAkB9B,OAAuB,EAAE+B,UAAkB,EAAE9B,KAAa;QAI/ED,wBACAA;IAJX,MAAMmB,QAAQpB,aAAaC,SAASC;IACpC,IAAIkB,UAAU,YAAY,OAAO;IACjC,IAAIA,UAAU,aAAa,OAAO;IAClC,IAAIhB,QAAOH,yBAAAA,QAAQqB,aAAa,YAArBrB,yBAAyB,KAAK,GAAG,OAAO;IACnD,IAAIG,QAAOH,oBAAAA,QAAQuB,QAAQ,YAAhBvB,oBAAoB,MAAMC,OAAO,OAAO;IACnD,IAAI,CAACE,OAAO6B,QAAQ,CAACD,eAAeA,cAAc9B,OAAO,OAAO;IAChE,OAAO;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASgC,cAAcjC,OAAuB,EAAEC,KAAa;QAEvDD;IADX,IAAID,aAAaC,SAASC,WAAW,YAAY,OAAO;IACxD,IAAIE,QAAOH,yBAAAA,QAAQqB,aAAa,YAArBrB,yBAAyB,KAAK,GAAG,OAAO;IACnD,IAAIU,wBAAwBV,WAAW,GAAG;QACxC,OAAO;IACT;IACA,OAAO;AACT;AAEA,sGAAsG,GACtG,OAAO,SAASkC,kBAAkBlC,OAAuB,EAAEmC,eAAuB;QAC1DnC,mBAAgCA;IAAtD,MAAMoC,SAASjC,QAAOH,oBAAAA,QAAQuB,QAAQ,YAAhBvB,oBAAoB,KAAKG,QAAOH,sBAAAA,QAAQ+B,UAAU,YAAlB/B,sBAAsB;IAC5E,OAAOoC,SAAS,IAAIA,SAASxB,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAACqB,mBAAmB,OAAO;AAChF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type BookingState } from './booking-manage';
|
|
18
|
+
/**
|
|
19
|
+
* A booking as the customer REST API publishes it (AGL-3643):
|
|
20
|
+
* `GET /v1/sites/{siteId}/bookings/{bookingId}`, and the `booking` every
|
|
21
|
+
* booking event carries, so a subscriber reads one documented object rather
|
|
22
|
+
* than the plugin's storage.
|
|
23
|
+
*
|
|
24
|
+
* Money is integer cents, in US dollars — a booking is charged in USD. Times
|
|
25
|
+
* are ISO 8601 in UTC, with the zone the booking was made in beside them.
|
|
26
|
+
* Never Stripe's handles, the CRM reference or the reminder stamps: those are
|
|
27
|
+
* how the platform runs a booking, not what one is.
|
|
28
|
+
*
|
|
29
|
+
* Pure: no I/O, so the API handler, the event raisers and a spec share it.
|
|
30
|
+
*/
|
|
31
|
+
export interface BookingView {
|
|
32
|
+
id: string;
|
|
33
|
+
object: 'booking';
|
|
34
|
+
serviceId: string | null;
|
|
35
|
+
serviceName: string | null;
|
|
36
|
+
/**
|
|
37
|
+
* `confirmed`, `pendingPayment` (a paid booking whose checkout is still
|
|
38
|
+
* open), `expired` (that checkout ran out and the slot was released) or
|
|
39
|
+
* `canceled`.
|
|
40
|
+
*/
|
|
41
|
+
status: BookingState;
|
|
42
|
+
name: string | null;
|
|
43
|
+
email: string | null;
|
|
44
|
+
phone: string | null;
|
|
45
|
+
address: Record<string, unknown> | string | null;
|
|
46
|
+
startsAt: string | null;
|
|
47
|
+
endsAt: string | null;
|
|
48
|
+
timeZone: string | null;
|
|
49
|
+
currency: 'usd';
|
|
50
|
+
/** What the guest paid, tax included; 0 for a free booking. */
|
|
51
|
+
paidCents: number;
|
|
52
|
+
taxCents: number;
|
|
53
|
+
refundedCents: number;
|
|
54
|
+
checkedIn: boolean;
|
|
55
|
+
checkedInAt: string | null;
|
|
56
|
+
/** Where a moved booking started before its latest move, else `null`. */
|
|
57
|
+
rescheduledFrom: string | null;
|
|
58
|
+
created: string | null;
|
|
59
|
+
}
|
|
60
|
+
/** Epoch milliseconds, a Firestore Timestamp or a Date, as ISO 8601. */
|
|
61
|
+
export declare function bookingInstant(value: unknown): string | null;
|
|
62
|
+
/** One stored booking as the API publishes it. `nowMs` decides an expired hold. */
|
|
63
|
+
export declare function bookingViewFromData(id: string, data: Record<string, unknown>, nowMs?: number): BookingView;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { bookingState } from "./booking-manage.js";
|
|
18
|
+
const text = (value)=>{
|
|
19
|
+
const out = typeof value === 'string' ? value.trim() : '';
|
|
20
|
+
return out ? out : null;
|
|
21
|
+
};
|
|
22
|
+
const cents = (value)=>{
|
|
23
|
+
const out = Math.round(Number(value != null ? value : 0));
|
|
24
|
+
return Number.isFinite(out) && out > 0 ? out : 0;
|
|
25
|
+
};
|
|
26
|
+
/** Epoch milliseconds, a Firestore Timestamp or a Date, as ISO 8601. */ export function bookingInstant(value) {
|
|
27
|
+
if (value == null) return null;
|
|
28
|
+
if (typeof value === 'number') return Number.isFinite(value) && value > 0 ? new Date(value).toISOString() : null;
|
|
29
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString();
|
|
30
|
+
const stamp = value;
|
|
31
|
+
if (typeof stamp.toMillis === 'function') return bookingInstant(stamp.toMillis());
|
|
32
|
+
if (typeof stamp.toDate === 'function') return bookingInstant(stamp.toDate());
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function addressOf(value) {
|
|
36
|
+
if (typeof value === 'string') return text(value);
|
|
37
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) return _extends({}, value);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/** One stored booking as the API publishes it. `nowMs` decides an expired hold. */ export function bookingViewFromData(id, data, nowMs = Date.now()) {
|
|
41
|
+
var _data_checkedInAtMs, _data_expiresAtMs, _data_startsAtMs, _data_endsAtMs, _data_rescheduledFromMs;
|
|
42
|
+
const checkedInAtMs = Number((_data_checkedInAtMs = data['checkedInAtMs']) != null ? _data_checkedInAtMs : 0);
|
|
43
|
+
return {
|
|
44
|
+
id,
|
|
45
|
+
object: 'booking',
|
|
46
|
+
serviceId: text(data['serviceId']),
|
|
47
|
+
serviceName: text(data['serviceName']),
|
|
48
|
+
status: bookingState({
|
|
49
|
+
status: typeof data['status'] === 'string' ? data['status'] : undefined,
|
|
50
|
+
expiresAtMs: Number((_data_expiresAtMs = data['expiresAtMs']) != null ? _data_expiresAtMs : 0) || undefined
|
|
51
|
+
}, nowMs),
|
|
52
|
+
name: text(data['name']),
|
|
53
|
+
email: text(data['email']),
|
|
54
|
+
phone: text(data['phone']),
|
|
55
|
+
address: addressOf(data['address']),
|
|
56
|
+
startsAt: bookingInstant(Number((_data_startsAtMs = data['startsAtMs']) != null ? _data_startsAtMs : 0)),
|
|
57
|
+
endsAt: bookingInstant(Number((_data_endsAtMs = data['endsAtMs']) != null ? _data_endsAtMs : 0)),
|
|
58
|
+
timeZone: text(data['timezone']),
|
|
59
|
+
currency: 'usd',
|
|
60
|
+
paidCents: cents(data['paidAmountCents']),
|
|
61
|
+
taxCents: cents(data['taxCents']),
|
|
62
|
+
refundedCents: cents(data['refundedCents']),
|
|
63
|
+
checkedIn: checkedInAtMs > 0,
|
|
64
|
+
checkedInAt: checkedInAtMs > 0 ? bookingInstant(checkedInAtMs) : null,
|
|
65
|
+
rescheduledFrom: bookingInstant(Number((_data_rescheduledFromMs = data['rescheduledFromMs']) != null ? _data_rescheduledFromMs : 0)),
|
|
66
|
+
created: bookingInstant(data['createdAt'])
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=booking-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/bookings/src/lib/model/booking-view.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { bookingState, type BookingState } from './booking-manage'\n\n/**\n * A booking as the customer REST API publishes it (AGL-3643):\n * `GET /v1/sites/{siteId}/bookings/{bookingId}`, and the `booking` every\n * booking event carries, so a subscriber reads one documented object rather\n * than the plugin's storage.\n *\n * Money is integer cents, in US dollars — a booking is charged in USD. Times\n * are ISO 8601 in UTC, with the zone the booking was made in beside them.\n * Never Stripe's handles, the CRM reference or the reminder stamps: those are\n * how the platform runs a booking, not what one is.\n *\n * Pure: no I/O, so the API handler, the event raisers and a spec share it.\n */\nexport interface BookingView {\n id: string\n object: 'booking'\n serviceId: string | null\n serviceName: string | null\n /**\n * `confirmed`, `pendingPayment` (a paid booking whose checkout is still\n * open), `expired` (that checkout ran out and the slot was released) or\n * `canceled`.\n */\n status: BookingState\n name: string | null\n email: string | null\n phone: string | null\n address: Record<string, unknown> | string | null\n startsAt: string | null\n endsAt: string | null\n timeZone: string | null\n currency: 'usd'\n /** What the guest paid, tax included; 0 for a free booking. */\n paidCents: number\n taxCents: number\n refundedCents: number\n checkedIn: boolean\n checkedInAt: string | null\n /** Where a moved booking started before its latest move, else `null`. */\n rescheduledFrom: string | null\n created: string | null\n}\n\nconst text = (value: unknown): string | null => {\n const out = typeof value === 'string' ? value.trim() : ''\n return out ? out : null\n}\n\nconst cents = (value: unknown): number => {\n const out = Math.round(Number(value ?? 0))\n return Number.isFinite(out) && out > 0 ? out : 0\n}\n\n/** Epoch milliseconds, a Firestore Timestamp or a Date, as ISO 8601. */\nexport function bookingInstant(value: unknown): string | null {\n if (value == null) return null\n if (typeof value === 'number') return Number.isFinite(value) && value > 0 ? new Date(value).toISOString() : null\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString()\n const stamp = value as { toMillis?: () => number; toDate?: () => Date }\n if (typeof stamp.toMillis === 'function') return bookingInstant(stamp.toMillis())\n if (typeof stamp.toDate === 'function') return bookingInstant(stamp.toDate())\n return null\n}\n\nfunction addressOf(value: unknown): BookingView['address'] {\n if (typeof value === 'string') return text(value)\n if (value && typeof value === 'object' && !Array.isArray(value)) return { ...(value as Record<string, unknown>) }\n return null\n}\n\n/** One stored booking as the API publishes it. `nowMs` decides an expired hold. */\nexport function bookingViewFromData(id: string, data: Record<string, unknown>, nowMs = Date.now()): BookingView {\n const checkedInAtMs = Number(data['checkedInAtMs'] ?? 0)\n return {\n id,\n object: 'booking',\n serviceId: text(data['serviceId']),\n serviceName: text(data['serviceName']),\n status: bookingState(\n {\n status: typeof data['status'] === 'string' ? data['status'] : undefined,\n expiresAtMs: Number(data['expiresAtMs'] ?? 0) || undefined,\n },\n nowMs,\n ),\n name: text(data['name']),\n email: text(data['email']),\n phone: text(data['phone']),\n address: addressOf(data['address']),\n startsAt: bookingInstant(Number(data['startsAtMs'] ?? 0)),\n endsAt: bookingInstant(Number(data['endsAtMs'] ?? 0)),\n timeZone: text(data['timezone']),\n currency: 'usd',\n paidCents: cents(data['paidAmountCents']),\n taxCents: cents(data['taxCents']),\n refundedCents: cents(data['refundedCents']),\n checkedIn: checkedInAtMs > 0,\n checkedInAt: checkedInAtMs > 0 ? bookingInstant(checkedInAtMs) : null,\n rescheduledFrom: bookingInstant(Number(data['rescheduledFromMs'] ?? 0)),\n created: bookingInstant(data['createdAt']),\n }\n}\n"],"names":["bookingState","text","value","out","trim","cents","Math","round","Number","isFinite","bookingInstant","Date","toISOString","isNaN","getTime","stamp","toMillis","toDate","addressOf","Array","isArray","bookingViewFromData","id","data","nowMs","now","checkedInAtMs","object","serviceId","serviceName","status","undefined","expiresAtMs","name","email","phone","address","startsAt","endsAt","timeZone","currency","paidCents","taxCents","refundedCents","checkedIn","checkedInAt","rescheduledFrom","created"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,YAAY,QAA2B,sBAAkB;AA6ClE,MAAMC,OAAO,CAACC;IACZ,MAAMC,MAAM,OAAOD,UAAU,WAAWA,MAAME,IAAI,KAAK;IACvD,OAAOD,MAAMA,MAAM;AACrB;AAEA,MAAME,QAAQ,CAACH;IACb,MAAMC,MAAMG,KAAKC,KAAK,CAACC,OAAON,gBAAAA,QAAS;IACvC,OAAOM,OAAOC,QAAQ,CAACN,QAAQA,MAAM,IAAIA,MAAM;AACjD;AAEA,sEAAsE,GACtE,OAAO,SAASO,eAAeR,KAAc;IAC3C,IAAIA,SAAS,MAAM,OAAO;IAC1B,IAAI,OAAOA,UAAU,UAAU,OAAOM,OAAOC,QAAQ,CAACP,UAAUA,QAAQ,IAAI,IAAIS,KAAKT,OAAOU,WAAW,KAAK;IAC5G,IAAIV,iBAAiBS,MAAM,OAAOH,OAAOK,KAAK,CAACX,MAAMY,OAAO,MAAM,OAAOZ,MAAMU,WAAW;IAC1F,MAAMG,QAAQb;IACd,IAAI,OAAOa,MAAMC,QAAQ,KAAK,YAAY,OAAON,eAAeK,MAAMC,QAAQ;IAC9E,IAAI,OAAOD,MAAME,MAAM,KAAK,YAAY,OAAOP,eAAeK,MAAME,MAAM;IAC1E,OAAO;AACT;AAEA,SAASC,UAAUhB,KAAc;IAC/B,IAAI,OAAOA,UAAU,UAAU,OAAOD,KAAKC;IAC3C,IAAIA,SAAS,OAAOA,UAAU,YAAY,CAACiB,MAAMC,OAAO,CAAClB,QAAQ,OAAO,aAAMA;IAC9E,OAAO;AACT;AAEA,iFAAiF,GACjF,OAAO,SAASmB,oBAAoBC,EAAU,EAAEC,IAA6B,EAAEC,QAAQb,KAAKc,GAAG,EAAE;QAClEF,qBASHA,mBAQQA,kBACFA,gBAQSA;IA1BzC,MAAMG,gBAAgBlB,QAAOe,sBAAAA,IAAI,CAAC,gBAAgB,YAArBA,sBAAyB;IACtD,OAAO;QACLD;QACAK,QAAQ;QACRC,WAAW3B,KAAKsB,IAAI,CAAC,YAAY;QACjCM,aAAa5B,KAAKsB,IAAI,CAAC,cAAc;QACrCO,QAAQ9B,aACN;YACE8B,QAAQ,OAAOP,IAAI,CAAC,SAAS,KAAK,WAAWA,IAAI,CAAC,SAAS,GAAGQ;YAC9DC,aAAaxB,QAAOe,oBAAAA,IAAI,CAAC,cAAc,YAAnBA,oBAAuB,MAAMQ;QACnD,GACAP;QAEFS,MAAMhC,KAAKsB,IAAI,CAAC,OAAO;QACvBW,OAAOjC,KAAKsB,IAAI,CAAC,QAAQ;QACzBY,OAAOlC,KAAKsB,IAAI,CAAC,QAAQ;QACzBa,SAASlB,UAAUK,IAAI,CAAC,UAAU;QAClCc,UAAU3B,eAAeF,QAAOe,mBAAAA,IAAI,CAAC,aAAa,YAAlBA,mBAAsB;QACtDe,QAAQ5B,eAAeF,QAAOe,iBAAAA,IAAI,CAAC,WAAW,YAAhBA,iBAAoB;QAClDgB,UAAUtC,KAAKsB,IAAI,CAAC,WAAW;QAC/BiB,UAAU;QACVC,WAAWpC,MAAMkB,IAAI,CAAC,kBAAkB;QACxCmB,UAAUrC,MAAMkB,IAAI,CAAC,WAAW;QAChCoB,eAAetC,MAAMkB,IAAI,CAAC,gBAAgB;QAC1CqB,WAAWlB,gBAAgB;QAC3BmB,aAAanB,gBAAgB,IAAIhB,eAAegB,iBAAiB;QACjEoB,iBAAiBpC,eAAeF,QAAOe,0BAAAA,IAAI,CAAC,oBAAoB,YAAzBA,0BAA6B;QACpEwB,SAASrC,eAAea,IAAI,CAAC,YAAY;IAC3C;AACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type ApiV1Context } from '@aglyn/tenant-data-admin/server/api-v1-kit';
|
|
18
|
+
/**
|
|
19
|
+
* A site's bookings on the customer REST API,
|
|
20
|
+
* `/v1/sites/{siteId}/bookings/…` (AGL-3643). Registered from this plugin's
|
|
21
|
+
* console server declarations; the console's `/v1` router owns the pipeline
|
|
22
|
+
* in front — the key, the plan's API access, the request quota, the rate
|
|
23
|
+
* limit — and refuses a site the key's organization does not own before it
|
|
24
|
+
* hands a request here.
|
|
25
|
+
*
|
|
26
|
+
* Read-only: `bookings:read`, and the `bookings` plan feature. The scope is
|
|
27
|
+
* asked first and the plan second, the order every commerce resource asks
|
|
28
|
+
* them in (AGL-900), so a key without the scope is told about the scope
|
|
29
|
+
* whatever plan its organization is on.
|
|
30
|
+
*/
|
|
31
|
+
/** The statuses a booking is stored with, and so the ones `?status=` takes. */
|
|
32
|
+
export declare const BOOKING_LIST_STATUSES: readonly ["confirmed", "pendingPayment", "canceled"];
|
|
33
|
+
export declare function handleBookings(request: Request, ctx: ApiV1Context, segments: string[], url: URL): Promise<Response>;
|