@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.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(({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom);
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) ?? { ...slot, capacity: 0, booked: 0, free: 0, resources: [], dayKey: shopDayKey(slot.startAt, resource.timeZone) };
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,