washday-sdk 1.0.2 → 1.1.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.
Files changed (254) hide show
  1. package/README.md +126 -0
  2. package/babel.config.js +8 -0
  3. package/dist/api/attendance/delete.js +2 -0
  4. package/dist/api/attendance/get.js +83 -0
  5. package/dist/api/attendance/index.js +4 -0
  6. package/dist/api/attendance/post.js +39 -0
  7. package/dist/api/attendance/put.js +25 -0
  8. package/dist/api/auth/index.js +3 -0
  9. package/dist/api/auth/post.js +170 -0
  10. package/dist/api/axiosInstance.js +15 -11
  11. package/dist/api/cashierbox/delete.js +40 -0
  12. package/dist/api/cashierbox/get.js +63 -0
  13. package/dist/api/cashierbox/post.js +42 -0
  14. package/dist/api/cashierbox/put.js +42 -0
  15. package/dist/api/cashups/delete.js +25 -0
  16. package/dist/api/cashups/get.js +60 -0
  17. package/dist/api/cashups/index.js +4 -0
  18. package/dist/api/cashups/post.js +25 -0
  19. package/dist/api/cashups/put.js +52 -0
  20. package/dist/api/cfdi/delete.js +25 -0
  21. package/dist/api/cfdi/get.js +126 -0
  22. package/dist/api/cfdi/index.js +4 -0
  23. package/dist/api/cfdi/post.js +41 -0
  24. package/dist/api/cfdi/put.js +52 -0
  25. package/dist/api/companies/get.js +29 -0
  26. package/dist/api/companies/post.js +41 -0
  27. package/dist/api/companies/put.js +57 -0
  28. package/dist/api/countries/get.js +29 -0
  29. package/dist/api/countries/post.js +1 -0
  30. package/dist/api/countries/put.js +1 -0
  31. package/dist/api/csv/get.js +325 -0
  32. package/dist/api/csv/index.js +1 -0
  33. package/dist/api/customers/delete.js +40 -0
  34. package/dist/api/customers/get.js +75 -20
  35. package/dist/api/customers/index.js +4 -1
  36. package/dist/api/customers/post.js +41 -0
  37. package/dist/api/customers/put.js +52 -0
  38. package/dist/api/discounts/get.js +120 -0
  39. package/dist/api/discounts/post.js +40 -0
  40. package/dist/api/discounts/put.js +68 -0
  41. package/dist/api/index.js +316 -8
  42. package/dist/api/inventory/delete.js +26 -0
  43. package/dist/api/inventory/get.js +62 -0
  44. package/dist/api/inventory/index.js +4 -0
  45. package/dist/api/inventory/post.js +26 -0
  46. package/dist/api/inventory/put.js +41 -0
  47. package/dist/api/order/delete.js +40 -0
  48. package/dist/api/order/get.js +181 -0
  49. package/dist/api/order/index.js +4 -0
  50. package/dist/api/order/post.js +115 -0
  51. package/dist/api/order/put.js +202 -0
  52. package/dist/api/outsourcedOrders/delete.js +25 -0
  53. package/dist/api/outsourcedOrders/get.js +62 -0
  54. package/dist/api/outsourcedOrders/index.js +4 -0
  55. package/dist/api/outsourcedOrders/post.js +25 -0
  56. package/dist/api/outsourcedOrders/put.js +25 -0
  57. package/dist/api/partners/delete.js +25 -0
  58. package/dist/api/partners/get.js +40 -0
  59. package/dist/api/partners/index.js +4 -0
  60. package/dist/api/partners/post.js +25 -0
  61. package/dist/api/partners/put.js +25 -0
  62. package/dist/api/pdf/get.js +43 -0
  63. package/dist/api/pdf/index.js +1 -0
  64. package/dist/api/products/delete.js +40 -0
  65. package/dist/api/products/get.js +25 -0
  66. package/dist/api/products/index.js +4 -0
  67. package/dist/api/products/post.js +60 -0
  68. package/dist/api/products/put.js +40 -0
  69. package/dist/api/publics/get.js +26 -0
  70. package/dist/api/publics/index.js +1 -0
  71. package/dist/api/reports/get.js +303 -0
  72. package/dist/api/reports/index.js +1 -0
  73. package/dist/api/reviews/delete.js +15 -0
  74. package/dist/api/reviews/get.js +41 -0
  75. package/dist/api/reviews/index.js +4 -0
  76. package/dist/api/reviews/post.js +20 -0
  77. package/dist/api/reviews/put.js +52 -0
  78. package/dist/api/routes/delete.js +25 -0
  79. package/dist/api/routes/get.js +95 -0
  80. package/dist/api/routes/index.js +4 -0
  81. package/dist/api/routes/post.js +67 -0
  82. package/dist/api/routes/put.js +54 -0
  83. package/dist/api/sections/delete.js +25 -0
  84. package/dist/api/sections/get.js +71 -0
  85. package/dist/api/sections/index.js +4 -0
  86. package/dist/api/sections/post.js +26 -0
  87. package/dist/api/sections/put.js +25 -0
  88. package/dist/api/staff/delete.js +26 -0
  89. package/dist/api/staff/get.js +40 -0
  90. package/dist/api/staff/post.js +26 -0
  91. package/dist/api/staff/put.js +26 -0
  92. package/dist/api/stores/get.js +110 -0
  93. package/dist/api/stores/post.js +61 -0
  94. package/dist/api/stores/put.js +44 -0
  95. package/dist/api/stripe/get.js +1 -0
  96. package/dist/api/stripe/post.js +71 -0
  97. package/dist/api/stripe/put.js +1 -0
  98. package/dist/api/supplies/delete.js +26 -0
  99. package/dist/api/supplies/get.js +62 -0
  100. package/dist/api/supplies/post.js +26 -0
  101. package/dist/api/supplies/put.js +41 -0
  102. package/dist/api/users/delete.js +26 -0
  103. package/dist/api/users/post.js +25 -0
  104. package/dist/api/users/put.js +29 -0
  105. package/dist/enum/index.js +12 -7
  106. package/dist/index.js +3 -33
  107. package/dist/interfaces/Api.js +1 -3
  108. package/dist/interfaces/Apple.js +16 -0
  109. package/dist/interfaces/Attendance.js +1 -0
  110. package/dist/interfaces/Customer.js +1 -2
  111. package/dist/interfaces/Order.js +1 -2
  112. package/dist/interfaces/Permission.js +1 -2
  113. package/dist/interfaces/Product.js +1 -2
  114. package/dist/interfaces/Section.js +1 -2
  115. package/dist/interfaces/Store.js +1 -2
  116. package/dist/interfaces/StoreImage.js +1 -2
  117. package/dist/interfaces/User.js +1 -2
  118. package/dist/utils/apiUtils.js +9 -0
  119. package/dist/utils/index.js +2 -17
  120. package/dist/utils/orders/calculateOrderTotal.js +30 -21
  121. package/dist/utils/orders/calculateTotalTaxesIncluded.js +55 -29
  122. package/dist/utils/orders/calculateTotalTaxesOverPrice.js +56 -45
  123. package/dist/utils/orders/helpers.js +126 -17
  124. package/dist/utils/orders/index.js +3 -5
  125. package/dist/utils/receipt/generateReceiptHTML.js +157 -0
  126. package/dist/utils/util.js +63 -0
  127. package/docs/README.md +66 -0
  128. package/docs/examples/common-use-cases.md +487 -0
  129. package/docs/getting-started.md +237 -0
  130. package/docs/modules/attendance.md +404 -0
  131. package/jest.config.js +0 -0
  132. package/package.json +12 -4
  133. package/src/api/attendance/delete.ts +1 -0
  134. package/src/api/attendance/get.ts +81 -0
  135. package/src/api/attendance/index.ts +4 -0
  136. package/src/api/attendance/post.ts +37 -0
  137. package/src/api/attendance/put.ts +20 -0
  138. package/src/api/auth/index.ts +3 -0
  139. package/src/api/auth/post.ts +198 -0
  140. package/src/api/axiosInstance.ts +13 -3
  141. package/src/api/cashierbox/delete.ts +28 -0
  142. package/src/api/cashierbox/get.ts +53 -0
  143. package/src/api/cashierbox/post.ts +39 -0
  144. package/src/api/cashierbox/put.ts +32 -0
  145. package/src/api/cashups/delete.ts +15 -0
  146. package/src/api/cashups/get.ts +52 -0
  147. package/src/api/cashups/index.ts +4 -0
  148. package/src/api/cashups/post.ts +23 -0
  149. package/src/api/cashups/put.ts +53 -0
  150. package/src/api/cfdi/delete.ts +21 -0
  151. package/src/api/cfdi/get.ts +119 -0
  152. package/src/api/cfdi/index.ts +4 -0
  153. package/src/api/cfdi/post.ts +60 -0
  154. package/src/api/cfdi/put.ts +53 -0
  155. package/src/api/companies/get.ts +23 -0
  156. package/src/api/companies/post.ts +29 -0
  157. package/src/api/companies/put.ts +43 -0
  158. package/src/api/countries/get.ts +23 -0
  159. package/src/api/countries/post.ts +0 -0
  160. package/src/api/countries/put.ts +0 -0
  161. package/src/api/csv/get.ts +354 -0
  162. package/src/api/csv/index.ts +1 -0
  163. package/src/api/customers/delete.ts +29 -0
  164. package/src/api/customers/get.ts +80 -8
  165. package/src/api/customers/index.ts +4 -0
  166. package/src/api/customers/post.ts +48 -0
  167. package/src/api/customers/put.ts +68 -0
  168. package/src/api/discounts/get.ts +100 -0
  169. package/src/api/discounts/post.ts +35 -0
  170. package/src/api/discounts/put.ts +66 -0
  171. package/src/api/index.ts +318 -11
  172. package/src/api/inventory/delete.ts +16 -0
  173. package/src/api/inventory/get.ts +53 -0
  174. package/src/api/inventory/index.ts +4 -0
  175. package/src/api/inventory/post.ts +22 -0
  176. package/src/api/inventory/put.ts +35 -0
  177. package/src/api/order/delete.ts +29 -0
  178. package/src/api/order/get.ts +207 -0
  179. package/src/api/order/index.ts +4 -0
  180. package/src/api/order/post.ts +118 -0
  181. package/src/api/order/put.ts +224 -0
  182. package/src/api/outsourcedOrders/delete.ts +15 -0
  183. package/src/api/outsourcedOrders/get.ts +54 -0
  184. package/src/api/outsourcedOrders/index.ts +4 -0
  185. package/src/api/outsourcedOrders/post.ts +24 -0
  186. package/src/api/outsourcedOrders/put.ts +21 -0
  187. package/src/api/partners/delete.ts +15 -0
  188. package/src/api/partners/get.ts +28 -0
  189. package/src/api/partners/index.ts +4 -0
  190. package/src/api/partners/post.ts +21 -0
  191. package/src/api/partners/put.ts +21 -0
  192. package/src/api/pdf/get.ts +43 -0
  193. package/src/api/pdf/index.ts +1 -0
  194. package/src/api/products/delete.ts +28 -0
  195. package/src/api/products/get.ts +16 -0
  196. package/src/api/products/index.ts +4 -0
  197. package/src/api/products/post.ts +73 -0
  198. package/src/api/products/put.ts +49 -0
  199. package/src/api/publics/get.ts +16 -0
  200. package/src/api/publics/index.ts +1 -0
  201. package/src/api/reports/get.ts +329 -0
  202. package/src/api/reports/index.ts +1 -0
  203. package/src/api/reviews/delete.ts +15 -0
  204. package/src/api/reviews/get.ts +31 -0
  205. package/src/api/reviews/index.ts +4 -0
  206. package/src/api/reviews/post.ts +20 -0
  207. package/src/api/reviews/put.ts +53 -0
  208. package/src/api/routes/delete.ts +15 -0
  209. package/src/api/routes/get.ts +86 -0
  210. package/src/api/routes/index.ts +4 -0
  211. package/src/api/routes/post.ts +60 -0
  212. package/src/api/routes/put.ts +44 -0
  213. package/src/api/sections/delete.ts +15 -0
  214. package/src/api/sections/get.ts +67 -0
  215. package/src/api/sections/index.ts +4 -0
  216. package/src/api/sections/post.ts +22 -0
  217. package/src/api/sections/put.ts +23 -0
  218. package/src/api/staff/delete.ts +16 -0
  219. package/src/api/staff/get.ts +29 -0
  220. package/src/api/staff/post.ts +17 -0
  221. package/src/api/staff/put.ts +17 -0
  222. package/src/api/stores/get.ts +93 -0
  223. package/src/api/stores/post.ts +49 -0
  224. package/src/api/stores/put.ts +35 -0
  225. package/src/api/stripe/get.ts +0 -0
  226. package/src/api/stripe/post.ts +59 -0
  227. package/src/api/stripe/put.ts +0 -0
  228. package/src/api/supplies/delete.ts +16 -0
  229. package/src/api/supplies/get.ts +53 -0
  230. package/src/api/supplies/post.ts +26 -0
  231. package/src/api/supplies/put.ts +33 -0
  232. package/src/api/users/delete.ts +16 -0
  233. package/src/api/users/post.ts +18 -0
  234. package/src/api/users/put.ts +36 -0
  235. package/src/enum/index.ts +9 -1
  236. package/src/index.ts +1 -4
  237. package/src/interfaces/Api.ts +307 -2
  238. package/src/interfaces/Apple.ts +74 -0
  239. package/src/interfaces/Attendance.ts +45 -0
  240. package/src/interfaces/Customer.ts +15 -11
  241. package/src/interfaces/Order.ts +38 -1
  242. package/src/interfaces/Product.ts +1 -0
  243. package/src/interfaces/Store.ts +41 -0
  244. package/src/utils/apiUtils.ts +11 -0
  245. package/src/utils/index.ts +6 -1
  246. package/src/utils/orders/calculateOrderTotal.test.js +930 -0
  247. package/src/utils/orders/calculateOrderTotal.ts +60 -15
  248. package/src/utils/orders/calculateTotalTaxesIncluded.ts +57 -25
  249. package/src/utils/orders/calculateTotalTaxesOverPrice.ts +57 -41
  250. package/src/utils/orders/helpers.ts +195 -47
  251. package/src/utils/orders/index.ts +3 -1
  252. package/src/utils/receipt/generateReceiptHTML.ts +163 -0
  253. package/src/utils/util.ts +65 -0
  254. package/tsconfig.json +13 -9
