@putiikkipalvelu/storefront-sdk 0.2.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -414,6 +414,138 @@ function createCategoriesResource(fetcher) {
414
414
  };
415
415
  }
416
416
 
417
+ // src/utils/pricing.ts
418
+ function isSaleActive(startDate, endDate) {
419
+ if (!startDate && !endDate) {
420
+ return true;
421
+ }
422
+ const now = /* @__PURE__ */ new Date();
423
+ const start = startDate ? new Date(startDate) : null;
424
+ const end = endDate ? new Date(endDate) : null;
425
+ if (start && !end) {
426
+ return now >= start;
427
+ }
428
+ if (!start && end) {
429
+ return now <= end;
430
+ }
431
+ if (start && end) {
432
+ return now >= start && now <= end;
433
+ }
434
+ return true;
435
+ }
436
+ function getPriceInfo(product, variation) {
437
+ if (variation) {
438
+ const isOnSale2 = isSaleActive(variation.saleStartDate, variation.saleEndDate) && variation.salePrice !== null;
439
+ const originalPrice2 = variation.price ?? product.price;
440
+ const effectivePrice2 = isOnSale2 ? variation.salePrice ?? originalPrice2 : originalPrice2;
441
+ return {
442
+ effectivePrice: effectivePrice2,
443
+ originalPrice: originalPrice2,
444
+ isOnSale: isOnSale2,
445
+ salePercent: isOnSale2 ? variation.salePercent ?? null : null
446
+ };
447
+ }
448
+ const isOnSale = isSaleActive(product.saleStartDate, product.saleEndDate) && product.salePrice !== null;
449
+ const originalPrice = product.price;
450
+ const effectivePrice = isOnSale ? product.salePrice ?? originalPrice : originalPrice;
451
+ return {
452
+ effectivePrice,
453
+ originalPrice,
454
+ isOnSale,
455
+ salePercent: isOnSale ? product.salePercent ?? null : null
456
+ };
457
+ }
458
+
459
+ // src/utils/cart-calculations.ts
460
+ function calculateCartWithCampaigns(items, campaigns) {
461
+ const buyXPayYCampaign = campaigns.find(
462
+ (c) => c.type === "BUY_X_PAY_Y" && c.isActive
463
+ );
464
+ const originalTotal = items.reduce((total, { product, variation, cartQuantity }) => {
465
+ const priceInfo = getPriceInfo(product, variation);
466
+ return total + priceInfo.effectivePrice * cartQuantity;
467
+ }, 0);
468
+ if (!buyXPayYCampaign?.BuyXPayYCampaign) {
469
+ const calculatedItems2 = items.map((item) => ({
470
+ item,
471
+ paidQuantity: item.cartQuantity,
472
+ freeQuantity: 0,
473
+ totalQuantity: item.cartQuantity
474
+ }));
475
+ return {
476
+ calculatedItems: calculatedItems2,
477
+ cartTotal: originalTotal,
478
+ originalTotal,
479
+ totalSavings: 0
480
+ };
481
+ }
482
+ const { buyQuantity, payQuantity, applicableCategories } = buyXPayYCampaign.BuyXPayYCampaign;
483
+ const applicableCategoryIds = new Set(
484
+ applicableCategories.map((c) => c.id)
485
+ );
486
+ const eligibleUnits = items.flatMap((item) => {
487
+ const { product, variation } = item;
488
+ const itemCategories = product.categories?.map((cat) => cat.id) || [];
489
+ const isEligible = itemCategories.some(
490
+ (id) => applicableCategoryIds.has(id)
491
+ );
492
+ if (isEligible) {
493
+ const priceInfo = getPriceInfo(product, variation);
494
+ return Array.from({ length: item.cartQuantity }, () => ({
495
+ price: priceInfo.effectivePrice,
496
+ productId: product.id,
497
+ variationId: variation?.id,
498
+ originalItem: item
499
+ }));
500
+ }
501
+ return [];
502
+ });
503
+ if (eligibleUnits.length < buyQuantity) {
504
+ const calculatedItems2 = items.map((item) => ({
505
+ item,
506
+ paidQuantity: item.cartQuantity,
507
+ freeQuantity: 0,
508
+ totalQuantity: item.cartQuantity
509
+ }));
510
+ return {
511
+ calculatedItems: calculatedItems2,
512
+ cartTotal: originalTotal,
513
+ originalTotal,
514
+ totalSavings: 0
515
+ };
516
+ }
517
+ eligibleUnits.sort((a, b) => a.price - b.price);
518
+ const numToMakeFree = buyQuantity - payQuantity;
519
+ const itemsToMakeFree = eligibleUnits.slice(0, numToMakeFree);
520
+ const totalSavings = itemsToMakeFree.reduce(
521
+ (sum, item) => sum + item.price,
522
+ 0
523
+ );
524
+ const freeCountMap = /* @__PURE__ */ new Map();
525
+ for (const freebie of itemsToMakeFree) {
526
+ const key = `${freebie.productId}${freebie.variationId ? `_${freebie.variationId}` : ""}`;
527
+ freeCountMap.set(key, (freeCountMap.get(key) || 0) + 1);
528
+ }
529
+ const calculatedItems = items.map((item) => {
530
+ const key = `${item.product.id}${item.variation?.id ? `_${item.variation.id}` : ""}`;
531
+ const freeQuantity = freeCountMap.get(key) || 0;
532
+ const paidQuantity = item.cartQuantity - freeQuantity;
533
+ return {
534
+ item,
535
+ paidQuantity: Math.max(0, paidQuantity),
536
+ freeQuantity,
537
+ totalQuantity: item.cartQuantity
538
+ };
539
+ });
540
+ const cartTotal = originalTotal - totalSavings;
541
+ return {
542
+ calculatedItems,
543
+ cartTotal,
544
+ originalTotal,
545
+ totalSavings
546
+ };
547
+ }
548
+
417
549
  // src/resources/cart.ts
