@stacksjs/commerce 0.70.55 → 0.70.57

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 (297) hide show
  1. package/package.json +10 -7
  2. package/src/carts/destroy.ts +0 -54
  3. package/src/carts/fetch.ts +0 -20
  4. package/src/carts/index.ts +0 -13
  5. package/src/carts/store.ts +0 -75
  6. package/src/carts/update.ts +0 -78
  7. package/src/coupons/destroy.ts +0 -49
  8. package/src/coupons/fetch.ts +0 -510
  9. package/src/coupons/index.ts +0 -30
  10. package/src/coupons/store.ts +0 -46
  11. package/src/coupons/update.ts +0 -147
  12. package/src/customers/destroy.ts +0 -62
  13. package/src/customers/fetch.ts +0 -20
  14. package/src/customers/index.ts +0 -17
  15. package/src/customers/store.ts +0 -45
  16. package/src/customers/update.ts +0 -44
  17. package/src/devices/destroy.ts +0 -54
  18. package/src/devices/export.ts +0 -98
  19. package/src/devices/fetch.ts +0 -125
  20. package/src/devices/index.ts +0 -31
  21. package/src/devices/store.ts +0 -82
  22. package/src/devices/update.ts +0 -125
  23. package/src/errors/destroy.ts +0 -54
  24. package/src/errors/fetch.ts +0 -197
  25. package/src/errors/index.ts +0 -28
  26. package/src/errors/update.ts +0 -116
  27. package/src/gift-cards/destroy.ts +0 -87
  28. package/src/gift-cards/fetch.ts +0 -431
  29. package/src/gift-cards/index.ts +0 -26
  30. package/src/gift-cards/store.ts +0 -56
  31. package/src/gift-cards/update.ts +0 -102
  32. package/src/index.ts +0 -78
  33. package/src/orders/destroy.ts +0 -111
  34. package/src/orders/events.ts +0 -121
  35. package/src/orders/export.ts +0 -158
  36. package/src/orders/fetch.ts +0 -379
  37. package/src/orders/guards.ts +0 -261
  38. package/src/orders/index.ts +0 -91
  39. package/src/orders/place-order.ts +0 -274
  40. package/src/orders/store.ts +0 -51
  41. package/src/orders/totals.ts +0 -190
  42. package/src/orders/update.ts +0 -164
  43. package/src/orders/webhook.ts +0 -312
  44. package/src/payments/destroy.ts +0 -56
  45. package/src/payments/fetch.ts +0 -288
  46. package/src/payments/index.ts +0 -16
  47. package/src/payments/store.ts +0 -63
  48. package/src/payments/update.ts +0 -52
  49. package/src/product.ts +0 -0
  50. package/src/products/categories/destroy.ts +0 -103
  51. package/src/products/categories/fetch.ts +0 -285
  52. package/src/products/categories/index.ts +0 -36
  53. package/src/products/categories/store.ts +0 -87
  54. package/src/products/categories/update.ts +0 -251
  55. package/src/products/index.ts +0 -35
  56. package/src/products/items/destroy.ts +0 -54
  57. package/src/products/items/fetch.ts +0 -282
  58. package/src/products/items/index.ts +0 -26
  59. package/src/products/items/store.ts +0 -84
  60. package/src/products/items/update.ts +0 -324
  61. package/src/products/manufacturers/destroy.ts +0 -56
  62. package/src/products/manufacturers/fetch.ts +0 -117
  63. package/src/products/manufacturers/index.ts +0 -24
  64. package/src/products/manufacturers/store.ts +0 -82
  65. package/src/products/manufacturers/update.ts +0 -113
  66. package/src/products/reviews/destroy.ts +0 -56
  67. package/src/products/reviews/fetch.ts +0 -63
  68. package/src/products/reviews/index.ts +0 -25
  69. package/src/products/reviews/store.ts +0 -46
  70. package/src/products/reviews/update.ts +0 -82
  71. package/src/products/units/destroy.ts +0 -56
  72. package/src/products/units/fetch.ts +0 -20
  73. package/src/products/units/index.ts +0 -13
  74. package/src/products/units/store.ts +0 -162
  75. package/src/products/units/update.ts +0 -159
  76. package/src/products/variants/destroy.ts +0 -56
  77. package/src/products/variants/fetch.ts +0 -20
  78. package/src/products/variants/index.ts +0 -21
  79. package/src/products/variants/store.ts +0 -136
  80. package/src/products/variants/update.ts +0 -104
  81. package/src/receipts/destroy.ts +0 -54
  82. package/src/receipts/export.ts +0 -0
  83. package/src/receipts/fetch.ts +0 -222
  84. package/src/receipts/index.ts +0 -24
  85. package/src/receipts/printer.ts +0 -69
  86. package/src/receipts/store.ts +0 -69
  87. package/src/receipts/update.ts +0 -124
  88. package/src/shippings/delivery-routes/destroy.ts +0 -54
  89. package/src/shippings/delivery-routes/fetch.ts +0 -42
  90. package/src/shippings/delivery-routes/index.ts +0 -26
  91. package/src/shippings/delivery-routes/store.ts +0 -69
  92. package/src/shippings/delivery-routes/update.ts +0 -120
  93. package/src/shippings/digital-deliveries/destroy.ts +0 -111
  94. package/src/shippings/digital-deliveries/fetch.ts +0 -20
  95. package/src/shippings/digital-deliveries/index.ts +0 -26
  96. package/src/shippings/digital-deliveries/store.ts +0 -76
  97. package/src/shippings/digital-deliveries/update.ts +0 -129
  98. package/src/shippings/drivers/destroy.ts +0 -78
  99. package/src/shippings/drivers/fetch.ts +0 -20
  100. package/src/shippings/drivers/index.ts +0 -24
  101. package/src/shippings/drivers/store.ts +0 -69
  102. package/src/shippings/drivers/update.ts +0 -117
  103. package/src/shippings/index.ts +0 -39
  104. package/src/shippings/license-keys/destroy.ts +0 -111
  105. package/src/shippings/license-keys/fetch.ts +0 -20
  106. package/src/shippings/license-keys/index.ts +0 -26
  107. package/src/shippings/license-keys/store.ts +0 -77
  108. package/src/shippings/license-keys/update.ts +0 -117
  109. package/src/shippings/shipping-methods/destroy.ts +0 -111
  110. package/src/shippings/shipping-methods/fetch.ts +0 -62
  111. package/src/shippings/shipping-methods/index.ts +0 -28
  112. package/src/shippings/shipping-methods/store.ts +0 -123
  113. package/src/shippings/shipping-methods/update.ts +0 -120
  114. package/src/shippings/shipping-rates/destroy.ts +0 -102
  115. package/src/shippings/shipping-rates/fetch.ts +0 -320
  116. package/src/shippings/shipping-rates/index.ts +0 -36
  117. package/src/shippings/shipping-rates/store.ts +0 -102
  118. package/src/shippings/shipping-rates/update.ts +0 -139
  119. package/src/shippings/shipping-zones/destroy.ts +0 -111
  120. package/src/shippings/shipping-zones/fetch.ts +0 -96
  121. package/src/shippings/shipping-zones/index.ts +0 -26
  122. package/src/shippings/shipping-zones/store.ts +0 -74
  123. package/src/shippings/shipping-zones/update.ts +0 -156
  124. package/src/tax/destroy.ts +0 -54
  125. package/src/tax/fetch.ts +0 -20
  126. package/src/tax/index.ts +0 -24
  127. package/src/tax/store.ts +0 -69
  128. package/src/tax/update.ts +0 -119
  129. package/src/tests/coupons.test.ts +0 -90
  130. package/src/tests/customers.test.ts +0 -39
  131. package/src/tests/device.test.ts +0 -111
  132. package/src/tests/digitals.test.ts +0 -31
  133. package/src/tests/drivers.test.ts +0 -25
  134. package/src/tests/gift-cards.test.ts +0 -36
  135. package/src/tests/license.test.ts +0 -31
  136. package/src/tests/manufacturers.test.ts +0 -80
  137. package/src/tests/orders.test.ts +0 -25
  138. package/src/tests/payments.test.ts +0 -75
  139. package/src/tests/rates.test.ts +0 -367
  140. package/src/tests/receipts.test.ts +0 -287
  141. package/src/tests/restaurant.test.ts +0 -522
  142. package/src/tests/reviews.test.ts +0 -165
  143. package/src/tests/routes.test.ts +0 -51
  144. package/src/tests/setup.ts +0 -149
  145. package/src/tests/shipping.test.ts +0 -88
  146. package/src/tests/tax.test.ts +0 -25
  147. package/src/tests/units.test.ts +0 -110
  148. package/src/tests/variants.test.ts +0 -133
  149. package/src/tests/waitlist.test.ts +0 -303
  150. package/src/tests/zones.test.ts +0 -86
  151. package/src/types.ts +0 -312
  152. package/src/utils/typed-stats.ts +0 -67
  153. package/src/waitlists/index.ts +0 -19
  154. package/src/waitlists/products/destroy.ts +0 -65
  155. package/src/waitlists/products/export.ts +0 -113
  156. package/src/waitlists/products/fetch.ts +0 -362
  157. package/src/waitlists/products/index.ts +0 -33
  158. package/src/waitlists/products/store.ts +0 -112
  159. package/src/waitlists/products/update.ts +0 -125
  160. package/src/waitlists/restaurant/destroy.ts +0 -65
  161. package/src/waitlists/restaurant/export.ts +0 -107
  162. package/src/waitlists/restaurant/fetch.ts +0 -538
  163. package/src/waitlists/restaurant/index.ts +0 -43
  164. package/src/waitlists/restaurant/store.ts +0 -109
  165. package/src/waitlists/restaurant/update.ts +0 -198
  166. /package/dist/{src/coupons → coupons}/destroy.d.ts +0 -0
  167. /package/dist/{src/coupons → coupons}/fetch.d.ts +0 -0
  168. /package/dist/{src/coupons → coupons}/index.d.ts +0 -0
  169. /package/dist/{src/coupons → coupons}/store.d.ts +0 -0
  170. /package/dist/{src/coupons → coupons}/update.d.ts +0 -0
  171. /package/dist/{src/customers → customers}/destroy.d.ts +0 -0
  172. /package/dist/{src/customers → customers}/fetch.d.ts +0 -0
  173. /package/dist/{src/customers → customers}/index.d.ts +0 -0
  174. /package/dist/{src/customers → customers}/store.d.ts +0 -0
  175. /package/dist/{src/customers → customers}/update.d.ts +0 -0
  176. /package/dist/{src/devices → devices}/destroy.d.ts +0 -0
  177. /package/dist/{src/devices → devices}/export.d.ts +0 -0
  178. /package/dist/{src/devices → devices}/fetch.d.ts +0 -0
  179. /package/dist/{src/devices → devices}/index.d.ts +0 -0
  180. /package/dist/{src/devices → devices}/store.d.ts +0 -0
  181. /package/dist/{src/devices → devices}/update.d.ts +0 -0
  182. /package/dist/{src/errors → errors}/destroy.d.ts +0 -0
  183. /package/dist/{src/errors → errors}/fetch.d.ts +0 -0
  184. /package/dist/{src/errors → errors}/index.d.ts +0 -0
  185. /package/dist/{src/errors → errors}/update.d.ts +0 -0
  186. /package/dist/{src/gift-cards → gift-cards}/destroy.d.ts +0 -0
  187. /package/dist/{src/gift-cards → gift-cards}/fetch.d.ts +0 -0
  188. /package/dist/{src/gift-cards → gift-cards}/index.d.ts +0 -0
  189. /package/dist/{src/gift-cards → gift-cards}/store.d.ts +0 -0
  190. /package/dist/{src/gift-cards → gift-cards}/update.d.ts +0 -0
  191. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
  192. /package/dist/{src/orders → orders}/destroy.d.ts +0 -0
  193. /package/dist/{src/orders → orders}/events.d.ts +0 -0
  194. /package/dist/{src/orders → orders}/export.d.ts +0 -0
  195. /package/dist/{src/orders → orders}/fetch.d.ts +0 -0
  196. /package/dist/{src/orders → orders}/guards.d.ts +0 -0
  197. /package/dist/{src/orders → orders}/index.d.ts +0 -0
  198. /package/dist/{src/orders → orders}/place-order.d.ts +0 -0
  199. /package/dist/{src/orders → orders}/store.d.ts +0 -0
  200. /package/dist/{src/orders → orders}/totals.d.ts +0 -0
  201. /package/dist/{src/orders → orders}/update.d.ts +0 -0
  202. /package/dist/{src/orders → orders}/webhook.d.ts +0 -0
  203. /package/dist/{src/payments → payments}/destroy.d.ts +0 -0
  204. /package/dist/{src/payments → payments}/fetch.d.ts +0 -0
  205. /package/dist/{src/payments → payments}/index.d.ts +0 -0
  206. /package/dist/{src/payments → payments}/store.d.ts +0 -0
  207. /package/dist/{src/payments → payments}/update.d.ts +0 -0
  208. /package/dist/{src/products → products}/categories/destroy.d.ts +0 -0
  209. /package/dist/{src/products → products}/categories/fetch.d.ts +0 -0
  210. /package/dist/{src/products → products}/categories/index.d.ts +0 -0
  211. /package/dist/{src/products → products}/categories/store.d.ts +0 -0
  212. /package/dist/{src/products → products}/categories/update.d.ts +0 -0
  213. /package/dist/{src/products → products}/index.d.ts +0 -0
  214. /package/dist/{src/products → products}/items/destroy.d.ts +0 -0
  215. /package/dist/{src/products → products}/items/fetch.d.ts +0 -0
  216. /package/dist/{src/products → products}/items/index.d.ts +0 -0
  217. /package/dist/{src/products → products}/items/store.d.ts +0 -0
  218. /package/dist/{src/products → products}/items/update.d.ts +0 -0
  219. /package/dist/{src/products → products}/manufacturers/destroy.d.ts +0 -0
  220. /package/dist/{src/products → products}/manufacturers/fetch.d.ts +0 -0
  221. /package/dist/{src/products → products}/manufacturers/index.d.ts +0 -0
  222. /package/dist/{src/products → products}/manufacturers/store.d.ts +0 -0
  223. /package/dist/{src/products → products}/manufacturers/update.d.ts +0 -0
  224. /package/dist/{src/products → products}/reviews/destroy.d.ts +0 -0
  225. /package/dist/{src/products → products}/reviews/fetch.d.ts +0 -0
  226. /package/dist/{src/products → products}/reviews/index.d.ts +0 -0
  227. /package/dist/{src/products → products}/reviews/store.d.ts +0 -0
  228. /package/dist/{src/products → products}/reviews/update.d.ts +0 -0
  229. /package/dist/{src/products → products}/units/destroy.d.ts +0 -0
  230. /package/dist/{src/products → products}/units/fetch.d.ts +0 -0
  231. /package/dist/{src/products → products}/units/index.d.ts +0 -0
  232. /package/dist/{src/products → products}/units/store.d.ts +0 -0
  233. /package/dist/{src/products → products}/units/update.d.ts +0 -0
  234. /package/dist/{src/products → products}/variants/destroy.d.ts +0 -0
  235. /package/dist/{src/products → products}/variants/fetch.d.ts +0 -0
  236. /package/dist/{src/products → products}/variants/index.d.ts +0 -0
  237. /package/dist/{src/products → products}/variants/store.d.ts +0 -0
  238. /package/dist/{src/products → products}/variants/update.d.ts +0 -0
  239. /package/dist/{src/receipts → receipts}/destroy.d.ts +0 -0
  240. /package/dist/{src/receipts → receipts}/fetch.d.ts +0 -0
  241. /package/dist/{src/receipts → receipts}/index.d.ts +0 -0
  242. /package/dist/{src/receipts → receipts}/store.d.ts +0 -0
  243. /package/dist/{src/receipts → receipts}/update.d.ts +0 -0
  244. /package/dist/{src/shippings → shippings}/delivery-routes/destroy.d.ts +0 -0
  245. /package/dist/{src/shippings → shippings}/delivery-routes/fetch.d.ts +0 -0
  246. /package/dist/{src/shippings → shippings}/delivery-routes/index.d.ts +0 -0
  247. /package/dist/{src/shippings → shippings}/delivery-routes/store.d.ts +0 -0
  248. /package/dist/{src/shippings → shippings}/delivery-routes/update.d.ts +0 -0
  249. /package/dist/{src/shippings → shippings}/digital-deliveries/destroy.d.ts +0 -0
  250. /package/dist/{src/shippings → shippings}/digital-deliveries/fetch.d.ts +0 -0
  251. /package/dist/{src/shippings → shippings}/digital-deliveries/index.d.ts +0 -0
  252. /package/dist/{src/shippings → shippings}/digital-deliveries/store.d.ts +0 -0
  253. /package/dist/{src/shippings → shippings}/digital-deliveries/update.d.ts +0 -0
  254. /package/dist/{src/shippings → shippings}/drivers/destroy.d.ts +0 -0
  255. /package/dist/{src/shippings → shippings}/drivers/fetch.d.ts +0 -0
  256. /package/dist/{src/shippings → shippings}/drivers/index.d.ts +0 -0
  257. /package/dist/{src/shippings → shippings}/drivers/store.d.ts +0 -0
  258. /package/dist/{src/shippings → shippings}/drivers/update.d.ts +0 -0
  259. /package/dist/{src/shippings → shippings}/index.d.ts +0 -0
  260. /package/dist/{src/shippings → shippings}/license-keys/destroy.d.ts +0 -0
  261. /package/dist/{src/shippings → shippings}/license-keys/fetch.d.ts +0 -0
  262. /package/dist/{src/shippings → shippings}/license-keys/index.d.ts +0 -0
  263. /package/dist/{src/shippings → shippings}/license-keys/store.d.ts +0 -0
  264. /package/dist/{src/shippings → shippings}/license-keys/update.d.ts +0 -0
  265. /package/dist/{src/shippings → shippings}/shipping-methods/destroy.d.ts +0 -0
  266. /package/dist/{src/shippings → shippings}/shipping-methods/fetch.d.ts +0 -0
  267. /package/dist/{src/shippings → shippings}/shipping-methods/index.d.ts +0 -0
  268. /package/dist/{src/shippings → shippings}/shipping-methods/store.d.ts +0 -0
  269. /package/dist/{src/shippings → shippings}/shipping-methods/update.d.ts +0 -0
  270. /package/dist/{src/shippings → shippings}/shipping-rates/destroy.d.ts +0 -0
  271. /package/dist/{src/shippings → shippings}/shipping-rates/fetch.d.ts +0 -0
  272. /package/dist/{src/shippings → shippings}/shipping-rates/index.d.ts +0 -0
  273. /package/dist/{src/shippings → shippings}/shipping-rates/store.d.ts +0 -0
  274. /package/dist/{src/shippings → shippings}/shipping-rates/update.d.ts +0 -0
  275. /package/dist/{src/shippings → shippings}/shipping-zones/destroy.d.ts +0 -0
  276. /package/dist/{src/shippings → shippings}/shipping-zones/fetch.d.ts +0 -0
  277. /package/dist/{src/shippings → shippings}/shipping-zones/index.d.ts +0 -0
  278. /package/dist/{src/shippings → shippings}/shipping-zones/store.d.ts +0 -0
  279. /package/dist/{src/shippings → shippings}/shipping-zones/update.d.ts +0 -0
  280. /package/dist/{src/tax → tax}/destroy.d.ts +0 -0
  281. /package/dist/{src/tax → tax}/fetch.d.ts +0 -0
  282. /package/dist/{src/tax → tax}/index.d.ts +0 -0
  283. /package/dist/{src/tax → tax}/store.d.ts +0 -0
  284. /package/dist/{src/tax → tax}/update.d.ts +0 -0
  285. /package/dist/{src/types.d.ts → types.d.ts} +0 -0
  286. /package/dist/{src/utils → utils}/typed-stats.d.ts +0 -0
  287. /package/dist/{src/waitlists → waitlists}/index.d.ts +0 -0
  288. /package/dist/{src/waitlists → waitlists}/products/destroy.d.ts +0 -0
  289. /package/dist/{src/waitlists → waitlists}/products/fetch.d.ts +0 -0
  290. /package/dist/{src/waitlists → waitlists}/products/index.d.ts +0 -0
  291. /package/dist/{src/waitlists → waitlists}/products/store.d.ts +0 -0
  292. /package/dist/{src/waitlists → waitlists}/products/update.d.ts +0 -0
  293. /package/dist/{src/waitlists → waitlists}/restaurant/destroy.d.ts +0 -0
  294. /package/dist/{src/waitlists → waitlists}/restaurant/fetch.d.ts +0 -0
  295. /package/dist/{src/waitlists → waitlists}/restaurant/index.d.ts +0 -0
  296. /package/dist/{src/waitlists → waitlists}/restaurant/store.d.ts +0 -0
  297. /package/dist/{src/waitlists → waitlists}/restaurant/update.d.ts +0 -0
