146 lines
4.7 KiB
TypeScript
146 lines
4.7 KiB
TypeScript
import { db } from '../db/db_index'
|
|
import {
|
|
couponApplicableProducts,
|
|
couponApplicableUsers,
|
|
couponUsage,
|
|
coupons,
|
|
reservedCoupons,
|
|
} from '../db/schema'
|
|
import { and, eq, gt, isNull, or } from 'drizzle-orm'
|
|
import type { InferSelectModel } from 'drizzle-orm'
|
|
import type { UserCoupon, UserCouponApplicableProduct, UserCouponApplicableUser, UserCouponUsage, UserCouponWithRelations } from '@packages/shared'
|
|
|
|
type CouponRow = InferSelectModel<typeof coupons>
|
|
type CouponUsageRow = InferSelectModel<typeof couponUsage>
|
|
type CouponApplicableUserRow = InferSelectModel<typeof couponApplicableUsers>
|
|
type CouponApplicableProductRow = InferSelectModel<typeof couponApplicableProducts>
|
|
type ReservedCouponRow = InferSelectModel<typeof reservedCoupons>
|
|
|
|
const mapCoupon = (coupon: CouponRow): UserCoupon => ({
|
|
id: coupon.id,
|
|
couponCode: coupon.couponCode,
|
|
isUserBased: coupon.isUserBased,
|
|
discountPercent: coupon.discountPercent ? coupon.discountPercent.toString() : null,
|
|
flatDiscount: coupon.flatDiscount ? coupon.flatDiscount.toString() : null,
|
|
minOrder: coupon.minOrder ? coupon.minOrder.toString() : null,
|
|
productIds: coupon.productIds,
|
|
maxValue: coupon.maxValue ? coupon.maxValue.toString() : null,
|
|
isApplyForAll: coupon.isApplyForAll,
|
|
validTill: coupon.validTill ?? null,
|
|
maxLimitForUser: coupon.maxLimitForUser ?? null,
|
|
isInvalidated: coupon.isInvalidated,
|
|
exclusiveApply: coupon.exclusiveApply,
|
|
createdAt: coupon.createdAt,
|
|
})
|
|
|
|
const mapUsage = (usage: CouponUsageRow): UserCouponUsage => ({
|
|
id: usage.id,
|
|
userId: usage.userId,
|
|
couponId: usage.couponId,
|
|
orderId: usage.orderId ?? null,
|
|
orderItemId: usage.orderItemId ?? null,
|
|
usedAt: usage.usedAt,
|
|
})
|
|
|
|
const mapApplicableUser = (applicable: CouponApplicableUserRow): UserCouponApplicableUser => ({
|
|
id: applicable.id,
|
|
couponId: applicable.couponId,
|
|
userId: applicable.userId,
|
|
})
|
|
|
|
const mapApplicableProduct = (applicable: CouponApplicableProductRow): UserCouponApplicableProduct => ({
|
|
id: applicable.id,
|
|
couponId: applicable.couponId,
|
|
productId: applicable.productId,
|
|
})
|
|
|
|
const mapCouponWithRelations = (coupon: CouponRow & {
|
|
usages: CouponUsageRow[]
|
|
applicableUsers: CouponApplicableUserRow[]
|
|
applicableProducts: CouponApplicableProductRow[]
|
|
}): UserCouponWithRelations => ({
|
|
...mapCoupon(coupon),
|
|
usages: coupon.usages.map(mapUsage),
|
|
applicableUsers: coupon.applicableUsers.map(mapApplicableUser),
|
|
applicableProducts: coupon.applicableProducts.map(mapApplicableProduct),
|
|
})
|
|
|
|
export async function getActiveCouponsWithRelations(userId: number): Promise<UserCouponWithRelations[]> {
|
|
const allCoupons = await db.query.coupons.findMany({
|
|
where: and(
|
|
eq(coupons.isInvalidated, false),
|
|
or(
|
|
isNull(coupons.validTill),
|
|
gt(coupons.validTill, new Date())
|
|
)
|
|
),
|
|
with: {
|
|
usages: {
|
|
where: eq(couponUsage.userId, userId),
|
|
},
|
|
applicableUsers: true,
|
|
applicableProducts: true,
|
|
},
|
|
})
|
|
|
|
return allCoupons.map(mapCouponWithRelations)
|
|
}
|
|
|
|
export async function getAllCouponsWithRelations(userId: number): Promise<UserCouponWithRelations[]> {
|
|
const allCoupons = await db.query.coupons.findMany({
|
|
with: {
|
|
usages: {
|
|
where: eq(couponUsage.userId, userId),
|
|
},
|
|
applicableUsers: true,
|
|
applicableProducts: true,
|
|
},
|
|
})
|
|
|
|
return allCoupons.map(mapCouponWithRelations)
|
|
}
|
|
|
|
export async function getReservedCouponByCode(secretCode: string): Promise<ReservedCouponRow | null> {
|
|
const reserved = await db.query.reservedCoupons.findFirst({
|
|
where: and(
|
|
eq(reservedCoupons.secretCode, secretCode.toUpperCase()),
|
|
eq(reservedCoupons.isRedeemed, false)
|
|
),
|
|
})
|
|
|
|
return reserved || null
|
|
}
|
|
|
|
export async function redeemReservedCoupon(userId: number, reservedCoupon: ReservedCouponRow): Promise<UserCoupon> {
|
|
const couponResult = await db.transaction(async (tx) => {
|
|
const [coupon] = await tx.insert(coupons).values({
|
|
couponCode: reservedCoupon.couponCode,
|
|
isUserBased: true,
|
|
discountPercent: reservedCoupon.discountPercent,
|
|
flatDiscount: reservedCoupon.flatDiscount,
|
|
minOrder: reservedCoupon.minOrder,
|
|
productIds: reservedCoupon.productIds,
|
|
maxValue: reservedCoupon.maxValue,
|
|
isApplyForAll: false,
|
|
validTill: reservedCoupon.validTill,
|
|
maxLimitForUser: reservedCoupon.maxLimitForUser,
|
|
exclusiveApply: reservedCoupon.exclusiveApply,
|
|
createdBy: reservedCoupon.createdBy,
|
|
}).returning()
|
|
|
|
await tx.insert(couponApplicableUsers).values({
|
|
couponId: coupon.id,
|
|
userId,
|
|
})
|
|
|
|
await tx.update(reservedCoupons).set({
|
|
isRedeemed: true,
|
|
redeemedBy: userId,
|
|
redeemedAt: new Date(),
|
|
}).where(eq(reservedCoupons.id, reservedCoupon.id))
|
|
|
|
return coupon
|
|
})
|
|
|
|
return mapCoupon(couponResult)
|
|
}
|