@@ -11,36 +11,79 @@ export const calculateOrderTotal = (
11
11
  storeSettings: IStore,
12
12
  hasShippingCost: boolean,
13
13
  storeDiscounts: any[] = [],
14
- discountCodeObj: any
14
+ discountCodeObj: any,
15
+ redeemPointsDiscount: number = 0 // 💡 NUEVO parámetro
15
16
  ): any => {
16
17
  try {
17
18
  const appliedOrderDiscounts: any = {};
18
- const productTableCalculator = storeSettings?.taxesType === 'over_price' ? calculateTotalTaxesOverPrice : calculateTotalTaxesIncluded;
19
- const productTableImports = productTableCalculator(order, selectedCustomer, storeSettings, storeDiscounts, appliedOrderDiscounts, discountCodeObj);
20
19
 
21
- // PRODUCT LINE TOTALS
20
+ const productTableCalculator =
21
+ storeSettings?.taxesType === 'over_price'
22
+ ? calculateTotalTaxesOverPrice
23
+ : calculateTotalTaxesIncluded;
24
+
25
+ const productTableImports = productTableCalculator(
26
+ order,
27
+ selectedCustomer,
28
+ storeSettings,
29
+ storeDiscounts,
30
+ appliedOrderDiscounts,
31
+ discountCodeObj
32
+ );
33
+
34
+ // === PRODUCT LINE TOTALS ===
22
35
  const {
23
36
  totalQuantity,
24
37
  totalImportWithDiscount,
25
38
  totalImportWithoutDiscount,
26
39
  totalImporTaxes,
27
- totalDiscountAmount
40
+ totalDiscountAmount,
41
+ totalSubtotalAmount
28
42
  } = getProductLineTotals(productTableImports);
29
43
 
30
- // DISCOUNT TOTAL AND SHIPPING SERVICE COST
44
+ // === DISCOUNT CODE (monetario tipo NUMBER que se aplica una sola vez) ===
31
45
  let discountCodeAmount = 0;
32
- let shippingCost = getShippingCost(discountCodeObj, hasShippingCost, storeSettings);
46
+ if (
47
+ discountCodeObj &&
48
+ discountCodeObj.type === DiscountCodeTypes.NUMBER &&
49
+ discountCodeObj.applyOnceOnOrder
50
+ ) {
51
+ const includesProducts =
52
+ discountCodeObj.applyToAllProducts ||
53
+ order.products.some((curr: any) =>
54
+ discountCodeObj.products.includes(curr._id)
55
+ );
33
56
 
34
- if (discountCodeObj && discountCodeObj.type === DiscountCodeTypes.NUMBER && discountCodeObj.applyOnceOnOrder) {
35
- const includesProducts = discountCodeObj.applyToAllProducts || order.products.some((curr: any) => discountCodeObj.products.includes(curr._id));
36
57
  discountCodeAmount = includesProducts ? discountCodeObj.value : 0;
37
58
  }
38
59
 
39
- // ORDER TOTAL CALCULATION AND CREDIT APPLIED
40
- let orderTotalWithOutCredit = +(totalImportWithDiscount + totalImporTaxes + shippingCost - discountCodeAmount).toFixed(2);
41
- let creditApplied = getCreditApplied(selectedCustomer, orderTotalWithOutCredit);
42
- let orderTotal = +(orderTotalWithOutCredit - creditApplied).toFixed(2);
60
+ // === SHIPPING COST ===
61
+ const shippingCost = getShippingCost(
62
+ discountCodeObj,
63
+ hasShippingCost,
64
+ storeSettings
65
+ );
66
+
67
+ // === TOTAL ANTES DE CRÉDITOS/PUNTOS ===
68
+ const orderTotalWithOutCredit = +(
69
+ totalImportWithDiscount +
70
+ shippingCost -
71
+ discountCodeAmount
72
+ ).toFixed(2);
73
+
74
+ // === APLICAR CRÉDITO Y PUNTOS REDIMIDOS ===
75
+ const creditApplied = getCreditApplied(
76
+ selectedCustomer,
77
+ orderTotalWithOutCredit
78
+ );
79
+
80
+ const orderTotal = +(
81
+ orderTotalWithOutCredit -
82
+ creditApplied -
83
+ redeemPointsDiscount
84
+ ).toFixed(2);
43
85
 
86
+ // === RETURN FINAL OBJECT ===
44
87
  return {
45
88
  ...order,
46
89
  totalQuantity,
@@ -49,12 +92,14 @@ export const calculateOrderTotal = (
49
92
  taxesTotal: totalImporTaxes,
50
93
  total: orderTotal,
51
94
  creditApplied,
95
+ redeemPointsApplied: redeemPointsDiscount, // ✅ NUEVO CAMPO
52
96
  productTotalWithoutDiscount: totalImportWithoutDiscount,
53
97
  totalDiscountAmount: totalDiscountAmount + discountCodeAmount,
54
98
  shippingServiceTotal: shippingCost,
55
- appliedOrderDiscounts
99
+ appliedOrderDiscounts,
100
+ subtotal: totalSubtotalAmount
56
101
  };
57
102
  } catch (error) {
58
103
  throw error;
59
104
  }
60
- };
105
+ };
@@ -1,3 +1,4 @@
1
+ import { DiscountCodeTypes } from "../../enum";
1
2
  import { IOrderProduct } from "../../interfaces/Product";
2
3
  import { getProductTaxesPercentage } from "./helpers";
3
4
 
@@ -8,25 +9,39 @@ export const calculateTotalTaxesIncluded = (
8
9
  storeDiscounts: any[],
9
10
  appliedOrderDiscounts: any,
10
11
  discountCodeObj: any | null
11
- ) => {
12
+ ): any => {
12
13
  const productTableImports = [...order.products, ...order.buyAndGetProducts].map((current: IOrderProduct) => {
14
+ const qty = current.qty || 0;
15
+ const taxPercentage = getProductTaxesPercentage(current, storeSettings);
16
+ const taxFactor = 1 + taxPercentage / 100;
17
+
18
+ // Convertir extraAmount de bruto a neto
19
+ const extraAmountGross = current.extraAmount || 0;
20
+ const extraAmountNet = extraAmountGross / taxFactor;
21
+
22
+ // Precio bruto del producto (sin extraAmount)
23
+ const productGrossPrice = order.express ? current.expressPrice : current.price;
24
+ // Convertir precio bruto a neto
25
+ const unitNetPrice = productGrossPrice / taxFactor;
26
+
27
+ // Determinar el porcentaje de descuento (si aplica)
13
28
  let discPercentageInteger = 0;
14
29
  let productPercentageDiscount = 0;
15
30
  let customerDiscount = 0;
16
- let qty = current.qty || 0;
17
- const extraAmountPerUnit = current.extraAmount / qty;
18
- const prodPrice = (order.express ? current.expressPrice : current.price) + extraAmountPerUnit;
19
-
20
31
  if (!order.discountCode) {
21
32
  const discountsToApply = storeDiscounts.filter(
22
- (discount) => discount.products.includes(current._id) && discount.isActive
33
+ (discount: any) => discount.isActive && discount.products.some((p: any) => {
34
+ if (typeof p === 'string') {
35
+ return p === current._id
36
+ } else if (typeof p === 'object') {
37
+ return p._id === current._id
38
+ }
39
+ return false
40
+ })
23
41
  );
24
42
  customerDiscount = selectedCustomer?.customer?.discount || 0;
25
43
  productPercentageDiscount = discountsToApply.reduce((prev, next) => {
26
- if (next.type === 'percentage') {
27
- return prev + next.value;
28
- }
29
- return prev;
44
+ return next.type === 'percentage' ? prev + next.value : prev;
30
45
  }, 0);
31
46
  discPercentageInteger = +((productPercentageDiscount + customerDiscount) / 100).toFixed(2);
32
47
  discountsToApply.forEach((discount) => (appliedOrderDiscounts[discount._id] = discount));
@@ -45,30 +60,47 @@ export const calculateTotalTaxesIncluded = (
45
60
  if (discountType === 'percentage' && current.isBuyAndGetProduct) {
46
61
  discPercentageInteger = +(discountValue / 100).toFixed(2);
47
62
  }
63
+ if (discountType === 'free' && current.isBuyAndGetProduct) {
64
+ discPercentageInteger = 1;
65
+ }
66
+ } else if (discountCodeObj.type === DiscountCodeTypes.FREE_ITEM && ((current as any).isFreeItem)) {
67
+ discPercentageInteger = 1;
48
68
  }
49
69
  }
50
70
 
51
- const taxPercentage = getProductTaxesPercentage(current, storeSettings);
52
- const taxPercentageInteger = taxPercentage / 100;
71
+ // === NUEVO: Aplicar descuento sobre el precio total ajustado (producto base + extra prorrateado) ===
72
+ // Si hay cantidad, repartir el extra neto entre cada unidad
73
+ const unitExtraNet = qty > 0 ? extraAmountNet / qty : 0;
74
+ // Precio neto ajustado: precio base + extra (por unidad)
75
+ const adjustedUnitNetPrice = unitNetPrice + unitExtraNet;
76
+ // Calcular el descuento sobre el precio ajustado
77
+ const discountAmountPerUnit = discPercentageInteger ? adjustedUnitNetPrice * discPercentageInteger : 0;
78
+ // Precio neto con descuento aplicado por unidad
79
+ const discountedUnitNetPrice = adjustedUnitNetPrice - discountAmountPerUnit;
80
+
81
+ // === Totales de la línea ===
82
+ // Total neto sin descuento: precio base total + extra (ya prorrateado en el total)
83
+ const productNetTotalWithoutDiscount = (unitNetPrice * qty) + extraAmountNet;
84
+ // Total neto con descuento: se aplica el descuento sobre el precio ajustado por unidad
85
+ const productNetTotalWithDiscount = discountedUnitNetPrice * qty;
86
+
87
+ // Reaplicar IVA a ambos totales
88
+ const productLineImportTotal = +(productNetTotalWithoutDiscount * taxFactor).toFixed(2);
89
+ const productLineTotalWithDiscount = +(productNetTotalWithDiscount * taxFactor).toFixed(2);
53
90
 
54
- const unitPrice = prodPrice / (1 + taxPercentageInteger);
55
- const discountAmountPerUnit = discPercentageInteger
56
- ? unitPrice * discPercentageInteger
57
- : (current.discountAmount || 0) / (1 + taxPercentageInteger);
91
+ // Calcular el total de impuestos como la diferencia entre bruto y neto con descuento
92
+ const totalTaxesApplied = +(productLineTotalWithDiscount - productNetTotalWithDiscount).toFixed(2);
58
93
 
59
- const prodPriceWithDiscountPerUnit = unitPrice - discountAmountPerUnit;
60
- const taxesAmountPerUnit = prodPriceWithDiscountPerUnit * taxPercentageInteger;
61
- const totalTaxesApplied = +(taxesAmountPerUnit * qty).toFixed(2);
62
- const lineDiscount = qty * discountAmountPerUnit;
63
- const prodLinePriceWithDiscount = +(qty * prodPriceWithDiscountPerUnit).toFixed(2);
94
+ const lineDiscount = +(discountAmountPerUnit * qty).toFixed(2);
64
95
 
65
96
  return {
66
97
  product: current,
67
- qty: qty,
68
- productLineImportTotal: unitPrice * qty,
69
- productLineTotalWithDiscount: prodLinePriceWithDiscount,
98
+ qty,
99
+ productLineImportTotal, // Total bruto sin descuento (incluye extra y IVA)
100
+ productLineTotalWithDiscount, // Total bruto con descuento aplicado sobre el precio ajustado
70
101
  productLineTaxesTotal: totalTaxesApplied,
71
- lineDiscountAmount: lineDiscount
102
+ lineDiscountAmount: lineDiscount,
103
+ productLineSubtotal: productNetTotalWithoutDiscount // Subtotal neto (sin IVA)
72
104
  };
73
105
  });
74
106
  return productTableImports;
@@ -8,35 +8,41 @@ export const calculateTotalTaxesOverPrice = (
8
8
  storeDiscounts: any[],
9
9
  appliedOrderDiscounts: any,
10
10
  discountCodeObj: any | null
11
-
12
-
13
11
  ) => {
14
12
  const productTableImports = [...order.products, ...order.buyAndGetProducts].map((current) => {
15
13
  let discPercentageInteger = 0;
16
14
  let productPercentageDiscount = 0;
17
15
  let customerDiscount = 0;
18
- let qty = current.qty;
19
- // extraAmount es en general sin importar el qty, entonces aqui lo repartimos por igual
20
- const extraAmountPerUnit = current.extraAmount / qty;
21
- const prodPrice = (order.express ? current.expressPrice : current.price) + extraAmountPerUnit;
16
+ const qty = current.qty || current.quantity; // cantidad
17
+
18
+ // Precio base sin impuestos (en modo over_price el precio es neto)
19
+ const productBasePrice = order.express ? current.expressPrice : current.price;
22
20
 
21
+ // === NUEVO: Considerar extraAmount en el precio a descontar ===
22
+ // extraAmount es el ajuste total para la línea; se reparte por unidad
23
+ const extraAmount = current.extraAmount || 0;
24
+ const unitExtra = qty > 0 ? extraAmount / qty : 0;
25
+ // Precio ajustado: suma del precio base y el extra por unidad
26
+ const adjustedPrice = productBasePrice + unitExtra;
27
+
28
+ // Cálculo de descuento sobre el precio ajustado
23
29
  if (!order.discountCode) {
24
- //IF ORDER HAS A DISCOUNT CODE WE DON'T APPLY ANY OTHER DISCOUNT (AUTOMATICALLY OR CUSTOMER DISCOUNT)
25
- //THIS WILL GET THE DISCOUNTS OF THE CURRENT PRODUCT
26
- // get store discounts if product applies and customer discount
27
30
  const discountsToApply = storeDiscounts.filter(
28
- (discount) => discount.products.includes(current._id) && discount.isActive
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
+ })
29
39
  );
30
40
  customerDiscount = selectedCustomer?.customer?.discount || 0;
31
- productPercentageDiscount = discountsToApply.reduce((prev, next) => {
32
- if (next.type === 'percentage') {
33
- // for now we just sum percetange type discounts
34
- return prev + next.value;
35
- }
36
- return prev;
41
+ productPercentageDiscount = discountsToApply.reduce((acc, discount) => {
42
+ return discount.type === 'percentage' ? acc + discount.value : acc;
37
43
  }, 0);
38
44
  discPercentageInteger = +((productPercentageDiscount + customerDiscount) / 100).toFixed(2);
39
- discountsToApply.forEach((discount) => (appliedOrderDiscounts[discount._id] = discount)); // it will do this for every product, if two products has the same discount it will just overwrite
45
+ discountsToApply.forEach((discount) => (appliedOrderDiscounts[discount._id] = discount));
40
46
  } else {
41
47
  if (discountCodeObj.type === DiscountCodeTypes.PERCENTAGE) {
42
48
  discPercentageInteger = +(discountCodeObj.value / 100).toFixed(2);
@@ -45,39 +51,49 @@ export const calculateTotalTaxesOverPrice = (
45
51
  ? +(discountCodeObj.value / 100).toFixed(2)
46
52
  : 0;
47
53
  }
48
- }
49
- if (discountCodeObj.type === DiscountCodeTypes.BUY_X_GET_Y) {
50
- const discountType = discountCodeObj.buyAndGetConditions[0].getDiscountType;
51
- const discountValue = discountCodeObj.buyAndGetConditions[0].discountValue;
52
- if (discountType === BuyAndGetConditionsTypes.PERCENTAGE && current.isBuyAndGetProduct) {
53
- discPercentageInteger = +(discountValue / 100).toFixed(2);
54
+ } else if (discountCodeObj.type === DiscountCodeTypes.BUY_X_GET_Y) {
55
+ const condition = discountCodeObj.buyAndGetConditions[0];
56
+ if (condition.getDiscountType === BuyAndGetConditionsTypes.PERCENTAGE && current.isBuyAndGetProduct) {
57
+ discPercentageInteger = +(condition.discountValue / 100).toFixed(2);
54
58
  }
59
+ if (condition.getDiscountType === BuyAndGetConditionsTypes.FREE && current.isBuyAndGetProduct) {
60
+ discPercentageInteger = 1;
61
+ }
62
+ } else if (discountCodeObj.type === DiscountCodeTypes.FREE_ITEM && current.isFreeItem) {
63
+ discPercentageInteger = 1;
55
64
  }
56
65
  }
57
66
 
58
- //GET DISCOUNT AMOUNT (IN CASE discPercentageInteger is 0 it could be a discount of a integer number)
59
- const discountAmountPerUnit = discPercentageInteger
60
- ? prodPrice * discPercentageInteger
61
- : (current.discountAmount || 0);
62
- //GET PRODUCT PRICE WITH DISCOUNT
63
- const prodPriceWithDiscountPerUnit = prodPrice - discountAmountPerUnit;
64
- //GET PRODUCT TAXES APPLIED TO FINAL PRICE PER UNIT
65
- const taxPercentage = getProductTaxesPercentage(current, storeSettings);
66
- const taxPercentageInteger = taxPercentage / 100;
67
- const prodPriceWithoutTaxesPerUnit = prodPriceWithDiscountPerUnit;
68
- const taxesAmountPerUnit = prodPriceWithoutTaxesPerUnit * taxPercentageInteger;
69
- const totalTaxesApplied = +(taxesAmountPerUnit * qty).toFixed(2);
70
- let lineDiscount = qty * discountAmountPerUnit;
71
- let prodLinePriceWithDiscount = +(qty * prodPriceWithDiscountPerUnit).toFixed(2);
67
+ // Calcular el descuento por unidad sobre el precio ajustado
68
+ const productDiscount = adjustedPrice * discPercentageInteger;
69
+ const discountedAdjustedPrice = adjustedPrice - productDiscount;
70
+
71
+ // Subtotal para la línea (precio descontado por unidad * cantidad) SIN impuestos
72
+ const subtotal = discountedAdjustedPrice * qty;
73
+
74
+ // Calcular impuestos sobre el subtotal
75
+ const taxPercentage = getProductTaxesPercentage(current, storeSettings) / 100;
76
+ const taxes = subtotal * taxPercentage;
77
+
78
+ // Resultados finales:
79
+ const totalTaxesApplied = +taxes.toFixed(2);
80
+ const lineDiscount = +(productDiscount * qty).toFixed(2);
81
+
82
+ // Precio total con descuento, reaplicando impuestos
83
+ const prodLinePriceWithDiscount = +((subtotal * (1 + taxPercentage))).toFixed(2);
72
84
 
73
85
  return {
74
86
  product: current,
75
- qty: qty,
76
- productLineImportTotal: prodPrice * qty, //this is line without discount
87
+ qty,
88
+ // Total bruto original sin descuento: (precio base * cantidad) + extraAmount
89
+ productLineImportTotal: (productBasePrice * qty) + extraAmount,
90
+ // Total final con descuento, con impuestos incluidos
77
91
  productLineTotalWithDiscount: prodLinePriceWithDiscount,
78
92
  productLineTaxesTotal: totalTaxesApplied,
79
- lineDiscountAmount: lineDiscount
93
+ lineDiscountAmount: lineDiscount,
94
+ // Subtotal neto (sin impuestos) con descuento aplicado
95
+ productLineSubtotal: subtotal
80
96
  };
81
97
  });
82
98
  return productTableImports;
83
- };
99
+ };
@@ -1,73 +1,221 @@
1
- import { DiscountCodeTypes } from "../../enum";
1
+ import { BuyAndGetConditionsTypes, DiscountCodeTypes } from "../../enum";
2
2
  import { ICustomer } from "../../interfaces/Customer";
3
3
  import { IOrderProduct, ProductLineTotals } from "../../interfaces/Product";
4
4
  import { IStore, ITaxConfig } from "../../interfaces/Store";
5
5
 
6
6
  export const getProductTaxesPercentage = (productObj: IOrderProduct, store: IStore): number => {
7
- const getTaxValue = (tax: ITaxConfig | null | undefined, isTaxExempt: boolean): number => {
8
- if (!tax) {
9
- // If no store tax configured, always return 0
10
- return 0;
11
- }
7
+ const getTaxValue = (tax: ITaxConfig | null | undefined, isTaxExempt: boolean): number => {
8
+ if (!tax) {
9
+ // If no store tax configured, always return 0
10
+ return 0;
11
+ }
12
12
 
13
- if (isTaxExempt) {
14
- // If store tax is configured and taxExempt is true, return 0
15
- return 0;
16
- }
13
+ if (isTaxExempt) {
14
+ // If store tax is configured and taxExempt is true, return 0
15
+ return 0;
16
+ }
17
17
 
18
- // If store tax configured and taxExempt is false, return store tax
19
- return tax.value;
20
- };
18
+ // If store tax configured and taxExempt is false, return store tax
19
+ return tax.value;
20
+ };
21
21
 
22
- const tax1 = getTaxValue(store.taxOne, !productObj.taxExemptOne);
23
- const tax2 = getTaxValue(store.taxTwo, !productObj.taxExemptTwo);
24
- const tax3 = getTaxValue(store.taxThree, !productObj.taxExemptThree);
22
+ const tax1 = getTaxValue(store.taxOne, productObj.taxExemptOne);
23
+ const tax2 = getTaxValue(store.taxTwo, productObj.taxExemptTwo);
24
+ const tax3 = getTaxValue(store.taxThree, productObj.taxExemptThree);
25
25
 
26
- return tax1 + tax2 + tax3;
26
+ return tax1 + tax2 + tax3;
27
27
  };
28
28
 
29
29
  export const getProductLineTotals = (productLinesTotals: ProductLineTotals[]) => {
30
- const totalQuantity = +productLinesTotals.reduce((acum, line) => acum + line.qty, 0).toFixed(2);
31
- const totalImportWithDiscount = +productLinesTotals
32
- .reduce((acum, line) => acum + line.productLineTotalWithDiscount, 0)
33
- .toFixed(2);
34
- const totalImportWithoutDiscount = +productLinesTotals
35
- .reduce((acum, line) => acum + line.productLineImportTotal, 0)
36
- .toFixed(2);
37
- const totalImporTaxes = +productLinesTotals
38
- .reduce((acum, line) => acum + line.productLineTaxesTotal, 0)
39
- .toFixed(2);
40
- const totalDiscountAmount = +productLinesTotals
41
- .reduce((acum, line) => acum + line.lineDiscountAmount, 0)
42
- .toFixed(2);
30
+ const totalQuantity = +productLinesTotals.reduce((acum, line) => acum + line.qty, 0).toFixed(2);
31
+ const totalImportWithDiscount = +productLinesTotals
32
+ .reduce((acum, line) => acum + line.productLineTotalWithDiscount, 0)
33
+ .toFixed(2);
34
+ const totalImportWithoutDiscount = +productLinesTotals
35
+ .reduce((acum, line) => acum + line.productLineImportTotal, 0)
36
+ .toFixed(2);
37
+ const totalImporTaxes = +productLinesTotals
38
+ .reduce((acum, line) => acum + line.productLineTaxesTotal, 0)
39
+ .toFixed(2);
40
+ const totalDiscountAmount = +productLinesTotals
41
+ .reduce((acum, line) => acum + line.lineDiscountAmount, 0)
42
+ .toFixed(2);
43
+ const totalSubtotalAmount = +productLinesTotals
44
+ .reduce((acum, line) => acum + line.productLineSubtotal, 0)
45
+ .toFixed(2);
43
46
 
44
- return {
45
- totalQuantity,
46
- totalImportWithDiscount,
47
- totalImportWithoutDiscount,
48
- totalImporTaxes,
49
- totalDiscountAmount
50
- };
47
+ return {
48
+ totalQuantity,
49
+ totalImportWithDiscount,
50
+ totalImportWithoutDiscount,
51
+ totalImporTaxes,
52
+ totalDiscountAmount,
53
+ totalSubtotalAmount
54
+ };
51
55
  };
52
56
 
53
57
  export const getShippingCost = (discountObject: any, requireShippingService: boolean, store: IStore): number => {
54
- const shippingCost = requireShippingService ? store?.orderPageConfig?.shippingServiceCost ?? 0 : 0;
58
+ const shippingCost = requireShippingService ? store?.orderPageConfig?.shippingServiceCost ?? 0 : 0;
55
59
 
56
- if (discountObject?.type === DiscountCodeTypes.FREE_DELIVERY) {
57
- return 0;
58
- }
60
+ if (discountObject?.type === DiscountCodeTypes.FREE_DELIVERY) {
61
+ return 0;
62
+ }
59
63
 
60
- return shippingCost;
64
+ return shippingCost;
61
65
  };
62
66
 
63
67
  export const getCreditApplied = (selectedCustomer: { customer: ICustomer } | undefined, orderTotal: number): number => {
64
- const customerCredit = selectedCustomer?.customer?.credit ?? 0;
68
+ const customerCredit = selectedCustomer?.customer?.credit ?? 0;
65
69
 
66
- if (customerCredit === 0 || !selectedCustomer) {
67
- return 0;
68
- }
70
+ if (customerCredit === 0 || !selectedCustomer) {
71
+ return 0;
72
+ }
69
73
 
70
- return Math.min(customerCredit, orderTotal);
74
+ return Math.min(customerCredit, orderTotal);
71
75
  };
72
76
 
77
+ export const applyDiscountToProducts = (
78
+ discountCode: {
79
+ freeProductSetting: any;
80
+ products: any;
81
+ value: number;
82
+ type: string;
83
+ applyToAllProducts: boolean;
84
+ applyOnceOnOrder: boolean;
85
+ buyAndGetConditions: any,
86
+ },
87
+ productsArr: any,
88
+ isExpress: boolean,
89
+ storeDiscounts: any = [],
90
+ selectedCustomer = { discount: 0 }
91
+ ) => {
92
+ if (!discountCode) {
93
+ // Caso sin discountCode: se aplican descuentos automáticos solo sobre el precio del producto
94
+ return {
95
+ newOrderProds: productsArr.map((prod: any) => {
96
+ const discountsToApply = storeDiscounts.filter(
97
+ (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
104
+ })
105
+ );
106
+ const customerDiscount = selectedCustomer?.discount / 100 || 0;
107
+ const productPercentageDiscount = discountsToApply.reduce((prev: number, next: any) => {
108
+ if (next.type === 'percentage') {
109
+ return prev + next.value / 100;
110
+ }
111
+ return prev;
112
+ }, 0);
113
+ const prodPrice = isExpress ? prod.expressPrice : prod.price;
114
+ // Se remueve el extraAmountPerUnit para que el descuento se aplique solo sobre prodPrice
115
+ const discountAmount = +((productPercentageDiscount + customerDiscount) * prodPrice).toFixed(2);
116
+ return { ...prod, discountAmount: discountAmount };
117
+ }),
118
+ buyAndGetProds: []
119
+ };
120
+ }
121
+
122
+ // Reiniciar los descuentos en los productos
123
+ let newOrderProds = productsArr.map((prod: any) => ({ ...prod, discountAmount: 0 }));
73
124
 
125
+ // Array para productos de descuento BUY_X_GET_Y o FREE_ITEM
126
+ const buyAndGetProds = [];
127
+
128
+ if (discountCode && discountCode.type === DiscountCodeTypes.PERCENTAGE) {
129
+ const percentageDiscount = discountCode.value / 100;
130
+ if (discountCode.applyToAllProducts) {
131
+ newOrderProds = newOrderProds.map((prod: any) => {
132
+ const prodPrice = isExpress ? prod.expressPrice : prod.price;
133
+ // Aplica descuento solo sobre prodPrice (no se incluye extraAmountPerUnit)
134
+ const discountAmount = +(percentageDiscount * prodPrice).toFixed(2);
135
+ return { ...prod, discountAmount: discountAmount };
136
+ });
137
+ } 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;
146
+ }
147
+ }
148
+ }
149
+ }
150
+
151
+ if (discountCode && discountCode.type === DiscountCodeTypes.NUMBER) {
152
+ const discountAmount = discountCode.value;
153
+ if (!discountCode.applyOnceOnOrder) {
154
+ if (discountCode.applyToAllProducts) {
155
+ newOrderProds = newOrderProds.map((prod: any) => ({ ...prod, discountAmount: discountAmount }));
156
+ } 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;
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ if (discountCode.buyAndGetConditions && discountCode.type === DiscountCodeTypes.BUY_X_GET_Y) {
170
+ const buyConditions = discountCode.buyAndGetConditions[0].buyConditions;
171
+ const getConditions = discountCode.buyAndGetConditions[0].getConditions;
172
+ const discountType = discountCode.buyAndGetConditions[0].getDiscountType;
173
+ 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
+ }
184
+
185
+ if (buyConditionFulfilledCounter) {
186
+ for (let prodCondition of getConditions) {
187
+ // Se utiliza solo el precio del producto (sin extraAmount) para calcular el descuento
188
+ const prodPrice = isExpress
189
+ ? prodCondition.getProduct.expressPrice
190
+ : prodCondition.getProduct.price;
191
+ const percentageDiscount = discountValue / 100;
192
+ const discountAmount =
193
+ discountType === BuyAndGetConditionsTypes.FREE
194
+ ? prodPrice
195
+ : +(percentageDiscount * prodPrice).toFixed(2);
196
+ buyAndGetProds.push({
197
+ ...prodCondition.getProduct,
198
+ qty: buyConditionFulfilledCounter * prodCondition.qty,
199
+ quantity: buyConditionFulfilledCounter * prodCondition.qty,
200
+ discountAmount: discountAmount,
201
+ isBuyAndGetProduct: true
202
+ });
203
+ }
204
+ }
205
+ } else if (discountCode.type === DiscountCodeTypes.FREE_ITEM) {
206
+ const freeProductsSettinggs = discountCode.freeProductSetting[0];
207
+ const freeProduct = freeProductsSettinggs.product;
208
+ const prodPrice = isExpress ? freeProduct.expressPrice : freeProduct.price;
209
+ // Para free item, se asume que el producto es gratuito, es decir, se descuenta el precio del producto
210
+ const discountAmount = prodPrice;
211
+ buyAndGetProds.push({
212
+ ...freeProduct,
213
+ qty: freeProductsSettinggs.qty,
214
+ quantity: freeProductsSettinggs.qty,
215
+ discountAmount: discountAmount,
216
+ isBuyAndGetProduct: true,
217
+ isFreeItem: true
218
+ });
219
+ }
220
+ return { newOrderProds, buyAndGetProds };
221
+ };
@@ -1,5 +1,7 @@
1
1
  import { calculateOrderTotal } from './calculateOrderTotal';
2
+ import { applyDiscountToProducts } from './helpers';
2
3
 
3
4
  export {
4
- calculateOrderTotal
5
+ calculateOrderTotal,
6
+ applyDiscountToProducts
5
7
  }