Files
domain-wall/packages/logic/domains/coupon/repository.ts
2025-10-20 18:59:38 +03:00

492 lines
14 KiB
TypeScript

import {
and,
asc,
desc,
eq,
gte,
isNull,
lte,
or,
type Database,
} from "@pkg/db";
import { coupon } from "@pkg/db/schema";
import { getError, Logger } from "@pkg/logger";
import { ERROR_CODES, type Result } from "@pkg/result";
import {
couponModel,
type CouponModel,
type CreateCouponPayload,
type UpdateCouponPayload,
} from "./data";
export class CouponRepository {
private db: Database;
constructor(db: Database) {
this.db = db;
}
async getAllCoupons(): Promise<Result<CouponModel[]>> {
try {
const results = await this.db.query.coupon.findMany({
orderBy: [desc(coupon.createdAt)],
});
const out = [] as CouponModel[];
for (const result of results) {
const parsed = couponModel.safeParse(result);
if (!parsed.success) {
Logger.error("Failed to parse coupon");
Logger.error(parsed.error);
continue;
}
out.push(parsed.data);
}
return { data: out };
} catch (e) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "Failed to fetch coupons",
detail:
"An error occurred while retrieving coupons from the database",
userHint: "Please try refreshing the page",
actionable: false,
},
e,
),
};
}
}
async getCouponById(id: number): Promise<Result<CouponModel>> {
try {
const result = await this.db.query.coupon.findFirst({
where: eq(coupon.id, id),
});
if (!result) {
return {
error: getError({
code: ERROR_CODES.NOT_FOUND_ERROR,
message: "Coupon not found",
detail: "No coupon exists with the provided ID",
userHint: "Please check the coupon ID and try again",
actionable: true,
}),
};
}
const parsed = couponModel.safeParse(result);
if (!parsed.success) {
Logger.error("Failed to parse coupon", result);
return {
error: getError({
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
message: "Failed to parse coupon",
userHint: "Please try again",
detail: "Failed to parse coupon",
}),
};
}
return { data: parsed.data };
} catch (e) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "Failed to fetch coupon",
detail:
"An error occurred while retrieving the coupon from the database",
userHint: "Please try refreshing the page",
actionable: false,
},
e,
),
};
}
}
async createCoupon(payload: CreateCouponPayload): Promise<Result<number>> {
try {
// Check if coupon code already exists
const existing = await this.db.query.coupon.findFirst({
where: eq(coupon.code, payload.code),
});
if (existing) {
return {
error: getError({
code: ERROR_CODES.DATABASE_ERROR,
message: "Coupon code already exists",
detail: "A coupon with this code already exists in the system",
userHint: "Please use a different coupon code",
actionable: true,
}),
};
}
const result = await this.db
.insert(coupon)
.values({
code: payload.code,
description: payload.description || null,
discountType: payload.discountType,
discountValue: payload.discountValue.toString(),
maxUsageCount: payload.maxUsageCount,
minOrderValue: payload.minOrderValue
? payload.minOrderValue.toString()
: null,
maxDiscountAmount: payload.maxDiscountAmount
? payload.maxDiscountAmount.toString()
: null,
startDate: new Date(payload.startDate),
endDate: payload.endDate ? new Date(payload.endDate) : null,
isActive: payload.isActive,
createdBy: payload.createdBy || null,
})
.returning({ id: coupon.id })
.execute();
if (!result || result.length === 0) {
throw new Error("Failed to create coupon record");
}
return { data: result[0].id };
} catch (e) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "Failed to create coupon",
detail: "An error occurred while creating the coupon",
userHint: "Please try again",
actionable: false,
},
e,
),
};
}
}
async updateCoupon(payload: UpdateCouponPayload): Promise<Result<boolean>> {
try {
if (!payload.id) {
return {
error: getError({
code: ERROR_CODES.VALIDATION_ERROR,
message: "Invalid coupon ID",
detail: "No coupon ID was provided for the update operation",
userHint: "Please provide a valid coupon ID",
actionable: true,
}),
};
}
// Check if coupon exists
const existing = await this.db.query.coupon.findFirst({
where: eq(coupon.id, payload.id),
});
if (!existing) {
return {
error: getError({
code: ERROR_CODES.NOT_FOUND_ERROR,
message: "Coupon not found",
detail: "No coupon exists with the provided ID",
userHint: "Please check the coupon ID and try again",
actionable: true,
}),
};
}
// If changing the code, check if the new code already exists
if (payload.code && payload.code !== existing.code) {
const codeExists = await this.db.query.coupon.findFirst({
where: eq(coupon.code, payload.code),
});
if (codeExists) {
return {
error: getError({
code: ERROR_CODES.DATABASE_ERROR,
message: "Coupon code already exists",
detail: "A coupon with this code already exists in the system",
userHint: "Please use a different coupon code",
actionable: true,
}),
};
}
}
// Build the update object with only the fields that are provided
const updateValues: Record<string, any> = {};
if (payload.code !== undefined) updateValues.code = payload.code;
if (payload.description !== undefined)
updateValues.description = payload.description;
if (payload.discountType !== undefined)
updateValues.discountType = payload.discountType;
if (payload.discountValue !== undefined)
updateValues.discountValue = payload.discountValue.toString();
if (payload.maxUsageCount !== undefined)
updateValues.maxUsageCount = payload.maxUsageCount;
if (payload.minOrderValue !== undefined)
updateValues.minOrderValue = payload.minOrderValue?.toString() || null;
if (payload.maxDiscountAmount !== undefined)
updateValues.maxDiscountAmount =
payload.maxDiscountAmount?.toString() || null;
if (payload.startDate !== undefined)
updateValues.startDate = new Date(payload.startDate);
if (payload.endDate !== undefined)
updateValues.endDate = payload.endDate
? new Date(payload.endDate)
: null;
if (payload.isActive !== undefined)
updateValues.isActive = payload.isActive;
updateValues.updatedAt = new Date();
await this.db
.update(coupon)
.set(updateValues)
.where(eq(coupon.id, payload.id))
.execute();
return { data: true };
} catch (e) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "Failed to update coupon",
detail: "An error occurred while updating the coupon",
userHint: "Please try again",
actionable: false,
},
e,
),
};
}
}
async deleteCoupon(id: number): Promise<Result<boolean>> {
try {
// Check if coupon exists
const existing = await this.db.query.coupon.findFirst({
where: eq(coupon.id, id),
});
if (!existing) {
return {
error: getError({
code: ERROR_CODES.NOT_FOUND_ERROR,
message: "Coupon not found",
detail: "No coupon exists with the provided ID",
userHint: "Please check the coupon ID and try again",
actionable: true,
}),
};
}
await this.db.delete(coupon).where(eq(coupon.id, id)).execute();
return { data: true };
} catch (e) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "Failed to delete coupon",
detail: "An error occurred while deleting the coupon",
userHint: "Please try again",
actionable: false,
},
e,
),
};
}
}
async toggleCouponStatus(
id: number,
isActive: boolean,
): Promise<Result<boolean>> {
try {
// Check if coupon exists
const existing = await this.db.query.coupon.findFirst({
where: eq(coupon.id, id),
});
if (!existing) {
return {
error: getError({
code: ERROR_CODES.NOT_FOUND_ERROR,
message: "Coupon not found",
detail: "No coupon exists with the provided ID",
userHint: "Please check the coupon ID and try again",
actionable: true,
}),
};
}
await this.db
.update(coupon)
.set({
isActive,
updatedAt: new Date(),
})
.where(eq(coupon.id, id))
.execute();
return { data: true };
} catch (e) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "Failed to update coupon status",
detail: "An error occurred while updating the coupon's status",
userHint: "Please try again",
actionable: false,
},
e,
),
};
}
}
async getCouponByCode(code: string): Promise<Result<CouponModel>> {
try {
const result = await this.db.query.coupon.findFirst({
where: eq(coupon.code, code),
});
if (!result) {
return {
error: getError({
code: ERROR_CODES.NOT_FOUND_ERROR,
message: "Coupon not found",
detail: "No coupon exists with the provided code",
userHint: "Please check the coupon code and try again",
actionable: true,
}),
};
}
const parsed = couponModel.safeParse(result);
if (!parsed.success) {
Logger.error("Failed to parse coupon", result);
return {
error: getError({
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
message: "Failed to parse coupon",
userHint: "Please try again",
detail: "Failed to parse coupon",
}),
};
}
return { data: parsed.data };
} catch (e) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "Failed to fetch coupon",
detail:
"An error occurred while retrieving the coupon from the database",
userHint: "Please try again",
actionable: false,
},
e,
),
};
}
}
async getActiveCoupons(): Promise<Result<CouponModel[]>> {
try {
const now = new Date();
const results = await this.db.query.coupon.findMany({
where: and(
eq(coupon.isActive, true),
lte(coupon.startDate, now),
// Either endDate is null (no end date) or it's greater than now
or(isNull(coupon.endDate), gte(coupon.endDate, now)),
),
orderBy: [asc(coupon.code)],
});
const out = [] as CouponModel[];
for (const result of results) {
const parsed = couponModel.safeParse(result);
if (!parsed.success) {
Logger.error("Failed to parse coupon", result);
continue;
}
out.push(parsed.data);
}
return { data: out };
} catch (e) {
return {
error: getError(
{
code: ERROR_CODES.DATABASE_ERROR,
message: "Failed to fetch active coupons",
detail:
"An error occurred while retrieving active coupons from the database",
userHint: "Please try refreshing the page",
actionable: false,
},
e,
),
};
}
}
async getBestActiveCoupon(): Promise<Result<CouponModel>> {
try {
const now = new Date();
// Fetch all active coupons that are currently valid
const activeCoupons = await this.db.query.coupon.findMany({
where: and(
eq(coupon.isActive, true),
lte(coupon.startDate, now),
// Either endDate is null (no end date) or it's greater than now
or(isNull(coupon.endDate), gte(coupon.endDate, now)),
),
orderBy: [
// Order by discount type (PERCENTAGE first) and then by discount value (descending)
asc(coupon.discountType),
desc(coupon.discountValue),
],
});
if (!activeCoupons || activeCoupons.length === 0) {
return {}; // No active coupons found
}
// Get the first (best) coupon
const bestCoupon = activeCoupons[0];
// Check if max usage limit is reached
if (
bestCoupon.maxUsageCount !== null &&
bestCoupon.currentUsageCount >= bestCoupon.maxUsageCount
) {
return {}; // Coupon usage limit reached
}
const parsed = couponModel.safeParse(bestCoupon);
if (!parsed.success) {
Logger.error("Failed to parse coupon", bestCoupon);
return {}; // Return null on error, don't break ticket search
}
return { data: parsed.data };
} catch (e) {
Logger.error("Error fetching active coupons", e);
return {}; // Return null on error, don't break ticket search
}
}
}