@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.cjs +400 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +432 -95
- package/dist/index.d.ts +432 -95
- package/dist/index.js +390 -161
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
725
|
+
* const result = await client.cart.validate(
|
|
726
|
+
* { cartId },
|
|
727
|
+
* cartItems,
|
|
728
|
+
* storeConfig.campaigns
|
|
729
|
+
* );
|
|
586
730
|
*
|
|
587
|
-
* if (hasChanges) {
|
|
588
|
-
* if (changes.
|
|
589
|
-
* notify(
|
|
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
|
|
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
|
|
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
|
|
784
|
+
* @returns Unified shipping options (pickupPoints sorted by distance, homeDelivery sorted by price)
|
|
629
785
|
*
|
|
630
786
|
* @example
|
|
631
787
|
* ```typescript
|
|
632
|
-
* const {
|
|
633
|
-
*
|
|
634
|
-
* // Show
|
|
635
|
-
*
|
|
636
|
-
* console.log(`${
|
|
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
|
|
640
|
-
*
|
|
641
|
-
* console.log(`${
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
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
|
|
651
|
-
*
|
|
652
|
-
*
|
|
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
|
|
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/
|
|
1398
|
-
function
|
|
1399
|
-
|
|
1400
|
-
|
|
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
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
|
|
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
|
|
1688
|
+
return `${prefix}${formattedAmount} ${currencySymbol}`;
|
|
1415
1689
|
}
|
|
1416
|
-
function
|
|
1417
|
-
if (
|
|
1418
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
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
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
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
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
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
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
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
|
};
|