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 type CouponUsageRow = InferSelectModel type CouponApplicableUserRow = InferSelectModel type CouponApplicableProductRow = InferSelectModel type ReservedCouponRow = InferSelectModel 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 { 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 { 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 { 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 { 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) }