washday-sdk 1.6.23 → 1.6.25
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/utils/orders/calculateOrderTotal.js +35 -1
- package/dist/utils/orders/calculateTotalTaxesIncluded.js +38 -12
- package/dist/utils/orders/calculateTotalTaxesOverPrice.js +36 -10
- package/dist/utils/orders/helpers.js +83 -32
- package/package.json +1 -1
- package/src/api/products/post.ts +1 -1
- package/src/api/products/put.ts +1 -1
- package/src/interfaces/Product.ts +4 -2
- package/src/interfaces/Store.ts +2 -0
- package/src/utils/orders/calculateOrderTotal.ts +39 -2
- package/src/utils/orders/calculateTotalTaxesIncluded.ts +40 -10
- package/src/utils/orders/calculateTotalTaxesOverPrice.ts +39 -9
- package/src/utils/orders/helpers.ts +95 -32
|
@@ -2,6 +2,40 @@ import { DiscountCodeTypes } from "../../enum";
|
|
|
2
2
|
import { calculateTotalTaxesIncluded } from "./calculateTotalTaxesIncluded";
|
|
3
3
|
import { calculateTotalTaxesOverPrice } from "./calculateTotalTaxesOverPrice";
|
|
4
4
|
import { getCreditApplied, getProductLineTotals, getShippingCost } from "./helpers";
|
|
5
|
+
const getNormalizedId = (value) => {
|
|
6
|
+
if (value === null || value === undefined) {
|
|
7
|
+
return "";
|
|
8
|
+
}
|
|
9
|
+
return String(value).trim();
|
|
10
|
+
};
|
|
11
|
+
const getProductIdentity = (product) => {
|
|
12
|
+
var _a;
|
|
13
|
+
const directId = getNormalizedId((_a = product === null || product === void 0 ? void 0 : product._id) !== null && _a !== void 0 ? _a : product === null || product === void 0 ? void 0 : product.productId);
|
|
14
|
+
if (directId) {
|
|
15
|
+
return directId;
|
|
16
|
+
}
|
|
17
|
+
const storeProductId = product === null || product === void 0 ? void 0 : product.storeProductId;
|
|
18
|
+
if (typeof storeProductId === "string") {
|
|
19
|
+
return getNormalizedId(storeProductId);
|
|
20
|
+
}
|
|
21
|
+
return getNormalizedId(storeProductId === null || storeProductId === void 0 ? void 0 : storeProductId._id);
|
|
22
|
+
};
|
|
23
|
+
const getDiscountTargetIdentity = (target) => {
|
|
24
|
+
var _a, _b;
|
|
25
|
+
if (typeof target === "string") {
|
|
26
|
+
return getNormalizedId(target);
|
|
27
|
+
}
|
|
28
|
+
return getNormalizedId((_b = (_a = target === null || target === void 0 ? void 0 : target._id) !== null && _a !== void 0 ? _a : target === null || target === void 0 ? void 0 : target.productId) !== null && _b !== void 0 ? _b : target === null || target === void 0 ? void 0 : target.storeProductId);
|
|
29
|
+
};
|
|
30
|
+
const hasMatchingDiscountTarget = (targets = [], product) => {
|
|
31
|
+
const productId = getProductIdentity(product);
|
|
32
|
+
if (!productId) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return (Array.isArray(targets) ? targets : []).some((target) => {
|
|
36
|
+
return getDiscountTargetIdentity(target) === productId;
|
|
37
|
+
});
|
|
38
|
+
};
|
|
5
39
|
export const calculateOrderTotal = (order, selectedCustomer, storeSettings, hasShippingCost, storeDiscounts = [], discountCodeObj, redeemPointsDiscount = 0 // 💡 NUEVO parámetro
|
|
6
40
|
) => {
|
|
7
41
|
var _a;
|
|
@@ -19,7 +53,7 @@ export const calculateOrderTotal = (order, selectedCustomer, storeSettings, hasS
|
|
|
19
53
|
discountCodeObj.type === DiscountCodeTypes.NUMBER &&
|
|
20
54
|
discountCodeObj.applyOnceOnOrder) {
|
|
21
55
|
const includesProducts = discountCodeObj.applyToAllProducts ||
|
|
22
|
-
order.products.some((curr) => discountCodeObj.products
|
|
56
|
+
order.products.some((curr) => hasMatchingDiscountTarget(discountCodeObj.products, curr));
|
|
23
57
|
discountCodeAmount = includesProducts ? discountCodeObj.value : 0;
|
|
24
58
|
}
|
|
25
59
|
// === SHIPPING COST ===
|
|
@@ -1,9 +1,43 @@
|
|
|
1
1
|
import { DiscountCodeTypes } from "../../enum";
|
|
2
2
|
import { getProductTaxesPercentage } from "./helpers";
|
|
3
|
+
const getNormalizedId = (value) => {
|
|
4
|
+
if (value === null || value === undefined) {
|
|
5
|
+
return "";
|
|
6
|
+
}
|
|
7
|
+
return String(value).trim();
|
|
8
|
+
};
|
|
9
|
+
const getProductIdentity = (product) => {
|
|
10
|
+
var _a;
|
|
11
|
+
const directId = getNormalizedId((_a = product === null || product === void 0 ? void 0 : product._id) !== null && _a !== void 0 ? _a : product === null || product === void 0 ? void 0 : product.productId);
|
|
12
|
+
if (directId) {
|
|
13
|
+
return directId;
|
|
14
|
+
}
|
|
15
|
+
const storeProductId = product === null || product === void 0 ? void 0 : product.storeProductId;
|
|
16
|
+
if (typeof storeProductId === "string") {
|
|
17
|
+
return getNormalizedId(storeProductId);
|
|
18
|
+
}
|
|
19
|
+
return getNormalizedId(storeProductId === null || storeProductId === void 0 ? void 0 : storeProductId._id);
|
|
20
|
+
};
|
|
21
|
+
const getDiscountTargetIdentity = (target) => {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
if (typeof target === "string") {
|
|
24
|
+
return getNormalizedId(target);
|
|
25
|
+
}
|
|
26
|
+
return getNormalizedId((_b = (_a = target === null || target === void 0 ? void 0 : target._id) !== null && _a !== void 0 ? _a : target === null || target === void 0 ? void 0 : target.productId) !== null && _b !== void 0 ? _b : target === null || target === void 0 ? void 0 : target.storeProductId);
|
|
27
|
+
};
|
|
28
|
+
const hasMatchingDiscountTarget = (targets = [], currentProduct) => {
|
|
29
|
+
const currentId = getProductIdentity(currentProduct);
|
|
30
|
+
if (!currentId) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return (Array.isArray(targets) ? targets : []).some((target) => {
|
|
34
|
+
return getDiscountTargetIdentity(target) === currentId;
|
|
35
|
+
});
|
|
36
|
+
};
|
|
3
37
|
export const calculateTotalTaxesIncluded = (order, selectedCustomer, storeSettings, storeDiscounts, appliedOrderDiscounts, discountCodeObj) => {
|
|
4
38
|
const productTableImports = [...order.products, ...order.buyAndGetProducts].map((current) => {
|
|
5
|
-
var _a
|
|
6
|
-
const qty = current.qty || 0;
|
|
39
|
+
var _a;
|
|
40
|
+
const qty = current.qty || current.quantity || 0;
|
|
7
41
|
const taxPercentage = getProductTaxesPercentage(current, storeSettings);
|
|
8
42
|
const taxFactor = 1 + taxPercentage / 100;
|
|
9
43
|
// Convertir extraAmount de bruto a neto
|
|
@@ -19,15 +53,7 @@ export const calculateTotalTaxesIncluded = (order, selectedCustomer, storeSettin
|
|
|
19
53
|
let customerDiscount = 0;
|
|
20
54
|
// ORDER DOES NOT HAVE A DISCOUNT CODE
|
|
21
55
|
if (!order.discountCode) {
|
|
22
|
-
const discountsToApply = storeDiscounts.filter((discount) => discount.isActive && discount.products
|
|
23
|
-
if (typeof p === 'string') {
|
|
24
|
-
return p === current._id;
|
|
25
|
-
}
|
|
26
|
-
else if (typeof p === 'object') {
|
|
27
|
-
return p._id === current._id;
|
|
28
|
-
}
|
|
29
|
-
return false;
|
|
30
|
-
}));
|
|
56
|
+
const discountsToApply = storeDiscounts.filter((discount) => discount.isActive && hasMatchingDiscountTarget(discount.products, current));
|
|
31
57
|
customerDiscount = ((_a = selectedCustomer === null || selectedCustomer === void 0 ? void 0 : selectedCustomer.customer) === null || _a === void 0 ? void 0 : _a.discount) || 0;
|
|
32
58
|
productPercentageDiscount = discountsToApply.reduce((prev, next) => {
|
|
33
59
|
return next.type === 'percentage' ? prev + next.value : prev;
|
|
@@ -41,7 +67,7 @@ export const calculateTotalTaxesIncluded = (order, selectedCustomer, storeSettin
|
|
|
41
67
|
if ((discountCodeObj === null || discountCodeObj === void 0 ? void 0 : discountCodeObj.type) === DiscountCodeTypes.PERCENTAGE) {
|
|
42
68
|
discPercentageInteger = +(discountCodeObj.value / 100).toFixed(2);
|
|
43
69
|
if (!discountCodeObj.applyToAllProducts) {
|
|
44
|
-
discPercentageInteger = (
|
|
70
|
+
discPercentageInteger = hasMatchingDiscountTarget(discountCodeObj.products, current)
|
|
45
71
|
? +(discountCodeObj.value / 100).toFixed(2)
|
|
46
72
|
: 0;
|
|
47
73
|
}
|
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
import { BuyAndGetConditionsTypes, DiscountCodeTypes } from "../../enum";
|
|
2
2
|
import { getProductTaxesPercentage } from "./helpers";
|
|
3
|
+
const getNormalizedId = (value) => {
|
|
4
|
+
if (value === null || value === undefined) {
|
|
5
|
+
return "";
|
|
6
|
+
}
|
|
7
|
+
return String(value).trim();
|
|
8
|
+
};
|
|
9
|
+
const getProductIdentity = (product) => {
|
|
10
|
+
var _a;
|
|
11
|
+
const directId = getNormalizedId((_a = product === null || product === void 0 ? void 0 : product._id) !== null && _a !== void 0 ? _a : product === null || product === void 0 ? void 0 : product.productId);
|
|
12
|
+
if (directId) {
|
|
13
|
+
return directId;
|
|
14
|
+
}
|
|
15
|
+
const storeProductId = product === null || product === void 0 ? void 0 : product.storeProductId;
|
|
16
|
+
if (typeof storeProductId === "string") {
|
|
17
|
+
return getNormalizedId(storeProductId);
|
|
18
|
+
}
|
|
19
|
+
return getNormalizedId(storeProductId === null || storeProductId === void 0 ? void 0 : storeProductId._id);
|
|
20
|
+
};
|
|
21
|
+
const getDiscountTargetIdentity = (target) => {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
if (typeof target === "string") {
|
|
24
|
+
return getNormalizedId(target);
|
|
25
|
+
}
|
|
26
|
+
return getNormalizedId((_b = (_a = target === null || target === void 0 ? void 0 : target._id) !== null && _a !== void 0 ? _a : target === null || target === void 0 ? void 0 : target.productId) !== null && _b !== void 0 ? _b : target === null || target === void 0 ? void 0 : target.storeProductId);
|
|
27
|
+
};
|
|
28
|
+
const hasMatchingDiscountTarget = (targets = [], currentProduct) => {
|
|
29
|
+
const currentId = getProductIdentity(currentProduct);
|
|
30
|
+
if (!currentId) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return (Array.isArray(targets) ? targets : []).some((target) => {
|
|
34
|
+
return getDiscountTargetIdentity(target) === currentId;
|
|
35
|
+
});
|
|
36
|
+
};
|
|
3
37
|
export const calculateTotalTaxesOverPrice = (order, selectedCustomer, storeSettings, storeDiscounts, appliedOrderDiscounts, discountCodeObj) => {
|
|
4
38
|
const productTableImports = [...order.products, ...order.buyAndGetProducts].map((current) => {
|
|
5
39
|
var _a;
|
|
@@ -17,15 +51,7 @@ export const calculateTotalTaxesOverPrice = (order, selectedCustomer, storeSetti
|
|
|
17
51
|
const adjustedPrice = productBasePrice + unitExtra;
|
|
18
52
|
// Cálculo de descuento sobre el precio ajustado
|
|
19
53
|
if (!order.discountCode) {
|
|
20
|
-
const discountsToApply = storeDiscounts.filter((discount) => discount.isActive && discount.products
|
|
21
|
-
if (typeof p === 'string') {
|
|
22
|
-
return p === current._id;
|
|
23
|
-
}
|
|
24
|
-
else if (typeof p === 'object') {
|
|
25
|
-
return p._id === current._id;
|
|
26
|
-
}
|
|
27
|
-
return false;
|
|
28
|
-
}));
|
|
54
|
+
const discountsToApply = storeDiscounts.filter((discount) => discount.isActive && hasMatchingDiscountTarget(discount.products, current));
|
|
29
55
|
customerDiscount = ((_a = selectedCustomer === null || selectedCustomer === void 0 ? void 0 : selectedCustomer.customer) === null || _a === void 0 ? void 0 : _a.discount) || 0;
|
|
30
56
|
productPercentageDiscount = discountsToApply.reduce((acc, discount) => {
|
|
31
57
|
return discount.type === 'percentage' ? acc + discount.value : acc;
|
|
@@ -37,7 +63,7 @@ export const calculateTotalTaxesOverPrice = (order, selectedCustomer, storeSetti
|
|
|
37
63
|
if (discountCodeObj.type === DiscountCodeTypes.PERCENTAGE) {
|
|
38
64
|
discPercentageInteger = +(discountCodeObj.value / 100).toFixed(2);
|
|
39
65
|
if (!discountCodeObj.applyToAllProducts) {
|
|
40
|
-
discPercentageInteger = discountCodeObj.products
|
|
66
|
+
discPercentageInteger = hasMatchingDiscountTarget(discountCodeObj.products, current)
|
|
41
67
|
? +(discountCodeObj.value / 100).toFixed(2)
|
|
42
68
|
: 0;
|
|
43
69
|
}
|
|
@@ -1,4 +1,71 @@
|
|
|
1
1
|
import { BuyAndGetConditionsTypes, DiscountCodeTypes } from "../../enum";
|
|
2
|
+
const getNormalizedId = (value) => {
|
|
3
|
+
if (value === null || value === undefined) {
|
|
4
|
+
return "";
|
|
5
|
+
}
|
|
6
|
+
return String(value).trim();
|
|
7
|
+
};
|
|
8
|
+
const getProductIdentity = (product) => {
|
|
9
|
+
var _a;
|
|
10
|
+
const directId = getNormalizedId((_a = product === null || product === void 0 ? void 0 : product._id) !== null && _a !== void 0 ? _a : product === null || product === void 0 ? void 0 : product.productId);
|
|
11
|
+
if (directId) {
|
|
12
|
+
return directId;
|
|
13
|
+
}
|
|
14
|
+
const storeProductId = product === null || product === void 0 ? void 0 : product.storeProductId;
|
|
15
|
+
if (typeof storeProductId === "string") {
|
|
16
|
+
return getNormalizedId(storeProductId);
|
|
17
|
+
}
|
|
18
|
+
return getNormalizedId(storeProductId === null || storeProductId === void 0 ? void 0 : storeProductId._id);
|
|
19
|
+
};
|
|
20
|
+
const getDiscountTargetIdentity = (target) => {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
if (typeof target === "string") {
|
|
23
|
+
return getNormalizedId(target);
|
|
24
|
+
}
|
|
25
|
+
return getNormalizedId((_b = (_a = target === null || target === void 0 ? void 0 : target._id) !== null && _a !== void 0 ? _a : target === null || target === void 0 ? void 0 : target.productId) !== null && _b !== void 0 ? _b : target === null || target === void 0 ? void 0 : target.storeProductId);
|
|
26
|
+
};
|
|
27
|
+
const matchesDiscountTarget = (product, target) => {
|
|
28
|
+
const productId = getProductIdentity(product);
|
|
29
|
+
const targetId = getDiscountTargetIdentity(target);
|
|
30
|
+
return Boolean(productId && targetId && productId === targetId);
|
|
31
|
+
};
|
|
32
|
+
const getLineQty = (product) => {
|
|
33
|
+
var _a, _b;
|
|
34
|
+
const qty = Number((_b = (_a = product === null || product === void 0 ? void 0 : product.qty) !== null && _a !== void 0 ? _a : product === null || product === void 0 ? void 0 : product.quantity) !== null && _b !== void 0 ? _b : 0);
|
|
35
|
+
return Number.isFinite(qty) ? qty : 0;
|
|
36
|
+
};
|
|
37
|
+
const getQtyOfProduct = (lines, productId) => {
|
|
38
|
+
const targetId = getDiscountTargetIdentity(productId);
|
|
39
|
+
if (!targetId) {
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
return lines.reduce((acc, line) => {
|
|
43
|
+
if (getProductIdentity(line) !== targetId) {
|
|
44
|
+
return acc;
|
|
45
|
+
}
|
|
46
|
+
return acc + getLineQty(line);
|
|
47
|
+
}, 0);
|
|
48
|
+
};
|
|
49
|
+
const getFulfillmentCount = (buyConditions = [], lines = []) => {
|
|
50
|
+
if (!Array.isArray(buyConditions) || buyConditions.length === 0) {
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
const fulfillments = buyConditions.map((condition) => {
|
|
54
|
+
var _a, _b;
|
|
55
|
+
const requiredQty = Number((_a = condition === null || condition === void 0 ? void 0 : condition.qty) !== null && _a !== void 0 ? _a : 0);
|
|
56
|
+
if (!Number.isFinite(requiredQty) || requiredQty <= 0) {
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
const boughtQty = getQtyOfProduct(lines, (_b = condition === null || condition === void 0 ? void 0 : condition.buyProduct) === null || _b === void 0 ? void 0 : _b._id);
|
|
60
|
+
return Math.floor(boughtQty / requiredQty);
|
|
61
|
+
});
|
|
62
|
+
return fulfillments.length > 0 ? Math.min(...fulfillments) : 0;
|
|
63
|
+
};
|
|
64
|
+
const getTargetProductIdSet = (products = []) => {
|
|
65
|
+
return new Set((Array.isArray(products) ? products : [])
|
|
66
|
+
.map((item) => getDiscountTargetIdentity(item))
|
|
67
|
+
.filter((id) => id.length > 0));
|
|
68
|
+
};
|
|
2
69
|
export const getProductTaxesPercentage = (productObj, store) => {
|
|
3
70
|
const getTaxValue = (tax, isTaxExempt) => {
|
|
4
71
|
if (!tax) {
|
|
@@ -65,13 +132,7 @@ export const applyDiscountToProducts = (discountCode, productsArr, isExpress, st
|
|
|
65
132
|
return {
|
|
66
133
|
newOrderProds: productsArr.map((prod) => {
|
|
67
134
|
const discountsToApply = storeDiscounts.filter((discount) => discount.isActive && discount.products.some((p) => {
|
|
68
|
-
|
|
69
|
-
return p === prod._id;
|
|
70
|
-
}
|
|
71
|
-
else if (typeof p === 'object') {
|
|
72
|
-
return p._id === prod._id;
|
|
73
|
-
}
|
|
74
|
-
return false;
|
|
135
|
+
return matchesDiscountTarget(prod, p);
|
|
75
136
|
}));
|
|
76
137
|
const customerDiscount = (selectedCustomer === null || selectedCustomer === void 0 ? void 0 : selectedCustomer.discount) / 100 || 0;
|
|
77
138
|
const productPercentageDiscount = discountsToApply.reduce((prev, next) => {
|
|
@@ -103,16 +164,15 @@ export const applyDiscountToProducts = (discountCode, productsArr, isExpress, st
|
|
|
103
164
|
});
|
|
104
165
|
}
|
|
105
166
|
else {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
const prodPrice = isExpress ? prodObj.expressPrice : prodObj.price;
|
|
111
|
-
const discountAmount = +(percentageDiscount * prodPrice).toFixed(2);
|
|
112
|
-
prodObj.discountAmount = discountAmount;
|
|
113
|
-
newOrderProds[prodIdx] = prodObj;
|
|
167
|
+
const discountProductIds = getTargetProductIdSet(discountCode.products);
|
|
168
|
+
newOrderProds = newOrderProds.map((prod) => {
|
|
169
|
+
if (!discountProductIds.has(getProductIdentity(prod))) {
|
|
170
|
+
return prod;
|
|
114
171
|
}
|
|
115
|
-
|
|
172
|
+
const prodPrice = isExpress ? prod.expressPrice : prod.price;
|
|
173
|
+
const discountAmount = +(percentageDiscount * prodPrice).toFixed(2);
|
|
174
|
+
return Object.assign(Object.assign({}, prod), { discountAmount });
|
|
175
|
+
});
|
|
116
176
|
}
|
|
117
177
|
}
|
|
118
178
|
if (discountCode && discountCode.type === DiscountCodeTypes.NUMBER) {
|
|
@@ -122,14 +182,13 @@ export const applyDiscountToProducts = (discountCode, productsArr, isExpress, st
|
|
|
122
182
|
newOrderProds = newOrderProds.map((prod) => (Object.assign(Object.assign({}, prod), { discountAmount: discountAmount })));
|
|
123
183
|
}
|
|
124
184
|
else {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
prodObj.discountAmount = discountAmount;
|
|
130
|
-
newOrderProds[prodIdx] = prodObj;
|
|
185
|
+
const discountProductIds = getTargetProductIdSet(discountCode.products);
|
|
186
|
+
newOrderProds = newOrderProds.map((prod) => {
|
|
187
|
+
if (!discountProductIds.has(getProductIdentity(prod))) {
|
|
188
|
+
return prod;
|
|
131
189
|
}
|
|
132
|
-
|
|
190
|
+
return Object.assign(Object.assign({}, prod), { discountAmount });
|
|
191
|
+
});
|
|
133
192
|
}
|
|
134
193
|
}
|
|
135
194
|
}
|
|
@@ -138,15 +197,7 @@ export const applyDiscountToProducts = (discountCode, productsArr, isExpress, st
|
|
|
138
197
|
const getConditions = discountCode.buyAndGetConditions[0].getConditions;
|
|
139
198
|
const discountType = discountCode.buyAndGetConditions[0].getDiscountType;
|
|
140
199
|
const discountValue = discountCode.buyAndGetConditions[0].discountValue;
|
|
141
|
-
|
|
142
|
-
for (let prodCondition of buyConditions) {
|
|
143
|
-
const orderProd = productsArr.find((ordProd) => ordProd._id === prodCondition.buyProduct._id);
|
|
144
|
-
// Nota: Aquí solo se verifica una condición de producto
|
|
145
|
-
if (orderProd) {
|
|
146
|
-
let qty = orderProd.qty || orderProd.quantity;
|
|
147
|
-
buyConditionFulfilledCounter = Math.floor(qty / prodCondition.qty);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
200
|
+
const buyConditionFulfilledCounter = getFulfillmentCount(buyConditions, productsArr);
|
|
150
201
|
if (buyConditionFulfilledCounter) {
|
|
151
202
|
for (let prodCondition of getConditions) {
|
|
152
203
|
// Se utiliza solo el precio del producto (sin extraAmount) para calcular el descuento
|
package/package.json
CHANGED
package/src/api/products/post.ts
CHANGED
package/src/api/products/put.ts
CHANGED
|
@@ -19,7 +19,7 @@ export const updateById = async function (this: WashdayClientInstance, id: strin
|
|
|
19
19
|
name?: string;
|
|
20
20
|
price?: number;
|
|
21
21
|
expressPrice?: number;
|
|
22
|
-
type?: 'normal' | 'weight';
|
|
22
|
+
type?: 'normal' | 'weight' | 'length';
|
|
23
23
|
pieces?: number;
|
|
24
24
|
sku?: string;
|
|
25
25
|
overlayText?: string;
|
|
@@ -26,7 +26,7 @@ export interface IProduct {
|
|
|
26
26
|
taxExemptOne: boolean,
|
|
27
27
|
taxExemptTwo: boolean,
|
|
28
28
|
taxExemptThree: boolean,
|
|
29
|
-
type:
|
|
29
|
+
type: 'normal' | 'weight' | 'length',
|
|
30
30
|
isActive: boolean,
|
|
31
31
|
showInApp: boolean,
|
|
32
32
|
owner: IUser | string,
|
|
@@ -50,6 +50,8 @@ export interface IStoreProduct extends IProduct { }
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
export interface IOrderProduct extends IProduct {
|
|
53
|
+
lineId?: string,
|
|
54
|
+
orderProductLineID?: string,
|
|
53
55
|
extraAmount: number,
|
|
54
56
|
properties: Array<string>,
|
|
55
57
|
quantity: number,
|
|
@@ -57,4 +59,4 @@ export interface IOrderProduct extends IProduct {
|
|
|
57
59
|
isBuyAndGetProduct: boolean,
|
|
58
60
|
storeProductId: IStoreProduct | string
|
|
59
61
|
qty?: number
|
|
60
|
-
}
|
|
62
|
+
}
|
package/src/interfaces/Store.ts
CHANGED
|
@@ -77,6 +77,7 @@ export interface IStore {
|
|
|
77
77
|
cashierBoxes?: ICashierBox,
|
|
78
78
|
ticketStructure?: ITicketStructure | null,
|
|
79
79
|
ticketForLaundryStructure?: ITicketForLaundryStructure | null,
|
|
80
|
+
labelTicketStructure?: ILabelTicketStructure | null,
|
|
80
81
|
ticketScanMode?: 'order' | 'customer',
|
|
81
82
|
storeDiscounts: IStoreDiscount | null,
|
|
82
83
|
discountCodes: IDiscountCode | null,
|
|
@@ -302,6 +303,7 @@ export interface ITicketStructure {
|
|
|
302
303
|
showCredit: Boolean,
|
|
303
304
|
}
|
|
304
305
|
|
|
306
|
+
export interface ILabelTicketStructure extends ITicketStructure {}
|
|
305
307
|
export interface ITicketForLaundryStructure {
|
|
306
308
|
showTotalPieces: Boolean,
|
|
307
309
|
showEmisionDate: Boolean,
|
|
@@ -5,6 +5,43 @@ import { calculateTotalTaxesIncluded } from "./calculateTotalTaxesIncluded";
|
|
|
5
5
|
import { calculateTotalTaxesOverPrice } from "./calculateTotalTaxesOverPrice";
|
|
6
6
|
import { getCreditApplied, getProductLineTotals, getShippingCost } from "./helpers";
|
|
7
7
|
|
|
8
|
+
const getNormalizedId = (value: any): string => {
|
|
9
|
+
if (value === null || value === undefined) {
|
|
10
|
+
return "";
|
|
11
|
+
}
|
|
12
|
+
return String(value).trim();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const getProductIdentity = (product: any): string => {
|
|
16
|
+
const directId = getNormalizedId(product?._id ?? product?.productId);
|
|
17
|
+
if (directId) {
|
|
18
|
+
return directId;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const storeProductId = product?.storeProductId;
|
|
22
|
+
if (typeof storeProductId === "string") {
|
|
23
|
+
return getNormalizedId(storeProductId);
|
|
24
|
+
}
|
|
25
|
+
return getNormalizedId(storeProductId?._id);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getDiscountTargetIdentity = (target: any): string => {
|
|
29
|
+
if (typeof target === "string") {
|
|
30
|
+
return getNormalizedId(target);
|
|
31
|
+
}
|
|
32
|
+
return getNormalizedId(target?._id ?? target?.productId ?? target?.storeProductId);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const hasMatchingDiscountTarget = (targets: any[] = [], product: any): boolean => {
|
|
36
|
+
const productId = getProductIdentity(product);
|
|
37
|
+
if (!productId) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return (Array.isArray(targets) ? targets : []).some((target: any) => {
|
|
41
|
+
return getDiscountTargetIdentity(target) === productId;
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
8
45
|
export const calculateOrderTotal = (
|
|
9
46
|
order: any,
|
|
10
47
|
selectedCustomer: { customer: ICustomer } | undefined,
|
|
@@ -51,7 +88,7 @@ export const calculateOrderTotal = (
|
|
|
51
88
|
const includesProducts =
|
|
52
89
|
discountCodeObj.applyToAllProducts ||
|
|
53
90
|
order.products.some((curr: any) =>
|
|
54
|
-
discountCodeObj.products
|
|
91
|
+
hasMatchingDiscountTarget(discountCodeObj.products, curr)
|
|
55
92
|
);
|
|
56
93
|
|
|
57
94
|
discountCodeAmount = includesProducts ? discountCodeObj.value : 0;
|
|
@@ -102,4 +139,4 @@ export const calculateOrderTotal = (
|
|
|
102
139
|
} catch (error) {
|
|
103
140
|
throw error;
|
|
104
141
|
}
|
|
105
|
-
};
|
|
142
|
+
};
|
|
@@ -2,6 +2,43 @@ import { DiscountCodeTypes } from "../../enum";
|
|
|
2
2
|
import { IOrderProduct } from "../../interfaces/Product";
|
|
3
3
|
import { getProductTaxesPercentage } from "./helpers";
|
|
4
4
|
|
|
5
|
+
const getNormalizedId = (value: any): string => {
|
|
6
|
+
if (value === null || value === undefined) {
|
|
7
|
+
return "";
|
|
8
|
+
}
|
|
9
|
+
return String(value).trim();
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const getProductIdentity = (product: any): string => {
|
|
13
|
+
const directId = getNormalizedId(product?._id ?? product?.productId);
|
|
14
|
+
if (directId) {
|
|
15
|
+
return directId;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const storeProductId = product?.storeProductId;
|
|
19
|
+
if (typeof storeProductId === "string") {
|
|
20
|
+
return getNormalizedId(storeProductId);
|
|
21
|
+
}
|
|
22
|
+
return getNormalizedId(storeProductId?._id);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const getDiscountTargetIdentity = (target: any): string => {
|
|
26
|
+
if (typeof target === "string") {
|
|
27
|
+
return getNormalizedId(target);
|
|
28
|
+
}
|
|
29
|
+
return getNormalizedId(target?._id ?? target?.productId ?? target?.storeProductId);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const hasMatchingDiscountTarget = (targets: any[] = [], currentProduct: any): boolean => {
|
|
33
|
+
const currentId = getProductIdentity(currentProduct);
|
|
34
|
+
if (!currentId) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return (Array.isArray(targets) ? targets : []).some((target: any) => {
|
|
38
|
+
return getDiscountTargetIdentity(target) === currentId;
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
5
42
|
export const calculateTotalTaxesIncluded = (
|
|
6
43
|
order: any,
|
|
7
44
|
selectedCustomer: any,
|
|
@@ -11,7 +48,7 @@ export const calculateTotalTaxesIncluded = (
|
|
|
11
48
|
discountCodeObj: any | null
|
|
12
49
|
): any => {
|
|
13
50
|
const productTableImports = [...order.products, ...order.buyAndGetProducts].map((current: IOrderProduct) => {
|
|
14
|
-
const qty = current.qty || 0;
|
|
51
|
+
const qty = current.qty || current.quantity || 0;
|
|
15
52
|
const taxPercentage = getProductTaxesPercentage(current, storeSettings);
|
|
16
53
|
const taxFactor = 1 + taxPercentage / 100;
|
|
17
54
|
|
|
@@ -31,14 +68,7 @@ export const calculateTotalTaxesIncluded = (
|
|
|
31
68
|
// ORDER DOES NOT HAVE A DISCOUNT CODE
|
|
32
69
|
if (!order.discountCode) {
|
|
33
70
|
const discountsToApply = storeDiscounts.filter(
|
|
34
|
-
(discount: any) => discount.isActive && discount.products
|
|
35
|
-
if (typeof p === 'string') {
|
|
36
|
-
return p === current._id
|
|
37
|
-
} else if (typeof p === 'object') {
|
|
38
|
-
return p._id === current._id
|
|
39
|
-
}
|
|
40
|
-
return false
|
|
41
|
-
})
|
|
71
|
+
(discount: any) => discount.isActive && hasMatchingDiscountTarget(discount.products, current)
|
|
42
72
|
);
|
|
43
73
|
customerDiscount = selectedCustomer?.customer?.discount || 0;
|
|
44
74
|
productPercentageDiscount = discountsToApply.reduce((prev, next) => {
|
|
@@ -52,7 +82,7 @@ export const calculateTotalTaxesIncluded = (
|
|
|
52
82
|
if (discountCodeObj?.type === DiscountCodeTypes.PERCENTAGE) {
|
|
53
83
|
discPercentageInteger = +(discountCodeObj.value / 100).toFixed(2);
|
|
54
84
|
if (!discountCodeObj.applyToAllProducts) {
|
|
55
|
-
discPercentageInteger = discountCodeObj.products
|
|
85
|
+
discPercentageInteger = hasMatchingDiscountTarget(discountCodeObj.products, current)
|
|
56
86
|
? +(discountCodeObj.value / 100).toFixed(2)
|
|
57
87
|
: 0;
|
|
58
88
|
}
|
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
import { BuyAndGetConditionsTypes, DiscountCodeTypes } from "../../enum";
|
|
2
2
|
import { getProductTaxesPercentage } from "./helpers";
|
|
3
3
|
|
|
4
|
+
const getNormalizedId = (value: any): string => {
|
|
5
|
+
if (value === null || value === undefined) {
|
|
6
|
+
return "";
|
|
7
|
+
}
|
|
8
|
+
return String(value).trim();
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const getProductIdentity = (product: any): string => {
|
|
12
|
+
const directId = getNormalizedId(product?._id ?? product?.productId);
|
|
13
|
+
if (directId) {
|
|
14
|
+
return directId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const storeProductId = product?.storeProductId;
|
|
18
|
+
if (typeof storeProductId === "string") {
|
|
19
|
+
return getNormalizedId(storeProductId);
|
|
20
|
+
}
|
|
21
|
+
return getNormalizedId(storeProductId?._id);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const getDiscountTargetIdentity = (target: any): string => {
|
|
25
|
+
if (typeof target === "string") {
|
|
26
|
+
return getNormalizedId(target);
|
|
27
|
+
}
|
|
28
|
+
return getNormalizedId(target?._id ?? target?.productId ?? target?.storeProductId);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const hasMatchingDiscountTarget = (targets: any[] = [], currentProduct: any): boolean => {
|
|
32
|
+
const currentId = getProductIdentity(currentProduct);
|
|
33
|
+
if (!currentId) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return (Array.isArray(targets) ? targets : []).some((target: any) => {
|
|
37
|
+
return getDiscountTargetIdentity(target) === currentId;
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
4
41
|
export const calculateTotalTaxesOverPrice = (
|
|
5
42
|
order: any,
|
|
6
43
|
selectedCustomer: any,
|
|
@@ -28,14 +65,7 @@ export const calculateTotalTaxesOverPrice = (
|
|
|
28
65
|
// Cálculo de descuento sobre el precio ajustado
|
|
29
66
|
if (!order.discountCode) {
|
|
30
67
|
const discountsToApply = storeDiscounts.filter(
|
|
31
|
-
(discount: any) => discount.isActive && discount.products
|
|
32
|
-
if (typeof p === 'string') {
|
|
33
|
-
return p === current._id
|
|
34
|
-
} else if (typeof p === 'object') {
|
|
35
|
-
return p._id === current._id
|
|
36
|
-
}
|
|
37
|
-
return false
|
|
38
|
-
})
|
|
68
|
+
(discount: any) => discount.isActive && hasMatchingDiscountTarget(discount.products, current)
|
|
39
69
|
);
|
|
40
70
|
customerDiscount = selectedCustomer?.customer?.discount || 0;
|
|
41
71
|
productPercentageDiscount = discountsToApply.reduce((acc, discount) => {
|
|
@@ -47,7 +77,7 @@ export const calculateTotalTaxesOverPrice = (
|
|
|
47
77
|
if (discountCodeObj.type === DiscountCodeTypes.PERCENTAGE) {
|
|
48
78
|
discPercentageInteger = +(discountCodeObj.value / 100).toFixed(2);
|
|
49
79
|
if (!discountCodeObj.applyToAllProducts) {
|
|
50
|
-
discPercentageInteger = discountCodeObj.products
|
|
80
|
+
discPercentageInteger = hasMatchingDiscountTarget(discountCodeObj.products, current)
|
|
51
81
|
? +(discountCodeObj.value / 100).toFixed(2)
|
|
52
82
|
: 0;
|
|
53
83
|
}
|
|
@@ -3,6 +3,85 @@ import { ICustomer } from "../../interfaces/Customer";
|
|
|
3
3
|
import { IOrderProduct, ProductLineTotals } from "../../interfaces/Product";
|
|
4
4
|
import { IStore, ITaxConfig } from "../../interfaces/Store";
|
|
5
5
|
|
|
6
|
+
const getNormalizedId = (value: any): string => {
|
|
7
|
+
if (value === null || value === undefined) {
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
10
|
+
return String(value).trim();
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const getProductIdentity = (product: any): string => {
|
|
14
|
+
const directId = getNormalizedId(product?._id ?? product?.productId);
|
|
15
|
+
if (directId) {
|
|
16
|
+
return directId;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const storeProductId = product?.storeProductId;
|
|
20
|
+
if (typeof storeProductId === "string") {
|
|
21
|
+
return getNormalizedId(storeProductId);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return getNormalizedId(storeProductId?._id);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const getDiscountTargetIdentity = (target: any): string => {
|
|
28
|
+
if (typeof target === "string") {
|
|
29
|
+
return getNormalizedId(target);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return getNormalizedId(target?._id ?? target?.productId ?? target?.storeProductId);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const matchesDiscountTarget = (product: any, target: any): boolean => {
|
|
36
|
+
const productId = getProductIdentity(product);
|
|
37
|
+
const targetId = getDiscountTargetIdentity(target);
|
|
38
|
+
return Boolean(productId && targetId && productId === targetId);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const getLineQty = (product: any): number => {
|
|
42
|
+
const qty = Number(product?.qty ?? product?.quantity ?? 0);
|
|
43
|
+
return Number.isFinite(qty) ? qty : 0;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getQtyOfProduct = (lines: any[], productId: any): number => {
|
|
47
|
+
const targetId = getDiscountTargetIdentity(productId);
|
|
48
|
+
if (!targetId) {
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return lines.reduce((acc: number, line: any) => {
|
|
53
|
+
if (getProductIdentity(line) !== targetId) {
|
|
54
|
+
return acc;
|
|
55
|
+
}
|
|
56
|
+
return acc + getLineQty(line);
|
|
57
|
+
}, 0);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getFulfillmentCount = (buyConditions: any[] = [], lines: any[] = []): number => {
|
|
61
|
+
if (!Array.isArray(buyConditions) || buyConditions.length === 0) {
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const fulfillments = buyConditions.map((condition: any) => {
|
|
66
|
+
const requiredQty = Number(condition?.qty ?? 0);
|
|
67
|
+
if (!Number.isFinite(requiredQty) || requiredQty <= 0) {
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
const boughtQty = getQtyOfProduct(lines, condition?.buyProduct?._id);
|
|
71
|
+
return Math.floor(boughtQty / requiredQty);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return fulfillments.length > 0 ? Math.min(...fulfillments) : 0;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const getTargetProductIdSet = (products: any[] = []): Set<string> => {
|
|
78
|
+
return new Set(
|
|
79
|
+
(Array.isArray(products) ? products : [])
|
|
80
|
+
.map((item: any) => getDiscountTargetIdentity(item))
|
|
81
|
+
.filter((id) => id.length > 0)
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
6
85
|
export const getProductTaxesPercentage = (productObj: IOrderProduct, store: IStore): number => {
|
|
7
86
|
const getTaxValue = (tax: ITaxConfig | null | undefined, isTaxExempt: boolean): number => {
|
|
8
87
|
if (!tax) {
|
|
@@ -95,12 +174,7 @@ export const applyDiscountToProducts = (
|
|
|
95
174
|
newOrderProds: productsArr.map((prod: any) => {
|
|
96
175
|
const discountsToApply = storeDiscounts.filter(
|
|
97
176
|
(discount: any) => discount.isActive && discount.products.some((p: any) =>{
|
|
98
|
-
|
|
99
|
-
return p === prod._id
|
|
100
|
-
} else if(typeof p === 'object') {
|
|
101
|
-
return p._id === prod._id
|
|
102
|
-
}
|
|
103
|
-
return false
|
|
177
|
+
return matchesDiscountTarget(prod, p);
|
|
104
178
|
})
|
|
105
179
|
);
|
|
106
180
|
const customerDiscount = selectedCustomer?.discount / 100 || 0;
|
|
@@ -135,16 +209,15 @@ export const applyDiscountToProducts = (
|
|
|
135
209
|
return { ...prod, discountAmount: discountAmount };
|
|
136
210
|
});
|
|
137
211
|
} else {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
const prodPrice = isExpress ? prodObj.expressPrice : prodObj.price;
|
|
143
|
-
const discountAmount = +(percentageDiscount * prodPrice).toFixed(2);
|
|
144
|
-
prodObj.discountAmount = discountAmount;
|
|
145
|
-
newOrderProds[prodIdx] = prodObj;
|
|
212
|
+
const discountProductIds = getTargetProductIdSet(discountCode.products);
|
|
213
|
+
newOrderProds = newOrderProds.map((prod: any) => {
|
|
214
|
+
if (!discountProductIds.has(getProductIdentity(prod))) {
|
|
215
|
+
return prod;
|
|
146
216
|
}
|
|
147
|
-
|
|
217
|
+
const prodPrice = isExpress ? prod.expressPrice : prod.price;
|
|
218
|
+
const discountAmount = +(percentageDiscount * prodPrice).toFixed(2);
|
|
219
|
+
return { ...prod, discountAmount };
|
|
220
|
+
});
|
|
148
221
|
}
|
|
149
222
|
}
|
|
150
223
|
|
|
@@ -154,14 +227,13 @@ export const applyDiscountToProducts = (
|
|
|
154
227
|
if (discountCode.applyToAllProducts) {
|
|
155
228
|
newOrderProds = newOrderProds.map((prod: any) => ({ ...prod, discountAmount: discountAmount }));
|
|
156
229
|
} else {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
prodObj.discountAmount = discountAmount;
|
|
162
|
-
newOrderProds[prodIdx] = prodObj;
|
|
230
|
+
const discountProductIds = getTargetProductIdSet(discountCode.products);
|
|
231
|
+
newOrderProds = newOrderProds.map((prod: any) => {
|
|
232
|
+
if (!discountProductIds.has(getProductIdentity(prod))) {
|
|
233
|
+
return prod;
|
|
163
234
|
}
|
|
164
|
-
|
|
235
|
+
return { ...prod, discountAmount };
|
|
236
|
+
});
|
|
165
237
|
}
|
|
166
238
|
}
|
|
167
239
|
}
|
|
@@ -171,16 +243,7 @@ export const applyDiscountToProducts = (
|
|
|
171
243
|
const getConditions = discountCode.buyAndGetConditions[0].getConditions;
|
|
172
244
|
const discountType = discountCode.buyAndGetConditions[0].getDiscountType;
|
|
173
245
|
const discountValue = discountCode.buyAndGetConditions[0].discountValue;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
for (let prodCondition of buyConditions) {
|
|
177
|
-
const orderProd = productsArr.find((ordProd: any) => ordProd._id === prodCondition.buyProduct._id);
|
|
178
|
-
// Nota: Aquí solo se verifica una condición de producto
|
|
179
|
-
if (orderProd) {
|
|
180
|
-
let qty = orderProd.qty || orderProd.quantity;
|
|
181
|
-
buyConditionFulfilledCounter = Math.floor(qty / prodCondition.qty);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
246
|
+
const buyConditionFulfilledCounter = getFulfillmentCount(buyConditions, productsArr);
|
|
184
247
|
|
|
185
248
|
if (buyConditionFulfilledCounter) {
|
|
186
249
|
for (let prodCondition of getConditions) {
|