@putiikkipalvelu/storefront-sdk 0.2.4 → 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.cjs +183 -196
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -108
- package/dist/index.d.ts +106 -108
- package/dist/index.js +176 -189
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,81 +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
|
-
* Get
|
|
624
|
-
* Returns
|
|
761
|
+
* Get shipping options for a specific postal code.
|
|
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
|
*
|
|
766
|
+
* @param postalCode - Customer's postal code (e.g., "00100")
|
|
626
767
|
* @param options - Fetch options including optional cartItems for weight-based filtering
|
|
627
|
-
* @returns
|
|
768
|
+
* @returns Unified shipping options (pickupPoints sorted by distance, homeDelivery sorted by price)
|
|
628
769
|
*
|
|
629
770
|
* @example
|
|
630
771
|
* ```typescript
|
|
631
|
-
* const {
|
|
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}€`);
|
|
780
|
+
* });
|
|
632
781
|
*
|
|
633
|
-
*
|
|
634
|
-
*
|
|
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
|
+
* }
|
|
635
788
|
* });
|
|
636
789
|
* ```
|
|
637
790
|
*
|
|
638
791
|
* @example Weight-based filtering
|
|
639
792
|
* ```typescript
|
|
640
|
-
*
|
|
641
|
-
* const { shipmentMethods } = await client.shipping.getMethods({
|
|
793
|
+
* const options = await client.shipping.getOptions("00100", {
|
|
642
794
|
* cartItems: cartItems
|
|
643
795
|
* });
|
|
796
|
+
* // Only shows methods that support the cart's total weight
|
|
644
797
|
* ```
|
|
645
|
-
*/
|
|
646
|
-
async getMethods(options) {
|
|
647
|
-
const params = new URLSearchParams();
|
|
648
|
-
if (options?.cartItems?.length) {
|
|
649
|
-
const cartWeight = calculateCartWeight(options.cartItems);
|
|
650
|
-
params.set("cartWeight", cartWeight.toString());
|
|
651
|
-
}
|
|
652
|
-
const queryString = params.toString();
|
|
653
|
-
const url = `/api/storefront/v1/shipment-methods${queryString ? `?${queryString}` : ""}`;
|
|
654
|
-
return fetcher.request(url, {
|
|
655
|
-
method: "GET",
|
|
656
|
-
...options
|
|
657
|
-
});
|
|
658
|
-
},
|
|
659
|
-
/**
|
|
660
|
-
* Get shipment methods with pickup locations for a specific postal code.
|
|
661
|
-
* Calls the Shipit API to fetch nearby pickup points (parcel lockers, etc.)
|
|
662
|
-
*
|
|
663
|
-
* @param postalCode - Customer's postal code (e.g., "00100")
|
|
664
|
-
* @param options - Fetch options including optional cartItems for weight-based filtering
|
|
665
|
-
* @returns Shipment methods and nearby pickup locations with pricing
|
|
666
798
|
*
|
|
667
|
-
* @example
|
|
799
|
+
* @example International shipping
|
|
668
800
|
* ```typescript
|
|
669
|
-
* const
|
|
670
|
-
*
|
|
671
|
-
* // Show pickup locations
|
|
672
|
-
* pricedLocations.forEach(location => {
|
|
673
|
-
* console.log(`${location.name} - ${location.carrier}`);
|
|
674
|
-
* console.log(` ${location.address1}, ${location.city}`);
|
|
675
|
-
* console.log(` ${location.distanceInKilometers.toFixed(1)} km away`);
|
|
676
|
-
* console.log(` Price: ${(location.merchantPrice ?? 0) / 100}€`);
|
|
801
|
+
* const options = await client.shipping.getOptions("112 22", {
|
|
802
|
+
* country: "SE"
|
|
677
803
|
* });
|
|
678
804
|
* ```
|
|
679
|
-
*
|
|
680
|
-
* @example Weight-based filtering with postal code
|
|
681
|
-
* ```typescript
|
|
682
|
-
* const { shipmentMethods, pricedLocations } = await client.shipping.getWithLocations(
|
|
683
|
-
* "00100",
|
|
684
|
-
* { cartItems: cartItems }
|
|
685
|
-
* );
|
|
686
|
-
*
|
|
687
|
-
* // Only shows methods that support the cart's total weight
|
|
688
|
-
* ```
|
|
689
805
|
*/
|
|
690
|
-
async
|
|
806
|
+
async getOptions(postalCode, options) {
|
|
691
807
|
const params = new URLSearchParams();
|
|
692
808
|
if (options?.cartItems?.length) {
|
|
693
809
|
const cartWeight = calculateCartWeight(options.cartItems);
|
|
694
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);
|
|
695
816
|
}
|
|
696
817
|
const queryString = params.toString();
|
|
697
818
|
const url = `/api/storefront/v1/shipment-methods/${encodeURIComponent(postalCode)}${queryString ? `?${queryString}` : ""}`;
|
|
@@ -699,6 +820,12 @@ function createShippingResource(fetcher) {
|
|
|
699
820
|
method: "GET",
|
|
700
821
|
...options
|
|
701
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);
|
|
702
829
|
}
|
|
703
830
|
};
|
|
704
831
|
}
|
|
@@ -1426,146 +1553,6 @@ function createStorefrontClient(config) {
|
|
|
1426
1553
|
checkout: createCheckoutResource(fetcher)
|
|
1427
1554
|
};
|
|
1428
1555
|
}
|
|
1429
|
-
|
|
1430
|
-
// src/utils/pricing.ts
|
|
1431
|
-
function isSaleActive(startDate, endDate) {
|
|
1432
|
-
if (!startDate && !endDate) {
|
|
1433
|
-
return true;
|
|
1434
|
-
}
|
|
1435
|
-
const now = /* @__PURE__ */ new Date();
|
|
1436
|
-
const start = startDate ? new Date(startDate) : null;
|
|
1437
|
-
const end = endDate ? new Date(endDate) : null;
|
|
1438
|
-
if (start && !end) {
|
|
1439
|
-
return now >= start;
|
|
1440
|
-
}
|
|
1441
|
-
if (!start && end) {
|
|
1442
|
-
return now <= end;
|
|
1443
|
-
}
|
|
1444
|
-
if (start && end) {
|
|
1445
|
-
return now >= start && now <= end;
|
|
1446
|
-
}
|
|
1447
|
-
return true;
|
|
1448
|
-
}
|
|
1449
|
-
function getPriceInfo(product, variation) {
|
|
1450
|
-
if (variation) {
|
|
1451
|
-
const isOnSale2 = isSaleActive(variation.saleStartDate, variation.saleEndDate) && variation.salePrice !== null;
|
|
1452
|
-
const originalPrice2 = variation.price ?? product.price;
|
|
1453
|
-
const effectivePrice2 = isOnSale2 ? variation.salePrice ?? originalPrice2 : originalPrice2;
|
|
1454
|
-
return {
|
|
1455
|
-
effectivePrice: effectivePrice2,
|
|
1456
|
-
originalPrice: originalPrice2,
|
|
1457
|
-
isOnSale: isOnSale2,
|
|
1458
|
-
salePercent: isOnSale2 ? variation.salePercent ?? null : null
|
|
1459
|
-
};
|
|
1460
|
-
}
|
|
1461
|
-
const isOnSale = isSaleActive(product.saleStartDate, product.saleEndDate) && product.salePrice !== null;
|
|
1462
|
-
const originalPrice = product.price;
|
|
1463
|
-
const effectivePrice = isOnSale ? product.salePrice ?? originalPrice : originalPrice;
|
|
1464
|
-
return {
|
|
1465
|
-
effectivePrice,
|
|
1466
|
-
originalPrice,
|
|
1467
|
-
isOnSale,
|
|
1468
|
-
salePercent: isOnSale ? product.salePercent ?? null : null
|
|
1469
|
-
};
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
// src/utils/cart-calculations.ts
|
|
1473
|
-
function calculateCartWithCampaigns(items, campaigns) {
|
|
1474
|
-
const buyXPayYCampaign = campaigns.find(
|
|
1475
|
-
(c) => c.type === "BUY_X_PAY_Y" && c.isActive
|
|
1476
|
-
);
|
|
1477
|
-
const originalTotal = items.reduce((total, { product, variation, cartQuantity }) => {
|
|
1478
|
-
const priceInfo = getPriceInfo(product, variation);
|
|
1479
|
-
return total + priceInfo.effectivePrice * cartQuantity;
|
|
1480
|
-
}, 0);
|
|
1481
|
-
const freeShipping = {
|
|
1482
|
-
isEligible: false,
|
|
1483
|
-
minimumSpend: 0,
|
|
1484
|
-
remainingAmount: 0
|
|
1485
|
-
};
|
|
1486
|
-
if (!buyXPayYCampaign?.BuyXPayYCampaign) {
|
|
1487
|
-
const calculatedItems2 = items.map((item) => ({
|
|
1488
|
-
item,
|
|
1489
|
-
paidQuantity: item.cartQuantity,
|
|
1490
|
-
freeQuantity: 0,
|
|
1491
|
-
totalQuantity: item.cartQuantity
|
|
1492
|
-
}));
|
|
1493
|
-
return {
|
|
1494
|
-
calculatedItems: calculatedItems2,
|
|
1495
|
-
cartTotal: originalTotal,
|
|
1496
|
-
originalTotal,
|
|
1497
|
-
totalSavings: 0,
|
|
1498
|
-
freeShipping
|
|
1499
|
-
};
|
|
1500
|
-
}
|
|
1501
|
-
const { buyQuantity, payQuantity, applicableCategories } = buyXPayYCampaign.BuyXPayYCampaign;
|
|
1502
|
-
const applicableCategoryIds = new Set(
|
|
1503
|
-
applicableCategories.map((c) => c.id)
|
|
1504
|
-
);
|
|
1505
|
-
const eligibleUnits = items.flatMap((item) => {
|
|
1506
|
-
const { product, variation } = item;
|
|
1507
|
-
const itemCategories = product.categories?.map((cat) => cat.id) || [];
|
|
1508
|
-
const isEligible = itemCategories.some(
|
|
1509
|
-
(id) => applicableCategoryIds.has(id)
|
|
1510
|
-
);
|
|
1511
|
-
if (isEligible) {
|
|
1512
|
-
const priceInfo = getPriceInfo(product, variation);
|
|
1513
|
-
return Array.from({ length: item.cartQuantity }, () => ({
|
|
1514
|
-
price: priceInfo.effectivePrice,
|
|
1515
|
-
productId: product.id,
|
|
1516
|
-
variationId: variation?.id,
|
|
1517
|
-
originalItem: item
|
|
1518
|
-
}));
|
|
1519
|
-
}
|
|
1520
|
-
return [];
|
|
1521
|
-
});
|
|
1522
|
-
if (eligibleUnits.length < buyQuantity) {
|
|
1523
|
-
const calculatedItems2 = items.map((item) => ({
|
|
1524
|
-
item,
|
|
1525
|
-
paidQuantity: item.cartQuantity,
|
|
1526
|
-
freeQuantity: 0,
|
|
1527
|
-
totalQuantity: item.cartQuantity
|
|
1528
|
-
}));
|
|
1529
|
-
return {
|
|
1530
|
-
calculatedItems: calculatedItems2,
|
|
1531
|
-
cartTotal: originalTotal,
|
|
1532
|
-
originalTotal,
|
|
1533
|
-
totalSavings: 0,
|
|
1534
|
-
freeShipping
|
|
1535
|
-
};
|
|
1536
|
-
}
|
|
1537
|
-
eligibleUnits.sort((a, b) => a.price - b.price);
|
|
1538
|
-
const numToMakeFree = buyQuantity - payQuantity;
|
|
1539
|
-
const itemsToMakeFree = eligibleUnits.slice(0, numToMakeFree);
|
|
1540
|
-
const totalSavings = itemsToMakeFree.reduce(
|
|
1541
|
-
(sum, item) => sum + item.price,
|
|
1542
|
-
0
|
|
1543
|
-
);
|
|
1544
|
-
const freeCountMap = /* @__PURE__ */ new Map();
|
|
1545
|
-
for (const freebie of itemsToMakeFree) {
|
|
1546
|
-
const key = `${freebie.productId}${freebie.variationId ? `_${freebie.variationId}` : ""}`;
|
|
1547
|
-
freeCountMap.set(key, (freeCountMap.get(key) || 0) + 1);
|
|
1548
|
-
}
|
|
1549
|
-
const calculatedItems = items.map((item) => {
|
|
1550
|
-
const key = `${item.product.id}${item.variation?.id ? `_${item.variation.id}` : ""}`;
|
|
1551
|
-
const freeQuantity = freeCountMap.get(key) || 0;
|
|
1552
|
-
const paidQuantity = item.cartQuantity - freeQuantity;
|
|
1553
|
-
return {
|
|
1554
|
-
item,
|
|
1555
|
-
paidQuantity: Math.max(0, paidQuantity),
|
|
1556
|
-
freeQuantity,
|
|
1557
|
-
totalQuantity: item.cartQuantity
|
|
1558
|
-
};
|
|
1559
|
-
});
|
|
1560
|
-
const cartTotal = originalTotal - totalSavings;
|
|
1561
|
-
return {
|
|
1562
|
-
calculatedItems,
|
|
1563
|
-
cartTotal,
|
|
1564
|
-
originalTotal,
|
|
1565
|
-
totalSavings,
|
|
1566
|
-
freeShipping
|
|
1567
|
-
};
|
|
1568
|
-
}
|
|
1569
1556
|
export {
|
|
1570
1557
|
AuthError,
|
|
1571
1558
|
NotFoundError,
|