@final-commerce/common 2.5.1-preprod.3 → 2.6.0-staging.1
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 +182 -33
- package/dist/index.d.ts +182 -33
- package/dist/index.js +352 -62
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +335 -57
- package/dist/index.mjs.map +1 -1
- package/dist/pos-types/index.d.mts +175 -121
- package/dist/pos-types/index.d.ts +175 -121
- package/dist/pos-types/index.js +0 -1
- package/dist/pos-types/index.js.map +1 -1
- package/dist/pos-types/index.mjs +0 -1
- package/dist/pos-types/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,7 +20,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var index_exports = {};
|
|
21
21
|
__export(index_exports, {
|
|
22
22
|
ADYEN_ROLLOUT_COUNTRY_CODES: () => ADYEN_ROLLOUT_COUNTRY_CODES,
|
|
23
|
-
AFFIRM_PRICE_PLANS: () => AFFIRM_PRICE_PLANS,
|
|
24
23
|
AttributeType: () => AttributeType,
|
|
25
24
|
BookingRatePeriod: () => BookingRatePeriod,
|
|
26
25
|
BookingResourceKind: () => BookingResourceKind,
|
|
@@ -99,6 +98,7 @@ __export(index_exports, {
|
|
|
99
98
|
UserTypes: () => UserTypes,
|
|
100
99
|
addAmounts: () => addAmounts,
|
|
101
100
|
amountToString: () => amountToString,
|
|
101
|
+
ancestorChain: () => ancestorChain,
|
|
102
102
|
assertUnitSane: () => assertUnitSane,
|
|
103
103
|
availableIn: () => availableIn,
|
|
104
104
|
baseFor: () => baseFor,
|
|
@@ -112,6 +112,14 @@ __export(index_exports, {
|
|
|
112
112
|
buildResolvedConfig: () => buildResolvedConfig,
|
|
113
113
|
calculatePercentage: () => calculatePercentage,
|
|
114
114
|
canTransition: () => canTransition,
|
|
115
|
+
cheapestCompositeItem: () => cheapestCompositeItem,
|
|
116
|
+
cheapestCompositePrice: () => cheapestCompositePrice,
|
|
117
|
+
compositeItemCost: () => compositeItemCost,
|
|
118
|
+
compositeItemUnavailable: () => compositeItemUnavailable,
|
|
119
|
+
compositeMakeable: () => compositeMakeable,
|
|
120
|
+
compositeModifierMismatches: () => compositeModifierMismatches,
|
|
121
|
+
compositeReservationMismatches: () => compositeReservationMismatches,
|
|
122
|
+
compositeVariantInStock: () => compositeVariantInStock,
|
|
115
123
|
conditionSummary: () => conditionSummary,
|
|
116
124
|
convertFixedFeeToLocalCurrency: () => convertFixedFeeToLocalCurrency,
|
|
117
125
|
createAmount: () => createAmount,
|
|
@@ -146,14 +154,13 @@ __export(index_exports, {
|
|
|
146
154
|
getPricingRegionFromCountry: () => getPricingRegionFromCountry,
|
|
147
155
|
getSettlementTotal: () => getSettlementTotal,
|
|
148
156
|
getTotalCollected: () => getTotalCollected,
|
|
149
|
-
|
|
157
|
+
hasSellableChoice: () => hasSellableChoice,
|
|
150
158
|
holdExpiresAt: () => holdExpiresAt,
|
|
151
159
|
inferStatesFromLegacyStatus: () => inferStatesFromLegacyStatus,
|
|
152
160
|
isAdyenRolloutCountry: () => isAdyenRolloutCountry,
|
|
153
|
-
|
|
161
|
+
isChoiceAvailableAtOutlet: () => isChoiceAvailableAtOutlet,
|
|
154
162
|
isConditionOperator: () => isConditionOperator,
|
|
155
163
|
isFulfillmentState: () => isFulfillmentState,
|
|
156
|
-
isInCart: () => isInCart,
|
|
157
164
|
isPaymentState: () => isPaymentState,
|
|
158
165
|
isSupportedCurrency: () => isSupportedCurrency,
|
|
159
166
|
isSystemFamilyCode: () => isSystemFamilyCode,
|
|
@@ -162,6 +169,7 @@ __export(index_exports, {
|
|
|
162
169
|
mapToLegacyStatus: () => mapToLegacyStatus,
|
|
163
170
|
maxPrecisionFor: () => maxPrecisionFor,
|
|
164
171
|
mergeFragment: () => mergeFragment,
|
|
172
|
+
modifiersSellable: () => modifiersSellable,
|
|
165
173
|
multiplyAmount: () => multiplyAmount,
|
|
166
174
|
normalizeMongoIdString: () => normalizeMongoIdString,
|
|
167
175
|
occupancyIn: () => occupancyIn,
|
|
@@ -169,9 +177,14 @@ __export(index_exports, {
|
|
|
169
177
|
parseCurrencyCode: () => parseCurrencyCode,
|
|
170
178
|
parseStripeCurrencyCode: () => parseStripeCurrencyCode,
|
|
171
179
|
planWindow: () => planWindow,
|
|
180
|
+
poolDemand: () => poolDemand,
|
|
181
|
+
poolOf: () => poolOf,
|
|
182
|
+
poolShortfall: () => poolShortfall,
|
|
183
|
+
poolStockAt: () => poolStockAt,
|
|
172
184
|
presentAt: () => presentAt,
|
|
173
185
|
removeFragment: () => removeFragment,
|
|
174
186
|
resolveFamily: () => resolveFamily,
|
|
187
|
+
resolveModifiersForProduct: () => resolveModifiersForProduct,
|
|
175
188
|
resolveTemplate: () => resolveTemplate,
|
|
176
189
|
resolveUnit: () => resolveUnit,
|
|
177
190
|
roundToPrecision: () => roundToPrecision,
|
|
@@ -187,7 +200,6 @@ __export(index_exports, {
|
|
|
187
200
|
toMinorUnits: () => toMinorUnits,
|
|
188
201
|
validateConfig: () => validateConfig,
|
|
189
202
|
validateFragmentCompatibility: () => validateFragmentCompatibility,
|
|
190
|
-
validateOrderStatuses: () => validateOrderStatuses,
|
|
191
203
|
violatesFinancialInvariant: () => violatesFinancialInvariant,
|
|
192
204
|
windowEndsAt: () => windowEndsAt,
|
|
193
205
|
withinPresence: () => withinPresence
|
|
@@ -433,7 +445,6 @@ var InventorySpecificActionType = /* @__PURE__ */ ((InventorySpecificActionType2
|
|
|
433
445
|
InventorySpecificActionType2["PO_RECEIVED"] = "PO_RECEIVED";
|
|
434
446
|
InventorySpecificActionType2["EXTERNAL_SALE"] = "EXTERNAL_SALE";
|
|
435
447
|
InventorySpecificActionType2["EXTERNAL_RESTOCK"] = "EXTERNAL_RESTOCK";
|
|
436
|
-
InventorySpecificActionType2["CUSTOM"] = "CUSTOM";
|
|
437
448
|
return InventorySpecificActionType2;
|
|
438
449
|
})(InventorySpecificActionType || {});
|
|
439
450
|
var ExtensionCategory = /* @__PURE__ */ ((ExtensionCategory2) => {
|
|
@@ -620,6 +631,7 @@ var ProductType = /* @__PURE__ */ ((ProductType2) => {
|
|
|
620
631
|
ProductType2["SIMPLE"] = "simple";
|
|
621
632
|
ProductType2["VARIABLE"] = "variable";
|
|
622
633
|
ProductType2["BOOKING"] = "booking";
|
|
634
|
+
ProductType2["COMPOSITE"] = "composite";
|
|
623
635
|
return ProductType2;
|
|
624
636
|
})(ProductType || {});
|
|
625
637
|
var ProductStatus = /* @__PURE__ */ ((ProductStatus2) => {
|
|
@@ -1550,6 +1562,312 @@ function normalizeMongoIdString(value) {
|
|
|
1550
1562
|
return null;
|
|
1551
1563
|
}
|
|
1552
1564
|
|
|
1565
|
+
// utils/composite-price.util.ts
|
|
1566
|
+
var compositeItemCost = (item) => item.extraCharge * item.quantity;
|
|
1567
|
+
function cheapestCompositeItem(items) {
|
|
1568
|
+
let cheapest;
|
|
1569
|
+
items.forEach((item, index) => {
|
|
1570
|
+
if (!item.available) return;
|
|
1571
|
+
if (cheapest === void 0 || compositeItemCost(item) < compositeItemCost(items[cheapest])) cheapest = index;
|
|
1572
|
+
});
|
|
1573
|
+
return cheapest;
|
|
1574
|
+
}
|
|
1575
|
+
function cheapestCompositePrice(parts) {
|
|
1576
|
+
let total = 0;
|
|
1577
|
+
for (const part of parts) {
|
|
1578
|
+
if (part.min === 0) continue;
|
|
1579
|
+
const index = cheapestCompositeItem(part.items) ?? cheapestCompositeItem(part.items.map((item) => ({ ...item, available: true })));
|
|
1580
|
+
if (index === void 0) return void 0;
|
|
1581
|
+
total += part.min * compositeItemCost(part.items[index]);
|
|
1582
|
+
}
|
|
1583
|
+
return total;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
// utils/modifier-resolution.util.ts
|
|
1587
|
+
function ancestorChain(categoryId, categoriesById) {
|
|
1588
|
+
const chain = [];
|
|
1589
|
+
const seen = /* @__PURE__ */ new Set([categoryId]);
|
|
1590
|
+
let current = categoriesById.get(categoryId)?.parentId ?? void 0;
|
|
1591
|
+
while (current && !seen.has(current)) {
|
|
1592
|
+
chain.push(current);
|
|
1593
|
+
seen.add(current);
|
|
1594
|
+
current = categoriesById.get(current)?.parentId ?? void 0;
|
|
1595
|
+
}
|
|
1596
|
+
return chain;
|
|
1597
|
+
}
|
|
1598
|
+
function clampRule(rule) {
|
|
1599
|
+
if (rule.min != null && rule.max != null && rule.min > rule.max) {
|
|
1600
|
+
return { ...rule, min: rule.max };
|
|
1601
|
+
}
|
|
1602
|
+
return rule;
|
|
1603
|
+
}
|
|
1604
|
+
function mergeRules(a, b) {
|
|
1605
|
+
const required = a.required || b.required;
|
|
1606
|
+
const min = a.min == null ? b.min : b.min == null ? a.min : Math.max(a.min, b.min);
|
|
1607
|
+
const max = a.max == null ? b.max : b.max == null ? a.max : Math.min(a.max, b.max);
|
|
1608
|
+
return clampRule({ required, min, max, sortOrder: Math.min(a.sortOrder, b.sortOrder) });
|
|
1609
|
+
}
|
|
1610
|
+
function toRule(row) {
|
|
1611
|
+
const required = row.required ?? false;
|
|
1612
|
+
return clampRule({
|
|
1613
|
+
required,
|
|
1614
|
+
// `required` is the gate: a stale min left on a placement that went optional is
|
|
1615
|
+
// ignored. A cleared min/max stays the explicit null it arrived as — nothing
|
|
1616
|
+
// downstream distinguishes null from absent.
|
|
1617
|
+
min: required ? row.min : void 0,
|
|
1618
|
+
max: row.max,
|
|
1619
|
+
sortOrder: row.sortOrder ?? 0
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
function toResolvedChoices(modifier) {
|
|
1623
|
+
return (modifier.choices ?? []).map((choice, index) => ({
|
|
1624
|
+
_id: String(choice._id),
|
|
1625
|
+
name: choice.name,
|
|
1626
|
+
price: choice.price ?? 0,
|
|
1627
|
+
isDefault: choice.isDefault,
|
|
1628
|
+
defaultQuantity: choice.defaultQuantity,
|
|
1629
|
+
unavailableOutletIds: choice.unavailableOutletIds?.map(String),
|
|
1630
|
+
sortOrder: choice.sortOrder ?? index
|
|
1631
|
+
})).sort((a, b) => a.sortOrder - b.sortOrder);
|
|
1632
|
+
}
|
|
1633
|
+
function resolveModifiersForProduct(inputs) {
|
|
1634
|
+
const categoriesById = new Map(
|
|
1635
|
+
inputs.categories.filter((category) => !category.isDeleted).map((category) => [String(category._id), category])
|
|
1636
|
+
);
|
|
1637
|
+
const modifiersById = new Map(
|
|
1638
|
+
inputs.modifiers.filter((modifier) => !modifier.isDeleted).map((modifier) => [String(modifier._id), modifier])
|
|
1639
|
+
);
|
|
1640
|
+
const directCategories = new Set(inputs.categoryIds.map(String));
|
|
1641
|
+
const inheritedCategories = /* @__PURE__ */ new Set();
|
|
1642
|
+
for (const categoryId of directCategories) {
|
|
1643
|
+
for (const ancestor of ancestorChain(categoryId, categoriesById)) {
|
|
1644
|
+
if (!directCategories.has(ancestor)) inheritedCategories.add(ancestor);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
const rules = /* @__PURE__ */ new Map();
|
|
1648
|
+
for (const placement of inputs.categoryModifiers) {
|
|
1649
|
+
if (placement.isDeleted) continue;
|
|
1650
|
+
const categoryId = String(placement.categoryId);
|
|
1651
|
+
const precedence = directCategories.has(categoryId) ? 0 : inheritedCategories.has(categoryId) ? 1 : -1;
|
|
1652
|
+
if (precedence === -1) continue;
|
|
1653
|
+
const modifierId = String(placement.modifierId);
|
|
1654
|
+
if (!modifiersById.has(modifierId)) continue;
|
|
1655
|
+
const incoming = toRule(placement);
|
|
1656
|
+
const existing = rules.get(modifierId);
|
|
1657
|
+
if (!existing || precedence < existing.precedence) {
|
|
1658
|
+
rules.set(modifierId, { precedence, rule: incoming });
|
|
1659
|
+
} else if (precedence === existing.precedence) {
|
|
1660
|
+
rules.set(modifierId, { precedence, rule: mergeRules(existing.rule, incoming) });
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
const resolved = [];
|
|
1664
|
+
for (const [modifierId, { rule }] of rules) {
|
|
1665
|
+
const modifier = modifiersById.get(modifierId);
|
|
1666
|
+
if (!modifier) continue;
|
|
1667
|
+
const required = rule.required;
|
|
1668
|
+
resolved.push({
|
|
1669
|
+
_id: modifierId,
|
|
1670
|
+
name: modifier.name,
|
|
1671
|
+
selectionType: modifier.selectionType,
|
|
1672
|
+
allowQuantity: modifier.allowQuantity,
|
|
1673
|
+
required,
|
|
1674
|
+
// single-selection is implicitly capped at 1 — min/max are never carried.
|
|
1675
|
+
min: modifier.selectionType === "single" ? void 0 : rule.min,
|
|
1676
|
+
max: modifier.selectionType === "single" ? void 0 : rule.max,
|
|
1677
|
+
sortOrder: rule.sortOrder,
|
|
1678
|
+
choices: toResolvedChoices(modifier)
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
return resolved.sort((a, b) => a.sortOrder - b.sortOrder || a.name.localeCompare(b.name));
|
|
1682
|
+
}
|
|
1683
|
+
function isChoiceAvailableAtOutlet(choice, outletId) {
|
|
1684
|
+
return !(choice.unavailableOutletIds ?? []).includes(outletId);
|
|
1685
|
+
}
|
|
1686
|
+
function hasSellableChoice(modifier, outletId) {
|
|
1687
|
+
return modifier.choices.some((choice) => isChoiceAvailableAtOutlet(choice, outletId));
|
|
1688
|
+
}
|
|
1689
|
+
var modifiersSellable = (modifiers, outletId) => modifiers.every((modifier) => !modifier.required || hasSellableChoice(modifier, outletId));
|
|
1690
|
+
|
|
1691
|
+
// utils/composite-availability.util.ts
|
|
1692
|
+
function poolStockAt(rows, outletId) {
|
|
1693
|
+
if (!rows.some((row) => row.manageStock === true)) return null;
|
|
1694
|
+
return Math.max(0, rows.find((row) => String(row.outletId) === outletId)?.quantity ?? 0);
|
|
1695
|
+
}
|
|
1696
|
+
function compositeVariantInStock(variantId, quantity, outlet) {
|
|
1697
|
+
const shelf = outlet.shelves.get(variantId);
|
|
1698
|
+
if (!shelf || shelf.unresolved) return false;
|
|
1699
|
+
const onHand = outlet.poolStock.get(shelf.pool);
|
|
1700
|
+
if (onHand === void 0) return false;
|
|
1701
|
+
if (onHand === null || shelf.backorder) return true;
|
|
1702
|
+
return onHand >= (shelf.unit ? toBase(quantity, shelf.unit) : quantity);
|
|
1703
|
+
}
|
|
1704
|
+
function compositeItemUnavailable(item, rows) {
|
|
1705
|
+
const { outlet } = rows;
|
|
1706
|
+
const sellableHere = (productId2) => !outlet || !outlet.hiddenProductIds.has(productId2) && !outlet.unsellableProductIds.has(productId2);
|
|
1707
|
+
if (item.categoryId) {
|
|
1708
|
+
const variants = rows.categoryVariants.get(String(item.categoryId));
|
|
1709
|
+
if (!variants) return "deleted";
|
|
1710
|
+
const offered = variants.filter((variantId2) => sellableHere(rows.variantProducts.get(variantId2) ?? ""));
|
|
1711
|
+
if (!offered.length) return "empty";
|
|
1712
|
+
if (!outlet) return null;
|
|
1713
|
+
const unmarked = offered.filter((variantId2) => !outlet.markedVariantIds.has(variantId2));
|
|
1714
|
+
if (!unmarked.length) return "marked_sold_out";
|
|
1715
|
+
return unmarked.some((variantId2) => compositeVariantInStock(variantId2, item.quantity, outlet)) ? null : "out_of_stock";
|
|
1716
|
+
}
|
|
1717
|
+
const variantId = String(item.variantId);
|
|
1718
|
+
const productId = rows.variantProducts.get(variantId);
|
|
1719
|
+
if (!productId || !rows.productStatuses.has(productId)) return "deleted";
|
|
1720
|
+
if (rows.productStatuses.get(productId) !== "active" /* ACTIVE */) return "inactive";
|
|
1721
|
+
if (!outlet) return null;
|
|
1722
|
+
if (outlet.hiddenProductIds.has(productId)) return "hidden";
|
|
1723
|
+
if (outlet.unsellableProductIds.has(productId)) return "empty";
|
|
1724
|
+
if (outlet.markedVariantIds.has(variantId)) return "marked_sold_out";
|
|
1725
|
+
if (rows.bookableProductIds.has(productId)) {
|
|
1726
|
+
if (!outlet.bookableSeats) return "by_date";
|
|
1727
|
+
return (outlet.bookableSeats.get(productId) ?? 0) >= item.quantity ? null : "fully_booked";
|
|
1728
|
+
}
|
|
1729
|
+
return compositeVariantInStock(variantId, item.quantity, outlet) ? null : "out_of_stock";
|
|
1730
|
+
}
|
|
1731
|
+
function compositeMakeable(parts, rows) {
|
|
1732
|
+
const { outlet } = rows;
|
|
1733
|
+
if (!outlet) throw new Error("compositeMakeable counts at one outlet: rows.outlet is required");
|
|
1734
|
+
const required = parts.map((part, partIndex) => ({ ...part, partIndex })).filter((part) => part.min > 0);
|
|
1735
|
+
const isBookable = (variantId) => rows.bookableProductIds.has(rows.variantProducts.get(String(variantId)) ?? "");
|
|
1736
|
+
if (required.some((part) => part.items.some((item) => item.variantId && isBookable(item.variantId))))
|
|
1737
|
+
return { byDate: true };
|
|
1738
|
+
const optionsOf = (item) => (item.categoryId ? rows.categoryVariants.get(String(item.categoryId)) ?? [] : [String(item.variantId)]).flatMap(
|
|
1739
|
+
(variantId) => {
|
|
1740
|
+
const reason = compositeItemUnavailable({ variantId, quantity: item.quantity }, rows);
|
|
1741
|
+
const shelf = outlet.shelves.get(variantId);
|
|
1742
|
+
if (reason && reason !== "out_of_stock" || !shelf) return [];
|
|
1743
|
+
const stock = outlet.poolStock.get(shelf.pool);
|
|
1744
|
+
const onHand = shelf.unresolved || stock === void 0 ? 0 : stock === null || shelf.backorder ? null : stock;
|
|
1745
|
+
const perPick = shelf.unit ? toBase(item.quantity, shelf.unit) : item.quantity;
|
|
1746
|
+
return [{ itemId: item.id, pool: shelf.pool, perPick, onHand }];
|
|
1747
|
+
}
|
|
1748
|
+
);
|
|
1749
|
+
const counted = required.map((part) => ({ ...part, options: part.items.flatMap(optionsOf) }));
|
|
1750
|
+
const order = [...counted].sort((a, b) => Number(a.options.length !== 1) - Number(b.options.length !== 1));
|
|
1751
|
+
const picksLeft = (option, reserved) => option.onHand === null ? Infinity : Math.max(0, Math.floor((option.onHand - (reserved.get(option.pool) ?? 0)) / option.perPick));
|
|
1752
|
+
const shortAt = (n) => {
|
|
1753
|
+
const reserved = /* @__PURE__ */ new Map();
|
|
1754
|
+
for (const part of order) {
|
|
1755
|
+
let wanted = part.min * n;
|
|
1756
|
+
const ranked = [...part.options].sort((a, b) => picksLeft(b, reserved) - picksLeft(a, reserved));
|
|
1757
|
+
for (const option of ranked) {
|
|
1758
|
+
if (wanted <= 0) break;
|
|
1759
|
+
const taken = Math.min(wanted, picksLeft(option, reserved));
|
|
1760
|
+
if (option.onHand !== null)
|
|
1761
|
+
reserved.set(option.pool, (reserved.get(option.pool) ?? 0) + taken * option.perPick);
|
|
1762
|
+
wanted -= taken;
|
|
1763
|
+
}
|
|
1764
|
+
if (wanted > 0) return part.partIndex;
|
|
1765
|
+
}
|
|
1766
|
+
return void 0;
|
|
1767
|
+
};
|
|
1768
|
+
const whyRows = [];
|
|
1769
|
+
const rowPools = /* @__PURE__ */ new Map();
|
|
1770
|
+
const makes = (has, uses) => has === null ? null : Math.floor(has / uses);
|
|
1771
|
+
for (const part of counted) {
|
|
1772
|
+
const [option] = part.options;
|
|
1773
|
+
if (part.options.length === 1) {
|
|
1774
|
+
const uses = option.perPick * part.min;
|
|
1775
|
+
const row = whyRows.find((candidate) => rowPools.get(candidate) === option.pool);
|
|
1776
|
+
if (!row) {
|
|
1777
|
+
const created = { itemIds: [option.itemId], uses, has: option.onHand, makes: makes(option.onHand, uses) };
|
|
1778
|
+
rowPools.set(created, option.pool);
|
|
1779
|
+
whyRows.push(created);
|
|
1780
|
+
continue;
|
|
1781
|
+
}
|
|
1782
|
+
row.itemIds.push(option.itemId);
|
|
1783
|
+
row.uses += uses;
|
|
1784
|
+
if (option.onHand === null) row.has = null;
|
|
1785
|
+
row.makes = makes(row.has, row.uses);
|
|
1786
|
+
continue;
|
|
1787
|
+
}
|
|
1788
|
+
const perPool = /* @__PURE__ */ new Map();
|
|
1789
|
+
for (const candidate of part.options)
|
|
1790
|
+
perPool.set(candidate.pool, Math.max(perPool.get(candidate.pool) ?? 0, picksLeft(candidate, /* @__PURE__ */ new Map())));
|
|
1791
|
+
const total = [...perPool.values()].reduce((sum, picks) => sum + picks, 0);
|
|
1792
|
+
const has = total === Infinity ? null : total;
|
|
1793
|
+
whyRows.push({
|
|
1794
|
+
itemIds: part.items.map((item) => item.id),
|
|
1795
|
+
partIndex: part.partIndex,
|
|
1796
|
+
uses: part.min,
|
|
1797
|
+
has,
|
|
1798
|
+
makes: makes(has, part.min)
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
if (counted.every((part) => part.options.some((option) => option.onHand === null)))
|
|
1802
|
+
return { count: null, rows: whyRows };
|
|
1803
|
+
const ceiling = Math.min(
|
|
1804
|
+
...counted.filter((part) => !part.options.some((option) => option.onHand === null)).map(
|
|
1805
|
+
(part) => Math.floor(part.options.reduce((sum, option) => sum + picksLeft(option, /* @__PURE__ */ new Map()), 0) / part.min)
|
|
1806
|
+
)
|
|
1807
|
+
);
|
|
1808
|
+
let [low, high] = [0, ceiling];
|
|
1809
|
+
while (low < high) {
|
|
1810
|
+
const mid = Math.ceil((low + high) / 2);
|
|
1811
|
+
if (shortAt(mid) === void 0) low = mid;
|
|
1812
|
+
else high = mid - 1;
|
|
1813
|
+
}
|
|
1814
|
+
const short = shortAt(low + 1);
|
|
1815
|
+
const shortPart = counted.find((part) => part.partIndex === short);
|
|
1816
|
+
const limitedBy = whyRows.findIndex(
|
|
1817
|
+
(row) => shortPart?.options.length === 1 ? row.partIndex === void 0 && row.itemIds.includes(shortPart.options[0].itemId) : row.partIndex === short
|
|
1818
|
+
);
|
|
1819
|
+
return { count: low, rows: whyRows, ...limitedBy >= 0 ? { limitedBy } : {} };
|
|
1820
|
+
}
|
|
1821
|
+
var poolOf = (line) => line.stockVariantId || line.variantId;
|
|
1822
|
+
function poolDemand(lines) {
|
|
1823
|
+
const demand = /* @__PURE__ */ new Map();
|
|
1824
|
+
const add = (line, times) => demand.set(
|
|
1825
|
+
poolOf(line),
|
|
1826
|
+
(demand.get(poolOf(line)) ?? 0) + (line.unit ? toBase(line.quantity * times, line.unit) : line.quantity * times)
|
|
1827
|
+
);
|
|
1828
|
+
for (const line of lines) {
|
|
1829
|
+
if (line.components) for (const component of line.components) add(component, line.quantity);
|
|
1830
|
+
else add(line, 1);
|
|
1831
|
+
}
|
|
1832
|
+
return demand;
|
|
1833
|
+
}
|
|
1834
|
+
function poolShortfall(demand, poolStock, backordered = /* @__PURE__ */ new Set()) {
|
|
1835
|
+
for (const [poolId, wanted] of demand) {
|
|
1836
|
+
const available = poolStock.get(poolId);
|
|
1837
|
+
if (available === null || available === void 0 || backordered.has(poolId)) continue;
|
|
1838
|
+
if (wanted > available) return { poolId, available, wanted };
|
|
1839
|
+
}
|
|
1840
|
+
return null;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
// utils/composite-reservation.util.ts
|
|
1844
|
+
function compositeModifierMismatches(lineItems) {
|
|
1845
|
+
return lineItems.flatMap(
|
|
1846
|
+
(line) => line.components?.length ? (line.modifiers ?? []).flatMap(
|
|
1847
|
+
(modifier) => modifier.componentIndex !== void 0 && line.components[modifier.componentIndex] !== void 0 ? [] : [
|
|
1848
|
+
`Modifier ${modifier.modifierName ?? "?"} on composite line ${line.internalId ?? "?"} names no component (componentIndex ${modifier.componentIndex}) \u2014 a composite has no modifiers of its own`
|
|
1849
|
+
]
|
|
1850
|
+
) : []
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
function compositeReservationMismatches(order) {
|
|
1854
|
+
return (order.reservations ?? []).flatMap((reservation) => {
|
|
1855
|
+
if (reservation.lineItemInternalId === void 0) return [];
|
|
1856
|
+
const name = `Booking ${String(reservation.bookingId ?? "?")}`;
|
|
1857
|
+
const line = order.lineItems?.find((candidate) => candidate.internalId === reservation.lineItemInternalId);
|
|
1858
|
+
if (!line) return [`${name} names line ${reservation.lineItemInternalId}, which is not on the order`];
|
|
1859
|
+
const component = reservation.componentIndex === void 0 ? void 0 : line.components?.[reservation.componentIndex];
|
|
1860
|
+
if (!component) return [`${name} names no component ${reservation.componentIndex} of line ${line.internalId}`];
|
|
1861
|
+
if (String(component.productId) !== String(reservation.productId))
|
|
1862
|
+
return [
|
|
1863
|
+
`${name} is for ${String(reservation.productId)}, but that component sold ${String(component.productId)}`
|
|
1864
|
+
];
|
|
1865
|
+
if (reservation.price !== 0 || reservation.total !== 0)
|
|
1866
|
+
return [`${name} carries money (${reservation.price}/${reservation.total}); its component already does`];
|
|
1867
|
+
return [];
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1553
1871
|
// units/family-code.ts
|
|
1554
1872
|
var MAX_SLUG_LENGTH = 32;
|
|
1555
1873
|
var FALLBACK_SLUG = "family";
|
|
@@ -2688,51 +3006,6 @@ function inferStatesFromLegacyStatus(order) {
|
|
|
2688
3006
|
return { payment: "unpaid", fulfillment: "pending" };
|
|
2689
3007
|
}
|
|
2690
3008
|
|
|
2691
|
-
// order-state-machine/in-cart.ts
|
|
2692
|
-
function hasInCartFlag(order) {
|
|
2693
|
-
return order.inCart != null;
|
|
2694
|
-
}
|
|
2695
|
-
function isInCart(order) {
|
|
2696
|
-
if (order.inCart != null) return order.inCart.active === true;
|
|
2697
|
-
if (order.fulfillmentState) return order.fulfillmentState === "draft";
|
|
2698
|
-
return (order.status ?? "").trim().toLowerCase() === "in-cart";
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2701
|
-
// order-state-machine/order-status.ts
|
|
2702
|
-
function validateOrderStatuses(statuses) {
|
|
2703
|
-
if (!Array.isArray(statuses)) {
|
|
2704
|
-
return [{ index: -1, field: "orderStatuses", message: "must be an array" }];
|
|
2705
|
-
}
|
|
2706
|
-
const errors = [];
|
|
2707
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2708
|
-
statuses.forEach((raw, index) => {
|
|
2709
|
-
const s = raw ?? {};
|
|
2710
|
-
const add = (field, message) => errors.push({ index, field, message });
|
|
2711
|
-
if (typeof s.id !== "string" || !s.id.trim()) add("id", "must be a non-empty string");
|
|
2712
|
-
else if (seen.has(s.id)) add("id", `duplicate id "${s.id}"`);
|
|
2713
|
-
else seen.add(s.id);
|
|
2714
|
-
if (typeof s.label !== "string" || !s.label.trim()) add("label", "must be a non-empty string");
|
|
2715
|
-
for (const field of ["color", "icon"]) {
|
|
2716
|
-
if (s[field] !== void 0 && typeof s[field] !== "string") add(field, "must be a string");
|
|
2717
|
-
}
|
|
2718
|
-
if (s.fulfillmentState !== void 0 && !(typeof s.fulfillmentState === "string" && isFulfillmentState(s.fulfillmentState))) {
|
|
2719
|
-
add("fulfillmentState", `"${String(s.fulfillmentState)}" is not a fulfillment state`);
|
|
2720
|
-
}
|
|
2721
|
-
if (s.requiresPaymentState !== void 0) {
|
|
2722
|
-
if (!Array.isArray(s.requiresPaymentState) || s.requiresPaymentState.length === 0) {
|
|
2723
|
-
add("requiresPaymentState", "must be a non-empty array of payment states");
|
|
2724
|
-
} else {
|
|
2725
|
-
for (const p of s.requiresPaymentState) {
|
|
2726
|
-
if (!(typeof p === "string" && isPaymentState(p))) {
|
|
2727
|
-
add("requiresPaymentState", `"${String(p)}" is not a payment state`);
|
|
2728
|
-
}
|
|
2729
|
-
}
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
});
|
|
2733
|
-
return errors;
|
|
2734
|
-
}
|
|
2735
|
-
|
|
2736
3009
|
// order-state-machine/write-strategy.ts
|
|
2737
3010
|
var LegacyWriteStrategy = class {
|
|
2738
3011
|
applyState(_order) {
|
|
@@ -3244,7 +3517,9 @@ function buildGrid(rule, timeZone, from, to, now, busy = []) {
|
|
|
3244
3517
|
const start = startAt.getTime();
|
|
3245
3518
|
if (start < askedFrom || start >= askedTo) return false;
|
|
3246
3519
|
if (start < notBefore || start > notAfter) return false;
|
|
3247
|
-
return !blocked.some(
|
|
3520
|
+
return !blocked.some(
|
|
3521
|
+
({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom
|
|
3522
|
+
);
|
|
3248
3523
|
}).sort((first, second) => +first.startAt - +second.startAt);
|
|
3249
3524
|
}
|
|
3250
3525
|
function slotsForWindow(grid, startAt, endAt) {
|
|
@@ -3363,7 +3638,14 @@ function buildAvailability({
|
|
|
3363
3638
|
for (const slot of slots2) {
|
|
3364
3639
|
const { taken, foreignProductId } = occupancyIn(mine, productId, slot.startAt, slot.bufferEndAt, shared);
|
|
3365
3640
|
const free = foreignProductId ? 0 : Math.max(capacity - taken, 0);
|
|
3366
|
-
const entry = byInstant.get(+slot.startAt) ?? {
|
|
3641
|
+
const entry = byInstant.get(+slot.startAt) ?? {
|
|
3642
|
+
...slot,
|
|
3643
|
+
capacity: 0,
|
|
3644
|
+
booked: 0,
|
|
3645
|
+
free: 0,
|
|
3646
|
+
resources: [],
|
|
3647
|
+
dayKey: shopDayKey(slot.startAt, resource.timeZone)
|
|
3648
|
+
};
|
|
3367
3649
|
entry.capacity += capacity;
|
|
3368
3650
|
entry.booked += foreignProductId ? capacity : taken;
|
|
3369
3651
|
entry.free += free;
|
|
@@ -3441,14 +3723,9 @@ var ADYEN_ROLLOUT_COUNTRY_CODES = /* @__PURE__ */ new Set(["CA"]);
|
|
|
3441
3723
|
function isAdyenRolloutCountry(alpha2) {
|
|
3442
3724
|
return ADYEN_ROLLOUT_COUNTRY_CODES.has(alpha2);
|
|
3443
3725
|
}
|
|
3444
|
-
var AFFIRM_PRICE_PLANS = ["core", "standard", "signature"];
|
|
3445
|
-
function isAffirmPricePlan(value) {
|
|
3446
|
-
return typeof value === "string" && AFFIRM_PRICE_PLANS.includes(value);
|
|
3447
|
-
}
|
|
3448
3726
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3449
3727
|
0 && (module.exports = {
|
|
3450
3728
|
ADYEN_ROLLOUT_COUNTRY_CODES,
|
|
3451
|
-
AFFIRM_PRICE_PLANS,
|
|
3452
3729
|
AttributeType,
|
|
3453
3730
|
BookingRatePeriod,
|
|
3454
3731
|
BookingResourceKind,
|
|
@@ -3527,6 +3804,7 @@ function isAffirmPricePlan(value) {
|
|
|
3527
3804
|
UserTypes,
|
|
3528
3805
|
addAmounts,
|
|
3529
3806
|
amountToString,
|
|
3807
|
+
ancestorChain,
|
|
3530
3808
|
assertUnitSane,
|
|
3531
3809
|
availableIn,
|
|
3532
3810
|
baseFor,
|
|
@@ -3540,6 +3818,14 @@ function isAffirmPricePlan(value) {
|
|
|
3540
3818
|
buildResolvedConfig,
|
|
3541
3819
|
calculatePercentage,
|
|
3542
3820
|
canTransition,
|
|
3821
|
+
cheapestCompositeItem,
|
|
3822
|
+
cheapestCompositePrice,
|
|
3823
|
+
compositeItemCost,
|
|
3824
|
+
compositeItemUnavailable,
|
|
3825
|
+
compositeMakeable,
|
|
3826
|
+
compositeModifierMismatches,
|
|
3827
|
+
compositeReservationMismatches,
|
|
3828
|
+
compositeVariantInStock,
|
|
3543
3829
|
conditionSummary,
|
|
3544
3830
|
convertFixedFeeToLocalCurrency,
|
|
3545
3831
|
createAmount,
|
|
@@ -3574,14 +3860,13 @@ function isAffirmPricePlan(value) {
|
|
|
3574
3860
|
getPricingRegionFromCountry,
|
|
3575
3861
|
getSettlementTotal,
|
|
3576
3862
|
getTotalCollected,
|
|
3577
|
-
|
|
3863
|
+
hasSellableChoice,
|
|
3578
3864
|
holdExpiresAt,
|
|
3579
3865
|
inferStatesFromLegacyStatus,
|
|
3580
3866
|
isAdyenRolloutCountry,
|
|
3581
|
-
|
|
3867
|
+
isChoiceAvailableAtOutlet,
|
|
3582
3868
|
isConditionOperator,
|
|
3583
3869
|
isFulfillmentState,
|
|
3584
|
-
isInCart,
|
|
3585
3870
|
isPaymentState,
|
|
3586
3871
|
isSupportedCurrency,
|
|
3587
3872
|
isSystemFamilyCode,
|
|
@@ -3590,6 +3875,7 @@ function isAffirmPricePlan(value) {
|
|
|
3590
3875
|
mapToLegacyStatus,
|
|
3591
3876
|
maxPrecisionFor,
|
|
3592
3877
|
mergeFragment,
|
|
3878
|
+
modifiersSellable,
|
|
3593
3879
|
multiplyAmount,
|
|
3594
3880
|
normalizeMongoIdString,
|
|
3595
3881
|
occupancyIn,
|
|
@@ -3597,9 +3883,14 @@ function isAffirmPricePlan(value) {
|
|
|
3597
3883
|
parseCurrencyCode,
|
|
3598
3884
|
parseStripeCurrencyCode,
|
|
3599
3885
|
planWindow,
|
|
3886
|
+
poolDemand,
|
|
3887
|
+
poolOf,
|
|
3888
|
+
poolShortfall,
|
|
3889
|
+
poolStockAt,
|
|
3600
3890
|
presentAt,
|
|
3601
3891
|
removeFragment,
|
|
3602
3892
|
resolveFamily,
|
|
3893
|
+
resolveModifiersForProduct,
|
|
3603
3894
|
resolveTemplate,
|
|
3604
3895
|
resolveUnit,
|
|
3605
3896
|
roundToPrecision,
|
|
@@ -3615,7 +3906,6 @@ function isAffirmPricePlan(value) {
|
|
|
3615
3906
|
toMinorUnits,
|
|
3616
3907
|
validateConfig,
|
|
3617
3908
|
validateFragmentCompatibility,
|
|
3618
|
-
validateOrderStatuses,
|
|
3619
3909
|
violatesFinancialInvariant,
|
|
3620
3910
|
windowEndsAt,
|
|
3621
3911
|
withinPresence
|