@final-commerce/common 2.4.0 → 2.5.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 +180 -2
- package/dist/index.d.ts +180 -2
- package/dist/index.js +349 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +332 -2
- package/dist/index.mjs.map +1 -1
- package/dist/pos-types/index.d.mts +171 -84
- package/dist/pos-types/index.d.ts +171 -84
- 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.js
CHANGED
|
@@ -98,6 +98,7 @@ __export(index_exports, {
|
|
|
98
98
|
UserTypes: () => UserTypes,
|
|
99
99
|
addAmounts: () => addAmounts,
|
|
100
100
|
amountToString: () => amountToString,
|
|
101
|
+
ancestorChain: () => ancestorChain,
|
|
101
102
|
assertUnitSane: () => assertUnitSane,
|
|
102
103
|
availableIn: () => availableIn,
|
|
103
104
|
baseFor: () => baseFor,
|
|
@@ -111,6 +112,14 @@ __export(index_exports, {
|
|
|
111
112
|
buildResolvedConfig: () => buildResolvedConfig,
|
|
112
113
|
calculatePercentage: () => calculatePercentage,
|
|
113
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,
|
|
114
123
|
conditionSummary: () => conditionSummary,
|
|
115
124
|
convertFixedFeeToLocalCurrency: () => convertFixedFeeToLocalCurrency,
|
|
116
125
|
createAmount: () => createAmount,
|
|
@@ -145,9 +154,11 @@ __export(index_exports, {
|
|
|
145
154
|
getPricingRegionFromCountry: () => getPricingRegionFromCountry,
|
|
146
155
|
getSettlementTotal: () => getSettlementTotal,
|
|
147
156
|
getTotalCollected: () => getTotalCollected,
|
|
157
|
+
hasSellableChoice: () => hasSellableChoice,
|
|
148
158
|
holdExpiresAt: () => holdExpiresAt,
|
|
149
159
|
inferStatesFromLegacyStatus: () => inferStatesFromLegacyStatus,
|
|
150
160
|
isAdyenRolloutCountry: () => isAdyenRolloutCountry,
|
|
161
|
+
isChoiceAvailableAtOutlet: () => isChoiceAvailableAtOutlet,
|
|
151
162
|
isConditionOperator: () => isConditionOperator,
|
|
152
163
|
isFulfillmentState: () => isFulfillmentState,
|
|
153
164
|
isPaymentState: () => isPaymentState,
|
|
@@ -158,6 +169,7 @@ __export(index_exports, {
|
|
|
158
169
|
mapToLegacyStatus: () => mapToLegacyStatus,
|
|
159
170
|
maxPrecisionFor: () => maxPrecisionFor,
|
|
160
171
|
mergeFragment: () => mergeFragment,
|
|
172
|
+
modifiersSellable: () => modifiersSellable,
|
|
161
173
|
multiplyAmount: () => multiplyAmount,
|
|
162
174
|
normalizeMongoIdString: () => normalizeMongoIdString,
|
|
163
175
|
occupancyIn: () => occupancyIn,
|
|
@@ -165,9 +177,14 @@ __export(index_exports, {
|
|
|
165
177
|
parseCurrencyCode: () => parseCurrencyCode,
|
|
166
178
|
parseStripeCurrencyCode: () => parseStripeCurrencyCode,
|
|
167
179
|
planWindow: () => planWindow,
|
|
180
|
+
poolDemand: () => poolDemand,
|
|
181
|
+
poolOf: () => poolOf,
|
|
182
|
+
poolShortfall: () => poolShortfall,
|
|
183
|
+
poolStockAt: () => poolStockAt,
|
|
168
184
|
presentAt: () => presentAt,
|
|
169
185
|
removeFragment: () => removeFragment,
|
|
170
186
|
resolveFamily: () => resolveFamily,
|
|
187
|
+
resolveModifiersForProduct: () => resolveModifiersForProduct,
|
|
171
188
|
resolveTemplate: () => resolveTemplate,
|
|
172
189
|
resolveUnit: () => resolveUnit,
|
|
173
190
|
roundToPrecision: () => roundToPrecision,
|
|
@@ -614,6 +631,7 @@ var ProductType = /* @__PURE__ */ ((ProductType2) => {
|
|
|
614
631
|
ProductType2["SIMPLE"] = "simple";
|
|
615
632
|
ProductType2["VARIABLE"] = "variable";
|
|
616
633
|
ProductType2["BOOKING"] = "booking";
|
|
634
|
+
ProductType2["COMPOSITE"] = "composite";
|
|
617
635
|
return ProductType2;
|
|
618
636
|
})(ProductType || {});
|
|
619
637
|
var ProductStatus = /* @__PURE__ */ ((ProductStatus2) => {
|
|
@@ -1544,6 +1562,309 @@ function normalizeMongoIdString(value) {
|
|
|
1544
1562
|
return null;
|
|
1545
1563
|
}
|
|
1546
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
|
+
return offered.some((variantId2) => compositeVariantInStock(variantId2, item.quantity, outlet)) ? null : "out_of_stock";
|
|
1714
|
+
}
|
|
1715
|
+
const variantId = String(item.variantId);
|
|
1716
|
+
const productId = rows.variantProducts.get(variantId);
|
|
1717
|
+
if (!productId || !rows.productStatuses.has(productId)) return "deleted";
|
|
1718
|
+
if (rows.productStatuses.get(productId) !== "active" /* ACTIVE */) return "inactive";
|
|
1719
|
+
if (!outlet) return null;
|
|
1720
|
+
if (outlet.hiddenProductIds.has(productId)) return "hidden";
|
|
1721
|
+
if (outlet.unsellableProductIds.has(productId)) return "empty";
|
|
1722
|
+
if (rows.bookableProductIds.has(productId)) {
|
|
1723
|
+
if (!outlet.bookableSeats) return "by_date";
|
|
1724
|
+
return (outlet.bookableSeats.get(productId) ?? 0) >= item.quantity ? null : "fully_booked";
|
|
1725
|
+
}
|
|
1726
|
+
return compositeVariantInStock(variantId, item.quantity, outlet) ? null : "out_of_stock";
|
|
1727
|
+
}
|
|
1728
|
+
function compositeMakeable(parts, rows) {
|
|
1729
|
+
const { outlet } = rows;
|
|
1730
|
+
if (!outlet) throw new Error("compositeMakeable counts at one outlet: rows.outlet is required");
|
|
1731
|
+
const required = parts.map((part, partIndex) => ({ ...part, partIndex })).filter((part) => part.min > 0);
|
|
1732
|
+
const isBookable = (variantId) => rows.bookableProductIds.has(rows.variantProducts.get(String(variantId)) ?? "");
|
|
1733
|
+
if (required.some((part) => part.items.some((item) => item.variantId && isBookable(item.variantId))))
|
|
1734
|
+
return { byDate: true };
|
|
1735
|
+
const optionsOf = (item) => (item.categoryId ? rows.categoryVariants.get(String(item.categoryId)) ?? [] : [String(item.variantId)]).flatMap(
|
|
1736
|
+
(variantId) => {
|
|
1737
|
+
const reason = compositeItemUnavailable({ variantId, quantity: item.quantity }, rows);
|
|
1738
|
+
const shelf = outlet.shelves.get(variantId);
|
|
1739
|
+
if (reason && reason !== "out_of_stock" || !shelf) return [];
|
|
1740
|
+
const stock = outlet.poolStock.get(shelf.pool);
|
|
1741
|
+
const onHand = shelf.unresolved || stock === void 0 ? 0 : stock === null || shelf.backorder ? null : stock;
|
|
1742
|
+
const perPick = shelf.unit ? toBase(item.quantity, shelf.unit) : item.quantity;
|
|
1743
|
+
return [{ itemId: item.id, pool: shelf.pool, perPick, onHand }];
|
|
1744
|
+
}
|
|
1745
|
+
);
|
|
1746
|
+
const counted = required.map((part) => ({ ...part, options: part.items.flatMap(optionsOf) }));
|
|
1747
|
+
const order = [...counted].sort((a, b) => Number(a.options.length !== 1) - Number(b.options.length !== 1));
|
|
1748
|
+
const picksLeft = (option, reserved) => option.onHand === null ? Infinity : Math.max(0, Math.floor((option.onHand - (reserved.get(option.pool) ?? 0)) / option.perPick));
|
|
1749
|
+
const shortAt = (n) => {
|
|
1750
|
+
const reserved = /* @__PURE__ */ new Map();
|
|
1751
|
+
for (const part of order) {
|
|
1752
|
+
let wanted = part.min * n;
|
|
1753
|
+
const ranked = [...part.options].sort((a, b) => picksLeft(b, reserved) - picksLeft(a, reserved));
|
|
1754
|
+
for (const option of ranked) {
|
|
1755
|
+
if (wanted <= 0) break;
|
|
1756
|
+
const taken = Math.min(wanted, picksLeft(option, reserved));
|
|
1757
|
+
if (option.onHand !== null)
|
|
1758
|
+
reserved.set(option.pool, (reserved.get(option.pool) ?? 0) + taken * option.perPick);
|
|
1759
|
+
wanted -= taken;
|
|
1760
|
+
}
|
|
1761
|
+
if (wanted > 0) return part.partIndex;
|
|
1762
|
+
}
|
|
1763
|
+
return void 0;
|
|
1764
|
+
};
|
|
1765
|
+
const whyRows = [];
|
|
1766
|
+
const rowPools = /* @__PURE__ */ new Map();
|
|
1767
|
+
const makes = (has, uses) => has === null ? null : Math.floor(has / uses);
|
|
1768
|
+
for (const part of counted) {
|
|
1769
|
+
const [option] = part.options;
|
|
1770
|
+
if (part.options.length === 1) {
|
|
1771
|
+
const uses = option.perPick * part.min;
|
|
1772
|
+
const row = whyRows.find((candidate) => rowPools.get(candidate) === option.pool);
|
|
1773
|
+
if (!row) {
|
|
1774
|
+
const created = { itemIds: [option.itemId], uses, has: option.onHand, makes: makes(option.onHand, uses) };
|
|
1775
|
+
rowPools.set(created, option.pool);
|
|
1776
|
+
whyRows.push(created);
|
|
1777
|
+
continue;
|
|
1778
|
+
}
|
|
1779
|
+
row.itemIds.push(option.itemId);
|
|
1780
|
+
row.uses += uses;
|
|
1781
|
+
if (option.onHand === null) row.has = null;
|
|
1782
|
+
row.makes = makes(row.has, row.uses);
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
const perPool = /* @__PURE__ */ new Map();
|
|
1786
|
+
for (const candidate of part.options)
|
|
1787
|
+
perPool.set(candidate.pool, Math.max(perPool.get(candidate.pool) ?? 0, picksLeft(candidate, /* @__PURE__ */ new Map())));
|
|
1788
|
+
const total = [...perPool.values()].reduce((sum, picks) => sum + picks, 0);
|
|
1789
|
+
const has = total === Infinity ? null : total;
|
|
1790
|
+
whyRows.push({
|
|
1791
|
+
itemIds: part.items.map((item) => item.id),
|
|
1792
|
+
partIndex: part.partIndex,
|
|
1793
|
+
uses: part.min,
|
|
1794
|
+
has,
|
|
1795
|
+
makes: makes(has, part.min)
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
if (counted.every((part) => part.options.some((option) => option.onHand === null)))
|
|
1799
|
+
return { count: null, rows: whyRows };
|
|
1800
|
+
const ceiling = Math.min(
|
|
1801
|
+
...counted.filter((part) => !part.options.some((option) => option.onHand === null)).map(
|
|
1802
|
+
(part) => Math.floor(part.options.reduce((sum, option) => sum + picksLeft(option, /* @__PURE__ */ new Map()), 0) / part.min)
|
|
1803
|
+
)
|
|
1804
|
+
);
|
|
1805
|
+
let [low, high] = [0, ceiling];
|
|
1806
|
+
while (low < high) {
|
|
1807
|
+
const mid = Math.ceil((low + high) / 2);
|
|
1808
|
+
if (shortAt(mid) === void 0) low = mid;
|
|
1809
|
+
else high = mid - 1;
|
|
1810
|
+
}
|
|
1811
|
+
const short = shortAt(low + 1);
|
|
1812
|
+
const shortPart = counted.find((part) => part.partIndex === short);
|
|
1813
|
+
const limitedBy = whyRows.findIndex(
|
|
1814
|
+
(row) => shortPart?.options.length === 1 ? row.partIndex === void 0 && row.itemIds.includes(shortPart.options[0].itemId) : row.partIndex === short
|
|
1815
|
+
);
|
|
1816
|
+
return { count: low, rows: whyRows, ...limitedBy >= 0 ? { limitedBy } : {} };
|
|
1817
|
+
}
|
|
1818
|
+
var poolOf = (line) => line.stockVariantId || line.variantId;
|
|
1819
|
+
function poolDemand(lines) {
|
|
1820
|
+
const demand = /* @__PURE__ */ new Map();
|
|
1821
|
+
const add = (line, times) => demand.set(
|
|
1822
|
+
poolOf(line),
|
|
1823
|
+
(demand.get(poolOf(line)) ?? 0) + (line.unit ? toBase(line.quantity * times, line.unit) : line.quantity * times)
|
|
1824
|
+
);
|
|
1825
|
+
for (const line of lines) {
|
|
1826
|
+
if (line.components) for (const component of line.components) add(component, line.quantity);
|
|
1827
|
+
else add(line, 1);
|
|
1828
|
+
}
|
|
1829
|
+
return demand;
|
|
1830
|
+
}
|
|
1831
|
+
function poolShortfall(demand, poolStock, backordered = /* @__PURE__ */ new Set()) {
|
|
1832
|
+
for (const [poolId, wanted] of demand) {
|
|
1833
|
+
const available = poolStock.get(poolId);
|
|
1834
|
+
if (available === null || available === void 0 || backordered.has(poolId)) continue;
|
|
1835
|
+
if (wanted > available) return { poolId, available, wanted };
|
|
1836
|
+
}
|
|
1837
|
+
return null;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
// utils/composite-reservation.util.ts
|
|
1841
|
+
function compositeModifierMismatches(lineItems) {
|
|
1842
|
+
return lineItems.flatMap(
|
|
1843
|
+
(line) => line.components?.length ? (line.modifiers ?? []).flatMap(
|
|
1844
|
+
(modifier) => modifier.componentIndex !== void 0 && line.components[modifier.componentIndex] !== void 0 ? [] : [
|
|
1845
|
+
`Modifier ${modifier.modifierName ?? "?"} on composite line ${line.internalId ?? "?"} names no component (componentIndex ${modifier.componentIndex}) \u2014 a composite has no modifiers of its own`
|
|
1846
|
+
]
|
|
1847
|
+
) : []
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
function compositeReservationMismatches(order) {
|
|
1851
|
+
return (order.reservations ?? []).flatMap((reservation) => {
|
|
1852
|
+
if (reservation.lineItemInternalId === void 0) return [];
|
|
1853
|
+
const name = `Booking ${String(reservation.bookingId ?? "?")}`;
|
|
1854
|
+
const line = order.lineItems?.find((candidate) => candidate.internalId === reservation.lineItemInternalId);
|
|
1855
|
+
if (!line) return [`${name} names line ${reservation.lineItemInternalId}, which is not on the order`];
|
|
1856
|
+
const component = reservation.componentIndex === void 0 ? void 0 : line.components?.[reservation.componentIndex];
|
|
1857
|
+
if (!component) return [`${name} names no component ${reservation.componentIndex} of line ${line.internalId}`];
|
|
1858
|
+
if (String(component.productId) !== String(reservation.productId))
|
|
1859
|
+
return [
|
|
1860
|
+
`${name} is for ${String(reservation.productId)}, but that component sold ${String(component.productId)}`
|
|
1861
|
+
];
|
|
1862
|
+
if (reservation.price !== 0 || reservation.total !== 0)
|
|
1863
|
+
return [`${name} carries money (${reservation.price}/${reservation.total}); its component already does`];
|
|
1864
|
+
return [];
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1547
1868
|
// units/family-code.ts
|
|
1548
1869
|
var MAX_SLUG_LENGTH = 32;
|
|
1549
1870
|
var FALLBACK_SLUG = "family";
|
|
@@ -3193,7 +3514,9 @@ function buildGrid(rule, timeZone, from, to, now, busy = []) {
|
|
|
3193
3514
|
const start = startAt.getTime();
|
|
3194
3515
|
if (start < askedFrom || start >= askedTo) return false;
|
|
3195
3516
|
if (start < notBefore || start > notAfter) return false;
|
|
3196
|
-
return !blocked.some(
|
|
3517
|
+
return !blocked.some(
|
|
3518
|
+
({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom
|
|
3519
|
+
);
|
|
3197
3520
|
}).sort((first, second) => +first.startAt - +second.startAt);
|
|
3198
3521
|
}
|
|
3199
3522
|
function slotsForWindow(grid, startAt, endAt) {
|
|
@@ -3312,7 +3635,14 @@ function buildAvailability({
|
|
|
3312
3635
|
for (const slot of slots2) {
|
|
3313
3636
|
const { taken, foreignProductId } = occupancyIn(mine, productId, slot.startAt, slot.bufferEndAt, shared);
|
|
3314
3637
|
const free = foreignProductId ? 0 : Math.max(capacity - taken, 0);
|
|
3315
|
-
const entry = byInstant.get(+slot.startAt) ?? {
|
|
3638
|
+
const entry = byInstant.get(+slot.startAt) ?? {
|
|
3639
|
+
...slot,
|
|
3640
|
+
capacity: 0,
|
|
3641
|
+
booked: 0,
|
|
3642
|
+
free: 0,
|
|
3643
|
+
resources: [],
|
|
3644
|
+
dayKey: shopDayKey(slot.startAt, resource.timeZone)
|
|
3645
|
+
};
|
|
3316
3646
|
entry.capacity += capacity;
|
|
3317
3647
|
entry.booked += foreignProductId ? capacity : taken;
|
|
3318
3648
|
entry.free += free;
|
|
@@ -3471,6 +3801,7 @@ function isAdyenRolloutCountry(alpha2) {
|
|
|
3471
3801
|
UserTypes,
|
|
3472
3802
|
addAmounts,
|
|
3473
3803
|
amountToString,
|
|
3804
|
+
ancestorChain,
|
|
3474
3805
|
assertUnitSane,
|
|
3475
3806
|
availableIn,
|
|
3476
3807
|
baseFor,
|
|
@@ -3484,6 +3815,14 @@ function isAdyenRolloutCountry(alpha2) {
|
|
|
3484
3815
|
buildResolvedConfig,
|
|
3485
3816
|
calculatePercentage,
|
|
3486
3817
|
canTransition,
|
|
3818
|
+
cheapestCompositeItem,
|
|
3819
|
+
cheapestCompositePrice,
|
|
3820
|
+
compositeItemCost,
|
|
3821
|
+
compositeItemUnavailable,
|
|
3822
|
+
compositeMakeable,
|
|
3823
|
+
compositeModifierMismatches,
|
|
3824
|
+
compositeReservationMismatches,
|
|
3825
|
+
compositeVariantInStock,
|
|
3487
3826
|
conditionSummary,
|
|
3488
3827
|
convertFixedFeeToLocalCurrency,
|
|
3489
3828
|
createAmount,
|
|
@@ -3518,9 +3857,11 @@ function isAdyenRolloutCountry(alpha2) {
|
|
|
3518
3857
|
getPricingRegionFromCountry,
|
|
3519
3858
|
getSettlementTotal,
|
|
3520
3859
|
getTotalCollected,
|
|
3860
|
+
hasSellableChoice,
|
|
3521
3861
|
holdExpiresAt,
|
|
3522
3862
|
inferStatesFromLegacyStatus,
|
|
3523
3863
|
isAdyenRolloutCountry,
|
|
3864
|
+
isChoiceAvailableAtOutlet,
|
|
3524
3865
|
isConditionOperator,
|
|
3525
3866
|
isFulfillmentState,
|
|
3526
3867
|
isPaymentState,
|
|
@@ -3531,6 +3872,7 @@ function isAdyenRolloutCountry(alpha2) {
|
|
|
3531
3872
|
mapToLegacyStatus,
|
|
3532
3873
|
maxPrecisionFor,
|
|
3533
3874
|
mergeFragment,
|
|
3875
|
+
modifiersSellable,
|
|
3534
3876
|
multiplyAmount,
|
|
3535
3877
|
normalizeMongoIdString,
|
|
3536
3878
|
occupancyIn,
|
|
@@ -3538,9 +3880,14 @@ function isAdyenRolloutCountry(alpha2) {
|
|
|
3538
3880
|
parseCurrencyCode,
|
|
3539
3881
|
parseStripeCurrencyCode,
|
|
3540
3882
|
planWindow,
|
|
3883
|
+
poolDemand,
|
|
3884
|
+
poolOf,
|
|
3885
|
+
poolShortfall,
|
|
3886
|
+
poolStockAt,
|
|
3541
3887
|
presentAt,
|
|
3542
3888
|
removeFragment,
|
|
3543
3889
|
resolveFamily,
|
|
3890
|
+
resolveModifiersForProduct,
|
|
3544
3891
|
resolveTemplate,
|
|
3545
3892
|
resolveUnit,
|
|
3546
3893
|
roundToPrecision,
|