@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.mjs
CHANGED
|
@@ -423,6 +423,7 @@ var ProductType = /* @__PURE__ */ ((ProductType2) => {
|
|
|
423
423
|
ProductType2["SIMPLE"] = "simple";
|
|
424
424
|
ProductType2["VARIABLE"] = "variable";
|
|
425
425
|
ProductType2["BOOKING"] = "booking";
|
|
426
|
+
ProductType2["COMPOSITE"] = "composite";
|
|
426
427
|
return ProductType2;
|
|
427
428
|
})(ProductType || {});
|
|
428
429
|
var ProductStatus = /* @__PURE__ */ ((ProductStatus2) => {
|
|
@@ -1353,6 +1354,309 @@ function normalizeMongoIdString(value) {
|
|
|
1353
1354
|
return null;
|
|
1354
1355
|
}
|
|
1355
1356
|
|
|
1357
|
+
// utils/composite-price.util.ts
|
|
1358
|
+
var compositeItemCost = (item) => item.extraCharge * item.quantity;
|
|
1359
|
+
function cheapestCompositeItem(items) {
|
|
1360
|
+
let cheapest;
|
|
1361
|
+
items.forEach((item, index) => {
|
|
1362
|
+
if (!item.available) return;
|
|
1363
|
+
if (cheapest === void 0 || compositeItemCost(item) < compositeItemCost(items[cheapest])) cheapest = index;
|
|
1364
|
+
});
|
|
1365
|
+
return cheapest;
|
|
1366
|
+
}
|
|
1367
|
+
function cheapestCompositePrice(parts) {
|
|
1368
|
+
let total = 0;
|
|
1369
|
+
for (const part of parts) {
|
|
1370
|
+
if (part.min === 0) continue;
|
|
1371
|
+
const index = cheapestCompositeItem(part.items) ?? cheapestCompositeItem(part.items.map((item) => ({ ...item, available: true })));
|
|
1372
|
+
if (index === void 0) return void 0;
|
|
1373
|
+
total += part.min * compositeItemCost(part.items[index]);
|
|
1374
|
+
}
|
|
1375
|
+
return total;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// utils/modifier-resolution.util.ts
|
|
1379
|
+
function ancestorChain(categoryId, categoriesById) {
|
|
1380
|
+
const chain = [];
|
|
1381
|
+
const seen = /* @__PURE__ */ new Set([categoryId]);
|
|
1382
|
+
let current = categoriesById.get(categoryId)?.parentId ?? void 0;
|
|
1383
|
+
while (current && !seen.has(current)) {
|
|
1384
|
+
chain.push(current);
|
|
1385
|
+
seen.add(current);
|
|
1386
|
+
current = categoriesById.get(current)?.parentId ?? void 0;
|
|
1387
|
+
}
|
|
1388
|
+
return chain;
|
|
1389
|
+
}
|
|
1390
|
+
function clampRule(rule) {
|
|
1391
|
+
if (rule.min != null && rule.max != null && rule.min > rule.max) {
|
|
1392
|
+
return { ...rule, min: rule.max };
|
|
1393
|
+
}
|
|
1394
|
+
return rule;
|
|
1395
|
+
}
|
|
1396
|
+
function mergeRules(a, b) {
|
|
1397
|
+
const required = a.required || b.required;
|
|
1398
|
+
const min = a.min == null ? b.min : b.min == null ? a.min : Math.max(a.min, b.min);
|
|
1399
|
+
const max = a.max == null ? b.max : b.max == null ? a.max : Math.min(a.max, b.max);
|
|
1400
|
+
return clampRule({ required, min, max, sortOrder: Math.min(a.sortOrder, b.sortOrder) });
|
|
1401
|
+
}
|
|
1402
|
+
function toRule(row) {
|
|
1403
|
+
const required = row.required ?? false;
|
|
1404
|
+
return clampRule({
|
|
1405
|
+
required,
|
|
1406
|
+
// `required` is the gate: a stale min left on a placement that went optional is
|
|
1407
|
+
// ignored. A cleared min/max stays the explicit null it arrived as — nothing
|
|
1408
|
+
// downstream distinguishes null from absent.
|
|
1409
|
+
min: required ? row.min : void 0,
|
|
1410
|
+
max: row.max,
|
|
1411
|
+
sortOrder: row.sortOrder ?? 0
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
function toResolvedChoices(modifier) {
|
|
1415
|
+
return (modifier.choices ?? []).map((choice, index) => ({
|
|
1416
|
+
_id: String(choice._id),
|
|
1417
|
+
name: choice.name,
|
|
1418
|
+
price: choice.price ?? 0,
|
|
1419
|
+
isDefault: choice.isDefault,
|
|
1420
|
+
defaultQuantity: choice.defaultQuantity,
|
|
1421
|
+
unavailableOutletIds: choice.unavailableOutletIds?.map(String),
|
|
1422
|
+
sortOrder: choice.sortOrder ?? index
|
|
1423
|
+
})).sort((a, b) => a.sortOrder - b.sortOrder);
|
|
1424
|
+
}
|
|
1425
|
+
function resolveModifiersForProduct(inputs) {
|
|
1426
|
+
const categoriesById = new Map(
|
|
1427
|
+
inputs.categories.filter((category) => !category.isDeleted).map((category) => [String(category._id), category])
|
|
1428
|
+
);
|
|
1429
|
+
const modifiersById = new Map(
|
|
1430
|
+
inputs.modifiers.filter((modifier) => !modifier.isDeleted).map((modifier) => [String(modifier._id), modifier])
|
|
1431
|
+
);
|
|
1432
|
+
const directCategories = new Set(inputs.categoryIds.map(String));
|
|
1433
|
+
const inheritedCategories = /* @__PURE__ */ new Set();
|
|
1434
|
+
for (const categoryId of directCategories) {
|
|
1435
|
+
for (const ancestor of ancestorChain(categoryId, categoriesById)) {
|
|
1436
|
+
if (!directCategories.has(ancestor)) inheritedCategories.add(ancestor);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
const rules = /* @__PURE__ */ new Map();
|
|
1440
|
+
for (const placement of inputs.categoryModifiers) {
|
|
1441
|
+
if (placement.isDeleted) continue;
|
|
1442
|
+
const categoryId = String(placement.categoryId);
|
|
1443
|
+
const precedence = directCategories.has(categoryId) ? 0 : inheritedCategories.has(categoryId) ? 1 : -1;
|
|
1444
|
+
if (precedence === -1) continue;
|
|
1445
|
+
const modifierId = String(placement.modifierId);
|
|
1446
|
+
if (!modifiersById.has(modifierId)) continue;
|
|
1447
|
+
const incoming = toRule(placement);
|
|
1448
|
+
const existing = rules.get(modifierId);
|
|
1449
|
+
if (!existing || precedence < existing.precedence) {
|
|
1450
|
+
rules.set(modifierId, { precedence, rule: incoming });
|
|
1451
|
+
} else if (precedence === existing.precedence) {
|
|
1452
|
+
rules.set(modifierId, { precedence, rule: mergeRules(existing.rule, incoming) });
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
const resolved = [];
|
|
1456
|
+
for (const [modifierId, { rule }] of rules) {
|
|
1457
|
+
const modifier = modifiersById.get(modifierId);
|
|
1458
|
+
if (!modifier) continue;
|
|
1459
|
+
const required = rule.required;
|
|
1460
|
+
resolved.push({
|
|
1461
|
+
_id: modifierId,
|
|
1462
|
+
name: modifier.name,
|
|
1463
|
+
selectionType: modifier.selectionType,
|
|
1464
|
+
allowQuantity: modifier.allowQuantity,
|
|
1465
|
+
required,
|
|
1466
|
+
// single-selection is implicitly capped at 1 — min/max are never carried.
|
|
1467
|
+
min: modifier.selectionType === "single" ? void 0 : rule.min,
|
|
1468
|
+
max: modifier.selectionType === "single" ? void 0 : rule.max,
|
|
1469
|
+
sortOrder: rule.sortOrder,
|
|
1470
|
+
choices: toResolvedChoices(modifier)
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
return resolved.sort((a, b) => a.sortOrder - b.sortOrder || a.name.localeCompare(b.name));
|
|
1474
|
+
}
|
|
1475
|
+
function isChoiceAvailableAtOutlet(choice, outletId) {
|
|
1476
|
+
return !(choice.unavailableOutletIds ?? []).includes(outletId);
|
|
1477
|
+
}
|
|
1478
|
+
function hasSellableChoice(modifier, outletId) {
|
|
1479
|
+
return modifier.choices.some((choice) => isChoiceAvailableAtOutlet(choice, outletId));
|
|
1480
|
+
}
|
|
1481
|
+
var modifiersSellable = (modifiers, outletId) => modifiers.every((modifier) => !modifier.required || hasSellableChoice(modifier, outletId));
|
|
1482
|
+
|
|
1483
|
+
// utils/composite-availability.util.ts
|
|
1484
|
+
function poolStockAt(rows, outletId) {
|
|
1485
|
+
if (!rows.some((row) => row.manageStock === true)) return null;
|
|
1486
|
+
return Math.max(0, rows.find((row) => String(row.outletId) === outletId)?.quantity ?? 0);
|
|
1487
|
+
}
|
|
1488
|
+
function compositeVariantInStock(variantId, quantity, outlet) {
|
|
1489
|
+
const shelf = outlet.shelves.get(variantId);
|
|
1490
|
+
if (!shelf || shelf.unresolved) return false;
|
|
1491
|
+
const onHand = outlet.poolStock.get(shelf.pool);
|
|
1492
|
+
if (onHand === void 0) return false;
|
|
1493
|
+
if (onHand === null || shelf.backorder) return true;
|
|
1494
|
+
return onHand >= (shelf.unit ? toBase(quantity, shelf.unit) : quantity);
|
|
1495
|
+
}
|
|
1496
|
+
function compositeItemUnavailable(item, rows) {
|
|
1497
|
+
const { outlet } = rows;
|
|
1498
|
+
const sellableHere = (productId2) => !outlet || !outlet.hiddenProductIds.has(productId2) && !outlet.unsellableProductIds.has(productId2);
|
|
1499
|
+
if (item.categoryId) {
|
|
1500
|
+
const variants = rows.categoryVariants.get(String(item.categoryId));
|
|
1501
|
+
if (!variants) return "deleted";
|
|
1502
|
+
const offered = variants.filter((variantId2) => sellableHere(rows.variantProducts.get(variantId2) ?? ""));
|
|
1503
|
+
if (!offered.length) return "empty";
|
|
1504
|
+
if (!outlet) return null;
|
|
1505
|
+
return offered.some((variantId2) => compositeVariantInStock(variantId2, item.quantity, outlet)) ? null : "out_of_stock";
|
|
1506
|
+
}
|
|
1507
|
+
const variantId = String(item.variantId);
|
|
1508
|
+
const productId = rows.variantProducts.get(variantId);
|
|
1509
|
+
if (!productId || !rows.productStatuses.has(productId)) return "deleted";
|
|
1510
|
+
if (rows.productStatuses.get(productId) !== "active" /* ACTIVE */) return "inactive";
|
|
1511
|
+
if (!outlet) return null;
|
|
1512
|
+
if (outlet.hiddenProductIds.has(productId)) return "hidden";
|
|
1513
|
+
if (outlet.unsellableProductIds.has(productId)) return "empty";
|
|
1514
|
+
if (rows.bookableProductIds.has(productId)) {
|
|
1515
|
+
if (!outlet.bookableSeats) return "by_date";
|
|
1516
|
+
return (outlet.bookableSeats.get(productId) ?? 0) >= item.quantity ? null : "fully_booked";
|
|
1517
|
+
}
|
|
1518
|
+
return compositeVariantInStock(variantId, item.quantity, outlet) ? null : "out_of_stock";
|
|
1519
|
+
}
|
|
1520
|
+
function compositeMakeable(parts, rows) {
|
|
1521
|
+
const { outlet } = rows;
|
|
1522
|
+
if (!outlet) throw new Error("compositeMakeable counts at one outlet: rows.outlet is required");
|
|
1523
|
+
const required = parts.map((part, partIndex) => ({ ...part, partIndex })).filter((part) => part.min > 0);
|
|
1524
|
+
const isBookable = (variantId) => rows.bookableProductIds.has(rows.variantProducts.get(String(variantId)) ?? "");
|
|
1525
|
+
if (required.some((part) => part.items.some((item) => item.variantId && isBookable(item.variantId))))
|
|
1526
|
+
return { byDate: true };
|
|
1527
|
+
const optionsOf = (item) => (item.categoryId ? rows.categoryVariants.get(String(item.categoryId)) ?? [] : [String(item.variantId)]).flatMap(
|
|
1528
|
+
(variantId) => {
|
|
1529
|
+
const reason = compositeItemUnavailable({ variantId, quantity: item.quantity }, rows);
|
|
1530
|
+
const shelf = outlet.shelves.get(variantId);
|
|
1531
|
+
if (reason && reason !== "out_of_stock" || !shelf) return [];
|
|
1532
|
+
const stock = outlet.poolStock.get(shelf.pool);
|
|
1533
|
+
const onHand = shelf.unresolved || stock === void 0 ? 0 : stock === null || shelf.backorder ? null : stock;
|
|
1534
|
+
const perPick = shelf.unit ? toBase(item.quantity, shelf.unit) : item.quantity;
|
|
1535
|
+
return [{ itemId: item.id, pool: shelf.pool, perPick, onHand }];
|
|
1536
|
+
}
|
|
1537
|
+
);
|
|
1538
|
+
const counted = required.map((part) => ({ ...part, options: part.items.flatMap(optionsOf) }));
|
|
1539
|
+
const order = [...counted].sort((a, b) => Number(a.options.length !== 1) - Number(b.options.length !== 1));
|
|
1540
|
+
const picksLeft = (option, reserved) => option.onHand === null ? Infinity : Math.max(0, Math.floor((option.onHand - (reserved.get(option.pool) ?? 0)) / option.perPick));
|
|
1541
|
+
const shortAt = (n) => {
|
|
1542
|
+
const reserved = /* @__PURE__ */ new Map();
|
|
1543
|
+
for (const part of order) {
|
|
1544
|
+
let wanted = part.min * n;
|
|
1545
|
+
const ranked = [...part.options].sort((a, b) => picksLeft(b, reserved) - picksLeft(a, reserved));
|
|
1546
|
+
for (const option of ranked) {
|
|
1547
|
+
if (wanted <= 0) break;
|
|
1548
|
+
const taken = Math.min(wanted, picksLeft(option, reserved));
|
|
1549
|
+
if (option.onHand !== null)
|
|
1550
|
+
reserved.set(option.pool, (reserved.get(option.pool) ?? 0) + taken * option.perPick);
|
|
1551
|
+
wanted -= taken;
|
|
1552
|
+
}
|
|
1553
|
+
if (wanted > 0) return part.partIndex;
|
|
1554
|
+
}
|
|
1555
|
+
return void 0;
|
|
1556
|
+
};
|
|
1557
|
+
const whyRows = [];
|
|
1558
|
+
const rowPools = /* @__PURE__ */ new Map();
|
|
1559
|
+
const makes = (has, uses) => has === null ? null : Math.floor(has / uses);
|
|
1560
|
+
for (const part of counted) {
|
|
1561
|
+
const [option] = part.options;
|
|
1562
|
+
if (part.options.length === 1) {
|
|
1563
|
+
const uses = option.perPick * part.min;
|
|
1564
|
+
const row = whyRows.find((candidate) => rowPools.get(candidate) === option.pool);
|
|
1565
|
+
if (!row) {
|
|
1566
|
+
const created = { itemIds: [option.itemId], uses, has: option.onHand, makes: makes(option.onHand, uses) };
|
|
1567
|
+
rowPools.set(created, option.pool);
|
|
1568
|
+
whyRows.push(created);
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
row.itemIds.push(option.itemId);
|
|
1572
|
+
row.uses += uses;
|
|
1573
|
+
if (option.onHand === null) row.has = null;
|
|
1574
|
+
row.makes = makes(row.has, row.uses);
|
|
1575
|
+
continue;
|
|
1576
|
+
}
|
|
1577
|
+
const perPool = /* @__PURE__ */ new Map();
|
|
1578
|
+
for (const candidate of part.options)
|
|
1579
|
+
perPool.set(candidate.pool, Math.max(perPool.get(candidate.pool) ?? 0, picksLeft(candidate, /* @__PURE__ */ new Map())));
|
|
1580
|
+
const total = [...perPool.values()].reduce((sum, picks) => sum + picks, 0);
|
|
1581
|
+
const has = total === Infinity ? null : total;
|
|
1582
|
+
whyRows.push({
|
|
1583
|
+
itemIds: part.items.map((item) => item.id),
|
|
1584
|
+
partIndex: part.partIndex,
|
|
1585
|
+
uses: part.min,
|
|
1586
|
+
has,
|
|
1587
|
+
makes: makes(has, part.min)
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
if (counted.every((part) => part.options.some((option) => option.onHand === null)))
|
|
1591
|
+
return { count: null, rows: whyRows };
|
|
1592
|
+
const ceiling = Math.min(
|
|
1593
|
+
...counted.filter((part) => !part.options.some((option) => option.onHand === null)).map(
|
|
1594
|
+
(part) => Math.floor(part.options.reduce((sum, option) => sum + picksLeft(option, /* @__PURE__ */ new Map()), 0) / part.min)
|
|
1595
|
+
)
|
|
1596
|
+
);
|
|
1597
|
+
let [low, high] = [0, ceiling];
|
|
1598
|
+
while (low < high) {
|
|
1599
|
+
const mid = Math.ceil((low + high) / 2);
|
|
1600
|
+
if (shortAt(mid) === void 0) low = mid;
|
|
1601
|
+
else high = mid - 1;
|
|
1602
|
+
}
|
|
1603
|
+
const short = shortAt(low + 1);
|
|
1604
|
+
const shortPart = counted.find((part) => part.partIndex === short);
|
|
1605
|
+
const limitedBy = whyRows.findIndex(
|
|
1606
|
+
(row) => shortPart?.options.length === 1 ? row.partIndex === void 0 && row.itemIds.includes(shortPart.options[0].itemId) : row.partIndex === short
|
|
1607
|
+
);
|
|
1608
|
+
return { count: low, rows: whyRows, ...limitedBy >= 0 ? { limitedBy } : {} };
|
|
1609
|
+
}
|
|
1610
|
+
var poolOf = (line) => line.stockVariantId || line.variantId;
|
|
1611
|
+
function poolDemand(lines) {
|
|
1612
|
+
const demand = /* @__PURE__ */ new Map();
|
|
1613
|
+
const add = (line, times) => demand.set(
|
|
1614
|
+
poolOf(line),
|
|
1615
|
+
(demand.get(poolOf(line)) ?? 0) + (line.unit ? toBase(line.quantity * times, line.unit) : line.quantity * times)
|
|
1616
|
+
);
|
|
1617
|
+
for (const line of lines) {
|
|
1618
|
+
if (line.components) for (const component of line.components) add(component, line.quantity);
|
|
1619
|
+
else add(line, 1);
|
|
1620
|
+
}
|
|
1621
|
+
return demand;
|
|
1622
|
+
}
|
|
1623
|
+
function poolShortfall(demand, poolStock, backordered = /* @__PURE__ */ new Set()) {
|
|
1624
|
+
for (const [poolId, wanted] of demand) {
|
|
1625
|
+
const available = poolStock.get(poolId);
|
|
1626
|
+
if (available === null || available === void 0 || backordered.has(poolId)) continue;
|
|
1627
|
+
if (wanted > available) return { poolId, available, wanted };
|
|
1628
|
+
}
|
|
1629
|
+
return null;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// utils/composite-reservation.util.ts
|
|
1633
|
+
function compositeModifierMismatches(lineItems) {
|
|
1634
|
+
return lineItems.flatMap(
|
|
1635
|
+
(line) => line.components?.length ? (line.modifiers ?? []).flatMap(
|
|
1636
|
+
(modifier) => modifier.componentIndex !== void 0 && line.components[modifier.componentIndex] !== void 0 ? [] : [
|
|
1637
|
+
`Modifier ${modifier.modifierName ?? "?"} on composite line ${line.internalId ?? "?"} names no component (componentIndex ${modifier.componentIndex}) \u2014 a composite has no modifiers of its own`
|
|
1638
|
+
]
|
|
1639
|
+
) : []
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1642
|
+
function compositeReservationMismatches(order) {
|
|
1643
|
+
return (order.reservations ?? []).flatMap((reservation) => {
|
|
1644
|
+
if (reservation.lineItemInternalId === void 0) return [];
|
|
1645
|
+
const name = `Booking ${String(reservation.bookingId ?? "?")}`;
|
|
1646
|
+
const line = order.lineItems?.find((candidate) => candidate.internalId === reservation.lineItemInternalId);
|
|
1647
|
+
if (!line) return [`${name} names line ${reservation.lineItemInternalId}, which is not on the order`];
|
|
1648
|
+
const component = reservation.componentIndex === void 0 ? void 0 : line.components?.[reservation.componentIndex];
|
|
1649
|
+
if (!component) return [`${name} names no component ${reservation.componentIndex} of line ${line.internalId}`];
|
|
1650
|
+
if (String(component.productId) !== String(reservation.productId))
|
|
1651
|
+
return [
|
|
1652
|
+
`${name} is for ${String(reservation.productId)}, but that component sold ${String(component.productId)}`
|
|
1653
|
+
];
|
|
1654
|
+
if (reservation.price !== 0 || reservation.total !== 0)
|
|
1655
|
+
return [`${name} carries money (${reservation.price}/${reservation.total}); its component already does`];
|
|
1656
|
+
return [];
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1356
1660
|
// units/family-code.ts
|
|
1357
1661
|
var MAX_SLUG_LENGTH = 32;
|
|
1358
1662
|
var FALLBACK_SLUG = "family";
|
|
@@ -3002,7 +3306,9 @@ function buildGrid(rule, timeZone, from, to, now, busy = []) {
|
|
|
3002
3306
|
const start = startAt.getTime();
|
|
3003
3307
|
if (start < askedFrom || start >= askedTo) return false;
|
|
3004
3308
|
if (start < notBefore || start > notAfter) return false;
|
|
3005
|
-
return !blocked.some(
|
|
3309
|
+
return !blocked.some(
|
|
3310
|
+
({ from: blockFrom, to: blockTo }) => start < blockTo && bufferEndAt.getTime() > blockFrom
|
|
3311
|
+
);
|
|
3006
3312
|
}).sort((first, second) => +first.startAt - +second.startAt);
|
|
3007
3313
|
}
|
|
3008
3314
|
function slotsForWindow(grid, startAt, endAt) {
|
|
@@ -3121,7 +3427,14 @@ function buildAvailability({
|
|
|
3121
3427
|
for (const slot of slots2) {
|
|
3122
3428
|
const { taken, foreignProductId } = occupancyIn(mine, productId, slot.startAt, slot.bufferEndAt, shared);
|
|
3123
3429
|
const free = foreignProductId ? 0 : Math.max(capacity - taken, 0);
|
|
3124
|
-
const entry = byInstant.get(+slot.startAt) ?? {
|
|
3430
|
+
const entry = byInstant.get(+slot.startAt) ?? {
|
|
3431
|
+
...slot,
|
|
3432
|
+
capacity: 0,
|
|
3433
|
+
booked: 0,
|
|
3434
|
+
free: 0,
|
|
3435
|
+
resources: [],
|
|
3436
|
+
dayKey: shopDayKey(slot.startAt, resource.timeZone)
|
|
3437
|
+
};
|
|
3125
3438
|
entry.capacity += capacity;
|
|
3126
3439
|
entry.booked += foreignProductId ? capacity : taken;
|
|
3127
3440
|
entry.free += free;
|
|
@@ -3279,6 +3592,7 @@ export {
|
|
|
3279
3592
|
UserTypes,
|
|
3280
3593
|
addAmounts,
|
|
3281
3594
|
amountToString,
|
|
3595
|
+
ancestorChain,
|
|
3282
3596
|
assertUnitSane,
|
|
3283
3597
|
availableIn,
|
|
3284
3598
|
baseFor,
|
|
@@ -3292,6 +3606,14 @@ export {
|
|
|
3292
3606
|
buildResolvedConfig,
|
|
3293
3607
|
calculatePercentage,
|
|
3294
3608
|
canTransition,
|
|
3609
|
+
cheapestCompositeItem,
|
|
3610
|
+
cheapestCompositePrice,
|
|
3611
|
+
compositeItemCost,
|
|
3612
|
+
compositeItemUnavailable,
|
|
3613
|
+
compositeMakeable,
|
|
3614
|
+
compositeModifierMismatches,
|
|
3615
|
+
compositeReservationMismatches,
|
|
3616
|
+
compositeVariantInStock,
|
|
3295
3617
|
conditionSummary,
|
|
3296
3618
|
convertFixedFeeToLocalCurrency,
|
|
3297
3619
|
createAmount,
|
|
@@ -3326,9 +3648,11 @@ export {
|
|
|
3326
3648
|
getPricingRegionFromCountry,
|
|
3327
3649
|
getSettlementTotal,
|
|
3328
3650
|
getTotalCollected,
|
|
3651
|
+
hasSellableChoice,
|
|
3329
3652
|
holdExpiresAt,
|
|
3330
3653
|
inferStatesFromLegacyStatus,
|
|
3331
3654
|
isAdyenRolloutCountry,
|
|
3655
|
+
isChoiceAvailableAtOutlet,
|
|
3332
3656
|
isConditionOperator,
|
|
3333
3657
|
isFulfillmentState,
|
|
3334
3658
|
isPaymentState,
|
|
@@ -3339,6 +3663,7 @@ export {
|
|
|
3339
3663
|
mapToLegacyStatus,
|
|
3340
3664
|
maxPrecisionFor,
|
|
3341
3665
|
mergeFragment,
|
|
3666
|
+
modifiersSellable,
|
|
3342
3667
|
multiplyAmount,
|
|
3343
3668
|
normalizeMongoIdString,
|
|
3344
3669
|
occupancyIn,
|
|
@@ -3346,9 +3671,14 @@ export {
|
|
|
3346
3671
|
parseCurrencyCode,
|
|
3347
3672
|
parseStripeCurrencyCode,
|
|
3348
3673
|
planWindow,
|
|
3674
|
+
poolDemand,
|
|
3675
|
+
poolOf,
|
|
3676
|
+
poolShortfall,
|
|
3677
|
+
poolStockAt,
|
|
3349
3678
|
presentAt,
|
|
3350
3679
|
removeFragment,
|
|
3351
3680
|
resolveFamily,
|
|
3681
|
+
resolveModifiersForProduct,
|
|
3352
3682
|
resolveTemplate,
|
|
3353
3683
|
resolveUnit,
|
|
3354
3684
|
roundToPrecision,
|