@wtree/payload-ecommerce-coupon 3.78.8 → 3.78.9
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 +118 -77
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +118 -77
- package/dist/index.mjs.map +1 -1
- package/dist/utilities/calculateValues.d.ts +18 -1
- package/dist/utilities/calculateValues.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -720,14 +720,6 @@ const createReferralProgramsCollection = (pluginConfig) => {
|
|
|
720
720
|
timestamps: true
|
|
721
721
|
};
|
|
722
722
|
};
|
|
723
|
-
//#endregion
|
|
724
|
-
//#region src/utilities/roundTo2.ts
|
|
725
|
-
/**
|
|
726
|
-
* Rounds a number to 2 decimal places (standard for monetary values).
|
|
727
|
-
*/
|
|
728
|
-
function roundTo2(value) {
|
|
729
|
-
return Math.round(value * 100) / 100;
|
|
730
|
-
}
|
|
731
723
|
function normalizeCurrencyCode(currencyCode) {
|
|
732
724
|
if (!currencyCode) return "AED";
|
|
733
725
|
return currencyCode.toUpperCase();
|
|
@@ -763,16 +755,35 @@ function getCartItemUnitPrice({ item, product, variant, currencyCode, defaultCur
|
|
|
763
755
|
}
|
|
764
756
|
//#endregion
|
|
765
757
|
//#region src/utilities/calculateValues.ts
|
|
758
|
+
/** Convert a normal-currency amount to integer cents. */
|
|
759
|
+
function toCents(amount) {
|
|
760
|
+
return Math.round(amount * 100);
|
|
761
|
+
}
|
|
762
|
+
/** Convert integer cents back to a normal-currency amount (2 dp max). */
|
|
763
|
+
function fromCents(cents) {
|
|
764
|
+
return Math.round(cents) / 100;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Calculate the discount amount for a coupon.
|
|
768
|
+
*
|
|
769
|
+
* @param coupon - Coupon document from DB (values in normal currency).
|
|
770
|
+
* @param cartTotal - Cart subtotal in normal currency.
|
|
771
|
+
* @returns Discount amount in normal currency (2 dp).
|
|
772
|
+
*/
|
|
766
773
|
function calculateCouponDiscount({ coupon, cartTotal }) {
|
|
767
|
-
|
|
774
|
+
const cartCents = toCents(cartTotal);
|
|
775
|
+
let discountCents = 0;
|
|
768
776
|
if (coupon.type === "percentage") {
|
|
769
|
-
|
|
770
|
-
if (coupon.maxDiscountAmount != null
|
|
777
|
+
discountCents = Math.floor(cartCents * coupon.value / 100);
|
|
778
|
+
if (coupon.maxDiscountAmount != null) {
|
|
779
|
+
const maxCents = toCents(coupon.maxDiscountAmount);
|
|
780
|
+
if (discountCents > maxCents) discountCents = maxCents;
|
|
781
|
+
}
|
|
771
782
|
} else if (coupon.type === "fixed") {
|
|
772
|
-
|
|
773
|
-
if (
|
|
783
|
+
discountCents = toCents(coupon.value);
|
|
784
|
+
if (discountCents > cartCents) discountCents = cartCents;
|
|
774
785
|
}
|
|
775
|
-
return
|
|
786
|
+
return fromCents(discountCents);
|
|
776
787
|
}
|
|
777
788
|
function relationId$5(value) {
|
|
778
789
|
if (value == null) return null;
|
|
@@ -793,19 +804,25 @@ function getRuleSplits(rule) {
|
|
|
793
804
|
customerSplit: typeof rule.customerSplit === "number" ? rule.customerSplit : typeof rule.refereeSplit === "number" ? rule.refereeSplit : 100 - partnerRaw
|
|
794
805
|
};
|
|
795
806
|
}
|
|
796
|
-
|
|
807
|
+
/**
|
|
808
|
+
* Calculate partner and customer reward for a single line item.
|
|
809
|
+
*
|
|
810
|
+
* ALL inputs are expected in CENTS.
|
|
811
|
+
* Returns rewards in CENTS, or null if the rule is inapplicable.
|
|
812
|
+
*/
|
|
813
|
+
function calculateItemRewardByRule({ rule, itemTotalCents, quantity, allowedTotalCommissionTypes }) {
|
|
797
814
|
const allowedTypes = allowedCommissionTypesSet(allowedTotalCommissionTypes);
|
|
798
815
|
if (rule.totalCommission) {
|
|
799
816
|
if (!allowedTypes.has(rule.totalCommission.type)) return null;
|
|
800
|
-
const
|
|
817
|
+
const resolvedMaxAmountCents = typeof rule.totalCommission.maxAmount === "number" && Number.isFinite(rule.totalCommission.maxAmount) ? toCents(rule.totalCommission.maxAmount) : null;
|
|
801
818
|
if (rule.totalCommission.type === "fixed" && rule.totalCommission.value == null) {
|
|
802
|
-
const
|
|
803
|
-
const
|
|
804
|
-
if (
|
|
805
|
-
let partner =
|
|
806
|
-
let customer =
|
|
807
|
-
if (
|
|
808
|
-
const maxPotForLine =
|
|
819
|
+
const partnerAmtPerUnitCents = typeof rule.partnerSplit === "number" ? toCents(rule.partnerSplit) : null;
|
|
820
|
+
const customerAmtPerUnitCents = typeof rule.customerSplit === "number" ? toCents(rule.customerSplit) : null;
|
|
821
|
+
if (partnerAmtPerUnitCents == null || customerAmtPerUnitCents == null) return null;
|
|
822
|
+
let partner = partnerAmtPerUnitCents * quantity;
|
|
823
|
+
let customer = customerAmtPerUnitCents * quantity;
|
|
824
|
+
if (resolvedMaxAmountCents != null) {
|
|
825
|
+
const maxPotForLine = resolvedMaxAmountCents * quantity;
|
|
809
826
|
const totalPot = partner + customer;
|
|
810
827
|
if (totalPot > maxPotForLine && totalPot > 0) {
|
|
811
828
|
const ratio = maxPotForLine / totalPot;
|
|
@@ -818,7 +835,6 @@ function calculateItemRewardByRule({ rule, itemTotal, quantity, allowedTotalComm
|
|
|
818
835
|
customer
|
|
819
836
|
};
|
|
820
837
|
}
|
|
821
|
-
let totalPot = 0;
|
|
822
838
|
if (rule.totalCommission.type === "percentage") {
|
|
823
839
|
const commissionValue = typeof rule.totalCommission.value === "number" && Number.isFinite(rule.totalCommission.value) ? rule.totalCommission.value : null;
|
|
824
840
|
if (commissionValue == null) {
|
|
@@ -826,58 +842,63 @@ function calculateItemRewardByRule({ rule, itemTotal, quantity, allowedTotalComm
|
|
|
826
842
|
if (partnerPercentInput == null || partnerPercentInput < 0 || partnerPercentInput > 100) return null;
|
|
827
843
|
const customerPercentInput = typeof rule.customerPercent === "number" ? rule.customerPercent : typeof rule.customerSplit === "number" ? rule.customerSplit : 100 - partnerPercentInput;
|
|
828
844
|
if (customerPercentInput == null || customerPercentInput < 0 || customerPercentInput > 100) return null;
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
if (
|
|
832
|
-
const maxPotForLine =
|
|
845
|
+
let partner = Math.floor(itemTotalCents * partnerPercentInput / 100);
|
|
846
|
+
let customer = Math.floor(itemTotalCents * customerPercentInput / 100);
|
|
847
|
+
if (resolvedMaxAmountCents != null) {
|
|
848
|
+
const maxPotForLine = resolvedMaxAmountCents * quantity;
|
|
833
849
|
const totalForLine = partner + customer;
|
|
834
850
|
if (totalForLine > maxPotForLine && totalForLine > 0) {
|
|
835
851
|
const ratio = maxPotForLine / totalForLine;
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
customer: Math.floor(customer * ratio)
|
|
839
|
-
};
|
|
852
|
+
partner = Math.floor(partner * ratio);
|
|
853
|
+
customer = Math.floor(customer * ratio);
|
|
840
854
|
}
|
|
841
855
|
}
|
|
842
856
|
return {
|
|
843
|
-
partner
|
|
844
|
-
customer
|
|
857
|
+
partner,
|
|
858
|
+
customer
|
|
845
859
|
};
|
|
846
860
|
}
|
|
847
|
-
|
|
848
|
-
|
|
861
|
+
let totalPotCents = Math.floor(itemTotalCents * commissionValue / 100);
|
|
862
|
+
if (resolvedMaxAmountCents != null) {
|
|
863
|
+
const maxPotForLine = resolvedMaxAmountCents * quantity;
|
|
864
|
+
if (totalPotCents > maxPotForLine) totalPotCents = maxPotForLine;
|
|
865
|
+
}
|
|
849
866
|
const splits = getRuleSplits(rule);
|
|
850
867
|
if (!splits) return null;
|
|
851
|
-
totalPot = rule.totalCommission.value * quantity;
|
|
852
|
-
if (resolvedMaxAmount != null) {
|
|
853
|
-
const maxPotForLine = resolvedMaxAmount * quantity;
|
|
854
|
-
if (totalPot > maxPotForLine) totalPot = maxPotForLine;
|
|
855
|
-
}
|
|
856
868
|
return {
|
|
857
|
-
partner: Math.floor(
|
|
858
|
-
customer: Math.floor(
|
|
869
|
+
partner: Math.floor(totalPotCents * splits.partnerSplit / 100),
|
|
870
|
+
customer: Math.floor(totalPotCents * splits.customerSplit / 100)
|
|
859
871
|
};
|
|
860
872
|
}
|
|
861
|
-
|
|
862
|
-
const
|
|
863
|
-
if (
|
|
873
|
+
{
|
|
874
|
+
const splits = getRuleSplits(rule);
|
|
875
|
+
if (!splits) return null;
|
|
876
|
+
let totalPotCents = toCents(rule.totalCommission.value) * quantity;
|
|
877
|
+
if (resolvedMaxAmountCents != null) {
|
|
878
|
+
const maxPotForLine = resolvedMaxAmountCents * quantity;
|
|
879
|
+
if (totalPotCents > maxPotForLine) totalPotCents = maxPotForLine;
|
|
880
|
+
}
|
|
881
|
+
return {
|
|
882
|
+
partner: Math.floor(totalPotCents * splits.partnerSplit / 100),
|
|
883
|
+
customer: Math.floor(totalPotCents * splits.customerSplit / 100)
|
|
884
|
+
};
|
|
864
885
|
}
|
|
865
|
-
const splits = getRuleSplits(rule);
|
|
866
|
-
if (!splits) return null;
|
|
867
|
-
return {
|
|
868
|
-
partner: Math.floor(totalPot * splits.partnerSplit / 100),
|
|
869
|
-
customer: Math.floor(totalPot * splits.customerSplit / 100)
|
|
870
|
-
};
|
|
871
886
|
}
|
|
872
887
|
if (rule.referrerReward && rule.refereeReward) {
|
|
873
888
|
let partner = 0;
|
|
874
|
-
if (rule.referrerReward.type === "percentage") partner =
|
|
875
|
-
else partner = rule.referrerReward.value * quantity;
|
|
876
|
-
if (rule.referrerReward.maxReward != null
|
|
889
|
+
if (rule.referrerReward.type === "percentage") partner = Math.floor(itemTotalCents * rule.referrerReward.value / 100);
|
|
890
|
+
else partner = toCents(rule.referrerReward.value) * quantity;
|
|
891
|
+
if (rule.referrerReward.maxReward != null) {
|
|
892
|
+
const maxCents = toCents(rule.referrerReward.maxReward);
|
|
893
|
+
if (partner > maxCents) partner = maxCents;
|
|
894
|
+
}
|
|
877
895
|
let customer = 0;
|
|
878
|
-
if (rule.refereeReward.type === "percentage") customer =
|
|
879
|
-
else customer = rule.refereeReward.value * quantity;
|
|
880
|
-
if (rule.refereeReward.maxReward != null
|
|
896
|
+
if (rule.refereeReward.type === "percentage") customer = Math.floor(itemTotalCents * rule.refereeReward.value / 100);
|
|
897
|
+
else customer = toCents(rule.refereeReward.value) * quantity;
|
|
898
|
+
if (rule.refereeReward.maxReward != null) {
|
|
899
|
+
const maxCents = toCents(rule.refereeReward.maxReward);
|
|
900
|
+
if (customer > maxCents) customer = maxCents;
|
|
901
|
+
}
|
|
881
902
|
return {
|
|
882
903
|
partner,
|
|
883
904
|
customer
|
|
@@ -893,12 +914,12 @@ function getItemCategoryIds(item) {
|
|
|
893
914
|
function getItemTagIds(item) {
|
|
894
915
|
return Array.isArray(item?.product?.tags) ? normalizeIds(item.product.tags) : [];
|
|
895
916
|
}
|
|
896
|
-
function selectBestRuleForItem({ rules, item,
|
|
917
|
+
function selectBestRuleForItem({ rules, item, itemTotalCents, quantity, cartTotalCents, minOrderAmountCents, allowedTotalCommissionTypes }) {
|
|
897
918
|
const allowedTypes = allowedCommissionTypesSet(allowedTotalCommissionTypes);
|
|
898
919
|
const eligibleRules = rules.filter((rule) => {
|
|
899
920
|
if (!(rule?.totalCommission?.type ? allowedTypes.has(rule.totalCommission.type) : true)) return false;
|
|
900
|
-
const
|
|
901
|
-
if (
|
|
921
|
+
const resolvedMinCents = minOrderAmountCents != null && Number.isFinite(minOrderAmountCents) ? minOrderAmountCents : typeof rule?.minOrderAmount === "number" && Number.isFinite(rule.minOrderAmount) ? toCents(rule.minOrderAmount) : null;
|
|
922
|
+
if (resolvedMinCents != null) return cartTotalCents >= resolvedMinCents;
|
|
902
923
|
return true;
|
|
903
924
|
});
|
|
904
925
|
const productId = relationId$5(item.product);
|
|
@@ -921,7 +942,7 @@ function selectBestRuleForItem({ rules, item, itemTotal, quantity, cartTotal, mi
|
|
|
921
942
|
for (const rule of candidates) {
|
|
922
943
|
const reward = calculateItemRewardByRule({
|
|
923
944
|
rule,
|
|
924
|
-
|
|
945
|
+
itemTotalCents,
|
|
925
946
|
quantity,
|
|
926
947
|
allowedTotalCommissionTypes
|
|
927
948
|
});
|
|
@@ -947,6 +968,10 @@ function selectBestRuleForItem({ rules, item, itemTotal, quantity, cartTotal, mi
|
|
|
947
968
|
}
|
|
948
969
|
return best;
|
|
949
970
|
}
|
|
971
|
+
/**
|
|
972
|
+
* Returns the effective minimum order amount for a program in NORMAL CURRENCY.
|
|
973
|
+
* Returns null if there is no minimum.
|
|
974
|
+
*/
|
|
950
975
|
function getProgramMinimumOrderAmount({ program, allowedTotalCommissionTypes }) {
|
|
951
976
|
if (typeof program?.minOrderAmount === "number" && Number.isFinite(program.minOrderAmount)) return program.minOrderAmount;
|
|
952
977
|
const rules = Array.isArray(program?.commissionRules) ? program.commissionRules : [];
|
|
@@ -959,50 +984,66 @@ function getProgramMinimumOrderAmount({ program, allowedTotalCommissionTypes })
|
|
|
959
984
|
if (!minValues.length) return null;
|
|
960
985
|
return Math.min(...minValues);
|
|
961
986
|
}
|
|
987
|
+
/**
|
|
988
|
+
* Calculate total partner commission and customer discount for a cart.
|
|
989
|
+
*
|
|
990
|
+
* All monetary inputs are in NORMAL CURRENCY.
|
|
991
|
+
* Returns results in NORMAL CURRENCY (2 dp).
|
|
992
|
+
*/
|
|
962
993
|
function calculateCommissionAndDiscount({ cartItems, program, currencyCode = "AED", cartTotal = 0, allowedTotalCommissionTypes }) {
|
|
963
994
|
const rules = Array.isArray(program?.commissionRules) ? program.commissionRules : [];
|
|
964
995
|
if (!rules.length) return {
|
|
965
996
|
partnerCommission: 0,
|
|
966
997
|
customerDiscount: 0
|
|
967
998
|
};
|
|
968
|
-
|
|
969
|
-
|
|
999
|
+
const cartTotalCents = toCents(cartTotal);
|
|
1000
|
+
const programMinOrderAmountCents = typeof program?.minOrderAmount === "number" && Number.isFinite(program.minOrderAmount) ? toCents(program.minOrderAmount) : null;
|
|
1001
|
+
let totalPartnerCents = 0;
|
|
1002
|
+
let totalCustomerCents = 0;
|
|
970
1003
|
for (const item of cartItems) {
|
|
971
1004
|
const product = typeof item.product === "object" ? item.product : {};
|
|
972
|
-
const
|
|
1005
|
+
const itemPriceCurrency = getCartItemUnitPrice({
|
|
973
1006
|
item,
|
|
974
1007
|
product,
|
|
975
1008
|
variant: typeof item.variant === "object" ? item.variant : {},
|
|
976
1009
|
currencyCode
|
|
977
1010
|
});
|
|
978
1011
|
const quantity = item.quantity ?? 1;
|
|
979
|
-
const
|
|
1012
|
+
const itemTotalCents = toCents(itemPriceCurrency) * quantity;
|
|
980
1013
|
const bestMatch = selectBestRuleForItem({
|
|
981
1014
|
rules,
|
|
982
1015
|
item: {
|
|
983
1016
|
...item,
|
|
984
1017
|
product
|
|
985
1018
|
},
|
|
986
|
-
|
|
1019
|
+
itemTotalCents,
|
|
987
1020
|
quantity,
|
|
988
|
-
|
|
989
|
-
|
|
1021
|
+
cartTotalCents,
|
|
1022
|
+
minOrderAmountCents: programMinOrderAmountCents,
|
|
990
1023
|
allowedTotalCommissionTypes
|
|
991
1024
|
});
|
|
992
1025
|
if (!bestMatch) continue;
|
|
993
|
-
|
|
994
|
-
|
|
1026
|
+
totalPartnerCents += bestMatch.reward.partner;
|
|
1027
|
+
totalCustomerCents += bestMatch.reward.customer;
|
|
995
1028
|
}
|
|
996
|
-
const
|
|
997
|
-
const
|
|
998
|
-
if (
|
|
999
|
-
if (
|
|
1029
|
+
const maxPartnerCents = typeof program?.maxPartnerCommissionPerOrder === "number" && Number.isFinite(program.maxPartnerCommissionPerOrder) ? toCents(program.maxPartnerCommissionPerOrder) : null;
|
|
1030
|
+
const maxCustomerCents = typeof program?.maxCustomerDiscountPerOrder === "number" && Number.isFinite(program.maxCustomerDiscountPerOrder) ? toCents(program.maxCustomerDiscountPerOrder) : null;
|
|
1031
|
+
if (maxPartnerCents != null) totalPartnerCents = Math.min(totalPartnerCents, maxPartnerCents);
|
|
1032
|
+
if (maxCustomerCents != null) totalCustomerCents = Math.min(totalCustomerCents, maxCustomerCents);
|
|
1000
1033
|
return {
|
|
1001
|
-
partnerCommission:
|
|
1002
|
-
customerDiscount:
|
|
1034
|
+
partnerCommission: fromCents(totalPartnerCents),
|
|
1035
|
+
customerDiscount: fromCents(totalCustomerCents)
|
|
1003
1036
|
};
|
|
1004
1037
|
}
|
|
1005
1038
|
//#endregion
|
|
1039
|
+
//#region src/utilities/roundTo2.ts
|
|
1040
|
+
/**
|
|
1041
|
+
* Rounds a number to 2 decimal places (standard for monetary values).
|
|
1042
|
+
*/
|
|
1043
|
+
function roundTo2(value) {
|
|
1044
|
+
return Math.round(value * 100) / 100;
|
|
1045
|
+
}
|
|
1046
|
+
//#endregion
|
|
1006
1047
|
//#region src/endpoints/applyCoupon.ts
|
|
1007
1048
|
function relationId$4(value) {
|
|
1008
1049
|
if (value == null) return null;
|