@@ -1,510 +0,0 @@
1
- import type { CouponCountStats, CouponRedemptionStats, CouponStats, CouponTimeStats } from '../types'
2
- import type { StacksExpressionBuilder } from '@stacksjs/database'
3
- import { db } from '@stacksjs/database'
4
- import { extractDate, formatDate } from '@stacksjs/orm'
5
- type CouponJsonResponse = ModelRow<typeof Coupon>
6
-
7
- /**
8
- * Process coupon data from the database
9
- * Parses JSON strings for applicable_products and applicable_categories
10
- */
11
- function processCouponData(coupon: CouponJsonResponse): CouponJsonResponse {
12
- // Create a copy to avoid modifying the original object
13
- const processed = { ...coupon }
14
-
15
- if (processed.endDate) {
16
- processed.endDate = extractDate(new Date(processed.endDate))
17
- }
18
- if (processed.startDate) {
19
- processed.startDate = extractDate(new Date(processed.startDate))
20
- }
21
-
22
- // Convert isActive to boolean if needed
23
- if (processed.isActive !== undefined) {
24
- processed.isActive = Boolean(processed.isActive)
25
- }
26
-
27
- return processed
28
- }
29
-
30
- /**
31
- * Fetch all coupons from the database
32
- */
33
- export async function fetchAll(): Promise<CouponJsonResponse[]> {
34
- const coupons = await db
35
- .selectFrom('coupons')
36
- .selectAll()
37
- .execute()
38
-
39
- return (coupons as CouponJsonResponse[]).map(processCouponData)
40
- }
41
-
42
- /**
43
- * Fetch a coupon by ID
44
- */
45
- export async function fetchById(id: number): Promise<CouponJsonResponse | undefined> {
46
- const coupon = await db
47
- .selectFrom('coupons')
48
- .where('id', '=', id)
49
- .selectAll()
50
- .executeTakeFirst()
51
-
52
- if (coupon) {
53
- return processCouponData(coupon as CouponJsonResponse)
54
- }
55
-
56
- return undefined
57
- }
58
-
59
- /**
60
- * Fetch a coupon by code
61
- */
62
- export async function fetchByCode(code: string): Promise<CouponJsonResponse | undefined> {
63
- const coupon = await db
64
- .selectFrom('coupons')
65
- .where('code', '=', code)
66
- .selectAll()
67
- .executeTakeFirst()
68
-
69
- if (coupon) {
70
- return processCouponData(coupon as CouponJsonResponse)
71
- }
72
-
73
- return undefined
74
- }
75
-
76
- /**
77
- * Fetch active coupons (is_active = true and within date range)
78
- */
79
- export async function fetchActive(): Promise<CouponJsonResponse[]> {
80
- const currentDate = formatDate(new Date())
81
-
82
- const coupons = await db
83
- .selectFrom('coupons')
84
- .where('is_active', '=', true)
85
- .where('start_date', '<=', currentDate)
86
- .where('end_date', '>=', currentDate)
87
- .selectAll()
88
- .execute()
89
-
90
- return (coupons as CouponJsonResponse[]).map(processCouponData)
91
- }
92
-
93
- /**
94
- * Get coupon statistics
95
- */
96
- export async function fetchStats(): Promise<CouponStats> {
97
- // Total coupons
98
- const totalCoupons = await db
99
- .selectFrom('coupons')
100
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
101
- .executeTakeFirst() as { count: number } | undefined
102
-
103
- // Active coupons
104
- const currentDate = formatDate(new Date())
105
- const activeCoupons = await db
106
- .selectFrom('coupons')
107
- .where('is_active', '=', true)
108
- .where('start_date', '<=', currentDate)
109
- .where('end_date', '>=', currentDate)
110
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
111
- .executeTakeFirst() as { count: number } | undefined
112
-
113
- // Coupons by discount type
114
- const couponsByType = await db
115
- .selectFrom('coupons')
116
- .select(['discount_type', (eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')] as any)
117
- .groupBy('discount_type')
118
- .execute() as { discount_type: string, count: number }[]
119
-
120
- // Most used coupons
121
- const mostUsedCoupons = await db
122
- .selectFrom('coupons')
123
- .select(['id', 'code', 'discount_type', 'discount_value', 'usage_count'])
124
- .orderBy('usage_count', 'desc')
125
- .limit(5)
126
- .execute() as { id: number, code: string, discount_type: string, discount_value: number, usage_count: number }[]
127
-
128
- // Upcoming coupons (active but not yet started)
129
- const upcomingCoupons = await db
130
- .selectFrom('coupons')
131
- .where('is_active', '=', true)
132
- .where('start_date', '>', currentDate)
133
- .select(['id', 'code', 'discount_type', 'discount_value', 'start_date', 'end_date'])
134
- .orderBy('start_date', 'asc')
135
- .limit(5)
136
- .execute() as { id: number, code: string, discount_type: string, discount_value: number, start_date: string | Date, end_date: string | Date }[]
137
-
138
- return {
139
- total: Number(totalCoupons?.count || 0),
140
- active: Number(activeCoupons?.count || 0),
141
- by_type: couponsByType,
142
- most_used: mostUsedCoupons,
143
- upcoming: upcomingCoupons,
144
- }
145
- }
146
-
147
- /**
148
- * Get count of active and inactive coupons for different time periods
149
- */
150
- export async function fetchCouponCounts(): Promise<CouponTimeStats> {
151
- const currentDate = new Date()
152
-
153
- // Calculate start dates for different periods
154
- const weekStart = new Date(currentDate)
155
- weekStart.setDate(currentDate.getDate() - 7)
156
-
157
- const monthStart = new Date(currentDate)
158
- monthStart.setMonth(currentDate.getMonth() - 1)
159
-
160
- const yearStart = new Date(currentDate)
161
- yearStart.setFullYear(currentDate.getFullYear() - 1)
162
-
163
- // All-time counts
164
- const allTimeCounts = await fetchCountsForPeriod(null, null, currentDate)
165
-
166
- // Week counts
167
- const weekCounts = await fetchCountsForPeriod(weekStart, null, currentDate)
168
-
169
- // Month counts
170
- const monthCounts = await fetchCountsForPeriod(monthStart, null, currentDate)
171
-
172
- // Year counts
173
- const yearCounts = await fetchCountsForPeriod(yearStart, null, currentDate)
174
-
175
- return {
176
- week: weekCounts,
177
- month: monthCounts,
178
- year: yearCounts,
179
- all_time: allTimeCounts,
180
- }
181
- }
182
-
183
- /**
184
- * Helper function to fetch coupon counts for a specific time period
185
- *
186
- * @param startDate - Start of the period (or null for all time)
187
- * @param endDate - End of the period (or null for current date)
188
- * @param currentDate - Current date for active/inactive calculation
189
- */
190
- async function fetchCountsForPeriod(
191
- startDate: Date | null,
192
- endDate: Date | null,
193
- currentDate: Date,
194
- ): Promise<CouponCountStats> {
195
- let query = db.selectFrom('coupons') as any
196
-
197
- // Apply date filter if startDate is provided
198
- if (startDate) {
199
- query = query.where('created_at', '>=', startDate.toISOString())
200
- }
201
-
202
- // Apply end date filter if endDate is provided
203
- if (endDate) {
204
- query = query.where('created_at', '<=', endDate.toISOString())
205
- }
206
-
207
- // Get total count
208
- const totalResult = await query
209
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
210
- .executeTakeFirst() as { count: number } | undefined
211
-
212
- // Get active count (is_active = true, start_date <= current date, end_date >= current date)
213
- // Create a new query instead of cloning
214
- let activeQuery = db.selectFrom('coupons') as any
215
-
216
- // Apply the same date filters as the total query if needed
217
- if (startDate) {
218
- activeQuery = activeQuery.where('created_at', '>=', startDate.toISOString())
219
- }
220
-
221
- if (endDate) {
222
- activeQuery = activeQuery.where('created_at', '<=', endDate.toISOString())
223
- }
224
-
225
- // Add active coupon conditions
226
- activeQuery = activeQuery
227
- .where('is_active', '=', true)
228
- .where('start_date', '<=', currentDate)
229
- .where('end_date', '>=', currentDate)
230
-
231
- const activeResult = await activeQuery
232
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
233
- .executeTakeFirst() as { count: number } | undefined
234
-
235
- // Calculate counts
236
- const total = Number(totalResult?.count || 0)
237
- const active = Number(activeResult?.count || 0)
238
- const inactive = total - active
239
-
240
- return {
241
- total,
242
- active,
243
- inactive,
244
- }
245
- }
246
-
247
- /**
248
- * Get detailed breakdown of active/inactive coupons by discount type
249
- */
250
- export async function fetchCouponCountsByType(): Promise<Record<string, CouponCountStats>> {
251
- const currentDate = new Date()
252
-
253
- // Get all discount types
254
- const discountTypes = await db
255
- .selectFrom('coupons')
256
- .select('discount_type' as any)
257
- .distinct()
258
- .execute() as { discount_type: string }[]
259
-
260
- const result: Record<string, CouponCountStats> = {}
261
-
262
- // For each discount type, get the counts
263
- for (const { discount_type } of discountTypes) {
264
- // Total count for this type
265
- const totalResult = await db
266
- .selectFrom('coupons')
267
- .where('discount_type', '=', discount_type)
268
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
269
- .executeTakeFirst() as { count: number } | undefined
270
-
271
- // Active count for this type - create a new query instead of cloning
272
- const activeResult = await db
273
- .selectFrom('coupons')
274
- .where('discount_type', '=', discount_type)
275
- .where('is_active', '=', true)
276
- .where('start_date', '<=', currentDate)
277
- .where('end_date', '>=', currentDate)
278
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
279
- .executeTakeFirst() as { count: number } | undefined
280
-
281
- const total = Number(totalResult?.count || 0)
282
- const active = Number(activeResult?.count || 0)
283
-
284
- result[discount_type] = {
285
- total,
286
- active,
287
- inactive: total - active,
288
- }
289
- }
290
-
291
- return result
292
- }
293
-
294
- /**
295
- * Fetch coupon redemption statistics based on usage_count
296
- */
297
- export async function fetchRedemptionStats(): Promise<CouponRedemptionStats> {
298
- // Total redemptions (sum of all usage_count)
299
- const totalResult = await db
300
- .selectFrom('coupons')
301
- .select(((eb: StacksExpressionBuilder) => eb.fn.sum('usage_count').as('total')) as any)
302
- .executeTakeFirst() as { total: number } | undefined
303
-
304
- const currentDate = new Date()
305
-
306
- // Calculate start dates for different periods
307
- const weekStart = new Date(currentDate)
308
- weekStart.setDate(currentDate.getDate() - 7)
309
-
310
- const monthStart = new Date(currentDate)
311
- monthStart.setMonth(currentDate.getMonth() - 1)
312
-
313
- const yearStart = new Date(currentDate)
314
- yearStart.setFullYear(currentDate.getFullYear() - 1)
315
-
316
- // Weekly redemptions (for coupons updated in the last week)
317
- const weekResult = await db
318
- .selectFrom('coupons')
319
- .where('updated_at', '>=', weekStart.toISOString())
320
- .select(((eb: StacksExpressionBuilder) => eb.fn.sum('usage_count').as('total')) as any)
321
- .executeTakeFirst() as { total: number } | undefined
322
-
323
- // Monthly redemptions
324
- const monthResult = await db
325
- .selectFrom('coupons')
326
- .where('updated_at', '>=', monthStart.toISOString())
327
- .select(((eb: StacksExpressionBuilder) => eb.fn.sum('usage_count').as('total')) as any)
328
- .executeTakeFirst() as { total: number } | undefined
329
-
330
- // Yearly redemptions
331
- const yearResult = await db
332
- .selectFrom('coupons')
333
- .where('updated_at', '>=', yearStart.toISOString())
334
- .select(((eb: StacksExpressionBuilder) => eb.fn.sum('usage_count').as('total')) as any)
335
- .executeTakeFirst() as { total: number } | undefined
336
-
337
- // Redemptions by discount type
338
- const byTypeResults = await db
339
- .selectFrom('coupons')
340
- .select(['discount_type', (eb: StacksExpressionBuilder) => eb.fn.sum('usage_count').as('total')] as any)
341
- .groupBy('discount_type')
342
- .execute() as { discount_type: string, total: number }[]
343
-
344
- const byType: Record<string, number> = {}
345
- byTypeResults.forEach((item: any) => {
346
- byType[item.discount_type] = Number(item.total || 0)
347
- })
348
-
349
- return {
350
- total: Number(totalResult?.total || 0),
351
- week: Number(weekResult?.total || 0),
352
- month: Number(monthResult?.total || 0),
353
- year: Number(yearResult?.total || 0),
354
- by_type: byType,
355
- }
356
- }
357
-
358
- /**
359
- * Get top redeemed coupons
360
- */
361
- export async function fetchTopRedeemedCoupons(limit: number = 5): Promise<any[]> {
362
- return await db
363
- .selectFrom('coupons')
364
- .select([
365
- 'id',
366
- 'code',
367
- 'discount_type',
368
- 'discount_value',
369
- 'usage_count',
370
- 'start_date',
371
- 'end_date',
372
- ])
373
- .where('usage_count', '>', 0)
374
- .orderBy('usage_count', 'desc')
375
- .limit(limit)
376
- .execute()
377
- }
378
-
379
- /**
380
- * Get redemption trend data in a simplified manner
381
- */
382
- export async function fetchRedemptionTrend(days: number = 30): Promise<any[]> {
383
- const endDate = new Date().toISOString()
384
- const startDate = new Date()
385
- startDate.setDate(startDate.getDate() - days)
386
-
387
- // Get coupons updated in the specified timeframe
388
- const coupons = await db
389
- .selectFrom('coupons')
390
- .select(['id', 'code', 'updated_at', 'usage_count'])
391
- .where('updated_at', '>=', startDate.toISOString())
392
- .where('updated_at', '<=', endDate)
393
- .where('usage_count', '>', 0)
394
- .orderBy('updated_at', 'desc')
395
- .execute()
396
-
397
- // Group by date (this is done in JS rather than SQL for simplicity)
398
- const trendData: Record<string, number> = {}
399
-
400
- coupons.forEach((coupon: any) => {
401
- // Format date as YYYY-MM-DD
402
- const dateString = coupon.updated_at
403
- ? new Date(coupon.updated_at).toISOString().split('T')[0]
404
- : 'unknown-date' // Fallback for undefined date
405
-
406
- // Add or increment the count for this date
407
- if (!trendData[dateString]) {
408
- trendData[dateString] = 0
409
- }
410
- trendData[dateString] += coupon?.usage_count || 0
411
- })
412
-
413
- // Convert to array format
414
- return Object.entries(trendData).map(([date, count]) => ({
415
- date,
416
- count,
417
- }))
418
- }
419
-
420
- /**
421
- * Get conversion rate (number of redemptions / number of active coupons)
422
- */
423
- export async function fetchConversionRate(): Promise<{
424
- rate: number
425
- total_active: number
426
- total_redeemed: number
427
- }> {
428
- const currentDate = new Date()
429
-
430
- // Get count of active coupons
431
- const activeResult = await db
432
- .selectFrom('coupons')
433
- .where('is_active', '=', true)
434
- .where('start_date', '<=', currentDate)
435
- .where('end_date', '>=', currentDate)
436
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
437
- .executeTakeFirst() as { count: number } | undefined
438
-
439
- // Get count of coupons with usage > 0
440
- const redeemedResult = await db
441
- .selectFrom('coupons')
442
- .where('usage_count', '>', 0)
443
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
444
- .executeTakeFirst() as { count: number } | undefined
445
-
446
- const totalActive = Number(activeResult?.count || 0)
447
- const totalRedeemed = Number(redeemedResult?.count || 0)
448
-
449
- // Calculate rate (avoid division by zero)
450
- const rate = totalActive > 0 ? (totalRedeemed / totalActive) * 100 : 0
451
-
452
- return {
453
- rate,
454
- total_active: totalActive,
455
- total_redeemed: totalRedeemed,
456
- }
457
- }
458
-
459
- /**
460
- * Calculate the month-over-month change in active coupons
461
- */
462
- export async function getActiveCouponsMoMChange(): Promise<{
463
- current_month: number
464
- previous_month: number
465
- difference: number
466
- percentage_change: number
467
- }> {
468
- const today = new Date()
469
-
470
- // Current month (start of current month to today)
471
- const currentMonthStart = new Date(today.getFullYear(), today.getMonth(), 1)
472
-
473
- // Previous month
474
- const previousMonthStart = new Date(today.getFullYear(), today.getMonth() - 1, 1)
475
- const previousMonthEnd = new Date(today.getFullYear(), today.getMonth(), 0)
476
-
477
- // Get active coupons for current month
478
- const currentMonthActive = await db
479
- .selectFrom('coupons')
480
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
481
- .where('is_active', '=', true)
482
- .where('start_date', '<=', today)
483
- .where('end_date', '>=', currentMonthStart)
484
- .executeTakeFirst() as { count: number } | undefined
485
-
486
- // Get active coupons for previous month
487
- const previousMonthActive = await db
488
- .selectFrom('coupons')
489
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
490
- .where('is_active', '=', true)
491
- .where('start_date', '<=', previousMonthEnd)
492
- .where('end_date', '>=', previousMonthStart)
493
- .executeTakeFirst() as { count: number } | undefined
494
-
495
- const currentCount = Number(currentMonthActive?.count || 0)
496
- const previousCount = Number(previousMonthActive?.count || 0)
497
- const difference = currentCount - previousCount
498
-
499
- // Calculate percentage change, handling division by zero
500
- const percentageChange = previousCount !== 0
501
- ? (difference / previousCount) * 100
502
- : (currentCount > 0 ? 100 : 0) // If previous month was 0, and current is > 0, that's a 100% increase
503
-
504
- return {
505
- current_month: currentCount,
506
- previous_month: previousCount,
507
- difference,
508
- percentage_change: percentageChange,
509
- }
510
- }
@@ -1,30 +0,0 @@
1
- export {
2
- deleteCoupon,
3
- deleteCoupons,
4
- deleteExpiredCoupons,
5
- } from './destroy'
6
-
7
- export {
8
- fetchActive,
9
- fetchAll,
10
- fetchByCode,
11
- fetchById,
12
- fetchConversionRate,
13
- fetchCouponCounts,
14
- fetchCouponCountsByType,
15
- fetchRedemptionStats,
16
- fetchRedemptionTrend,
17
- fetchStats,
18
- fetchTopRedeemedCoupons,
19
- getActiveCouponsMoMChange,
20
- } from './fetch'
21
-
22
- export {
23
- store,
24
- } from './store'
25
-
26
- export {
27
- redeem,
28
- update,
29
- } from './update'
30
- export type { CouponRedemptionResult } from './update'
@@ -1,46 +0,0 @@
1
- type CouponJsonResponse = ModelRow<typeof Coupon>
2
- type NewCoupon = NewModelData<typeof Coupon>
3
- import { db } from '@stacksjs/database'
4
- import { HttpError } from '@stacksjs/error-handling'
5
- import { isUniqueViolation } from '@stacksjs/orm'
6
-
7
- /**
8
- * Create a new coupon
9
- *
10
- * @param data The coupon data to store
11
- * @returns The newly created coupon record
12
- */
13
- export async function store(data: NewCoupon): Promise<CouponJsonResponse | undefined> {
14
- try {
15
- // Insert the coupon record
16
- const createdCoupon = await db
17
- .insertInto('coupons')
18
- .values(data)
19
- .executeTakeFirst()
20
-
21
- const couponId = Number(createdCoupon.insertId) || Number(createdCoupon.numInsertedOrUpdatedRows)
22
-
23
- if (couponId) {
24
- const coupon = await db
25
- .selectFrom('coupons')
26
- .where('id', '=', couponId)
27
- .selectAll()
28
- .executeTakeFirst()
29
-
30
- return coupon as CouponJsonResponse
31
- }
32
-
33
- return undefined
34
- }
35
- catch (error) {
36
- if (error instanceof HttpError)
37
- throw error
38
- // Cross-dialect duplicate detection (#1957) — replaces the MySQL-only
39
- // 'Duplicate entry' sniff so SQLite/Postgres duplicates also map to 409.
40
- if (isUniqueViolation(error))
41
- throw new HttpError(409, 'A coupon with this code already exists')
42
- if (error instanceof Error)
43
- throw new Error(`Failed to create coupon: ${error.message}`)
44
- throw error
45
- }
46
- }