@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.
- package/package.json +10 -7
- package/src/carts/destroy.ts +0 -54
- package/src/carts/fetch.ts +0 -20
- package/src/carts/index.ts +0 -13
- package/src/carts/store.ts +0 -75
- package/src/carts/update.ts +0 -78
- package/src/coupons/destroy.ts +0 -49
- package/src/coupons/fetch.ts +0 -510
- package/src/coupons/index.ts +0 -30
- package/src/coupons/store.ts +0 -46
- package/src/coupons/update.ts +0 -147
- package/src/customers/destroy.ts +0 -62
- package/src/customers/fetch.ts +0 -20
- package/src/customers/index.ts +0 -17
- package/src/customers/store.ts +0 -45
- package/src/customers/update.ts +0 -44
- package/src/devices/destroy.ts +0 -54
- package/src/devices/export.ts +0 -98
- package/src/devices/fetch.ts +0 -125
- package/src/devices/index.ts +0 -31
- package/src/devices/store.ts +0 -82
- package/src/devices/update.ts +0 -125
- package/src/errors/destroy.ts +0 -54
- package/src/errors/fetch.ts +0 -197
- package/src/errors/index.ts +0 -28
- package/src/errors/update.ts +0 -116
- package/src/gift-cards/destroy.ts +0 -87
- package/src/gift-cards/fetch.ts +0 -431
- package/src/gift-cards/index.ts +0 -26
- package/src/gift-cards/store.ts +0 -56
- package/src/gift-cards/update.ts +0 -102
- package/src/index.ts +0 -78
- package/src/orders/destroy.ts +0 -111
- package/src/orders/events.ts +0 -121
- package/src/orders/export.ts +0 -158
- package/src/orders/fetch.ts +0 -379
- package/src/orders/guards.ts +0 -261
- package/src/orders/index.ts +0 -91
- package/src/orders/place-order.ts +0 -274
- package/src/orders/store.ts +0 -51
- package/src/orders/totals.ts +0 -190
- package/src/orders/update.ts +0 -164
- package/src/orders/webhook.ts +0 -312
- package/src/payments/destroy.ts +0 -56
- package/src/payments/fetch.ts +0 -288
- package/src/payments/index.ts +0 -16
- package/src/payments/store.ts +0 -63
- package/src/payments/update.ts +0 -52
- package/src/product.ts +0 -0
- package/src/products/categories/destroy.ts +0 -103
- package/src/products/categories/fetch.ts +0 -285
- package/src/products/categories/index.ts +0 -36
- package/src/products/categories/store.ts +0 -87
- package/src/products/categories/update.ts +0 -251
- package/src/products/index.ts +0 -35
- package/src/products/items/destroy.ts +0 -54
- package/src/products/items/fetch.ts +0 -282
- package/src/products/items/index.ts +0 -26
- package/src/products/items/store.ts +0 -84
- package/src/products/items/update.ts +0 -324
- package/src/products/manufacturers/destroy.ts +0 -56
- package/src/products/manufacturers/fetch.ts +0 -117
- package/src/products/manufacturers/index.ts +0 -24
- package/src/products/manufacturers/store.ts +0 -82
- package/src/products/manufacturers/update.ts +0 -113
- package/src/products/reviews/destroy.ts +0 -56
- package/src/products/reviews/fetch.ts +0 -63
- package/src/products/reviews/index.ts +0 -25
- package/src/products/reviews/store.ts +0 -46
- package/src/products/reviews/update.ts +0 -82
- package/src/products/units/destroy.ts +0 -56
- package/src/products/units/fetch.ts +0 -20
- package/src/products/units/index.ts +0 -13
- package/src/products/units/store.ts +0 -162
- package/src/products/units/update.ts +0 -159
- package/src/products/variants/destroy.ts +0 -56
- package/src/products/variants/fetch.ts +0 -20
- package/src/products/variants/index.ts +0 -21
- package/src/products/variants/store.ts +0 -136
- package/src/products/variants/update.ts +0 -104
- package/src/receipts/destroy.ts +0 -54
- package/src/receipts/export.ts +0 -0
- package/src/receipts/fetch.ts +0 -222
- package/src/receipts/index.ts +0 -24
- package/src/receipts/printer.ts +0 -69
- package/src/receipts/store.ts +0 -69
- package/src/receipts/update.ts +0 -124
- package/src/shippings/delivery-routes/destroy.ts +0 -54
- package/src/shippings/delivery-routes/fetch.ts +0 -42
- package/src/shippings/delivery-routes/index.ts +0 -26
- package/src/shippings/delivery-routes/store.ts +0 -69
- package/src/shippings/delivery-routes/update.ts +0 -120
- package/src/shippings/digital-deliveries/destroy.ts +0 -111
- package/src/shippings/digital-deliveries/fetch.ts +0 -20
- package/src/shippings/digital-deliveries/index.ts +0 -26
- package/src/shippings/digital-deliveries/store.ts +0 -76
- package/src/shippings/digital-deliveries/update.ts +0 -129
- package/src/shippings/drivers/destroy.ts +0 -78
- package/src/shippings/drivers/fetch.ts +0 -20
- package/src/shippings/drivers/index.ts +0 -24
- package/src/shippings/drivers/store.ts +0 -69
- package/src/shippings/drivers/update.ts +0 -117
- package/src/shippings/index.ts +0 -39
- package/src/shippings/license-keys/destroy.ts +0 -111
- package/src/shippings/license-keys/fetch.ts +0 -20
- package/src/shippings/license-keys/index.ts +0 -26
- package/src/shippings/license-keys/store.ts +0 -77
- package/src/shippings/license-keys/update.ts +0 -117
- package/src/shippings/shipping-methods/destroy.ts +0 -111
- package/src/shippings/shipping-methods/fetch.ts +0 -62
- package/src/shippings/shipping-methods/index.ts +0 -28
- package/src/shippings/shipping-methods/store.ts +0 -123
- package/src/shippings/shipping-methods/update.ts +0 -120
- package/src/shippings/shipping-rates/destroy.ts +0 -102
- package/src/shippings/shipping-rates/fetch.ts +0 -320
- package/src/shippings/shipping-rates/index.ts +0 -36
- package/src/shippings/shipping-rates/store.ts +0 -102
- package/src/shippings/shipping-rates/update.ts +0 -139
- package/src/shippings/shipping-zones/destroy.ts +0 -111
- package/src/shippings/shipping-zones/fetch.ts +0 -96
- package/src/shippings/shipping-zones/index.ts +0 -26
- package/src/shippings/shipping-zones/store.ts +0 -74
- package/src/shippings/shipping-zones/update.ts +0 -156
- package/src/tax/destroy.ts +0 -54
- package/src/tax/fetch.ts +0 -20
- package/src/tax/index.ts +0 -24
- package/src/tax/store.ts +0 -69
- package/src/tax/update.ts +0 -119
- package/src/tests/coupons.test.ts +0 -90
- package/src/tests/customers.test.ts +0 -39
- package/src/tests/device.test.ts +0 -111
- package/src/tests/digitals.test.ts +0 -31
- package/src/tests/drivers.test.ts +0 -25
- package/src/tests/gift-cards.test.ts +0 -36
- package/src/tests/license.test.ts +0 -31
- package/src/tests/manufacturers.test.ts +0 -80
- package/src/tests/orders.test.ts +0 -25
- package/src/tests/payments.test.ts +0 -75
- package/src/tests/rates.test.ts +0 -367
- package/src/tests/receipts.test.ts +0 -287
- package/src/tests/restaurant.test.ts +0 -522
- package/src/tests/reviews.test.ts +0 -165
- package/src/tests/routes.test.ts +0 -51
- package/src/tests/setup.ts +0 -149
- package/src/tests/shipping.test.ts +0 -88
- package/src/tests/tax.test.ts +0 -25
- package/src/tests/units.test.ts +0 -110
- package/src/tests/variants.test.ts +0 -133
- package/src/tests/waitlist.test.ts +0 -303
- package/src/tests/zones.test.ts +0 -86
- package/src/types.ts +0 -312
- package/src/utils/typed-stats.ts +0 -67
- package/src/waitlists/index.ts +0 -19
- package/src/waitlists/products/destroy.ts +0 -65
- package/src/waitlists/products/export.ts +0 -113
- package/src/waitlists/products/fetch.ts +0 -362
- package/src/waitlists/products/index.ts +0 -33
- package/src/waitlists/products/store.ts +0 -112
- package/src/waitlists/products/update.ts +0 -125
- package/src/waitlists/restaurant/destroy.ts +0 -65
- package/src/waitlists/restaurant/export.ts +0 -107
- package/src/waitlists/restaurant/fetch.ts +0 -538
- package/src/waitlists/restaurant/index.ts +0 -43
- package/src/waitlists/restaurant/store.ts +0 -109
- package/src/waitlists/restaurant/update.ts +0 -198
- /package/dist/{src/coupons → coupons}/destroy.d.ts +0 -0
- /package/dist/{src/coupons → coupons}/fetch.d.ts +0 -0
- /package/dist/{src/coupons → coupons}/index.d.ts +0 -0
- /package/dist/{src/coupons → coupons}/store.d.ts +0 -0
- /package/dist/{src/coupons → coupons}/update.d.ts +0 -0
- /package/dist/{src/customers → customers}/destroy.d.ts +0 -0
- /package/dist/{src/customers → customers}/fetch.d.ts +0 -0
- /package/dist/{src/customers → customers}/index.d.ts +0 -0
- /package/dist/{src/customers → customers}/store.d.ts +0 -0
- /package/dist/{src/customers → customers}/update.d.ts +0 -0
- /package/dist/{src/devices → devices}/destroy.d.ts +0 -0
- /package/dist/{src/devices → devices}/export.d.ts +0 -0
- /package/dist/{src/devices → devices}/fetch.d.ts +0 -0
- /package/dist/{src/devices → devices}/index.d.ts +0 -0
- /package/dist/{src/devices → devices}/store.d.ts +0 -0
- /package/dist/{src/devices → devices}/update.d.ts +0 -0
- /package/dist/{src/errors → errors}/destroy.d.ts +0 -0
- /package/dist/{src/errors → errors}/fetch.d.ts +0 -0
- /package/dist/{src/errors → errors}/index.d.ts +0 -0
- /package/dist/{src/errors → errors}/update.d.ts +0 -0
- /package/dist/{src/gift-cards → gift-cards}/destroy.d.ts +0 -0
- /package/dist/{src/gift-cards → gift-cards}/fetch.d.ts +0 -0
- /package/dist/{src/gift-cards → gift-cards}/index.d.ts +0 -0
- /package/dist/{src/gift-cards → gift-cards}/store.d.ts +0 -0
- /package/dist/{src/gift-cards → gift-cards}/update.d.ts +0 -0
- /package/dist/{src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{src/orders → orders}/destroy.d.ts +0 -0
- /package/dist/{src/orders → orders}/events.d.ts +0 -0
- /package/dist/{src/orders → orders}/export.d.ts +0 -0
- /package/dist/{src/orders → orders}/fetch.d.ts +0 -0
- /package/dist/{src/orders → orders}/guards.d.ts +0 -0
- /package/dist/{src/orders → orders}/index.d.ts +0 -0
- /package/dist/{src/orders → orders}/place-order.d.ts +0 -0
- /package/dist/{src/orders → orders}/store.d.ts +0 -0
- /package/dist/{src/orders → orders}/totals.d.ts +0 -0
- /package/dist/{src/orders → orders}/update.d.ts +0 -0
- /package/dist/{src/orders → orders}/webhook.d.ts +0 -0
- /package/dist/{src/payments → payments}/destroy.d.ts +0 -0
- /package/dist/{src/payments → payments}/fetch.d.ts +0 -0
- /package/dist/{src/payments → payments}/index.d.ts +0 -0
- /package/dist/{src/payments → payments}/store.d.ts +0 -0
- /package/dist/{src/payments → payments}/update.d.ts +0 -0
- /package/dist/{src/products → products}/categories/destroy.d.ts +0 -0
- /package/dist/{src/products → products}/categories/fetch.d.ts +0 -0
- /package/dist/{src/products → products}/categories/index.d.ts +0 -0
- /package/dist/{src/products → products}/categories/store.d.ts +0 -0
- /package/dist/{src/products → products}/categories/update.d.ts +0 -0
- /package/dist/{src/products → products}/index.d.ts +0 -0
- /package/dist/{src/products → products}/items/destroy.d.ts +0 -0
- /package/dist/{src/products → products}/items/fetch.d.ts +0 -0
- /package/dist/{src/products → products}/items/index.d.ts +0 -0
- /package/dist/{src/products → products}/items/store.d.ts +0 -0
- /package/dist/{src/products → products}/items/update.d.ts +0 -0
- /package/dist/{src/products → products}/manufacturers/destroy.d.ts +0 -0
- /package/dist/{src/products → products}/manufacturers/fetch.d.ts +0 -0
- /package/dist/{src/products → products}/manufacturers/index.d.ts +0 -0
- /package/dist/{src/products → products}/manufacturers/store.d.ts +0 -0
- /package/dist/{src/products → products}/manufacturers/update.d.ts +0 -0
- /package/dist/{src/products → products}/reviews/destroy.d.ts +0 -0
- /package/dist/{src/products → products}/reviews/fetch.d.ts +0 -0
- /package/dist/{src/products → products}/reviews/index.d.ts +0 -0
- /package/dist/{src/products → products}/reviews/store.d.ts +0 -0
- /package/dist/{src/products → products}/reviews/update.d.ts +0 -0
- /package/dist/{src/products → products}/units/destroy.d.ts +0 -0
- /package/dist/{src/products → products}/units/fetch.d.ts +0 -0
- /package/dist/{src/products → products}/units/index.d.ts +0 -0
- /package/dist/{src/products → products}/units/store.d.ts +0 -0
- /package/dist/{src/products → products}/units/update.d.ts +0 -0
- /package/dist/{src/products → products}/variants/destroy.d.ts +0 -0
- /package/dist/{src/products → products}/variants/fetch.d.ts +0 -0
- /package/dist/{src/products → products}/variants/index.d.ts +0 -0
- /package/dist/{src/products → products}/variants/store.d.ts +0 -0
- /package/dist/{src/products → products}/variants/update.d.ts +0 -0
- /package/dist/{src/receipts → receipts}/destroy.d.ts +0 -0
- /package/dist/{src/receipts → receipts}/fetch.d.ts +0 -0
- /package/dist/{src/receipts → receipts}/index.d.ts +0 -0
- /package/dist/{src/receipts → receipts}/store.d.ts +0 -0
- /package/dist/{src/receipts → receipts}/update.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/delivery-routes/destroy.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/delivery-routes/fetch.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/delivery-routes/index.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/delivery-routes/store.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/delivery-routes/update.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/digital-deliveries/destroy.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/digital-deliveries/fetch.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/digital-deliveries/index.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/digital-deliveries/store.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/digital-deliveries/update.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/drivers/destroy.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/drivers/fetch.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/drivers/index.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/drivers/store.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/drivers/update.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/index.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/license-keys/destroy.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/license-keys/fetch.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/license-keys/index.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/license-keys/store.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/license-keys/update.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-methods/destroy.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-methods/fetch.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-methods/index.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-methods/store.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-methods/update.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-rates/destroy.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-rates/fetch.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-rates/index.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-rates/store.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-rates/update.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-zones/destroy.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-zones/fetch.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-zones/index.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-zones/store.d.ts +0 -0
- /package/dist/{src/shippings → shippings}/shipping-zones/update.d.ts +0 -0
- /package/dist/{src/tax → tax}/destroy.d.ts +0 -0
- /package/dist/{src/tax → tax}/fetch.d.ts +0 -0
- /package/dist/{src/tax → tax}/index.d.ts +0 -0
- /package/dist/{src/tax → tax}/store.d.ts +0 -0
- /package/dist/{src/tax → tax}/update.d.ts +0 -0
- /package/dist/{src/types.d.ts → types.d.ts} +0 -0
- /package/dist/{src/utils → utils}/typed-stats.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/index.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/products/destroy.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/products/fetch.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/products/index.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/products/store.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/products/update.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/restaurant/destroy.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/restaurant/fetch.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/restaurant/index.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/restaurant/store.d.ts +0 -0
- /package/dist/{src/waitlists → waitlists}/restaurant/update.d.ts +0 -0
package/src/coupons/fetch.ts
DELETED
|
@@ -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
|
-
}
|
package/src/coupons/index.ts
DELETED
|
@@ -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'
|
package/src/coupons/store.ts
DELETED
|
@@ -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
|
-
}
|