@putiikkipalvelu/storefront-sdk 0.2.5 → 0.3.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
@@ -610,6 +610,138 @@ function createCartResource(fetcher) {
610
610
  };
611
611
  }
612
612
 
613
+ // src/utils/pricing.ts
614
+ function isSaleActive(startDate, endDate) {
615
+ if (!startDate && !endDate) {
616
+ return true;
617
+ }
618
+ const now = /* @__PURE__ */ new Date();
619
+ const start = startDate ? new Date(startDate) : null;
620
+ const end = endDate ? new Date(endDate) : null;
621
+ if (start && !end) {
622
+ return now >= start;
623
+ }
624
+ if (!start && end) {
625
+ return now <= end;
626
+ }
627
+ if (start && end) {
628
+ return now >= start && now <= end;
629
+ }
630
+ return true;
631
+ }
632
+ function getPriceInfo(product, variation) {
633
+ if (variation) {
634
+ const isOnSale2 = isSaleActive(variation.saleStartDate, variation.saleEndDate) && variation.salePrice !== null;
635
+ const originalPrice2 = variation.price ?? product.price;
636
+ const effectivePrice2 = isOnSale2 ? variation.salePrice ?? originalPrice2 : originalPrice2;
637
+ return {
638
+ effectivePrice: effectivePrice2,
639
+ originalPrice: originalPrice2,
640
+ isOnSale: isOnSale2,
641
+ salePercent: isOnSale2 ? variation.salePercent ?? null : null
642
+ };
643
+ }
644
+ const isOnSale = isSaleActive(product.saleStartDate, product.saleEndDate) && product.salePrice !== null;
645
+ const originalPrice = product.price;
646
+ const effectivePrice = isOnSale ? product.salePrice ?? originalPrice : originalPrice;
647
+ return {
648
+ effectivePrice,
649
+ originalPrice,
650
+ isOnSale,
651
+ salePercent: isOnSale ? product.salePercent ?? null : null
652
+ };
653
+ }
654
+
655
+ // src/utils/cart-calculations.ts
656
+ function calculateCartWithCampaigns(items, campaigns) {
657
+ const buyXPayYCampaign = campaigns.find(
658
+ (c) => c.type === "BUY_X_PAY_Y" && c.isActive
659
+ );
660
+ const originalTotal = items.reduce((total, { product, variation, cartQuantity }) => {
661
+ const priceInfo = getPriceInfo(product, variation);
662
+ return total + priceInfo.effectivePrice * cartQuantity;
663
+ }, 0);
664
+ if (!buyXPayYCampaign?.BuyXPayYCampaign) {
665
+ const calculatedItems2 = items.map((item) => ({
666
+ item,
667
+ paidQuantity: item.cartQuantity,
668
+ freeQuantity: 0,
669
+ totalQuantity: item.cartQuantity
670
+ }));
671
+ return {
672
+ calculatedItems: calculatedItems2,
673
+ cartTotal: originalTotal,
674
+ originalTotal,
675
+ totalSavings: 0
676
+ };
677
+ }
678
+ const { buyQuantity, payQuantity, applicableCategories } = buyXPayYCampaign.BuyXPayYCampaign;
679
+ const applicableCategoryIds = new Set(
680
+ applicableCategories.map((c) => c.id)
681
+ );
682
+ const eligibleUnits = items.flatMap((item) => {
683
+ const { product, variation } = item;
684
+ const itemCategories = product.categories?.map((cat) => cat.id) || [];
685
+ const isEligible = itemCategories.some(
686
+ (id) => applicableCategoryIds.has(id)
687
+ );
688
+ if (isEligible) {
689
+ const priceInfo = getPriceInfo(product, variation);
690
+ return Array.from({ length: item.cartQuantity }, () => ({
691
+ price: priceInfo.effectivePrice,
692
+ productId: product.id,
693
+ variationId: variation?.id,
694
+ originalItem: item
695
+ }));
696
+ }
697
+ return [];
698
+ });
699
+ if (eligibleUnits.length < buyQuantity) {
700
+ const calculatedItems2 = items.map((item) => ({
701
+ item,
702
+ paidQuantity: item.cartQuantity,
703
+ freeQuantity: 0,
704
+ totalQuantity: item.cartQuantity
705
+ }));
706
+ return {
707
+ calculatedItems: calculatedItems2,
708
+ cartTotal: originalTotal,
709
+ originalTotal,
710
+ totalSavings: 0
711
+ };
712
+ }
713
+ eligibleUnits.sort((a, b) => a.price - b.price);
714
+ const numToMakeFree = buyQuantity - payQuantity;
715
+ const itemsToMakeFree = eligibleUnits.slice(0, numToMakeFree);
716
+ const totalSavings = itemsToMakeFree.reduce(
717
+ (sum, item) => sum + item.price,
718
+ 0
719
+ );
720
+ const freeCountMap = /* @__PURE__ */ new Map();
721
+ for (const freebie of itemsToMakeFree) {
722
+ const key = `${freebie.productId}${freebie.variationId ? `_${freebie.variationId}` : ""}`;
723
+ freeCountMap.set(key, (freeCountMap.get(key) || 0) + 1);
724
+ }
725
+ const calculatedItems = items.map((item) => {
726
+ const key = `${item.product.id}${item.variation?.id ? `_${item.variation.id}` : ""}`;
727
+ const freeQuantity = freeCountMap.get(key) || 0;
728
+ const paidQuantity = item.cartQuantity - freeQuantity;
729
+ return {
730
+ item,
731
+ paidQuantity: Math.max(0, paidQuantity),
732
+ freeQuantity,
733
+ totalQuantity: item.cartQuantity
734
+ };
735
+ });
736
+ const cartTotal = originalTotal - totalSavings;
737
+ return {
738
+ calculatedItems,
739
+ cartTotal,
740
+ originalTotal,
741
+ totalSavings
742
+ };
743
+ }
744
+
613
745
  // src/resources/shipping.ts
