freshyo/packages/db_helper_postgres/src/user-apis/coupon.ts
2026-03-26 00:34:31 +05:30

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)
}