@final-commerce/common 2.3.0-staging.6 → 2.3.0
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/index.d.mts +19 -193
- package/dist/index.d.ts +19 -193
- package/dist/index.js +12 -450
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -432
- package/dist/index.mjs.map +1 -1
- package/dist/pos-types/index.d.mts +101 -143
- package/dist/pos-types/index.d.ts +101 -143
- package/dist/pos-types/index.js.map +1 -1
- package/dist/pos-types/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -422,7 +422,6 @@ var StripeCurrencyCode = /* @__PURE__ */ ((StripeCurrencyCode2) => {
|
|
|
422
422
|
var ProductType = /* @__PURE__ */ ((ProductType2) => {
|
|
423
423
|
ProductType2["SIMPLE"] = "simple";
|
|
424
424
|
ProductType2["VARIABLE"] = "variable";
|
|
425
|
-
ProductType2["BOOKING"] = "booking";
|
|
426
425
|
return ProductType2;
|
|
427
426
|
})(ProductType || {});
|
|
428
427
|
var ProductStatus = /* @__PURE__ */ ((ProductStatus2) => {
|
|
@@ -440,50 +439,6 @@ var PaymentProcessor = /* @__PURE__ */ ((PaymentProcessor2) => {
|
|
|
440
439
|
return PaymentProcessor2;
|
|
441
440
|
})(PaymentProcessor || {});
|
|
442
441
|
|
|
443
|
-
// enums/booking.enum.ts
|
|
444
|
-
var BookingType = /* @__PURE__ */ ((BookingType3) => {
|
|
445
|
-
BookingType3["RENTAL"] = "rental";
|
|
446
|
-
BookingType3["APPOINTMENT"] = "appointment";
|
|
447
|
-
BookingType3["OVERNIGHT"] = "overnight";
|
|
448
|
-
BookingType3["GROUP"] = "group";
|
|
449
|
-
return BookingType3;
|
|
450
|
-
})(BookingType || {});
|
|
451
|
-
var BookingRatePeriod = /* @__PURE__ */ ((BookingRatePeriod2) => {
|
|
452
|
-
BookingRatePeriod2["SLOT"] = "slot";
|
|
453
|
-
BookingRatePeriod2["DAY"] = "day";
|
|
454
|
-
BookingRatePeriod2["NIGHT"] = "night";
|
|
455
|
-
BookingRatePeriod2["WEEK"] = "week";
|
|
456
|
-
BookingRatePeriod2["MONTH"] = "month";
|
|
457
|
-
return BookingRatePeriod2;
|
|
458
|
-
})(BookingRatePeriod || {});
|
|
459
|
-
var BookingResourceKind = /* @__PURE__ */ ((BookingResourceKind2) => {
|
|
460
|
-
BookingResourceKind2["ASSET"] = "asset";
|
|
461
|
-
BookingResourceKind2["ROOM"] = "room";
|
|
462
|
-
BookingResourceKind2["PROPERTY"] = "property";
|
|
463
|
-
BookingResourceKind2["SPACE"] = "space";
|
|
464
|
-
BookingResourceKind2["STAFF"] = "staff";
|
|
465
|
-
BookingResourceKind2["TABLE"] = "table";
|
|
466
|
-
return BookingResourceKind2;
|
|
467
|
-
})(BookingResourceKind || {});
|
|
468
|
-
var ReservationStatus = /* @__PURE__ */ ((ReservationStatus2) => {
|
|
469
|
-
ReservationStatus2["HELD"] = "held";
|
|
470
|
-
ReservationStatus2["CONFIRMED"] = "confirmed";
|
|
471
|
-
ReservationStatus2["CANCELLED"] = "cancelled";
|
|
472
|
-
ReservationStatus2["EXPIRED"] = "expired";
|
|
473
|
-
return ReservationStatus2;
|
|
474
|
-
})(ReservationStatus || {});
|
|
475
|
-
var BookingResourceStatus = /* @__PURE__ */ ((BookingResourceStatus2) => {
|
|
476
|
-
BookingResourceStatus2["ACTIVE"] = "active";
|
|
477
|
-
BookingResourceStatus2["INACTIVE"] = "inactive";
|
|
478
|
-
return BookingResourceStatus2;
|
|
479
|
-
})(BookingResourceStatus || {});
|
|
480
|
-
var ResourceAvailabilityState = /* @__PURE__ */ ((ResourceAvailabilityState2) => {
|
|
481
|
-
ResourceAvailabilityState2["AVAILABLE"] = "available";
|
|
482
|
-
ResourceAvailabilityState2["FULLY_BOOKED"] = "fullyBooked";
|
|
483
|
-
ResourceAvailabilityState2["NO_HOURS_IN_RANGE"] = "noHoursInRange";
|
|
484
|
-
return ResourceAvailabilityState2;
|
|
485
|
-
})(ResourceAvailabilityState || {});
|
|
486
|
-
|
|
487
442
|
// utils/currency.util.ts
|
|
488
443
|
var CURRENCY_CONFIG = {
|
|
489
444
|
// Major currencies with 2 decimal places
|
|
@@ -1290,6 +1245,8 @@ var REPORT_NEGATE_PATHS = /* @__PURE__ */ new Set([
|
|
|
1290
1245
|
// Fees breakdown — refunded magnitudes are money flowing back out
|
|
1291
1246
|
"feesBreakdown.productFee.refundedProductFees",
|
|
1292
1247
|
"feesBreakdown.cartFee.refundedCartFees",
|
|
1248
|
+
// Modifiers breakdown — refunded magnitudes are money flowing back out
|
|
1249
|
+
"modifiersBreakdown.refundTotal",
|
|
1293
1250
|
// Discounts breakdown — refunded magnitudes reverse discount reductions
|
|
1294
1251
|
"discountsBreakdown.productDiscounts.refundedProductDiscounts",
|
|
1295
1252
|
"discountsBreakdown.cartDiscounts.refundedCartDiscounts",
|
|
@@ -1693,15 +1650,6 @@ function sumRefundQuantities(refunds) {
|
|
|
1693
1650
|
}
|
|
1694
1651
|
return t;
|
|
1695
1652
|
}
|
|
1696
|
-
var asCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
1697
|
-
function countsOf(row) {
|
|
1698
|
-
const quantity = asCount(row.quantity);
|
|
1699
|
-
return {
|
|
1700
|
-
quantity,
|
|
1701
|
-
fulfilled: Math.min(asCount(row.fulfilledQuantity), quantity),
|
|
1702
|
-
returned: Math.min(asCount(row.returnedQuantity), quantity)
|
|
1703
|
-
};
|
|
1704
|
-
}
|
|
1705
1653
|
function buildOrderContext(order, extras) {
|
|
1706
1654
|
const o = order ?? {};
|
|
1707
1655
|
const total = typeof o.total === "number" && Number.isFinite(o.total) ? o.total : 0;
|
|
@@ -1709,7 +1657,6 @@ function buildOrderContext(order, extras) {
|
|
|
1709
1657
|
const paymentMethods = o.paymentMethods ?? [];
|
|
1710
1658
|
const refunds = o.refunds ?? o.refund ?? [];
|
|
1711
1659
|
const lineItems = o.lineItems ?? [];
|
|
1712
|
-
const reservations = o.reservations ?? [];
|
|
1713
1660
|
const totalPaid = sumNumbers(paymentMethods);
|
|
1714
1661
|
const totalRefunded = sumRefundQuantities(refunds);
|
|
1715
1662
|
const isFullyPaid = total > 0 ? balance <= 0 : totalPaid > 0 && balance <= 0;
|
|
@@ -1717,11 +1664,13 @@ function buildOrderContext(order, extras) {
|
|
|
1717
1664
|
let itemCount = 0;
|
|
1718
1665
|
let fulfilledItemCount = 0;
|
|
1719
1666
|
let returnedItemCount = 0;
|
|
1720
|
-
for (const
|
|
1721
|
-
const
|
|
1722
|
-
itemCount +=
|
|
1723
|
-
|
|
1724
|
-
|
|
1667
|
+
for (const li of lineItems) {
|
|
1668
|
+
const q = typeof li.quantity === "number" && Number.isFinite(li.quantity) ? Math.max(0, li.quantity) : 0;
|
|
1669
|
+
itemCount += q;
|
|
1670
|
+
const fq = typeof li.fulfilledQuantity === "number" && Number.isFinite(li.fulfilledQuantity) ? Math.max(0, li.fulfilledQuantity) : 0;
|
|
1671
|
+
fulfilledItemCount += Math.min(fq, q);
|
|
1672
|
+
const rq = typeof li.returnedQuantity === "number" && Number.isFinite(li.returnedQuantity) ? Math.max(0, li.returnedQuantity) : 0;
|
|
1673
|
+
returnedItemCount += Math.min(rq, q);
|
|
1725
1674
|
}
|
|
1726
1675
|
const isFullyReturned = itemCount > 0 ? returnedItemCount >= itemCount : returnedItemCount > 0;
|
|
1727
1676
|
const hasPartialReturns = returnedItemCount > 0 && !isFullyReturned;
|
|
@@ -1731,8 +1680,7 @@ function buildOrderContext(order, extras) {
|
|
|
1731
1680
|
balance,
|
|
1732
1681
|
paymentMethods,
|
|
1733
1682
|
refunds,
|
|
1734
|
-
lineItems
|
|
1735
|
-
reservations
|
|
1683
|
+
lineItems
|
|
1736
1684
|
},
|
|
1737
1685
|
computed: {
|
|
1738
1686
|
balance,
|
|
@@ -2812,358 +2760,6 @@ var ORDER_STATE_EVENTS = {
|
|
|
2812
2760
|
configUpdated: "config-updated"
|
|
2813
2761
|
};
|
|
2814
2762
|
|
|
2815
|
-
// booking/types.ts
|
|
2816
|
-
var BookingRulesError = class extends Error {
|
|
2817
|
-
constructor(message) {
|
|
2818
|
-
super(message);
|
|
2819
|
-
this.name = "BookingRulesError";
|
|
2820
|
-
}
|
|
2821
|
-
};
|
|
2822
|
-
|
|
2823
|
-
// booking/zoned-time.ts
|
|
2824
|
-
var MINUTE = 6e4;
|
|
2825
|
-
var formatters = /* @__PURE__ */ new Map();
|
|
2826
|
-
var formatterFor = (timeZone) => {
|
|
2827
|
-
const existing = formatters.get(timeZone);
|
|
2828
|
-
if (existing) return existing;
|
|
2829
|
-
let created;
|
|
2830
|
-
try {
|
|
2831
|
-
created = new Intl.DateTimeFormat("en-US", {
|
|
2832
|
-
timeZone,
|
|
2833
|
-
hour12: false,
|
|
2834
|
-
year: "numeric",
|
|
2835
|
-
month: "2-digit",
|
|
2836
|
-
day: "2-digit",
|
|
2837
|
-
hour: "2-digit",
|
|
2838
|
-
minute: "2-digit",
|
|
2839
|
-
second: "2-digit",
|
|
2840
|
-
weekday: "short"
|
|
2841
|
-
});
|
|
2842
|
-
} catch {
|
|
2843
|
-
throw new BookingRulesError(`Booking rules: "${timeZone}" is not a known time zone`);
|
|
2844
|
-
}
|
|
2845
|
-
formatters.set(timeZone, created);
|
|
2846
|
-
return created;
|
|
2847
|
-
};
|
|
2848
|
-
var WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
2849
|
-
function zonedParts(instant, timeZone) {
|
|
2850
|
-
const parts = {};
|
|
2851
|
-
for (const { type, value } of formatterFor(timeZone).formatToParts(instant)) parts[type] = value;
|
|
2852
|
-
const hour = Number(parts.hour) % 24;
|
|
2853
|
-
return {
|
|
2854
|
-
year: Number(parts.year),
|
|
2855
|
-
month: Number(parts.month),
|
|
2856
|
-
day: Number(parts.day),
|
|
2857
|
-
hour,
|
|
2858
|
-
minute: Number(parts.minute),
|
|
2859
|
-
second: Number(parts.second),
|
|
2860
|
-
weekday: WEEKDAYS.indexOf(parts.weekday)
|
|
2861
|
-
};
|
|
2862
|
-
}
|
|
2863
|
-
function offsetAt(instant, timeZone) {
|
|
2864
|
-
const { year, month, day, hour, minute, second } = zonedParts(instant, timeZone);
|
|
2865
|
-
return Date.UTC(year, month - 1, day, hour, minute, second) - Math.floor(instant.getTime() / 1e3) * 1e3;
|
|
2866
|
-
}
|
|
2867
|
-
function instantFromLocal(year, month, day, minutes, timeZone) {
|
|
2868
|
-
const naive = Date.UTC(year, month - 1, day) + minutes * MINUTE;
|
|
2869
|
-
const firstGuess = naive - offsetAt(new Date(naive), timeZone);
|
|
2870
|
-
return new Date(naive - offsetAt(new Date(firstGuess), timeZone));
|
|
2871
|
-
}
|
|
2872
|
-
function startOfZonedDay(instant, timeZone) {
|
|
2873
|
-
const { year, month, day } = zonedParts(instant, timeZone);
|
|
2874
|
-
return instantFromLocal(year, month, day, 0, timeZone);
|
|
2875
|
-
}
|
|
2876
|
-
function nextZonedDay(dayStart, timeZone) {
|
|
2877
|
-
const { year, month, day } = zonedParts(dayStart, timeZone);
|
|
2878
|
-
return instantFromLocal(year, month, day + 1, 0, timeZone);
|
|
2879
|
-
}
|
|
2880
|
-
function atZonedMinute(dayStart, minutes, timeZone) {
|
|
2881
|
-
const { year, month, day } = zonedParts(dayStart, timeZone);
|
|
2882
|
-
return instantFromLocal(year, month, day, minutes, timeZone);
|
|
2883
|
-
}
|
|
2884
|
-
|
|
2885
|
-
// booking/grid.ts
|
|
2886
|
-
var MINUTE2 = 6e4;
|
|
2887
|
-
var MAX_RANGE_DAYS = 366;
|
|
2888
|
-
var asTime = (value, field) => {
|
|
2889
|
-
if (value == null) return void 0;
|
|
2890
|
-
const time = new Date(value).getTime();
|
|
2891
|
-
if (Number.isNaN(time)) throw new BookingRulesError(`Booking rules: ${field} is not a date, got "${String(value)}"`);
|
|
2892
|
-
return time;
|
|
2893
|
-
};
|
|
2894
|
-
function parseHhMm(value, field) {
|
|
2895
|
-
const [hours, minutes] = value.split(":").map(Number);
|
|
2896
|
-
if (!Number.isInteger(hours) || !Number.isInteger(minutes)) {
|
|
2897
|
-
throw new BookingRulesError(`Booking rules: ${field} must be "HH:mm", got "${value}"`);
|
|
2898
|
-
}
|
|
2899
|
-
return hours * 60 + minutes;
|
|
2900
|
-
}
|
|
2901
|
-
function windowEndsAt(rule, now) {
|
|
2902
|
-
const { maxAdvanceDays, bookableTo } = rule.window;
|
|
2903
|
-
const ends = Math.min(
|
|
2904
|
-
maxAdvanceDays ? now.getTime() + maxAdvanceDays * 1440 * MINUTE2 : Number.POSITIVE_INFINITY,
|
|
2905
|
-
asTime(bookableTo, "window.bookableTo") ?? Number.POSITIVE_INFINITY
|
|
2906
|
-
);
|
|
2907
|
-
return Number.isFinite(ends) ? new Date(ends) : null;
|
|
2908
|
-
}
|
|
2909
|
-
function periodSlot(dayStart, timeZone, rule, openMinute, closeMinute) {
|
|
2910
|
-
const { ratePeriod, slot, checkInTime, checkOutTime } = rule;
|
|
2911
|
-
const at = (minutes) => atZonedMinute(dayStart, minutes, timeZone);
|
|
2912
|
-
if (ratePeriod === "night" /* NIGHT */) {
|
|
2913
|
-
if (!checkInTime || !checkOutTime) {
|
|
2914
|
-
throw new BookingRulesError("Overnight booking rules need both checkInTime and checkOutTime");
|
|
2915
|
-
}
|
|
2916
|
-
const checkIn = parseHhMm(checkInTime, "checkInTime");
|
|
2917
|
-
const checkOut = parseHhMm(checkOutTime, "checkOutTime");
|
|
2918
|
-
return {
|
|
2919
|
-
startAt: at(checkIn),
|
|
2920
|
-
endAt: at(1440 + checkOut),
|
|
2921
|
-
// Housekeeping sits between check-out and the next check-in: it costs no night.
|
|
2922
|
-
bufferEndAt: at(1440 + checkIn),
|
|
2923
|
-
canStart: true
|
|
2924
|
-
};
|
|
2925
|
-
}
|
|
2926
|
-
const from = ratePeriod === "day" /* DAY */ ? openMinute ?? 0 : 0;
|
|
2927
|
-
const to = ratePeriod === "day" /* DAY */ ? closeMinute ?? 1440 : 1440;
|
|
2928
|
-
return {
|
|
2929
|
-
startAt: at(from),
|
|
2930
|
-
endAt: at(to),
|
|
2931
|
-
bufferEndAt: at(to + (slot.bufferMinutes ?? 0)),
|
|
2932
|
-
canStart: true
|
|
2933
|
-
};
|
|
2934
|
-
}
|
|
2935
|
-
function freeGaps(openAt, closeAt, busy) {
|
|
2936
|
-
const inside = busy.map(({ startAt, bufferEndAt }) => ({ from: +startAt, to: +bufferEndAt })).filter(({ from, to }) => to > openAt && from < closeAt).sort((first, second) => first.from - second.from);
|
|
2937
|
-
const gaps = [];
|
|
2938
|
-
let cursor = openAt;
|
|
2939
|
-
for (const { from, to } of inside) {
|
|
2940
|
-
if (from > cursor) gaps.push({ from: cursor, to: from, endsAtBooking: true });
|
|
2941
|
-
cursor = Math.max(cursor, to);
|
|
2942
|
-
if (cursor >= closeAt) break;
|
|
2943
|
-
}
|
|
2944
|
-
if (cursor < closeAt) gaps.push({ from: cursor, to: closeAt, endsAtBooking: false });
|
|
2945
|
-
return gaps;
|
|
2946
|
-
}
|
|
2947
|
-
function buildGrid(rule, timeZone, from, to, now, busy = []) {
|
|
2948
|
-
if (to.getTime() - from.getTime() > MAX_RANGE_DAYS * 1440 * MINUTE2) {
|
|
2949
|
-
throw new BookingRulesError(`A booking range may span at most ${MAX_RANGE_DAYS} days`);
|
|
2950
|
-
}
|
|
2951
|
-
const { availability, slot, window, ratePeriod } = rule;
|
|
2952
|
-
const periodMode = ratePeriod !== "slot" /* SLOT */;
|
|
2953
|
-
const weeklyHours = availability.weeklyHours ?? [];
|
|
2954
|
-
const notBefore = Math.max(
|
|
2955
|
-
now.getTime() + (window.minNoticeMinutes ?? 0) * MINUTE2,
|
|
2956
|
-
asTime(window.bookableFrom, "window.bookableFrom") ?? 0,
|
|
2957
|
-
asTime(availability.availableFrom, "availability.availableFrom") ?? 0
|
|
2958
|
-
);
|
|
2959
|
-
const notAfter = windowEndsAt(rule, now)?.getTime() ?? Number.POSITIVE_INFINITY;
|
|
2960
|
-
const blocked = (availability.offMarket ?? []).map(({ from: blockFrom, to: blockTo }) => ({
|
|
2961
|
-
from: asTime(blockFrom, "availability.offMarket.from"),
|
|
2962
|
-
to: asTime(blockTo, "availability.offMarket.to")
|
|
2963
|
-
}));
|
|
2964
|
-
const arrivalDays = availability.arrivalDays;
|
|
2965
|
-
const step = slot.lengthMinutes + (slot.bufferMinutes ?? 0);
|
|
2966
|
-
const slots = [];
|
|
2967
|
-
const lastDay = startOfZonedDay(to, timeZone).getTime();
|
|
2968
|
-
let dayStart = startOfZonedDay(from, timeZone);
|
|
2969
|
-
while (dayStart.getTime() <= lastDay) {
|
|
2970
|
-
const weekday = zonedParts(dayStart, timeZone).weekday;
|
|
2971
|
-
const rows = weeklyHours.filter((entry) => entry.weekday === weekday);
|
|
2972
|
-
if (periodMode) {
|
|
2973
|
-
const openToday = weeklyHours.length === 0 || rows.length > 0;
|
|
2974
|
-
if (openToday) {
|
|
2975
|
-
const built = periodSlot(dayStart, timeZone, rule, rows[0]?.openMinute, rows[0]?.closeMinute);
|
|
2976
|
-
slots.push({ ...built, canStart: !arrivalDays?.length || arrivalDays.includes(weekday) });
|
|
2977
|
-
}
|
|
2978
|
-
} else {
|
|
2979
|
-
for (const row of rows) {
|
|
2980
|
-
const openAt = +atZonedMinute(dayStart, row.openMinute, timeZone);
|
|
2981
|
-
const closeAt = +atZonedMinute(dayStart, row.closeMinute, timeZone);
|
|
2982
|
-
for (const gap of freeGaps(openAt, closeAt, busy)) {
|
|
2983
|
-
const mustFit = (gap.endsAtBooking ? step : slot.lengthMinutes) * MINUTE2;
|
|
2984
|
-
for (let start = gap.from; start + mustFit <= gap.to; start += step * MINUTE2) {
|
|
2985
|
-
slots.push({
|
|
2986
|
-
startAt: new Date(start),
|
|
2987
|
-
endAt: new Date(start + slot.lengthMinutes * MINUTE2),
|
|
2988
|
-
bufferEndAt: new Date(start + step * MINUTE2),
|
|
2989
|
-
canStart: true
|
|
2990
|
-
});
|
|
2991
|
-
}
|
|
2992
|
-
}
|
|
2993
|
-
}
|
|
2994
|
-
}
|
|
2995
|
-
dayStart = nextZonedDay(dayStart, timeZone);
|
|
2996
|
-
}
|
|
2997
|
-
const askedFrom = from.getTime();
|
|
2998
|
-
const askedTo = to.getTime();
|
|
2999
|
-
return slots.filter(({ startAt, bufferEndAt }) => {
|
|
3000
|
-
const start = startAt.getTime();
|
|
3001
|
-
if (start < askedFrom || start >= askedTo) return false;
|
|
3002
|
-
if (start < notBefore || start > notAfter) return false;
|
|
3003
|
-
return !blocked.some(({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom);
|
|
3004
|
-
}).sort((first, second) => +first.startAt - +second.startAt);
|
|
3005
|
-
}
|
|
3006
|
-
function slotsForWindow(grid, startAt, endAt) {
|
|
3007
|
-
const startIndex = grid.findIndex((entry) => entry.startAt.getTime() === startAt.getTime());
|
|
3008
|
-
if (startIndex < 0) {
|
|
3009
|
-
throw new BookingRulesError(`${startAt.toISOString()} is not a bookable slot start for these rules`);
|
|
3010
|
-
}
|
|
3011
|
-
if (!grid[startIndex].canStart) {
|
|
3012
|
-
throw new BookingRulesError(`${startAt.toISOString()} is not an arrival day for these rules`);
|
|
3013
|
-
}
|
|
3014
|
-
const covered = [];
|
|
3015
|
-
for (let index = startIndex; index < grid.length; index++) {
|
|
3016
|
-
const entry = grid[index];
|
|
3017
|
-
const previous = covered[covered.length - 1];
|
|
3018
|
-
if (previous && entry.startAt.getTime() !== previous.bufferEndAt.getTime()) break;
|
|
3019
|
-
covered.push(entry);
|
|
3020
|
-
if (entry.endAt.getTime() === endAt.getTime()) return covered;
|
|
3021
|
-
if (entry.endAt.getTime() > endAt.getTime()) break;
|
|
3022
|
-
}
|
|
3023
|
-
throw new BookingRulesError(
|
|
3024
|
-
`${startAt.toISOString()} \u2192 ${endAt.toISOString()} does not line up with the bookable grid`
|
|
3025
|
-
);
|
|
3026
|
-
}
|
|
3027
|
-
|
|
3028
|
-
// booking/availability.ts
|
|
3029
|
-
var withinPresence = (slot, timeZone, presence) => {
|
|
3030
|
-
if (!presence?.length) return true;
|
|
3031
|
-
const dayStart = startOfZonedDay(slot.startAt, timeZone);
|
|
3032
|
-
const weekday = zonedParts(dayStart, timeZone).weekday;
|
|
3033
|
-
return presence.some(
|
|
3034
|
-
(shift) => shift.weekday === weekday && slot.startAt.getTime() >= atZonedMinute(dayStart, shift.openMinute, timeZone).getTime() && slot.endAt.getTime() <= atZonedMinute(dayStart, shift.closeMinute, timeZone).getTime()
|
|
3035
|
-
);
|
|
3036
|
-
};
|
|
3037
|
-
var overlaps = (entry, startAt, bufferEndAt) => entry.startAt.getTime() < bufferEndAt.getTime() && entry.bufferEndAt.getTime() > startAt.getTime();
|
|
3038
|
-
function occupancyIn(occupied, productId, startAt, bufferEndAt, shared) {
|
|
3039
|
-
const clashing = occupied.filter((entry) => overlaps(entry, startAt, bufferEndAt));
|
|
3040
|
-
const foreign = shared ? void 0 : clashing.find((entry) => entry.productId !== productId);
|
|
3041
|
-
return {
|
|
3042
|
-
taken: clashing.filter((entry) => shared || entry.productId === productId).reduce((sum, entry) => sum + entry.qty, 0),
|
|
3043
|
-
foreignProductId: foreign?.productId
|
|
3044
|
-
};
|
|
3045
|
-
}
|
|
3046
|
-
var stateFor = (hasShift, offerable, free) => {
|
|
3047
|
-
if (offerable === 0) {
|
|
3048
|
-
return hasShift() ? "fullyBooked" /* FULLY_BOOKED */ : "noHoursInRange" /* NO_HOURS_IN_RANGE */;
|
|
3049
|
-
}
|
|
3050
|
-
return free === 0 ? "fullyBooked" /* FULLY_BOOKED */ : "available" /* AVAILABLE */;
|
|
3051
|
-
};
|
|
3052
|
-
function buildAvailability({
|
|
3053
|
-
productId,
|
|
3054
|
-
bookingRulesId,
|
|
3055
|
-
bookingType,
|
|
3056
|
-
rule,
|
|
3057
|
-
resources,
|
|
3058
|
-
occupied,
|
|
3059
|
-
from,
|
|
3060
|
-
to,
|
|
3061
|
-
now
|
|
3062
|
-
}) {
|
|
3063
|
-
const shared = rule.slot.sharedAcrossProducts ?? false;
|
|
3064
|
-
const bookableUntil = windowEndsAt(rule, now);
|
|
3065
|
-
const header = {
|
|
3066
|
-
productId,
|
|
3067
|
-
bookingRulesId,
|
|
3068
|
-
bookingType,
|
|
3069
|
-
ratePeriod: rule.ratePeriod,
|
|
3070
|
-
// Passed through for the UI to label: a group slot under minimumToRun may not run.
|
|
3071
|
-
minimumToRun: rule.slot.minimumToRun,
|
|
3072
|
-
minStay: rule.slot.minStay,
|
|
3073
|
-
maxStay: rule.slot.maxStay,
|
|
3074
|
-
// Why a day can come back empty. Past this instant the rule refuses everything, so the UI
|
|
3075
|
-
// can say "beyond the booking window" where it would otherwise claim "nothing free".
|
|
3076
|
-
bookableUntil,
|
|
3077
|
-
// The comparison itself, not just the boundary: deciding whether a day lies past the
|
|
3078
|
-
// window is a rule, and a UI that re-derives it owns a copy of that rule — which is how
|
|
3079
|
-
// the same empty grid ends up explained one way on the till and another in a flow.
|
|
3080
|
-
beyondWindow: !!bookableUntil && from.getTime() > bookableUntil.getTime()
|
|
3081
|
-
};
|
|
3082
|
-
const byResource = /* @__PURE__ */ new Map();
|
|
3083
|
-
for (const entry of occupied) {
|
|
3084
|
-
byResource.set(entry.resourceId, [...byResource.get(entry.resourceId) ?? [], entry]);
|
|
3085
|
-
}
|
|
3086
|
-
const grids = resources.map((resource) => {
|
|
3087
|
-
const mine = byResource.get(resource.id) ?? [];
|
|
3088
|
-
const busy = (resource.seats ?? 1) === 1 ? mine : [];
|
|
3089
|
-
const slots = buildGrid(rule, resource.timeZone, from, to, now, busy).filter(
|
|
3090
|
-
(slot) => withinPresence(slot, resource.timeZone, resource.presence)
|
|
3091
|
-
);
|
|
3092
|
-
return { resource, slots };
|
|
3093
|
-
});
|
|
3094
|
-
const byInstant = /* @__PURE__ */ new Map();
|
|
3095
|
-
const freeSlotsPerResource = /* @__PURE__ */ new Map();
|
|
3096
|
-
for (const { resource, slots } of grids) {
|
|
3097
|
-
const capacity = resource.seats ?? 1;
|
|
3098
|
-
const mine = byResource.get(resource.id) ?? [];
|
|
3099
|
-
for (const slot of slots) {
|
|
3100
|
-
const { taken, foreignProductId } = occupancyIn(mine, productId, slot.startAt, slot.bufferEndAt, shared);
|
|
3101
|
-
const free = foreignProductId ? 0 : Math.max(capacity - taken, 0);
|
|
3102
|
-
const entry = byInstant.get(+slot.startAt) ?? { ...slot, capacity: 0, booked: 0, free: 0, resources: [] };
|
|
3103
|
-
entry.capacity += capacity;
|
|
3104
|
-
entry.booked += foreignProductId ? capacity : taken;
|
|
3105
|
-
entry.free += free;
|
|
3106
|
-
entry.canStart = entry.canStart || slot.canStart;
|
|
3107
|
-
entry.resources.push({ resourceId: resource.id, name: resource.name, capacity, booked: taken, free });
|
|
3108
|
-
if (free > 0) freeSlotsPerResource.set(resource.id, (freeSlotsPerResource.get(resource.id) ?? 0) + 1);
|
|
3109
|
-
byInstant.set(+slot.startAt, entry);
|
|
3110
|
-
}
|
|
3111
|
-
}
|
|
3112
|
-
const inWindow = grids.map(({ resource, slots }) => {
|
|
3113
|
-
const free = freeSlotsPerResource.get(resource.id) ?? 0;
|
|
3114
|
-
return {
|
|
3115
|
-
resourceId: resource.id,
|
|
3116
|
-
name: resource.name,
|
|
3117
|
-
slots: slots.length,
|
|
3118
|
-
free,
|
|
3119
|
-
// Lazily: the shift grid is a second walk, and only an empty answer needs it.
|
|
3120
|
-
state: stateFor(
|
|
3121
|
-
() => buildGrid(rule, resource.timeZone, from, to, now).filter(
|
|
3122
|
-
(slot) => withinPresence(slot, resource.timeZone, resource.presence)
|
|
3123
|
-
).length > 0,
|
|
3124
|
-
slots.length,
|
|
3125
|
-
free
|
|
3126
|
-
)
|
|
3127
|
-
};
|
|
3128
|
-
});
|
|
3129
|
-
return {
|
|
3130
|
-
...header,
|
|
3131
|
-
resources: inWindow,
|
|
3132
|
-
slots: [...byInstant.values()].sort((first, second) => +first.startAt - +second.startAt)
|
|
3133
|
-
};
|
|
3134
|
-
}
|
|
3135
|
-
function presentAt(resources, rows, outletId) {
|
|
3136
|
-
const byResource = /* @__PURE__ */ new Map();
|
|
3137
|
-
for (const row of rows) {
|
|
3138
|
-
byResource.set(row.resourceId, [...byResource.get(row.resourceId) ?? [], row]);
|
|
3139
|
-
}
|
|
3140
|
-
return resources.flatMap((resource) => {
|
|
3141
|
-
const own = byResource.get(resource.id) ?? [];
|
|
3142
|
-
if (own.length === 0 || !outletId) return [{ resource }];
|
|
3143
|
-
const here = own.find((row) => row.outletId === outletId);
|
|
3144
|
-
return here ? [{ resource, presence: here.weeklyHours }] : [];
|
|
3145
|
-
});
|
|
3146
|
-
}
|
|
3147
|
-
|
|
3148
|
-
// booking/plan.ts
|
|
3149
|
-
var DEFAULT_HOLD_MINUTES = 10;
|
|
3150
|
-
function planWindow({ rule, timeZone, presence, startAt, endAt, now, busy = [] }) {
|
|
3151
|
-
const grid = buildGrid(rule, timeZone, startAt, endAt, now, busy).filter(
|
|
3152
|
-
(slot) => withinPresence(slot, timeZone, presence)
|
|
3153
|
-
);
|
|
3154
|
-
const slots = slotsForWindow(grid, startAt, endAt);
|
|
3155
|
-
const { minStay, maxStay } = rule.slot;
|
|
3156
|
-
if (minStay && slots.length < minStay) {
|
|
3157
|
-
throw new BookingRulesError(`These rules ask for at least ${minStay} period(s), got ${slots.length}`);
|
|
3158
|
-
}
|
|
3159
|
-
if (maxStay && slots.length > maxStay) {
|
|
3160
|
-
throw new BookingRulesError(`These rules allow at most ${maxStay} period(s), got ${slots.length}`);
|
|
3161
|
-
}
|
|
3162
|
-
const last = slots[slots.length - 1];
|
|
3163
|
-
return { slots, endAt: last.endAt, bufferEndAt: last.bufferEndAt };
|
|
3164
|
-
}
|
|
3165
|
-
var holdExpiresAt = (rule, now) => new Date(now.getTime() + (rule.window.holdMinutes ?? DEFAULT_HOLD_MINUTES) * 6e4);
|
|
3166
|
-
|
|
3167
2763
|
// constants/adyen.constants.ts
|
|
3168
2764
|
var ADYEN_ROLLOUT_COUNTRY_CODES = /* @__PURE__ */ new Set(["CA"]);
|
|
3169
2765
|
function isAdyenRolloutCountry(alpha2) {
|
|
@@ -3172,11 +2768,6 @@ function isAdyenRolloutCountry(alpha2) {
|
|
|
3172
2768
|
export {
|
|
3173
2769
|
ADYEN_ROLLOUT_COUNTRY_CODES,
|
|
3174
2770
|
AttributeType,
|
|
3175
|
-
BookingRatePeriod,
|
|
3176
|
-
BookingResourceKind,
|
|
3177
|
-
BookingResourceStatus,
|
|
3178
|
-
BookingRulesError,
|
|
3179
|
-
BookingType,
|
|
3180
2771
|
CONDITION_OPERATORS,
|
|
3181
2772
|
COUNTRY_CURRENCIES,
|
|
3182
2773
|
CURRENCY_CONFIG,
|
|
@@ -3187,7 +2778,6 @@ export {
|
|
|
3187
2778
|
DEFAULT_CONFIG,
|
|
3188
2779
|
DEFAULT_CROSS_AXIS_RULES,
|
|
3189
2780
|
DEFAULT_DISPLAY_STATE_MAP,
|
|
3190
|
-
DEFAULT_HOLD_MINUTES,
|
|
3191
2781
|
DEFAULT_RESOLVED_CONFIG,
|
|
3192
2782
|
DEFAULT_UNIT_ID,
|
|
3193
2783
|
DualWriteStrategy,
|
|
@@ -3231,10 +2821,8 @@ export {
|
|
|
3231
2821
|
REPORT_NEGATE_PATHS,
|
|
3232
2822
|
ReceiptSendChannel,
|
|
3233
2823
|
ReceiptType,
|
|
3234
|
-
ReservationStatus,
|
|
3235
2824
|
ResidualPaymentMethod,
|
|
3236
2825
|
ResidualStatus,
|
|
3237
|
-
ResourceAvailabilityState,
|
|
3238
2826
|
STATE_SCHEMA,
|
|
3239
2827
|
STRIPE_SUPPORTED_COUNTRIES,
|
|
3240
2828
|
SYSTEM_FAMILIES,
|
|
@@ -3255,8 +2843,6 @@ export {
|
|
|
3255
2843
|
baseFulfillmentDefinitions,
|
|
3256
2844
|
baseOf,
|
|
3257
2845
|
basePaymentDefinitions,
|
|
3258
|
-
buildAvailability,
|
|
3259
|
-
buildGrid,
|
|
3260
2846
|
buildOrderContext,
|
|
3261
2847
|
buildPosPresetConfig,
|
|
3262
2848
|
buildResolvedConfig,
|
|
@@ -3296,7 +2882,6 @@ export {
|
|
|
3296
2882
|
getPricingRegionFromCountry,
|
|
3297
2883
|
getSettlementTotal,
|
|
3298
2884
|
getTotalCollected,
|
|
3299
|
-
holdExpiresAt,
|
|
3300
2885
|
inferStatesFromLegacyStatus,
|
|
3301
2886
|
isAdyenRolloutCountry,
|
|
3302
2887
|
isConditionOperator,
|
|
@@ -3311,12 +2896,9 @@ export {
|
|
|
3311
2896
|
mergeFragment,
|
|
3312
2897
|
multiplyAmount,
|
|
3313
2898
|
normalizeMongoIdString,
|
|
3314
|
-
occupancyIn,
|
|
3315
2899
|
parseAmountToMinorUnits,
|
|
3316
2900
|
parseCurrencyCode,
|
|
3317
2901
|
parseStripeCurrencyCode,
|
|
3318
|
-
planWindow,
|
|
3319
|
-
presentAt,
|
|
3320
2902
|
removeFragment,
|
|
3321
2903
|
resolveFamily,
|
|
3322
2904
|
resolveTemplate,
|
|
@@ -3325,15 +2907,12 @@ export {
|
|
|
3325
2907
|
sameFamily,
|
|
3326
2908
|
setOrderState,
|
|
3327
2909
|
signReportForDisplay,
|
|
3328
|
-
slotsForWindow,
|
|
3329
2910
|
stringifyMinorUnits,
|
|
3330
2911
|
subtractAmounts,
|
|
3331
2912
|
toBase,
|
|
3332
2913
|
toMinorUnits,
|
|
3333
2914
|
validateConfig,
|
|
3334
2915
|
validateFragmentCompatibility,
|
|
3335
|
-
violatesFinancialInvariant
|
|
3336
|
-
windowEndsAt,
|
|
3337
|
-
withinPresence
|
|
2916
|
+
violatesFinancialInvariant
|
|
3338
2917
|
};
|
|
3339
2918
|
//# sourceMappingURL=index.mjs.map
|