614
746
  function calculateCartWeight(items) {
615
747
  return items.reduce((total, item) => {
@@ -617,48 +749,70 @@ function calculateCartWeight(items) {
617
749
  return total + itemWeight * item.cartQuantity;
618
750
  }, 0);
619
751
  }
752
+ function calculateCartTotal(items) {
753
+ return items.reduce((total, item) => {
754
+ const itemPrice = item.variation ? item.variation.salePrice ?? item.variation.price : item.product.salePrice ?? item.product.price;
755
+ return total + itemPrice * item.cartQuantity;
756
+ }, 0);
757
+ }
620
758
  function createShippingResource(fetcher) {
621
759
  return {
622
760
  /**
623
761
  * Get shipping options for a specific postal code.
624
- * Returns home delivery methods and pickup locations.
762
+ * Returns pickup points and home delivery options in a unified format.
763
+ *
764
+ * **Pickup points are returned first** as they are more popular in Finland.
625
765
  *
626
766
  * @param postalCode - Customer's postal code (e.g., "00100")
627
767
  * @param options - Fetch options including optional cartItems for weight-based filtering
628
- * @returns Home delivery methods and pickup locations
768
+ * @returns Unified shipping options (pickupPoints sorted by distance, homeDelivery sorted by price)
629
769
  *
630
770
  * @example
631
771
  * ```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}€`);
772
+ * const { pickupPoints, homeDelivery } = await client.shipping.getOptions("00100");
773
+ *
774
+ * // Show pickup points (more popular in Finland)
775
+ * pickupPoints.forEach(point => {
776
+ * console.log(`${point.name} - ${point.carrier}`);
777
+ * console.log(` ${point.address}, ${point.city}`);
778
+ * console.log(` ${(point.distance! / 1000).toFixed(1)} km away`);
779
+ * console.log(` Price: ${point.price / 100}€`);
637
780
  * });
638
781
  *
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}€`);
782
+ * // Show home delivery options
783
+ * homeDelivery.forEach(option => {
784
+ * console.log(`${option.name}: ${option.price / 100}€`);
785
+ * if (option.estimatedDelivery) {
786
+ * console.log(` Delivery: ${option.estimatedDelivery} days`);
787
+ * }
645
788
  * });
646
789
  * ```
647
790
  *
648
791
  * @example Weight-based filtering
649
792
  * ```typescript
650
- * const { homeDeliveryMethods, pickupLocations } = await client.shipping.getWithLocations(
651
- * "00100",
652
- * { cartItems: cartItems }
653
- * );
793
+ * const options = await client.shipping.getOptions("00100", {
794
+ * cartItems: cartItems
795
+ * });
654
796
  * // Only shows methods that support the cart's total weight
655
797
  * ```
798
+ *
799
+ * @example International shipping
800
+ * ```typescript
801
+ * const options = await client.shipping.getOptions("112 22", {
802
+ * country: "SE"
803
+ * });
804
+ * ```
656
805
  */
657
- async getWithLocations(postalCode, options) {
806
+ async getOptions(postalCode, options) {
658
807
  const params = new URLSearchParams();
659
808
  if (options?.cartItems?.length) {
660
809
  const cartWeight = calculateCartWeight(options.cartItems);
661
810
  params.set("cartWeight", cartWeight.toString());
811
+ const cartTotal = options.campaigns ? calculateCartWithCampaigns(options.cartItems, options.campaigns).cartTotal : calculateCartTotal(options.cartItems);
812
+ params.set("cartTotal", cartTotal.toString());
813
+ }
814
+ if (options?.country) {
815
+ params.set("country", options.country);
662
816
  }
663
817
  const queryString = params.toString();
664
818
  const url = `/api/storefront/v1/shipment-methods/${encodeURIComponent(postalCode)}${queryString ? `?${queryString}` : ""}`;
@@ -666,6 +820,12 @@ function createShippingResource(fetcher) {
666
820
  method: "GET",
667
821
  ...options
668
822
  });
823
+ },
824
+ /**
825
+ * @deprecated Use getOptions() instead. This method is kept for backwards compatibility.
826
+ */
827
+ async getWithLocations(postalCode, options) {
828
+ return this.getOptions(postalCode, options);
669
829
  }
670
830
  };
671
831
  }
@@ -1393,146 +1553,6 @@ function createStorefrontClient(config) {
1393
1553
  checkout: createCheckoutResource(fetcher)
1394
1554
  };
1395
1555
  }
1396
-
1397
- // src/utils/pricing.ts
1398
- function isSaleActive(startDate, endDate) {
1399
- if (!startDate && !endDate) {
1400
- return true;
1401
- }
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;
1413
- }
1414
- return true;
1415
- }
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
- };
1427
- }
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
- };
1437
- }
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
- };
1467
- }
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
- };
1503
- }
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);
1515
- }
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
- };
1535
- }
1536
1556
  export {
1537
1557
  AuthError,
1538
1558
  NotFoundError,