@stacksjs/commerce 0.70.55 → 0.70.56

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,431 +0,0 @@
1
- import type { GiftCardStats } from '../types'
2
- import type { StacksExpressionBuilder } from '@stacksjs/database'
3
- import { db, sql } from '@stacksjs/database'
4
- import { formatDate, toTimestamp } from '@stacksjs/orm'
5
- import { aggregateStats } from '../utils/typed-stats'
6
- type GiftCardJsonResponse = ModelRow<typeof GiftCard>
7
-
8
- /**
9
- * Fetch all gift cards from the database
10
- */
11
- export async function fetchAll(): Promise<GiftCardJsonResponse[]> {
12
- const giftCards = await db
13
- .selectFrom('gift_cards')
14
- .selectAll()
15
- .execute()
16
-
17
- return giftCards as GiftCardJsonResponse[]
18
- }
19
-
20
- /**
21
- * Fetch a gift card by ID
22
- */
23
- export async function fetchById(id: number): Promise<GiftCardJsonResponse | undefined> {
24
- return await db
25
- .selectFrom('gift_cards')
26
- .where('id', '=', id)
27
- .selectAll()
28
- .executeTakeFirst() as GiftCardJsonResponse | undefined
29
- }
30
-
31
- /**
32
- * Fetch a gift card by code
33
- */
34
- export async function fetchByCode(code: string): Promise<GiftCardJsonResponse | undefined> {
35
- return await db
36
- .selectFrom('gift_cards')
37
- .where('code', '=', code)
38
- .selectAll()
39
- .executeTakeFirst() as GiftCardJsonResponse | undefined
40
- }
41
-
42
- /**
43
- * Fetch active gift cards (is_active = true and not expired)
44
- */
45
- export async function fetchActive(): Promise<GiftCardJsonResponse[]> {
46
- const giftCards = await db
47
- .selectFrom('gift_cards')
48
- .where('is_active', '=', true)
49
- .selectAll()
50
- .execute()
51
-
52
- return giftCards as GiftCardJsonResponse[]
53
- }
54
-
55
- /**
56
- * Get gift card statistics
57
- */
58
- export async function fetchStats(): Promise<GiftCardStats> {
59
- // Total gift cards
60
- const { count: totalCount } = await aggregateStats('gift_cards', { count: { kind: 'count' } })
61
-
62
- // Active gift cards
63
- const currentDate = toTimestamp(new Date())
64
-
65
- // bun-query-builder doesn't expose a typed `eb.or([...])` helper, so the
66
- // OR-with-NULL filter still uses the untyped expression-builder form.
67
- // The boundary `as any` is localized; the result row uses the typed helper.
68
- const activeGiftCards = await db
69
- .selectFrom('gift_cards')
70
- .where('is_active', '=', true)
71
- .where('status', '=', 'ACTIVE')
72
- .where(((eb: StacksExpressionBuilder) => eb.or([
73
- eb('expiry_date', '>=', currentDate),
74
- eb('expiry_date', 'is', null),
75
- ])) as any)
76
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
77
- .executeTakeFirst() as { count: number } | undefined
78
-
79
- // Gift cards by status
80
- const giftCardsByStatus = await db
81
- .selectFrom('gift_cards')
82
- .select(['status', (eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')] as any)
83
- .groupBy('status')
84
- .execute() as { status: string, count: number }[]
85
-
86
- // Balance distribution: each bucket needs a custom raw fraction filter, so
87
- // these stay as direct queries — but we use the typed helper for the
88
- // `count(id)` expression to avoid the `as any` chain on the result.
89
- const { count: lowBalanceCount } = await aggregateStats('gift_cards', { count: { kind: 'count' } }, qb =>
90
- qb.where(((eb: StacksExpressionBuilder) => sql`${eb.ref('current_balance')} / ${eb.ref('initial_balance')} < 0.25`) as any))
91
-
92
- const { count: mediumBalanceCount } = await aggregateStats('gift_cards', { count: { kind: 'count' } }, qb =>
93
- qb.where(((eb: StacksExpressionBuilder) => sql`${eb.ref('current_balance')} / ${eb.ref('initial_balance')} >= 0.25 AND ${eb.ref('current_balance')} / ${eb.ref('initial_balance')} <= 0.75`) as any))
94
-
95
- const { count: highBalanceCount } = await aggregateStats('gift_cards', { count: { kind: 'count' } }, qb =>
96
- qb.where(((eb: StacksExpressionBuilder) => sql`${eb.ref('current_balance')} / ${eb.ref('initial_balance')} > 0.75`) as any))
97
-
98
- // Expiring soon gift cards (next 30 days)
99
- const thirtyDaysFromNow = new Date()
100
- thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30)
101
-
102
- const expiringGiftCards = await db
103
- .selectFrom('gift_cards')
104
- .where('is_active', '=', true)
105
- .where('status', '=', 'ACTIVE')
106
- .where('expiry_date', '<=', toTimestamp(thirtyDaysFromNow))
107
- .where('expiry_date', '>=', currentDate)
108
- .selectAll()
109
- .limit(5)
110
- .execute()
111
-
112
- // Recently used gift cards
113
- const recentlyUsedGiftCards = await db
114
- .selectFrom('gift_cards')
115
- .where('last_used_date', 'is not', null)
116
- .selectAll()
117
- .limit(5)
118
- .execute()
119
-
120
- return {
121
- total: totalCount,
122
- active: Number(activeGiftCards?.count || 0),
123
- by_status: giftCardsByStatus.map((item: { status: string, count: number }) => ({
124
- status: item.status,
125
- count: Number(item.count),
126
- })),
127
- by_balance: {
128
- low: lowBalanceCount,
129
- medium: mediumBalanceCount,
130
- high: highBalanceCount,
131
- },
132
- expiring_soon: expiringGiftCards as GiftCardJsonResponse[],
133
- recently_used: recentlyUsedGiftCards as GiftCardJsonResponse[],
134
- }
135
- }
136
-
137
- /**
138
- * Check gift card balance by code
139
- */
140
- export async function checkBalance(code: string): Promise<{ valid: boolean, balance?: number, currency?: string, message?: string }> {
141
- const giftCard = await fetchByCode(code)
142
-
143
- if (!giftCard) {
144
- return { valid: false, message: 'Gift card not found' }
145
- }
146
-
147
- if (!giftCard.isActive || giftCard.status !== 'ACTIVE') {
148
- return {
149
- valid: false,
150
- balance: giftCard.currentBalance,
151
- currency: giftCard.currency,
152
- message: `Gift card is ${giftCard.status.toLowerCase()}`,
153
- }
154
- }
155
-
156
- // Check if expired
157
- if (giftCard.expiryDate) {
158
- const expiryDate = new Date(giftCard.expiryDate)
159
- const currentDate = new Date()
160
-
161
- if (expiryDate < currentDate) {
162
- return {
163
- valid: false,
164
- balance: giftCard.currentBalance,
165
- currency: giftCard.currency,
166
- message: 'Gift card has expired',
167
- }
168
- }
169
- }
170
-
171
- return {
172
- valid: true,
173
- balance: giftCard.currentBalance,
174
- currency: giftCard.currency,
175
- }
176
- }
177
-
178
- /**
179
- * Compare active gift cards between different time periods
180
- * @param daysRange Number of days to look back (7, 30, 60, etc.)
181
- */
182
- export async function compareActiveGiftCards(_daysRange: number = 30): Promise<{
183
- current_period: number
184
- previous_period: number
185
- difference: number
186
- percentage_change: number
187
- days_range: number
188
- }> {
189
- const today = new Date()
190
-
191
- // Current period (last N days)
192
- const currentPeriodStart = new Date(today)
193
- currentPeriodStart.setDate(today.getDate() - _daysRange)
194
-
195
- // Previous period (N days before the current period)
196
- const previousPeriodEnd = new Date(currentPeriodStart)
197
- previousPeriodEnd.setDate(previousPeriodEnd.getDate() - 1)
198
-
199
- const previousPeriodStart = new Date(previousPeriodEnd)
200
- previousPeriodStart.setDate(previousPeriodEnd.getDate() - _daysRange)
201
-
202
- // Get active gift cards for current period
203
- const currentPeriodActive = await db
204
- .selectFrom('gift_cards')
205
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
206
- .where('is_active', '=', true)
207
- .where('status', '=', 'ACTIVE')
208
- .where(((eb: StacksExpressionBuilder) => eb.or([
209
- eb('expiry_date', '>=', toTimestamp(today)),
210
- eb('expiry_date', 'is', null),
211
- ])) as any)
212
- .where('created_at', '>=', formatDate(currentPeriodStart))
213
- .where('created_at', '<=', formatDate(today))
214
- .executeTakeFirst() as { count: number } | undefined
215
-
216
- // Get active gift cards for previous period
217
- const previousPeriodActive = await db
218
- .selectFrom('gift_cards')
219
- .select(((eb: StacksExpressionBuilder) => eb.fn.count('id').as('count')) as any)
220
- .where('is_active', '=', true)
221
- .where('status', '=', 'ACTIVE')
222
- .where(((eb: StacksExpressionBuilder) => eb.or([
223
- eb('expiry_date', '>=', toTimestamp(previousPeriodStart)),
224
- eb('expiry_date', 'is', null),
225
- ])) as any)
226
- .where('created_at', '>=', formatDate(previousPeriodStart))
227
- .where('created_at', '<=', formatDate(previousPeriodEnd))
228
- .executeTakeFirst() as { count: number } | undefined
229
-
230
- const currentCount = Number(currentPeriodActive?.count || 0)
231
- const previousCount = Number(previousPeriodActive?.count || 0)
232
- const difference = currentCount - previousCount
233
-
234
- // Calculate percentage change, handling division by zero
235
- const percentageChange = previousCount !== 0
236
- ? (difference / previousCount) * 100
237
- : (currentCount > 0 ? 100 : 0)
238
-
239
- return {
240
- current_period: currentCount,
241
- previous_period: previousCount,
242
- difference,
243
- percentage_change: percentageChange,
244
- days_range: _daysRange,
245
- }
246
- }
247
-
248
- /**
249
- * Calculate gift card balances and initial values for different time periods
250
- * with clear increase/decrease indicators
251
- * @param daysRange Number of days to look back (7, 30, 60, etc.)
252
- */
253
- export async function calculateGiftCardValues(_daysRange: number = 30): Promise<{
254
- current_period: {
255
- total_initial_value: number
256
- total_remaining_balance: number
257
- utilization_rate: number
258
- card_count: number
259
- average_initial_value: number
260
- average_remaining_balance: number
261
- }
262
- previous_period: {
263
- total_initial_value: number
264
- total_remaining_balance: number
265
- utilization_rate: number
266
- card_count: number
267
- }
268
- comparison: {
269
- initial_value: {
270
- difference: number
271
- percentage: number
272
- is_increase: boolean
273
- }
274
- balance: {
275
- difference: number
276
- percentage: number
277
- is_increase: boolean
278
- }
279
- card_count: {
280
- difference: number
281
- percentage: number
282
- is_increase: boolean
283
- }
284
- }
285
- days_range: number
286
- }> {
287
- const today = new Date()
288
-
289
- // Current period (last N days)
290
- const currentPeriodStart = new Date(today)
291
- currentPeriodStart.setDate(today.getDate() - _daysRange)
292
-
293
- // Previous period (N days before the current period)
294
- const previousPeriodEnd = new Date(currentPeriodStart)
295
- previousPeriodEnd.setDate(previousPeriodEnd.getDate() - 1)
296
-
297
- const previousPeriodStart = new Date(previousPeriodEnd)
298
- previousPeriodStart.setDate(previousPeriodEnd.getDate() - _daysRange)
299
-
300
- // Get values for current period
301
- const currentPeriodValues = await db
302
- .selectFrom('gift_cards')
303
- .select(((eb: StacksExpressionBuilder) => [
304
- eb.fn.sum('initial_balance').as('total_initial'),
305
- eb.fn.sum('current_balance').as('total_balance'),
306
- eb.fn.count('id').as('card_count'),
307
- ]) as any)
308
- .where('is_active', '=', true)
309
- .where('status', '=', 'ACTIVE')
310
- .where(((eb: StacksExpressionBuilder) => eb.or([
311
- eb('expiry_date', '>=', toTimestamp(today)),
312
- eb('expiry_date', 'is', null),
313
- ])) as any)
314
- .where('created_at', '>=', formatDate(currentPeriodStart))
315
- .where('created_at', '<=', formatDate(today))
316
- .executeTakeFirst() as { total_initial: number, total_balance: number, card_count: number } | undefined
317
-
318
- // Get values for previous period
319
- const previousPeriodValues = await db
320
- .selectFrom('gift_cards')
321
- .select(((eb: StacksExpressionBuilder) => [
322
- eb.fn.sum('initial_balance').as('total_initial'),
323
- eb.fn.sum('current_balance').as('total_balance'),
324
- eb.fn.count('id').as('card_count'),
325
- ]) as any)
326
- .where('is_active', '=', true)
327
- .where('status', '=', 'ACTIVE')
328
- .where(((eb: StacksExpressionBuilder) => eb.or([
329
- eb('expiry_date', '>=', toTimestamp(previousPeriodStart)),
330
- eb('expiry_date', 'is', null),
331
- ])) as any)
332
- .where('created_at', '>=', formatDate(previousPeriodStart))
333
- .where('created_at', '<=', formatDate(previousPeriodEnd))
334
- .executeTakeFirst() as { total_initial: number, total_balance: number, card_count: number } | undefined
335
-
336
- // Calculate values for current period
337
- const currentInitialValue = Number(currentPeriodValues?.total_initial || 0)
338
- const currentBalance = Number(currentPeriodValues?.total_balance || 0)
339
- const currentCardCount = Number(currentPeriodValues?.card_count || 0)
340
-
341
- // Calculate values for previous period
342
- const previousInitialValue = Number(previousPeriodValues?.total_initial || 0)
343
- const previousBalance = Number(previousPeriodValues?.total_balance || 0)
344
- const previousCardCount = Number(previousPeriodValues?.card_count || 0)
345
-
346
- // Calculate utilization rates (percentage of gift card value that has been spent)
347
- const currentUtilizationRate = currentInitialValue > 0
348
- ? ((currentInitialValue - currentBalance) / currentInitialValue) * 100
349
- : 0
350
-
351
- const previousUtilizationRate = previousInitialValue > 0
352
- ? ((previousInitialValue - previousBalance) / previousInitialValue) * 100
353
- : 0
354
-
355
- // Calculate averages
356
- const currentAverageInitial = currentCardCount > 0 ? currentInitialValue / currentCardCount : 0
357
- const currentAverageBalance = currentCardCount > 0 ? currentBalance / currentCardCount : 0
358
-
359
- // Calculate differences
360
- const initialValueDifference = currentInitialValue - previousInitialValue
361
- const balanceDifference = currentBalance - previousBalance
362
- const cardCountDifference = currentCardCount - previousCardCount
363
-
364
- // Calculate percentage changes
365
- const initialValuePercentageChange = previousInitialValue !== 0
366
- ? (initialValueDifference / previousInitialValue) * 100
367
- : (currentInitialValue > 0 ? 100 : 0)
368
-
369
- const balancePercentageChange = previousBalance !== 0
370
- ? (balanceDifference / previousBalance) * 100
371
- : (currentBalance > 0 ? 100 : 0)
372
-
373
- const cardCountPercentageChange = previousCardCount !== 0
374
- ? (cardCountDifference / previousCardCount) * 100
375
- : (currentCardCount > 0 ? 100 : 0)
376
-
377
- return {
378
- current_period: {
379
- total_initial_value: currentInitialValue,
380
- total_remaining_balance: currentBalance,
381
- utilization_rate: currentUtilizationRate,
382
- card_count: currentCardCount,
383
- average_initial_value: currentAverageInitial,
384
- average_remaining_balance: currentAverageBalance,
385
- },
386
- previous_period: {
387
- total_initial_value: previousInitialValue,
388
- total_remaining_balance: previousBalance,
389
- utilization_rate: previousUtilizationRate,
390
- card_count: previousCardCount,
391
- },
392
- comparison: {
393
- initial_value: {
394
- difference: initialValueDifference,
395
- percentage: Math.abs(initialValuePercentageChange),
396
- is_increase: initialValueDifference >= 0,
397
- },
398
- balance: {
399
- difference: balanceDifference,
400
- percentage: Math.abs(balancePercentageChange),
401
- is_increase: balanceDifference >= 0,
402
- },
403
- card_count: {
404
- difference: cardCountDifference,
405
- percentage: Math.abs(cardCountPercentageChange),
406
- is_increase: cardCountDifference >= 0,
407
- },
408
- },
409
- days_range: _daysRange,
410
- }
411
- }
412
-
413
- /**
414
- * Get total value of all gift cards
415
- */
416
- export async function fetchTotalValue(): Promise<{
417
- total_value: number
418
- }> {
419
- const { total_value } = await aggregateStats('gift_cards', { total_value: { kind: 'sum', column: 'initial_balance' } })
420
- return { total_value }
421
- }
422
-
423
- /**
424
- * Get total current balance of all gift cards
425
- */
426
- export async function fetchTotalCurrentBalance(): Promise<{
427
- total_balance: number
428
- }> {
429
- const { total_balance } = await aggregateStats('gift_cards', { total_balance: { kind: 'sum', column: 'current_balance' } })
430
- return { total_balance }
431
- }
@@ -1,26 +0,0 @@
1
- export {
2
- bulkDestroy,
3
- destroy,
4
- } from './destroy'
5
-
6
- export {
7
- calculateGiftCardValues,
8
- checkBalance,
9
- compareActiveGiftCards,
10
- fetchActive,
11
- fetchAll,
12
- fetchByCode,
13
- fetchById,
14
- fetchStats,
15
- fetchTotalCurrentBalance,
16
- fetchTotalValue,
17
- } from './fetch'
18
-
19
- export {
20
- store,
21
- } from './store'
22
-
23
- export {
24
- update,
25
- updateBalance,
26
- } from './update'
@@ -1,56 +0,0 @@
1
- type GiftCardJsonResponse = ModelRow<typeof GiftCard>
2
- type NewGiftCard = NewModelData<typeof GiftCard>
3
- import { randomUUIDv7 } from 'bun'
4
- import { db } from '@stacksjs/database'
5
- import { HttpError } from '@stacksjs/error-handling'
6
- import { isUniqueViolation } from '@stacksjs/orm'
7
-
8
- /**
9
- * Create a new gift card
10
- *
11
- * @param data The gift card data to store
12
- * @returns The newly created gift card record
13
- */
14
- export async function store(data: NewGiftCard): Promise<GiftCardJsonResponse | undefined> {
15
- const d = data as Record<string, unknown>
16
- const giftCardData = {
17
- ...data,
18
- current_balance: d.current_balance ?? d.initial_balance,
19
- status: data.status || 'ACTIVE',
20
- is_active: d.is_active ?? true,
21
- uuid: randomUUIDv7(),
22
- }
23
-
24
- try {
25
- // Insert the gift card record
26
- const createdGiftCard = await db
27
- .insertInto('gift_cards')
28
- .values(giftCardData)
29
- .executeTakeFirst()
30
-
31
- const insertId = Number(createdGiftCard.insertId) || Number(createdGiftCard.numInsertedOrUpdatedRows)
32
-
33
- // If insert was successful, retrieve the newly created gift card
34
- if (insertId) {
35
- const giftCard = await db
36
- .selectFrom('gift_cards')
37
- .where('id', '=', insertId)
38
- .selectAll()
39
- .executeTakeFirst()
40
-
41
- return giftCard as GiftCardJsonResponse
42
- }
43
-
44
- return undefined
45
- }
46
- catch (error) {
47
- if (error instanceof HttpError)
48
- throw error
49
- // Cross-dialect duplicate detection (#1957).
50
- if (isUniqueViolation(error))
51
- throw new HttpError(409, 'A gift card with this code already exists')
52
- if (error instanceof Error)
53
- throw new Error(`Failed to create gift card: ${error.message}`)
54
- throw error
55
- }
56
- }
@@ -1,102 +0,0 @@
1
- import type { StacksExpressionBuilder } from '@stacksjs/database'
2
- import { db } from '@stacksjs/database'
3
- import { HttpError } from '@stacksjs/error-handling'
4
- import { formatDate, isUniqueViolation } from '@stacksjs/orm'
5
- type GiftCardJsonResponse = ModelRow<typeof GiftCard>
6
- type GiftCardUpdate = UpdateModelData<typeof GiftCard>
7
- import { fetchById } from './fetch'
8
-
9
- /**
10
- * Update a gift card by ID
11
- *
12
- * @param id The ID of the gift card to update
13
- * @param data The updated gift card data
14
- * @returns The updated gift card record
15
- */
16
- export async function update(id: number, data: Omit<GiftCardUpdate, 'id'>): Promise<GiftCardJsonResponse | undefined> {
17
- // Check if gift card exists
18
- const existingGiftCard = await fetchById(id)
19
- if (!existingGiftCard) {
20
- throw new Error(`Gift card with ID ${id} not found`)
21
- }
22
-
23
- try {
24
- // Update the gift card
25
- await db
26
- .updateTable('gift_cards')
27
- .set({
28
- ...data,
29
- updated_at: formatDate(new Date()),
30
- })
31
- .where('id', '=', id)
32
- .execute()
33
-
34
- // Fetch and return the updated gift card
35
- return await fetchById(id)
36
- }
37
- catch (error) {
38
- if (error instanceof HttpError)
39
- throw error
40
- // Cross-dialect duplicate detection (#1957).
41
- if (isUniqueViolation(error))
42
- throw new HttpError(409, 'A gift card with this code already exists')
43
- if (error instanceof Error)
44
- throw new Error(`Failed to update gift card: ${error.message}`)
45
- throw error
46
- }
47
- }
48
-
49
- /**
50
- * Update a gift card's balance atomically (stacksjs/stacks#1879 Co-8).
51
- *
52
- * Pre-fix: read balance → compute new → write. Two parallel $50
53
- * redemptions of a $100 card both saw `current_balance = 100`,
54
- * both wrote `50`, leaving `50` instead of `0`. The post-read
55
- * negative-balance guard only caught single-threaded misuse.
56
- *
57
- * Post-fix: single conditional UPDATE that does the arithmetic and
58
- * enforces every precondition in the WHERE clause. The database
59
- * guarantees only one writer wins the race. Throws when the row
60
- * was found but the precondition failed (insufficient balance,
61
- * inactive, missing) so the caller can distinguish from a
62
- * not-found row.
63
- *
64
- * @param id The ID of the gift card
65
- * @param amount The amount to adjust (positive to add, negative to deduct)
66
- * @returns The updated gift card with new balance
67
- */
68
- export async function updateBalance(id: number, amount: number): Promise<GiftCardJsonResponse | undefined> {
69
- const now = formatDate(new Date())
70
-
71
- // Single atomic UPDATE — the arithmetic happens server-side via
72
- // `current_balance + ?`, and the `>= 0` predicate stops over-spend.
73
- // The new status flips to USED when the resulting balance is 0,
74
- // computed inline via CASE so the status update is part of the
75
- // same statement.
76
- const result = await db
77
- .updateTable('gift_cards')
78
- .set({
79
- current_balance: (db as any).raw(`current_balance + ${amount}`),
80
- last_used_date: now,
81
- status: (db as any).raw(`CASE WHEN current_balance + ${amount} = 0 THEN 'USED' ELSE 'ACTIVE' END`),
82
- updated_at: now,
83
- })
84
- .where('id', '=', id)
85
- .where('is_active', '=', 1)
86
- .where('status', '=', 'ACTIVE')
87
- .where((eb: StacksExpressionBuilder) => eb.cmpr(eb.raw(`current_balance + ${amount}`), '>=', 0))
88
- .executeTakeFirst()
89
-
90
- const affected = Number((result as any)?.numUpdatedRows ?? (result as any)?.affectedRows ?? 0)
91
- if (affected > 0) {
92
- return await fetchById(id)
93
- }
94
-
95
- // UPDATE matched zero rows — diagnose for a useful error.
96
- const existing = await fetchById(id)
97
- if (!existing) throw new Error(`Gift card with ID ${id} not found`)
98
- if (!(existing as Record<string, unknown>).is_active || existing.status !== 'ACTIVE')
99
- throw new Error(`Gift card is not active`)
100
- // Fell through every other check — must be insufficient balance.
101
- throw new Error(`Insufficient gift card balance`)
102
- }
package/src/index.ts DELETED
@@ -1,78 +0,0 @@
1
- // Main commerce module index file
2
- import * as coupons from './coupons'
3
- import * as customers from './customers'
4
- import * as devices from './devices'
5
- import * as errors from './errors'
6
- import * as giftCards from './gift-cards'
7
- import * as orders from './orders'
8
- import * as payments from './payments'
9
- import * as products from './products'
10
- import * as receipts from './receipts'
11
- import * as shippings from './shippings'
12
- import * as tax from './tax'
13
- import * as waitlists from './waitlists'
14
- import * as restaurant from './waitlists/restaurant'
15
-
16
- type CouponsModule = typeof coupons
17
- type CustomersModule = typeof customers
18
- type ErrorsModule = typeof errors
19
- type ShippingsModule = typeof shippings
20
- type GiftCardsModule = typeof giftCards
21
- type OrdersModule = typeof orders
22
- type PaymentsModule = typeof payments
23
- type ProductsModule = typeof products
24
- type RestaurantModule = typeof restaurant
25
- type TaxModule = typeof tax
26
- type WaitlistsModule = typeof waitlists
27
- type DevicesModule = typeof devices
28
- type ReceiptsModule = typeof receipts
29
-
30
- export interface CommerceNamespace {
31
- coupons: CouponsModule
32
- customers: CustomersModule
33
- errors: ErrorsModule
34
- giftCards: GiftCardsModule
35
- orders: OrdersModule
36
- payments: PaymentsModule
37
- products: ProductsModule
38
- restaurant: RestaurantModule
39
- shippings: ShippingsModule
40
- tax: TaxModule
41
- waitlists: WaitlistsModule
42
- devices: DevicesModule
43
- receipts: ReceiptsModule
44
- }
45
-
46
- export const commerce: CommerceNamespace = {
47
- coupons,
48
- customers,
49
- devices,
50
- errors,
51
- giftCards,
52
- orders,
53
- payments,
54
- products,
55
- receipts,
56
- restaurant,
57
- shippings,
58
- tax,
59
- waitlists,
60
- }
61
-
62
- export default commerce
63
-
64
- export {
65
- coupons,
66
- customers,
67
- devices,
68
- errors,
69
- giftCards,
70
- orders,
71
- payments,
72
- products,
73
- receipts,
74
- restaurant,
75
- shippings,
76
- tax,
77
- waitlists,
78
- }