418
550
  function buildCartHeaders(options) {
419
551
  const headers = {};
@@ -576,33 +708,49 @@ function createCartResource(fetcher) {
576
708
  * Checks product availability, stock levels, and prices.
577
709
  * Auto-fixes issues (removes unavailable items, adjusts quantities).
578
710
  *
711
+ * Campaign conflict detection:
712
+ * - SDK calculates if BuyXPayY campaigns apply using calculateCartWithCampaigns()
713
+ * - Sends x-campaigns-apply header to backend
714
+ * - If campaigns apply AND discount code exists, backend removes it
715
+ * - Returns changes.discountCouponRemoved = true
716
+ *
579
717
  * @param options - Cart session options
718
+ * @param cartItems - Current cart items for campaign calculation
719
+ * @param campaigns - Active campaigns for conflict check
580
720
  * @param fetchOptions - Fetch options
581
721
  * @returns Validated cart with change metadata
582
722
  *
583
723
  * @example Validate before checkout
584
724
  * ```typescript
585
- * const { items, hasChanges, changes } = await client.cart.validate({ cartId });
725
+ * const result = await client.cart.validate(
726
+ * { cartId },
727
+ * cartItems,
728
+ * storeConfig.campaigns
729
+ * );
586
730
  *
587
- * if (hasChanges) {
588
- * if (changes.removedItems > 0) {
589
- * notify('Some items were removed (out of stock)');
590
- * }
591
- * if (changes.quantityAdjusted > 0) {
592
- * notify('Some quantities were adjusted');
593
- * }
594
- * if (changes.priceChanged > 0) {
595
- * notify('Some prices have changed');
731
+ * if (result.hasChanges) {
732
+ * if (result.changes.discountCouponRemoved) {
733
+ * notify("Alennuskoodi poistettu - kampanja-alennus aktivoitui");
596
734
  * }
735
+ * // Handle other changes...
597
736
  * }
598
737
  * ```
599
738
  */
600
- async validate(options, fetchOptions) {
739
+ async validate(options, cartItems, campaigns, fetchOptions) {
740
+ let campaignsApply = false;
741
+ if (cartItems && campaigns && cartItems.length > 0) {
742
+ const campaignResult = calculateCartWithCampaigns(cartItems, campaigns);
743
+ campaignsApply = campaignResult.totalSavings > 0;
744
+ }
745
+ const headers = {
746
+ ...buildCartHeaders(options),
747
+ "x-campaigns-apply": campaignsApply ? "true" : "false"
748
+ };
601
749
  return fetcher.request(
602
750
  "/api/storefront/v1/cart/validate",
603
751
  {
604
752
  method: "GET",
605
- headers: buildCartHeaders(options),
753
+ headers,
606
754
  ...fetchOptions
607
755
  }
608
756
  );
@@ -617,48 +765,71 @@ function calculateCartWeight(items) {
617
765
  return total + itemWeight * item.cartQuantity;
618
766
  }, 0);
619
767
  }
768
+ function calculateCartTotal(items) {
769
+ return items.reduce((total, item) => {
770
+ const itemPrice = item.variation ? item.variation.salePrice ?? item.variation.price : item.product.salePrice ?? item.product.price;
771
+ return total + itemPrice * item.cartQuantity;
772
+ }, 0);
773
+ }
620
774
  function createShippingResource(fetcher) {
621
775
  return {
622
776
  /**
623
777
  * Get shipping options for a specific postal code.
624
- * Returns home delivery methods and pickup locations.
778
+ * Returns pickup points and home delivery options in a unified format.
779
+ *
780
+ * **Pickup points are returned first** as they are more popular in Finland.
625
781
  *
626
782
  * @param postalCode - Customer's postal code (e.g., "00100")
627
783
  * @param options - Fetch options including optional cartItems for weight-based filtering
628
- * @returns Home delivery methods and pickup locations
784
+ * @returns Unified shipping options (pickupPoints sorted by distance, homeDelivery sorted by price)
629
785
  *
630
786
  * @example
631
787
  * ```typescript
632
- * const { homeDeliveryMethods, pickupLocations } = await client.shipping.getWithLocations("00100");
633
- *
634
- * // Show home delivery options
635
- * homeDeliveryMethods.forEach(method => {
636
- * console.log(`${method.name}: ${method.price / 100}€`);
788
+ * const { pickupPoints, homeDelivery } = await client.shipping.getOptions("00100");
789
+ *
790
+ * // Show pickup points (more popular in Finland)
791
+ * pickupPoints.forEach(point => {
792
+ * console.log(`${point.name} - ${point.carrier}`);
793
+ * console.log(` ${point.address}, ${point.city}`);
794
+ * console.log(` ${(point.distance! / 1000).toFixed(1)} km away`);
795
+ * console.log(` Price: ${point.price / 100}€`);
637
796
  * });
638
797
  *
639
- * // Show pickup locations
640
- * pickupLocations.forEach(location => {
641
- * console.log(`${location.name} - ${location.carrier}`);
642
- * console.log(` ${location.address1}, ${location.city}`);
643
- * console.log(` ${location.distanceInKilometers.toFixed(1)} km away`);
644
- * console.log(` Price: ${location.price / 100}€`);
798
+ * // Show home delivery options
799
+ * homeDelivery.forEach(option => {
800
+ * console.log(`${option.name}: ${option.price / 100}€`);
801
+ * if (option.estimatedDelivery) {
802
+ * console.log(` Delivery: ${option.estimatedDelivery} days`);
803
+ * }
645
804
  * });
646
805
  * ```
647
806
  *
648
807
  * @example Weight-based filtering
649
808
  * ```typescript
650
- * const { homeDeliveryMethods, pickupLocations } = await client.shipping.getWithLocations(
651
- * "00100",
652
- * { cartItems: cartItems }
653
- * );
809
+ * const options = await client.shipping.getOptions("00100", {
810
+ * cartItems: cartItems
811
+ * });
654
812
  * // Only shows methods that support the cart's total weight
655
813
  * ```
814
+ *
815
+ * @example International shipping
816
+ * ```typescript
817
+ * const options = await client.shipping.getOptions("112 22", {
818
+ * country: "SE"
819
+ * });
820
+ * ```
656
821
  */
657
- async getWithLocations(postalCode, options) {
822
+ async getOptions(postalCode, options) {
658
823
  const params = new URLSearchParams();
659
824
  if (options?.cartItems?.length) {
660
825
  const cartWeight = calculateCartWeight(options.cartItems);
661
826
  params.set("cartWeight", cartWeight.toString());
827
+ const cartTotal = options.campaigns ? calculateCartWithCampaigns(options.cartItems, options.campaigns).cartTotal : calculateCartTotal(options.cartItems);
828
+ const finalCartTotal = options.discountAmount ? Math.max(0, cartTotal - options.discountAmount) : cartTotal;
829
+ params.set("cartTotal", finalCartTotal.toString());
830
+ }
831
+ if (options?.country) {
832
+ params.set("country", options.country);
662
833
  }
663
834
  const queryString = params.toString();
664
835
  const url = `/api/storefront/v1/shipment-methods/${encodeURIComponent(postalCode)}${queryString ? `?${queryString}` : ""}`;
@@ -666,6 +837,12 @@ function createShippingResource(fetcher) {
666
837
  method: "GET",
667
838
  ...options
668
839
  });
840
+ },
841
+ /**
842
+ * @deprecated Use getOptions() instead. This method is kept for backwards compatibility.
843
+ */
844
+ async getWithLocations(postalCode, options) {
845
+ return this.getOptions(postalCode, options);
669
846
  }
670
847
  };
671
848
  }
@@ -1367,6 +1544,102 @@ function createCheckoutResource(fetcher) {
1367
1544
  };
1368
1545
  }
1369
1546
 
1547
+ // src/resources/discount-code.ts
1548
+ function createDiscountCodeResource(fetcher) {
1549
+ return {
1550
+ /**
1551
+ * Apply a discount code to the cart
1552
+ *
1553
+ * Checks for BuyXPayY campaign conflict before calling API.
1554
+ * Discount codes cannot be used when a campaign discount is active.
1555
+ *
1556
+ * @param params - Parameters including code, session info, and cart/campaign data for conflict check
1557
+ * @returns Applied discount details
1558
+ * @throws {StorefrontError} With code "CAMPAIGN_ACTIVE" if a BuyXPayY campaign is active
1559
+ *
1560
+ * @example
1561
+ * ```typescript
1562
+ * const result = await client.discountCode.apply({
1563
+ * code: "SUMMER20",
1564
+ * cartId: cartId,
1565
+ * cartItems: cart.items,
1566
+ * campaigns: storeConfig.campaigns,
1567
+ * });
1568
+ * ```
1569
+ */
1570
+ async apply(params) {
1571
+ const { code, cartId, sessionId, cartItems, campaigns } = params;
1572
+ if (cartItems && campaigns && cartItems.length > 0) {
1573
+ const campaignResult = calculateCartWithCampaigns(cartItems, campaigns);
1574
+ if (campaignResult.totalSavings > 0) {
1575
+ throw new StorefrontError(
1576
+ "Alennuskoodia ei voi k\xE4ytt\xE4\xE4 kun kampanja-alennus on voimassa",
1577
+ 400,
1578
+ "CAMPAIGN_ACTIVE"
1579
+ );
1580
+ }
1581
+ }
1582
+ const cartTotal = cartItems ? cartItems.reduce((sum, item) => {
1583
+ const priceInfo = getPriceInfo(item.product, item.variation);
1584
+ return sum + priceInfo.effectivePrice * item.cartQuantity;
1585
+ }, 0) : 0;
1586
+ return fetcher.request("/api/storefront/v1/discount-code/apply", {
1587
+ method: "POST",
1588
+ body: { code, cartTotal },
1589
+ headers: {
1590
+ ...cartId && { "x-cart-id": cartId },
1591
+ ...sessionId && { "x-session-id": sessionId }
1592
+ }
1593
+ });
1594
+ },
1595
+ /**
1596
+ * Get the currently applied discount code
1597
+ *
1598
+ * @param params - Session info (cartId or sessionId)
1599
+ * @returns Current discount or null if none applied
1600
+ *
1601
+ * @example
1602
+ * ```typescript
1603
+ * const { discount } = await client.discountCode.get({ cartId });
1604
+ * if (discount) {
1605
+ * console.log(`Code ${discount.code}: ${discount.discountValue}${discount.discountType === 'PERCENTAGE' ? '%' : '¢'} off`);
1606
+ * }
1607
+ * ```
1608
+ */
1609
+ async get(params = {}) {
1610
+ const { cartId, sessionId } = params;
1611
+ return fetcher.request("/api/storefront/v1/discount-code/apply", {
1612
+ method: "GET",
1613
+ headers: {
1614
+ ...cartId && { "x-cart-id": cartId },
1615
+ ...sessionId && { "x-session-id": sessionId }
1616
+ }
1617
+ });
1618
+ },
1619
+ /**
1620
+ * Remove the currently applied discount code
1621
+ *
1622
+ * @param params - Session info (cartId or sessionId)
1623
+ * @returns Success status
1624
+ *
1625
+ * @example
1626
+ * ```typescript
1627
+ * await client.discountCode.remove({ cartId });
1628
+ * ```
1629
+ */
1630
+ async remove(params = {}) {
1631
+ const { cartId, sessionId } = params;
1632
+ return fetcher.request("/api/storefront/v1/discount-code/apply", {
1633
+ method: "DELETE",
1634
+ headers: {
1635
+ ...cartId && { "x-cart-id": cartId },
1636
+ ...sessionId && { "x-session-id": sessionId }
1637
+ }
1638
+ });
1639
+ }
1640
+ };
1641
+ }
1642
+
1370
1643
  // src/client.ts
1371
1644
  function createStorefrontClient(config) {
1372
1645
  if (!config.apiKey) {
@@ -1390,148 +1663,100 @@ function createStorefrontClient(config) {
1390
1663
  shipping: createShippingResource(fetcher),
1391
1664
  customer: createCustomerResource(fetcher),
1392
1665
  order: createOrderResource(fetcher),
1393
- checkout: createCheckoutResource(fetcher)
1666
+ checkout: createCheckoutResource(fetcher),
1667
+ discountCode: createDiscountCodeResource(fetcher)
1394
1668
  };
1395
1669
  }
1396
1670
 
1397
- // src/utils/pricing.ts
1398
- function isSaleActive(startDate, endDate) {
1399
- if (!startDate && !endDate) {
1400
- return true;
1671
+ // src/utils/discount.ts
1672
+ function formatDiscountValue(discount, options = {}) {
1673
+ const {
1674
+ currencySymbol = "\u20AC",
1675
+ currencyPosition = "after",
1676
+ decimals = 2,
1677
+ showMinus = true
1678
+ } = options;
1679
+ const prefix = showMinus ? "-" : "";
1680
+ if (discount.discountType === "PERCENTAGE") {
1681
+ return `${prefix}${discount.discountValue}%`;
1401
1682
  }
1402
- const now = /* @__PURE__ */ new Date();
1403
- const start = startDate ? new Date(startDate) : null;
1404
- const end = endDate ? new Date(endDate) : null;
1405
- if (start && !end) {
1406
- return now >= start;
1407
- }
1408
- if (!start && end) {
1409
- return now <= end;
1410
- }
1411
- if (start && end) {
1412
- return now >= start && now <= end;
1683
+ const amount = (discount.discountValue / 100).toFixed(decimals);
1684
+ const formattedAmount = amount.replace(".", ",");
1685
+ if (currencyPosition === "before") {
1686
+ return `${prefix}${currencySymbol}${formattedAmount}`;
1413
1687
  }
1414
- return true;
1688
+ return `${prefix}${formattedAmount} ${currencySymbol}`;
1415
1689
  }
1416
- function getPriceInfo(product, variation) {
1417
- if (variation) {
1418
- const isOnSale2 = isSaleActive(variation.saleStartDate, variation.saleEndDate) && variation.salePrice !== null;
1419
- const originalPrice2 = variation.price ?? product.price;
1420
- const effectivePrice2 = isOnSale2 ? variation.salePrice ?? originalPrice2 : originalPrice2;
1421
- return {
1422
- effectivePrice: effectivePrice2,
1423
- originalPrice: originalPrice2,
1424
- isOnSale: isOnSale2,
1425
- salePercent: isOnSale2 ? variation.salePercent ?? null : null
1426
- };
1690
+ function calculateDiscountAmount(subtotal, discount) {
1691
+ if (discount.discountType === "PERCENTAGE") {
1692
+ return Math.round(subtotal * discount.discountValue / 100);
1427
1693
  }
1428
- const isOnSale = isSaleActive(product.saleStartDate, product.saleEndDate) && product.salePrice !== null;
1429
- const originalPrice = product.price;
1430
- const effectivePrice = isOnSale ? product.salePrice ?? originalPrice : originalPrice;
1431
- return {
1432
- effectivePrice,
1433
- originalPrice,
1434
- isOnSale,
1435
- salePercent: isOnSale ? product.salePercent ?? null : null
1436
- };
1694
+ return Math.min(discount.discountValue, subtotal);
1437
1695
  }
1438
-
1439
- // src/utils/cart-calculations.ts
1440
- function calculateCartWithCampaigns(items, campaigns) {
1441
- const buyXPayYCampaign = campaigns.find(
1442
- (c) => c.type === "BUY_X_PAY_Y" && c.isActive
1443
- );
1444
- const originalTotal = items.reduce((total, { product, variation, cartQuantity }) => {
1445
- const priceInfo = getPriceInfo(product, variation);
1446
- return total + priceInfo.effectivePrice * cartQuantity;
1447
- }, 0);
1448
- const freeShipping = {
1449
- isEligible: false,
1450
- minimumSpend: 0,
1451
- remainingAmount: 0
1452
- };
1453
- if (!buyXPayYCampaign?.BuyXPayYCampaign) {
1454
- const calculatedItems2 = items.map((item) => ({
1455
- item,
1456
- paidQuantity: item.cartQuantity,
1457
- freeQuantity: 0,
1458
- totalQuantity: item.cartQuantity
1459
- }));
1460
- return {
1461
- calculatedItems: calculatedItems2,
1462
- cartTotal: originalTotal,
1463
- originalTotal,
1464
- totalSavings: 0,
1465
- freeShipping
1466
- };
1696
+ var REMOVAL_MESSAGES = {
1697
+ CAMPAIGN_ACTIVE: {
1698
+ fi: "Alennuskoodi poistettu - kampanja-alennus aktivoitui",
1699
+ en: "Discount code removed - campaign discount activated"
1700
+ },
1701
+ MIN_ORDER_NOT_MET: {
1702
+ fi: "Alennuskoodi poistettu - ostoskorin summa alittaa minimitilauksen",
1703
+ en: "Discount code removed - cart total below minimum order"
1704
+ },
1705
+ CODE_INVALID: {
1706
+ fi: "Alennuskoodi poistettu - koodi ei ole en\xE4\xE4 voimassa",
1707
+ en: "Discount code removed - code is no longer valid"
1467
1708
  }
1468
- const { buyQuantity, payQuantity, applicableCategories } = buyXPayYCampaign.BuyXPayYCampaign;
1469
- const applicableCategoryIds = new Set(
1470
- applicableCategories.map((c) => c.id)
1471
- );
1472
- const eligibleUnits = items.flatMap((item) => {
1473
- const { product, variation } = item;
1474
- const itemCategories = product.categories?.map((cat) => cat.id) || [];
1475
- const isEligible = itemCategories.some(
1476
- (id) => applicableCategoryIds.has(id)
1477
- );
1478
- if (isEligible) {
1479
- const priceInfo = getPriceInfo(product, variation);
1480
- return Array.from({ length: item.cartQuantity }, () => ({
1481
- price: priceInfo.effectivePrice,
1482
- productId: product.id,
1483
- variationId: variation?.id,
1484
- originalItem: item
1485
- }));
1486
- }
1487
- return [];
1488
- });
1489
- if (eligibleUnits.length < buyQuantity) {
1490
- const calculatedItems2 = items.map((item) => ({
1491
- item,
1492
- paidQuantity: item.cartQuantity,
1493
- freeQuantity: 0,
1494
- totalQuantity: item.cartQuantity
1495
- }));
1496
- return {
1497
- calculatedItems: calculatedItems2,
1498
- cartTotal: originalTotal,
1499
- originalTotal,
1500
- totalSavings: 0,
1501
- freeShipping
1502
- };
1709
+ };
1710
+ var DEFAULT_REMOVAL_MESSAGE = {
1711
+ fi: "Alennuskoodi poistettu",
1712
+ en: "Discount code removed"
1713
+ };
1714
+ function getDiscountRemovalMessage(reason, locale = "fi") {
1715
+ if (!reason) {
1716
+ return DEFAULT_REMOVAL_MESSAGE[locale];
1503
1717
  }
1504
- eligibleUnits.sort((a, b) => a.price - b.price);
1505
- const numToMakeFree = buyQuantity - payQuantity;
1506
- const itemsToMakeFree = eligibleUnits.slice(0, numToMakeFree);
1507
- const totalSavings = itemsToMakeFree.reduce(
1508
- (sum, item) => sum + item.price,
1509
- 0
1510
- );
1511
- const freeCountMap = /* @__PURE__ */ new Map();
1512
- for (const freebie of itemsToMakeFree) {
1513
- const key = `${freebie.productId}${freebie.variationId ? `_${freebie.variationId}` : ""}`;
1514
- freeCountMap.set(key, (freeCountMap.get(key) || 0) + 1);
1718
+ return REMOVAL_MESSAGES[reason]?.[locale] ?? DEFAULT_REMOVAL_MESSAGE[locale];
1719
+ }
1720
+ var APPLY_ERROR_MESSAGES = {
1721
+ NOT_FOUND: {
1722
+ fi: "Alennuskoodia ei l\xF6ydy",
1723
+ en: "Discount code not found"
1724
+ },
1725
+ INACTIVE: {
1726
+ fi: "Alennuskoodi ei ole k\xE4yt\xF6ss\xE4",
1727
+ en: "Discount code is not active"
1728
+ },
1729
+ NOT_STARTED: {
1730
+ fi: "Alennuskoodi ei ole viel\xE4 voimassa",
1731
+ en: "Discount code is not yet valid"
1732
+ },
1733
+ EXPIRED: {
1734
+ fi: "Alennuskoodi on vanhentunut",
1735
+ en: "Discount code has expired"
1736
+ },
1737
+ MAX_USES_REACHED: {
1738
+ fi: "Alennuskoodi on k\xE4ytetty loppuun",
1739
+ en: "Discount code usage limit reached"
1740
+ },
1741
+ MIN_ORDER_NOT_MET: {
1742
+ fi: "Ostoskorin summa alittaa alennuskoodin minimitilauksen",
1743
+ en: "Cart total is below the minimum order for this code"
1744
+ },
1745
+ CAMPAIGN_ACTIVE: {
1746
+ fi: "Alennuskoodia ei voi k\xE4ytt\xE4\xE4 kun kampanja-alennus on voimassa",
1747
+ en: "Discount code cannot be used when a campaign discount is active"
1515
1748
  }
1516
- const calculatedItems = items.map((item) => {
1517
- const key = `${item.product.id}${item.variation?.id ? `_${item.variation.id}` : ""}`;
1518
- const freeQuantity = freeCountMap.get(key) || 0;
1519
- const paidQuantity = item.cartQuantity - freeQuantity;
1520
- return {
1521
- item,
1522
- paidQuantity: Math.max(0, paidQuantity),
1523
- freeQuantity,
1524
- totalQuantity: item.cartQuantity
1525
- };
1526
- });
1527
- const cartTotal = originalTotal - totalSavings;
1528
- return {
1529
- calculatedItems,
1530
- cartTotal,
1531
- originalTotal,
1532
- totalSavings,
1533
- freeShipping
1534
- };
1749
+ };
1750
+ var DEFAULT_APPLY_ERROR = {
1751
+ fi: "Alennuskoodin k\xE4ytt\xF6 ep\xE4onnistui",
1752
+ en: "Failed to apply discount code"
1753
+ };
1754
+ function getDiscountApplyErrorMessage(errorCode, locale = "fi") {
1755
+ if (!errorCode) {
1756
+ return DEFAULT_APPLY_ERROR[locale];
1757
+ }
1758
+ const messages = APPLY_ERROR_MESSAGES[errorCode];
1759
+ return messages?.[locale] ?? DEFAULT_APPLY_ERROR[locale];
1535
1760
  }
1536
1761
  export {
1537
1762
  AuthError,
@@ -1541,7 +1766,11 @@ export {
1541
1766
  ValidationError,
1542
1767
  VerificationRequiredError,
1543
1768
  calculateCartWithCampaigns,
1769
+ calculateDiscountAmount,
1544
1770
  createStorefrontClient,
1771
+ formatDiscountValue,
1772
+ getDiscountApplyErrorMessage,
1773
+ getDiscountRemovalMessage,
1545
1774
  getPriceInfo,
1546
1775
  isSaleActive
1547
1776
  };