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.
@@ -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.includes(curr._id));
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, _b;
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.some((p) => {
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 = ((_b = discountCodeObj.products) === null || _b === void 0 ? void 0 : _b.includes(current._id))
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.some((p) => {
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.includes(current._id)
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
- if (typeof p === 'string') {
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
- for (let discountProduct of discountCode.products) {
107
- const prodIdx = newOrderProds.findIndex((prod) => prod._id === discountProduct);
108
- if (prodIdx !== -1) {
109
- let prodObj = newOrderProds[prodIdx];
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
- for (let discountProduct of discountCode.products) {
126
- const prodIdx = newOrderProds.findIndex((prod) => prod._id === discountProduct);
127
- if (prodIdx !== -1) {
128
- let prodObj = newOrderProds[prodIdx];
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
- let buyConditionFulfilledCounter = 0;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "washday-sdk",
3
- "version": "1.6.23",
3
+ "version": "1.6.25",
4
4
  "description": "Washday utilities functions and API",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -8,7 +8,7 @@ export const create = async function (this: WashdayClientInstance, data: {
8
8
  name: string;
9
9
  price: number;
10
10
  expressPrice: number;
11
- type: 'normal' | 'weight';
11
+ type: 'normal' | 'weight' | 'length';
12
12
  pieces: number;
13
13
  sku?: string;
14
14
  overlayText: string;
@@ -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: String,
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
+ }
@@ -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.includes(curr._id)
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.some((p: any) => {
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?.includes(current._id)
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.some((p: any) => {
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.includes(current._id)
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
- if(typeof p === 'string') {
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
- for (let discountProduct of discountCode.products) {
139
- const prodIdx = newOrderProds.findIndex((prod: any) => prod._id === discountProduct);
140
- if (prodIdx !== -1) {
141
- let prodObj = newOrderProds[prodIdx];
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
- for (let discountProduct of discountCode.products) {
158
- const prodIdx = newOrderProds.findIndex((prod: any) => prod._id === discountProduct);
159
- if (prodIdx !== -1) {
160
- let prodObj = newOrderProds[prodIdx];
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
- let buyConditionFulfilledCounter = 0;
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) {