update: the danak services module and add new route to fetch the danak service
This commit is contained in:
@@ -25,6 +25,7 @@ import { ContactUsModule } from "./modules/contact-us/contact-us.module";
|
||||
import { CriticismModule } from "./modules/criticisms/criticisms.module";
|
||||
import { DanakServicesModule } from "./modules/danak-services/danak-services.module";
|
||||
import { DashboardModule } from "./modules/dashboards/dashboard.module";
|
||||
import { DiscountsModule } from "./modules/discounts/discounts.module";
|
||||
import { InvoicesModule } from "./modules/invoices/invoices.module";
|
||||
import { LandingModule } from "./modules/landing/landing.module";
|
||||
import { LearningModule } from "./modules/learnings/learning.module";
|
||||
@@ -72,6 +73,7 @@ import { WalletsModule } from "./modules/wallets/wallets.module";
|
||||
// LoggerModule,
|
||||
LandingModule,
|
||||
ReferralsModule,
|
||||
DiscountsModule,
|
||||
],
|
||||
controllers: [],
|
||||
})
|
||||
|
||||
@@ -484,36 +484,20 @@ export const enum LearningMessage {
|
||||
}
|
||||
|
||||
export const enum DiscountMessage {
|
||||
CAN_NOT_APPLICABLE = "این کد تخفیف برای این صورت حساب قابل اعمال نیست",
|
||||
TYPE_REQUIRED = "وارد کردن نوع تخفیف الزامی است",
|
||||
TYPE_INVALID = "نوع تخفیف نامعتبر است",
|
||||
CALCULATION_TYPE_REQUIRED = "وارد کردن نوع محاسبه تخفیف الزامی است",
|
||||
CALCULATION_TYPE_INVALID = "نوع محاسبه تخفیف نامعتبر است",
|
||||
AMOUNT_REQUIRED = "وارد کردن مقدار تخفیف الزامی است",
|
||||
AMOUNT_INVALID = "مقدار تخفیف باید عدد باشد",
|
||||
TITLE_REQUIRED = "وارد کردن مقدار عنوان الزامی است",
|
||||
TITLE_INVALID = "مقدار عنوان باید یک رشته باشد",
|
||||
CODE_REQUIRED = "وارد کردن کد تخفیف الزامی است",
|
||||
CODE_INVALID = "کد تخفیف نامعتبر است",
|
||||
START_DATE_REQUIRED = "وارد کردن تاریخ شروع الزامی است",
|
||||
START_DATE_INVALID = "تاریخ شروع نامعتبر است",
|
||||
END_DATE_REQUIRED = "وارد کردن تاریخ پایان الزامی است",
|
||||
END_DATE_INVALID = "تاریخ پایان نامعتبر است",
|
||||
SERVICES_INVALID = "لیست خدمات باید آرایهای از ایدی باشد",
|
||||
SERVICES_DUPLICATE = "خدمات نباید تکراری باشند",
|
||||
SERVICES_UUID_INVALID = "ایدی سرویس معتبرنیست",
|
||||
USER_UUID_INVALID = "ایدی کاربر معتبرنیست",
|
||||
USER_DUPLICATE = "کاربر نباید تکراری باشند",
|
||||
USER_INVALID = "لیست کاربرا باید آرایهای از ایدی باشد",
|
||||
NOT_FOUND = "کد تخفیف با این مشخصات یافت نشد",
|
||||
CODE_EXIST = "کد تخفیف قبلا ایجاد شده است",
|
||||
INVOICE_ID_REQUIRED = "شناسه صورت حساب نمیتواند خالی باشد",
|
||||
INVOICE_ID_STRING = "شناسه صورت حساب باید یک رشته باشد",
|
||||
DISCOUNT_CODE_REQUIRED = "کد تخفیف نمیتواند خالی باشد",
|
||||
DISCOUNT_CODE_STRING = "کد تخفیف باید یک رشته باشد",
|
||||
DISCOUNT_NOT_FOUND = "کد تخفیف یافت نشد یا غیرفعال است",
|
||||
DISCOUNT_NOT_APPLICABLE = "این کد تخفیف برای هیچ یک از پلنهای اشتراک در صورت حساب قابل اعمال نیست",
|
||||
ALREADY_USED = "این کد تخفیف قبلا استفاده شده است",
|
||||
TITLE_STRING = "عنوان باید یک رشته باشد",
|
||||
TITLE_REQUIRED = "عنوان الزامی است",
|
||||
DISCOUNT_TYPE_REQUIRED = "نوع تخفیف الزامی است",
|
||||
INVALID_DISCOUNT_TYPE = "نوع تخفیف نامعتبر است",
|
||||
VALUE_MUST_BE_A_NUMBER = "قیمت باید یک عدد صحیح باشد",
|
||||
VALUE_MUST_BE_AT_LEAST_0 = "قیمت باید حداقل ۰ باشد",
|
||||
VALUE_MUST_NOT_EXCEED_100 = "قیمت باید حداکثر ۱۰۰ باشد",
|
||||
START_DATE_MUST_BE_A_VALID_DATE_STRING = "تاریخ شروع باید یک تاریخ معتبر باشد",
|
||||
START_DATE_IS_REQUIRED = "تاریخ شروع الزامی است",
|
||||
END_DATE_MUST_BE_A_VALID_DATE_STRING = "تاریخ پایان باید یک تاریخ معتبر باشد",
|
||||
END_DATE_IS_REQUIRED = "تاریخ پایان الزامی است",
|
||||
IS_ACTIVE_MUST_BE_A_BOOLEAN = "وضعیت فعال بودن باید یک بولین باشد",
|
||||
TARGET_PRODUCTS_MUST_BE_AN_ARRAY = "تعداد محصولات باید یک آرایه باشد",
|
||||
EACH_TARGET_PRODUCT_MUST_BE_A_STRING = "هر محصول باید یک رشته باشد",
|
||||
}
|
||||
|
||||
export const enum EmailMessage {
|
||||
|
||||
@@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions {
|
||||
username: configService.getOrThrow<string>("DB_USER"),
|
||||
password: configService.getOrThrow<string>("DB_PASS"),
|
||||
autoLoadEntities: true,
|
||||
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
||||
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : false,
|
||||
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
||||
migrationsTableName: "typeorm_migrations",
|
||||
migrationsRun: false,
|
||||
|
||||
@@ -141,6 +141,12 @@ export class DanakServicesController {
|
||||
return this.danakServicesService.getDanakServiceByIdWithSubs(paramDto.id, isAdmin, userId);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "get danakService by id public" })
|
||||
@Get(":id/public")
|
||||
getDanakServiceByIdPublic(@Param() paramDto: ParamDto) {
|
||||
return this.danakServicesService.getDanakServiceByIdPublic(paramDto.id);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "update danak service" })
|
||||
@AuthGuards()
|
||||
@PermissionsDec(PermissionEnum.SERVICES)
|
||||
|
||||
@@ -6,6 +6,7 @@ import { DanakServiceReview } from "./danak-service-review.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { Ads } from "../../ads/entities/ads.entity";
|
||||
import { Announcement } from "../../announcements/entities/announcement.entity";
|
||||
import { DiscountService } from "../../discounts/entities/discount-product.entity";
|
||||
import { SubscriptionPlan } from "../../subscriptions/entities/subscription.entity";
|
||||
import { Ticket } from "../../tickets/entities/ticket.entity";
|
||||
import { ServicesLanguage } from "../enums/services-language.enum";
|
||||
@@ -76,6 +77,9 @@ export class DanakService extends BaseEntity {
|
||||
@OneToMany(() => DanakServiceReview, (danakServiceReview) => danakServiceReview.service)
|
||||
reviews: DanakServiceReview[];
|
||||
|
||||
@OneToMany(() => DiscountService, (discountService) => discountService.danakService)
|
||||
discountServices: DiscountService[];
|
||||
|
||||
@DeleteDateColumn()
|
||||
deletedAt?: Date;
|
||||
}
|
||||
|
||||
@@ -328,22 +328,7 @@ export class DanakServicesService {
|
||||
/******************************************** */
|
||||
|
||||
async searchServices(queryDto: DanakServicesGlobalSearchDto) {
|
||||
const servicesQuery = this.danakServicesRepository
|
||||
.createQueryBuilder("service")
|
||||
.leftJoin("service.category", "category")
|
||||
.leftJoin("service.subscriptionPlans", "subscriptionPlans")
|
||||
.select(["service.id", "service.name", "service.description", "service.icon", "category.id", "category.title"])
|
||||
.where("service.isActive = :isActive", { isActive: true })
|
||||
.andWhere("service.deletedAt IS NULL")
|
||||
.andWhere("category.isActive = :categoryActive", { categoryActive: true })
|
||||
.andWhere("category.deletedAt IS NULL")
|
||||
.andWhere("subscriptionPlans.isActive = :planActive", { planActive: true });
|
||||
|
||||
if (queryDto.q) {
|
||||
servicesQuery.andWhere("service.name ILIKE :searchQuery", { searchQuery: `%${queryDto.q}%` });
|
||||
}
|
||||
|
||||
const services = await servicesQuery.getMany();
|
||||
const services = await this.danakServicesRepository.searchServices(queryDto);
|
||||
|
||||
return {
|
||||
services,
|
||||
@@ -381,57 +366,11 @@ export class DanakServicesService {
|
||||
/******************************************** */
|
||||
|
||||
async getDanakServiceByIdWithSubs(serviceId: string, isAdmin: boolean, userId: string) {
|
||||
const serviceQueryBuilder = this.danakServicesRepository
|
||||
.createQueryBuilder("service")
|
||||
.where("service.deletedAt IS NULL")
|
||||
.leftJoin("service.category", "category")
|
||||
.addSelect(["category.id", "category.title"])
|
||||
.leftJoinAndSelect("service.images", "images")
|
||||
.leftJoinAndSelect("service.subscriptionPlans", "subscriptionPlans")
|
||||
.leftJoin("service.reviews", "reviews", "reviews.status = :reviewStatus", { reviewStatus: ReviewStatus.APPROVED })
|
||||
.leftJoin("reviews.user", "user")
|
||||
.addSelect([
|
||||
"reviews.id",
|
||||
"reviews.comment",
|
||||
"reviews.rating",
|
||||
"reviews.title",
|
||||
"reviews.createdAt",
|
||||
"reviews.status",
|
||||
"user.id",
|
||||
"user.firstName",
|
||||
"user.lastName",
|
||||
"user.profilePic",
|
||||
])
|
||||
// .loadRelationCountAndMap("service.reviewCount", "service.reviews")
|
||||
// .addSelect(
|
||||
// (subQuery) =>
|
||||
// subQuery
|
||||
// .select("CAST(AVG(rating) AS DECIMAL(10,2))", "averageRating")
|
||||
// .from(DanakServiceReview, "r")
|
||||
// .where("r.serviceId = service.id")
|
||||
// .andWhere("r.status = :reviewStatus", { reviewStatus: ReviewStatus.APPROVED }),
|
||||
// "averageRating",
|
||||
// )
|
||||
.andWhere("service.id = :serviceId", { serviceId })
|
||||
.groupBy("service.id,category.id, images.id, subscriptionPlans.id, reviews.id, user.id");
|
||||
const danakService = await this.danakServicesRepository.getDanakServiceById(serviceId, isAdmin);
|
||||
|
||||
if (!isAdmin) {
|
||||
serviceQueryBuilder
|
||||
.andWhere("service.isActive = :isActive", { isActive: true })
|
||||
.andWhere("subscriptionPlans.isActive = :planActive", { planActive: true });
|
||||
}
|
||||
|
||||
const danakService = await serviceQueryBuilder.getOne();
|
||||
if (!danakService) throw new BadRequestException(ServiceMessage.SERVICE_NOT_FOUND_BY_ID);
|
||||
|
||||
const averageRatingResult = await this.danakServiceReviewRepository
|
||||
.createQueryBuilder("r")
|
||||
.select("CAST(AVG(r.rating) AS DECIMAL(10,2))", "averageRating")
|
||||
.where("r.serviceId = :serviceId", { serviceId })
|
||||
.andWhere("r.status = :reviewStatus", { reviewStatus: ReviewStatus.APPROVED })
|
||||
.getRawOne();
|
||||
|
||||
const averageRating = parseFloat(averageRatingResult?.averageRating) || 0;
|
||||
const averageRating = await this.danakServiceReviewRepository.getAverageRating(danakService.id);
|
||||
|
||||
const hasSubscription = await this.checkUserPurchasedService(userId, serviceId);
|
||||
|
||||
@@ -443,25 +382,17 @@ export class DanakServicesService {
|
||||
}
|
||||
/******************************************** */
|
||||
|
||||
async getDanakServiceByIdPublic(serviceId: string) {
|
||||
const danakService = await this.danakServicesRepository.getDanakServiceById(serviceId, false);
|
||||
if (!danakService) throw new BadRequestException(ServiceMessage.SERVICE_NOT_FOUND_BY_ID);
|
||||
return { danakService };
|
||||
}
|
||||
/******************************************** */
|
||||
|
||||
async getServiceUsers(serviceId: string) {
|
||||
await this.findServiceById(serviceId);
|
||||
|
||||
const users = await this.danakServicesRepository
|
||||
.createQueryBuilder("service")
|
||||
.innerJoin("service.subscriptionPlans", "subscriptionPlans")
|
||||
.innerJoin("subscriptionPlans.userSubscriptions", "userSubscriptions")
|
||||
.innerJoin("user", "user", "userSubscriptions.userId = user.id")
|
||||
.select([
|
||||
"DISTINCT user.id AS id",
|
||||
"user.firstName AS firstName",
|
||||
"user.lastName AS lastName",
|
||||
"user.email AS email",
|
||||
"user.profilePic AS profilePic",
|
||||
])
|
||||
.where("service.id = :serviceId", { serviceId })
|
||||
.andWhere("userSubscriptions.status = :status", { status: SubscriptionStatus.ACTIVE })
|
||||
.andWhere("userSubscriptions.endDate > NOW()")
|
||||
.getRawMany();
|
||||
const users = await this.danakServicesRepository.getUsersByService(serviceId);
|
||||
|
||||
return { users };
|
||||
}
|
||||
@@ -469,31 +400,7 @@ export class DanakServicesService {
|
||||
/******************************************** */
|
||||
|
||||
async getDanakServiceReviews(queryDto: DanakServiceReviewQueryDto) {
|
||||
const { limit, skip } = PaginationUtils(queryDto);
|
||||
|
||||
const reviewsQueryBuilder = this.danakServiceReviewRepository
|
||||
.createQueryBuilder("review")
|
||||
.leftJoinAndSelect("review.user", "user")
|
||||
.leftJoinAndSelect("review.service", "service")
|
||||
.select([
|
||||
"review.id",
|
||||
"review.title",
|
||||
"review.comment",
|
||||
"review.rating",
|
||||
"review.status",
|
||||
"review.createdAt",
|
||||
"user.id",
|
||||
"user.firstName",
|
||||
"user.lastName",
|
||||
"user.profilePic",
|
||||
"service.id",
|
||||
"service.name",
|
||||
])
|
||||
.orderBy("review.createdAt", "DESC")
|
||||
.skip(skip)
|
||||
.take(limit);
|
||||
|
||||
const [reviews, count] = await reviewsQueryBuilder.getManyAndCount();
|
||||
const [reviews, count] = await this.danakServiceReviewRepository.getDanakServiceReviews(queryDto);
|
||||
|
||||
return {
|
||||
reviews,
|
||||
|
||||
@@ -2,11 +2,51 @@ import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||
import { DanakServiceReviewQueryDto } from "../DTO/danak-service-review-query.dto";
|
||||
import { DanakServiceReview } from "../entities/danak-service-review.entity";
|
||||
import { ReviewStatus } from "../enums/review-status.enum";
|
||||
|
||||
@Injectable()
|
||||
export class DanakServiceReviewRepository extends Repository<DanakServiceReview> {
|
||||
constructor(@InjectRepository(DanakServiceReview) danakServiceReviewRepository: Repository<DanakServiceReview>) {
|
||||
super(danakServiceReviewRepository.target, danakServiceReviewRepository.manager, danakServiceReviewRepository.queryRunner);
|
||||
}
|
||||
|
||||
async getAverageRating(serviceId: string): Promise<number> {
|
||||
const result = await this.createQueryBuilder("review")
|
||||
.select("CAST(AVG(review.rating) AS DECIMAL(10,2))", "averageRating")
|
||||
.where("review.serviceId = :serviceId", { serviceId })
|
||||
.andWhere("review.status = :status", { status: ReviewStatus.APPROVED })
|
||||
.getRawOne();
|
||||
|
||||
return result?.averageRating ? parseFloat(result.averageRating) : 0;
|
||||
}
|
||||
|
||||
async getDanakServiceReviews(queryDto: DanakServiceReviewQueryDto) {
|
||||
const { limit, skip } = PaginationUtils(queryDto);
|
||||
|
||||
const reviewsQueryBuilder = this.createQueryBuilder("review")
|
||||
.leftJoinAndSelect("review.user", "user")
|
||||
.leftJoinAndSelect("review.service", "service")
|
||||
.select([
|
||||
"review.id",
|
||||
"review.title",
|
||||
"review.comment",
|
||||
"review.rating",
|
||||
"review.status",
|
||||
"review.createdAt",
|
||||
"user.id",
|
||||
"user.firstName",
|
||||
"user.lastName",
|
||||
"user.profilePic",
|
||||
"service.id",
|
||||
"service.name",
|
||||
])
|
||||
.orderBy("review.createdAt", "DESC")
|
||||
.skip(skip)
|
||||
.take(limit);
|
||||
|
||||
return reviewsQueryBuilder.getManyAndCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Brackets, IsNull, Not, Repository } from "typeorm";
|
||||
|
||||
import { SubscriptionStatus } from "../../subscriptions/enums/subscription-status.enum";
|
||||
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||
import { DanakServicesQueryDto, DanakServicesSearchQueryDto } from "../DTO/danak-services-search-query.dto";
|
||||
import { DanakServicesGlobalSearchDto, DanakServicesQueryDto, DanakServicesSearchQueryDto } from "../DTO/danak-services-search-query.dto";
|
||||
import { DanakService } from "../entities/danak-service.entity";
|
||||
import { ReviewStatus } from "../enums/review-status.enum";
|
||||
|
||||
@Injectable()
|
||||
export class DanakServicesRepository extends Repository<DanakService> {
|
||||
@@ -86,4 +88,89 @@ export class DanakServicesRepository extends Repository<DanakService> {
|
||||
count,
|
||||
};
|
||||
}
|
||||
//+********************
|
||||
async getDanakServiceById(serviceId: string, isAdmin: boolean) {
|
||||
const serviceQueryBuilder = this.createQueryBuilder("service")
|
||||
.where("service.deletedAt IS NULL")
|
||||
.leftJoin("service.category", "category")
|
||||
.addSelect(["category.id", "category.title"])
|
||||
.leftJoinAndSelect("service.images", "images")
|
||||
.leftJoinAndSelect("service.subscriptionPlans", "subscriptionPlans")
|
||||
.leftJoin("service.reviews", "reviews", "reviews.status = :reviewStatus", { reviewStatus: ReviewStatus.APPROVED })
|
||||
.leftJoin("reviews.user", "user")
|
||||
.addSelect([
|
||||
"reviews.id",
|
||||
"reviews.comment",
|
||||
"reviews.rating",
|
||||
"reviews.title",
|
||||
"reviews.createdAt",
|
||||
"reviews.status",
|
||||
"user.id",
|
||||
"user.firstName",
|
||||
"user.lastName",
|
||||
"user.profilePic",
|
||||
])
|
||||
// .loadRelationCountAndMap("service.reviewCount", "service.reviews")
|
||||
// .addSelect(
|
||||
// (subQuery) =>
|
||||
// subQuery
|
||||
// .select("CAST(AVG(rating) AS DECIMAL(10,2))", "averageRating")
|
||||
// .from(DanakServiceReview, "r")
|
||||
// .where("r.serviceId = service.id")
|
||||
// .andWhere("r.status = :reviewStatus", { reviewStatus: ReviewStatus.APPROVED }),
|
||||
// "averageRating",
|
||||
// )
|
||||
.andWhere("service.id = :serviceId", { serviceId })
|
||||
.groupBy("service.id,category.id, images.id, subscriptionPlans.id, reviews.id, user.id");
|
||||
|
||||
if (!isAdmin) {
|
||||
serviceQueryBuilder
|
||||
.andWhere("service.isActive = :isActive", { isActive: true })
|
||||
.andWhere("subscriptionPlans.isActive = :planActive", { planActive: true });
|
||||
}
|
||||
|
||||
serviceQueryBuilder.orderBy("subscriptionPlans.price", "ASC");
|
||||
|
||||
const danakService = await serviceQueryBuilder.getOne();
|
||||
|
||||
return danakService;
|
||||
}
|
||||
//+********************
|
||||
async getUsersByService(serviceId: string) {
|
||||
const users = await this.createQueryBuilder("service")
|
||||
.innerJoin("service.subscriptionPlans", "subscriptionPlans")
|
||||
.innerJoin("subscriptionPlans.userSubscriptions", "userSubscriptions")
|
||||
.innerJoin("user", "user", "userSubscriptions.userId = user.id")
|
||||
.select([
|
||||
"DISTINCT user.id AS id",
|
||||
"user.firstName AS firstName",
|
||||
"user.lastName AS lastName",
|
||||
"user.email AS email",
|
||||
"user.profilePic AS profilePic",
|
||||
])
|
||||
.where("service.id = :serviceId", { serviceId })
|
||||
.andWhere("userSubscriptions.status = :status", { status: SubscriptionStatus.ACTIVE })
|
||||
.andWhere("userSubscriptions.endDate > NOW()")
|
||||
.getRawMany();
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
async searchServices(queryDto: DanakServicesGlobalSearchDto) {
|
||||
const servicesQuery = this.createQueryBuilder("service")
|
||||
.leftJoin("service.category", "category")
|
||||
.leftJoin("service.subscriptionPlans", "subscriptionPlans")
|
||||
.select(["service.id", "service.name", "service.description", "service.icon", "category.id", "category.title"])
|
||||
.where("service.isActive = :isActive", { isActive: true })
|
||||
.andWhere("service.deletedAt IS NULL")
|
||||
.andWhere("category.isActive = :categoryActive", { categoryActive: true })
|
||||
.andWhere("category.deletedAt IS NULL")
|
||||
.andWhere("subscriptionPlans.isActive = :planActive", { planActive: true });
|
||||
|
||||
if (queryDto.q) {
|
||||
servicesQuery.andWhere("service.name ILIKE :searchQuery", { searchQuery: `%${queryDto.q}%` });
|
||||
}
|
||||
|
||||
return servicesQuery.getMany();
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
+41
-33
@@ -1,51 +1,59 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { ArrayUnique, IsArray, IsDateString, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID } from "class-validator";
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { Type } from "class-transformer";
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Max,
|
||||
Min,
|
||||
ValidateIf,
|
||||
} from "class-validator";
|
||||
|
||||
import { DiscountMessage } from "../../../common/enums/message.enum";
|
||||
import { DiscountCalculationType, DiscountType } from "../enums/discount-type.enum";
|
||||
import { DiscountType } from "../enums/discount-type.enum";
|
||||
|
||||
export class CreateDiscountDto {
|
||||
@ApiProperty({ description: "title of discount", type: String, example: "discount 1" })
|
||||
@IsNotEmpty({ message: DiscountMessage.TITLE_REQUIRED })
|
||||
@IsString({ message: DiscountMessage.TITLE_INVALID })
|
||||
@IsString({ message: DiscountMessage.TITLE_STRING })
|
||||
@ApiProperty({ description: "The title of the discount", example: "Summer Sale" })
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ description: "discount type", enum: DiscountType, example: DiscountType.CODE })
|
||||
@IsNotEmpty({ message: DiscountMessage.TYPE_REQUIRED })
|
||||
@IsEnum(DiscountType, { message: DiscountMessage.TYPE_INVALID })
|
||||
@IsNotEmpty({ message: DiscountMessage.DISCOUNT_TYPE_REQUIRED })
|
||||
@IsEnum(DiscountType, { message: DiscountMessage.INVALID_DISCOUNT_TYPE })
|
||||
@ApiProperty({ description: "The type of discount", example: DiscountType.PERCENTAGE })
|
||||
type: DiscountType;
|
||||
|
||||
@ApiProperty({ description: "calculation type", enum: DiscountCalculationType, example: DiscountCalculationType.FIXED })
|
||||
@IsNotEmpty({ message: DiscountMessage.CALCULATION_TYPE_REQUIRED })
|
||||
@IsEnum(DiscountCalculationType, { message: DiscountMessage.CALCULATION_TYPE_INVALID })
|
||||
calculationType: DiscountCalculationType;
|
||||
@IsNumber({}, { message: DiscountMessage.VALUE_MUST_BE_A_NUMBER })
|
||||
@Min(0, { message: DiscountMessage.VALUE_MUST_BE_AT_LEAST_0 })
|
||||
@Max(100, { message: DiscountMessage.VALUE_MUST_NOT_EXCEED_100 })
|
||||
@Type(() => Number)
|
||||
@ApiProperty({ description: "The value of the discount", example: 20 })
|
||||
value: number;
|
||||
|
||||
@ApiProperty({ description: "amount of discount", type: Number, example: 100 })
|
||||
@IsNotEmpty({ message: DiscountMessage.AMOUNT_REQUIRED })
|
||||
@IsNumber({}, { message: DiscountMessage.AMOUNT_INVALID })
|
||||
amount: number;
|
||||
|
||||
@ApiProperty({ description: "start discount date", type: String, format: "date-time", example: "2025-01-01T00:00:00Z" })
|
||||
@IsNotEmpty({ message: DiscountMessage.START_DATE_REQUIRED })
|
||||
@IsDateString({}, { message: DiscountMessage.START_DATE_INVALID })
|
||||
@IsDateString({}, { message: DiscountMessage.START_DATE_MUST_BE_A_VALID_DATE_STRING })
|
||||
@IsNotEmpty({ message: DiscountMessage.START_DATE_IS_REQUIRED })
|
||||
@ApiProperty({ description: "The start date of the discount", example: "2023-06-01T00:00:00Z" })
|
||||
startDate: string;
|
||||
|
||||
@ApiProperty({ description: "end discount date", type: String, format: "date-time", example: "2025-12-31T23:59:59Z" })
|
||||
@IsNotEmpty({ message: DiscountMessage.END_DATE_REQUIRED })
|
||||
@IsDateString({}, { message: DiscountMessage.END_DATE_INVALID })
|
||||
@IsDateString({}, { message: DiscountMessage.END_DATE_MUST_BE_A_VALID_DATE_STRING })
|
||||
@IsNotEmpty({ message: DiscountMessage.END_DATE_IS_REQUIRED })
|
||||
@ApiProperty({ description: "The end date of the discount", example: "2023-06-30T23:59:59Z" })
|
||||
endDate: string;
|
||||
|
||||
@ApiPropertyOptional({ description: "لیست آیدیهای خدمات", type: [String], example: ["f47ac10b-58cc-4372-a567-0e02b2c3d479"] })
|
||||
@IsBoolean({ message: DiscountMessage.IS_ACTIVE_MUST_BE_A_BOOLEAN })
|
||||
@IsOptional()
|
||||
@ArrayUnique({ message: DiscountMessage.SERVICES_DUPLICATE })
|
||||
@IsArray({ message: DiscountMessage.SERVICES_INVALID })
|
||||
@IsUUID("4", { each: true, message: DiscountMessage.SERVICES_UUID_INVALID })
|
||||
subscriptionPlanIds?: string[];
|
||||
@ApiProperty({ description: "Indicates if the discount is active", example: true })
|
||||
isActive?: boolean;
|
||||
|
||||
@ApiPropertyOptional({ description: "لیست آیدیهای کاربران", type: [String], example: ["f47ac10b-58cc-4372-a567-0e02b2c3d479"] })
|
||||
@IsArray({ message: "Target products must be an array" })
|
||||
@IsOptional()
|
||||
@ArrayUnique({ message: DiscountMessage.USER_DUPLICATE })
|
||||
@IsArray({ message: DiscountMessage.USER_INVALID })
|
||||
@IsUUID("4", { each: true, message: DiscountMessage.USER_UUID_INVALID })
|
||||
userIds?: string[];
|
||||
@ValidateIf((o) => o.targetProducts !== null && o.targetProducts !== undefined)
|
||||
@IsString({ each: true, message: "Each target product must be a string" })
|
||||
@ApiProperty({ description: "List of target product IDs", example: ["prod-1", "prod-2"] })
|
||||
targetProducts?: string[];
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||
// import { IsOptional, IsString } from "class-validator";
|
||||
|
||||
// import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||
// import { FinancialMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
// export class SearchDiscountsDto extends PaginationDto {
|
||||
// @IsOptional()
|
||||
// @IsString({ message: FinancialMessage.SEARCH_QUERY_MUST_BE_A_STRING })
|
||||
// @ApiPropertyOptional({ description: "search query", example: "search query" })
|
||||
// q: string;
|
||||
// }
|
||||
@@ -0,0 +1,7 @@
|
||||
import { IsBoolean, IsNotEmpty } from "class-validator";
|
||||
|
||||
export class ToggleActiveDto {
|
||||
@IsBoolean()
|
||||
@IsNotEmpty()
|
||||
isActive: boolean;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Type } from "class-transformer";
|
||||
import { IsArray, IsBoolean, IsDateString, IsEnum, IsNumber, IsOptional, IsString, Max, Min, ValidateIf } from "class-validator";
|
||||
|
||||
import { DiscountStatus } from "../enums/discount-status.enum";
|
||||
import { DiscountType } from "../enums/discount-type.enum";
|
||||
|
||||
export class UpdateDiscountDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title?: string;
|
||||
|
||||
@IsEnum(DiscountType)
|
||||
@IsOptional()
|
||||
type?: DiscountType;
|
||||
|
||||
@IsEnum(DiscountStatus)
|
||||
@IsOptional()
|
||||
status?: DiscountStatus;
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Min(0)
|
||||
@Max(100)
|
||||
@Type(() => Number)
|
||||
value?: number;
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
startDate?: string;
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
endDate?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isActive?: boolean;
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@ValidateIf((o) => o.targetProducts !== null && o.targetProducts !== undefined)
|
||||
@IsString({ each: true })
|
||||
targetProducts?: string[];
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { IsNotEmpty, IsOptional, IsString } from "class-validator";
|
||||
|
||||
export class ValidateDiscountDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
code: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
productId?: string;
|
||||
}
|
||||
Executable → Regular
+60
-41
@@ -1,58 +1,77 @@
|
||||
// import { Body, Controller, Get, Param, Patch, Post, Query } from "@nestjs/common";
|
||||
// import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||
// import { Body, Controller, Post } from "@nestjs/common";
|
||||
// import { ApiOperation, ApiResponse } from "@nestjs/swagger";
|
||||
|
||||
// import { CreateDiscountDto } from "./DTO/create-discount.dto";
|
||||
// // import { SearchDiscountsDto } from "./DTO/discount-search-query.dto";
|
||||
// import { DiscountService } from "./providers/discounts.service";
|
||||
// import { Discount } from "./entities/discount.entity";
|
||||
// import { DiscountsService } from "./providers/discounts.service";
|
||||
// import { AdminRoute } from "../../common/decorators/admin.decorator";
|
||||
// import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
// import { UserDec } from "../../common/decorators/user.decorator";
|
||||
// import { ParamDto } from "../../common/DTO/param.dto";
|
||||
// import { PermissionsDec } from "../../common/decorators/permission.decorator";
|
||||
// import { PermissionEnum } from "../users/enums/permission.enum";
|
||||
|
||||
// @ApiTags("discounts")
|
||||
// @AuthGuards()
|
||||
// @Controller("discounts")
|
||||
// export class DiscountController {
|
||||
// constructor(private readonly discountService: DiscountService) {}
|
||||
|
||||
// //************************************ */
|
||||
// @AuthGuards()
|
||||
// export class DiscountsController {
|
||||
// constructor(private readonly discountsService: DiscountsService) {}
|
||||
|
||||
// @ApiOperation({ summary: "Create a new discount" })
|
||||
// @AdminRoute()
|
||||
// @PermissionsDec(PermissionEnum.DISCOUNTS)
|
||||
// @Post()
|
||||
// async create(@Body() createDiscountDto: CreateDiscountDto) {
|
||||
// return this.discountService.create(createDiscountDto);
|
||||
// @ApiResponse({ status: 201, description: "Discount created successfully" })
|
||||
// create(@Body() createDiscountDto: CreateDiscountDto): Promise<Discount> {
|
||||
// return this.discountsService.create(createDiscountDto);
|
||||
// }
|
||||
|
||||
// //************************************ */
|
||||
// // @AdminRoute()
|
||||
// // @PermissionsDec(PermissionEnum.DISCOUNTS)
|
||||
// // @Get()
|
||||
// // @ApiOperation({ summary: "Get all discounts" })
|
||||
// // findAll(@Query() query: any) {}
|
||||
|
||||
// @ApiOperation({ summary: "Retrieve all discounts" })
|
||||
// @Get()
|
||||
// async findAll(@Query() queryDto: SearchDiscountsDto) {
|
||||
// return this.discountService.findAll(queryDto);
|
||||
// }
|
||||
// // @AdminRoute()
|
||||
// // @PermissionsDec(PermissionEnum.DISCOUNTS)
|
||||
// // @Get(":id")
|
||||
// // @ApiOperation({ summary: "Get discount by ID" })
|
||||
// // findOne(@Param("id") id: string): Promise<Discount> {
|
||||
// // return this.discountsService.findById(id);
|
||||
// // }
|
||||
|
||||
// //************************************ */
|
||||
// // @AdminRoute()
|
||||
// // @PermissionsDec(PermissionEnum.DISCOUNTS)
|
||||
// // @Get(":id/products")
|
||||
// // @ApiOperation({ summary: "Get products for a discount" })
|
||||
// // getProducts(@Param("id") id: string): Promise<string[]> {
|
||||
// // return this.discountsService.getProductsForDiscount(id);
|
||||
// // }
|
||||
|
||||
// @ApiOperation({ summary: "Retrieve a discount by ID" })
|
||||
// @Get(":id")
|
||||
// async findOne(@Param() paramDto: ParamDto) {
|
||||
// return this.discountService.findOne(paramDto.id);
|
||||
// }
|
||||
// // @AdminRoute()
|
||||
// // @PermissionsDec(PermissionEnum.DISCOUNTS)
|
||||
// // @Put(":id")
|
||||
// // @ApiOperation({ summary: "Update discount" })
|
||||
// // update(@Param("id") id: string, @Body() updateDiscountDto: UpdateDiscountDto): Promise<Discount> {
|
||||
// // return this.discountsService.update(id, updateDiscountDto);
|
||||
// // }
|
||||
|
||||
// //************************************ */
|
||||
// // @AdminRoute()
|
||||
// // @PermissionsDec(PermissionEnum.DISCOUNTS)
|
||||
// // @Patch(":id/toggle-active")
|
||||
// // @ApiOperation({ summary: "Toggle discount active state" })
|
||||
// // toggleActive(@Param("id") id: string, @Body() toggleActiveDto: ToggleActiveDto): Promise<Discount> {
|
||||
// // return this.discountsService.toggleActive(id, toggleActiveDto.isActive);
|
||||
// // }
|
||||
|
||||
// @ApiOperation({ summary: "Retrieve a discounts by userID" })
|
||||
// @Get("/user-discounts")
|
||||
// async findDiscountsByUsers(@Query() queryDto: SearchDiscountsDto, @UserDec("id") userId: string) {
|
||||
// return this.discountService.findDiscountsByUser(queryDto, userId);
|
||||
// }
|
||||
// // @AdminRoute()
|
||||
// // @PermissionsDec(PermissionEnum.DISCOUNTS)
|
||||
// // @Delete(":id")
|
||||
// // @ApiOperation({ summary: "Delete discount" })
|
||||
// // remove(@Param("id") id: string): Promise<void> {
|
||||
// // return this.discountsService.delete(id);
|
||||
// // }
|
||||
|
||||
// //************************************ */
|
||||
|
||||
// @ApiOperation({ summary: "Toggle active/deactive discount" })
|
||||
// @Patch(":id/toggle")
|
||||
// async toggleActive(@Param() paramDto: ParamDto) {
|
||||
// return this.discountService.toggleActive(paramDto.id);
|
||||
// }
|
||||
|
||||
// //************************************ */
|
||||
// // @Post("validate")
|
||||
// // @ApiOperation({ summary: "Validate discount code" })
|
||||
// // validateDiscount(@Body() validateDiscountDto: ValidateDiscountDto, @UserDec() user: User): Promise<Discount> {
|
||||
// // return this.discountsService.validateDiscount(validateDiscountDto.code, user, validateDiscountDto.productId);
|
||||
// // }
|
||||
// }
|
||||
|
||||
Executable → Regular
+18
-14
@@ -1,16 +1,20 @@
|
||||
// import { Module } from "@nestjs/common";
|
||||
// import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
|
||||
// import { DiscountController } from "./discounts.controller";
|
||||
// import { Discount } from "./entities/discount.entity";
|
||||
// import { DiscountService } from "./providers/discounts.service";
|
||||
// import { DiscountRepository } from "./repositories/discount.repository";
|
||||
// import { SubscriptionsModule } from "../subscriptions/subscriptions.module";
|
||||
// import { UsersModule } from "../users/users.module";
|
||||
// import { DiscountsController } from "./discounts.controller";
|
||||
import { UsersModule } from "../users/users.module";
|
||||
import { DiscountService } from "./entities/discount-product.entity";
|
||||
import { Discount } from "./entities/discount.entity";
|
||||
import { UsageDiscount } from "./entities/usage-discount.entity";
|
||||
// import { DiscountsService } from "./providers/discounts.service";
|
||||
// import { DiscountServicesRepository } from "./repositories/discount-services.repository";
|
||||
// import { DiscountRepository } from "./repositories/discounts.repository";
|
||||
// import { UsageDiscountRepository } from "./repositories/usage-discounts.repository";
|
||||
|
||||
// @Module({
|
||||
// imports: [TypeOrmModule.forFeature([Discount]), SubscriptionsModule, UsersModule],
|
||||
// providers: [DiscountService, DiscountRepository],
|
||||
// controllers: [DiscountController],
|
||||
// })
|
||||
// export class DiscountModule {}
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Discount, UsageDiscount, DiscountService]), UsersModule],
|
||||
// controllers: [DiscountsController],
|
||||
// providers: [DiscountsService, DiscountRepository, UsageDiscountRepository, DiscountServicesRepository],
|
||||
// exports: [DiscountsService],
|
||||
})
|
||||
export class DiscountsModule {}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Entity, ManyToOne } from "typeorm";
|
||||
|
||||
import { Discount } from "./discount.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { DanakService } from "../../danak-services/entities/danak-service.entity";
|
||||
|
||||
@Entity()
|
||||
export class DiscountService extends BaseEntity {
|
||||
@ManyToOne(() => Discount, (discount) => discount.discountServices, { nullable: false, onDelete: "CASCADE" })
|
||||
discount: Discount;
|
||||
|
||||
@ManyToOne(() => DanakService, (danakService) => danakService.discountServices, { nullable: false })
|
||||
danakService: DanakService;
|
||||
}
|
||||
Executable → Regular
+16
-28
@@ -1,34 +1,24 @@
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Decimal from "decimal.js";
|
||||
import { Column, Entity, JoinTable, ManyToMany, OneToMany } from "typeorm";
|
||||
import { Column, Entity, OneToMany } from "typeorm";
|
||||
|
||||
import { DiscountService } from "./discount-product.entity";
|
||||
import { UsageDiscount } from "./usage-discount.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { DecimalTransformer } from "../../../common/transformers/decimal.transformer";
|
||||
import { Invoice } from "../../invoices/entities/invoice.entity";
|
||||
import { SubscriptionPlan } from "../../subscriptions/entities/subscription.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { DiscountCalculationType, DiscountType } from "../enums/discount-type.enum";
|
||||
import { DiscountType } from "../enums/discount-type.enum";
|
||||
|
||||
@Entity()
|
||||
export class Discount extends BaseEntity {
|
||||
@Column({ type: "varchar", length: 200, nullable: false })
|
||||
@Column({ type: "varchar", length: 150 })
|
||||
title: string;
|
||||
|
||||
@Column({ type: "enum", enum: DiscountType })
|
||||
@Column({ type: "varchar", length: 100, unique: true })
|
||||
code: string;
|
||||
|
||||
@Column({ type: "enum", enum: DiscountType, default: DiscountType.PERCENTAGE })
|
||||
type: DiscountType;
|
||||
|
||||
@Column({ type: "enum", enum: DiscountCalculationType })
|
||||
calculationType: DiscountCalculationType;
|
||||
|
||||
@Column({ type: "decimal", precision: 16, scale: 2, nullable: false, transformer: new DecimalTransformer() })
|
||||
amount: Decimal;
|
||||
|
||||
@Column({ type: "boolean", default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@Column({ nullable: true })
|
||||
code?: string;
|
||||
@Column({ type: "decimal", precision: 16, scale: 2, transformer: new DecimalTransformer() })
|
||||
value: number;
|
||||
|
||||
@Column({ type: "timestamptz" })
|
||||
startDate: Date;
|
||||
@@ -36,16 +26,14 @@ export class Discount extends BaseEntity {
|
||||
@Column({ type: "timestamptz" })
|
||||
endDate: Date;
|
||||
|
||||
@ManyToMany(() => SubscriptionPlan, { nullable: true })
|
||||
@JoinTable()
|
||||
subscriptionPlans: SubscriptionPlan[];
|
||||
@Column({ type: "int", default: 0 })
|
||||
usedCount: number;
|
||||
|
||||
@ManyToMany(() => User, (user) => user.discounts, { nullable: true })
|
||||
@JoinTable()
|
||||
users: User[];
|
||||
@Column({ type: "boolean", default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@OneToMany(() => Invoice, (invoice) => invoice.discount)
|
||||
invoice: Invoice;
|
||||
@OneToMany(() => DiscountService, (discountService) => discountService.discount, { cascade: true })
|
||||
discountServices: DiscountService[];
|
||||
|
||||
@OneToMany(() => UsageDiscount, (usageDiscount) => usageDiscount.discount)
|
||||
usages: UsageDiscount[];
|
||||
|
||||
Executable → Regular
+13
-8
@@ -1,18 +1,23 @@
|
||||
import { Column, Entity, JoinTable, ManyToMany, ManyToOne } from "typeorm";
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Decimal from "decimal.js";
|
||||
import { Column, Entity, ManyToOne } from "typeorm";
|
||||
|
||||
import { Discount } from "./discount.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { DecimalTransformer } from "../../../common/transformers/decimal.transformer";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
|
||||
@Entity()
|
||||
export class UsageDiscount extends BaseEntity {
|
||||
@Column({ type: "timestamptz" })
|
||||
usageDate: Date;
|
||||
|
||||
@ManyToOne(() => Discount, (discount) => discount.usages)
|
||||
@ManyToOne(() => Discount, (discount) => discount.usages, { nullable: false, onDelete: "CASCADE" })
|
||||
discount: Discount;
|
||||
|
||||
@ManyToMany(() => User, (user) => user.usageDiscounts)
|
||||
@JoinTable()
|
||||
users: User[];
|
||||
@ManyToOne(() => User, { nullable: false, onDelete: "CASCADE" })
|
||||
user: User;
|
||||
|
||||
@Column({ type: "decimal", precision: 16, scale: 2, transformer: new DecimalTransformer() })
|
||||
appliedAmount: Decimal;
|
||||
|
||||
@Column({ type: "timestamptz", nullable: false })
|
||||
usedAt: Date;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum DiscountStatus {
|
||||
ACTIVE = "active",
|
||||
INACTIVE = "inactive",
|
||||
EXPIRED = "expired",
|
||||
}
|
||||
Executable → Regular
+2
-7
@@ -1,9 +1,4 @@
|
||||
export enum DiscountType {
|
||||
DIRECT = "DIRECT",
|
||||
CODE = "CODE",
|
||||
}
|
||||
|
||||
export enum DiscountCalculationType {
|
||||
FIXED = "FIXED",
|
||||
PERCENTAGE = "PERCENTAGE",
|
||||
PERCENTAGE = 0,
|
||||
FIXED_AMOUNT = 1,
|
||||
}
|
||||
|
||||
Executable → Regular
+142
-130
@@ -1,182 +1,194 @@
|
||||
// import { randomBytes } from "node:crypto";
|
||||
|
||||
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
// import { In } from "typeorm";
|
||||
// // eslint-disable-next-line import/no-named-as-default
|
||||
// import Decimal from "decimal.js";
|
||||
|
||||
// import { CommonMessage, DiscountMessage, SubscriptionMessage, UserMessage } from "../../../common/enums/message.enum";
|
||||
// import { SubscriptionsPlanRepository } from "../../subscriptions/repositories/subscriptions.repository";
|
||||
// import { UserRepository } from "../../users/repositories/users.repository";
|
||||
// import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||
// import { User } from "../../users/entities/user.entity";
|
||||
// import { CreateDiscountDto } from "../DTO/create-discount.dto";
|
||||
// import { SearchDiscountsDto } from "../DTO/discount-search-query.dto";
|
||||
// import { DiscountRepository } from "../repositories/discount.repository";
|
||||
// import { UpdateDiscountDto } from "../DTO/update-discount.dto";
|
||||
// import { Discount } from "../entities/discount.entity";
|
||||
// import { DiscountType } from "../enums/discount-type.enum";
|
||||
// import { DiscountServicesRepository } from "../repositories/discount-services.repository";
|
||||
// import { DiscountRepository } from "../repositories/discounts.repository";
|
||||
// import { UsageDiscountRepository } from "../repositories/usage-discounts.repository";
|
||||
|
||||
// @Injectable()
|
||||
// export class DiscountService {
|
||||
// export class DiscountsService {
|
||||
// constructor(
|
||||
// private readonly discountRepository: DiscountRepository,
|
||||
// private readonly subscriptionPlanRepository: SubscriptionsPlanRepository,
|
||||
// private readonly userRepository: UserRepository,
|
||||
// private readonly discountServicesRepository: DiscountServicesRepository,
|
||||
// private readonly usageDiscountRepository: UsageDiscountRepository,
|
||||
// ) {}
|
||||
|
||||
// /******************************************** */
|
||||
|
||||
// async create(createDiscountDto: CreateDiscountDto) {
|
||||
// const { subscriptionPlanIds, ...discountData } = createDiscountDto;
|
||||
// const existingDiscount = await this.discountRepository.findByCode(createDiscountDto.title);
|
||||
// if (existingDiscount) {
|
||||
// throw new BadRequestException(DISCOUNT_ERROR_MESSAGES.ALREADY_EXISTS);
|
||||
// }
|
||||
|
||||
// const code = await this.generateUniqueCouponCode();
|
||||
// // Create the discount without product relations
|
||||
// const { targetProducts, ...discountData } = createDiscountDto;
|
||||
|
||||
// const discount = this.discountRepository.create({
|
||||
// const discount = await this.discountRepository.create({
|
||||
// ...discountData,
|
||||
// code,
|
||||
// startDate: new Date(createDiscountDto.startDate),
|
||||
// endDate: new Date(createDiscountDto.endDate),
|
||||
// status: createDiscountDto.status || DiscountStatus.ACTIVE,
|
||||
// isActive: createDiscountDto.isActive !== undefined ? createDiscountDto.isActive : true,
|
||||
// });
|
||||
|
||||
// if (createDiscountDto.subscriptionPlanIds && createDiscountDto.subscriptionPlanIds.length) {
|
||||
// const subscriptionPlans = await this.subscriptionPlanRepository.find({
|
||||
// where: { id: In(createDiscountDto.subscriptionPlanIds) },
|
||||
// });
|
||||
|
||||
// if (subscriptionPlans.length !== createDiscountDto.subscriptionPlanIds.length) {
|
||||
// throw new NotFoundException(SubscriptionMessage.NOT_FOUND);
|
||||
// }
|
||||
|
||||
// discount.subscriptionPlans = subscriptionPlans;
|
||||
// // Add product relations if provided
|
||||
// if (targetProducts && targetProducts.length > 0) {
|
||||
// await this.discountServicesRepository.createMany(discount.id, targetProducts);
|
||||
// }
|
||||
|
||||
// if (createDiscountDto.userIds) {
|
||||
// const users = await this.userRepository.find({ where: { id: In(createDiscountDto.userIds) } });
|
||||
|
||||
// if (users.length !== createDiscountDto.userIds.length) {
|
||||
// throw new NotFoundException(UserMessage.USER_NOT_FOUND);
|
||||
// }
|
||||
|
||||
// discount.users = users;
|
||||
// // Fetch the complete discount with relations
|
||||
// const fullDiscount = await this.discountRepository.findById(discount.id);
|
||||
// if (!fullDiscount) {
|
||||
// throw new NotFoundException(DISCOUNT_ERROR_MESSAGES.NOT_FOUND);
|
||||
// }
|
||||
|
||||
// await this.discountRepository.save(discount);
|
||||
|
||||
// return {
|
||||
// message: CommonMessage.CREATED,
|
||||
// discount,
|
||||
// };
|
||||
// return fullDiscount;
|
||||
// }
|
||||
|
||||
// /******************************************** */
|
||||
// async findAll(options?: any): Promise<[Discount[], number]> {
|
||||
// return this.discountRepository.findAll(options);
|
||||
// }
|
||||
|
||||
// async findByCode(code: string) {
|
||||
// const discount = await this.discountRepository.findOne({
|
||||
// where: { code, isActive: true },
|
||||
// relations: ["subscriptionPlans"],
|
||||
// });
|
||||
// async findById(id: string): Promise<Discount> {
|
||||
// const discount = await this.discountRepository.findById(id);
|
||||
// if (!discount) {
|
||||
// throw new NotFoundException(DiscountMessage.NOT_FOUND);
|
||||
// throw new NotFoundException(DISCOUNT_ERROR_MESSAGES.NOT_FOUND);
|
||||
// }
|
||||
// return { discount };
|
||||
// return discount;
|
||||
// }
|
||||
|
||||
// /******************************************** */
|
||||
// async update(id: string, updateDiscountDto: UpdateDiscountDto): Promise<Discount> {
|
||||
// // Validate discount exists
|
||||
// await this.findById(id);
|
||||
|
||||
// async findDiscountsByUser(queryDto: SearchDiscountsDto, userId: string) {
|
||||
// const user = await this.userRepository.findOneBy({
|
||||
// id: userId,
|
||||
// });
|
||||
// if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
// // Handle product relations separately
|
||||
// const { targetProducts, startDate: startDateStr, endDate: endDateStr, ...otherFields } = updateDiscountDto;
|
||||
|
||||
// const { limit, skip } = PaginationUtils(queryDto);
|
||||
// // Initialize update data with other fields
|
||||
// const updateData: Partial<Discount> = { ...otherFields };
|
||||
|
||||
// const queryBuilder = this.discountRepository.createQueryBuilder("discount");
|
||||
|
||||
// queryBuilder
|
||||
// .leftJoin("discount.users", "user")
|
||||
// .where("user.id = :userId", { userId: user.id })
|
||||
// .leftJoinAndSelect("discount.subscriptionPlans", "subscriptionPlans")
|
||||
// .leftJoin("subscriptionPlans.service", "service")
|
||||
// .addSelect(["service.id", "service.name", "service.title", "service.link", "service.icon", "service.description"]);
|
||||
|
||||
// if (queryDto.q) {
|
||||
// queryBuilder.andWhere("discount.code ILIKE :q", { q: `%${queryDto.q}%` });
|
||||
// // Add date fields if they exist
|
||||
// if (startDateStr) {
|
||||
// updateData.startDate = new Date(startDateStr);
|
||||
// }
|
||||
|
||||
// queryBuilder.orderBy("discount.createdAt", "DESC").skip(skip).take(limit);
|
||||
|
||||
// const [discounts, count] = await queryBuilder.getManyAndCount();
|
||||
|
||||
// return {
|
||||
// discounts,
|
||||
// count,
|
||||
// paginate: true,
|
||||
// };
|
||||
// }
|
||||
|
||||
// /******************************************** */
|
||||
|
||||
// async findAll(queryDto: SearchDiscountsDto) {
|
||||
// const { limit, skip } = PaginationUtils(queryDto);
|
||||
|
||||
// const queryBuilder = this.discountRepository.createQueryBuilder("discount");
|
||||
|
||||
// queryBuilder
|
||||
// .leftJoinAndSelect("discount.subscriptionPlans", "subscriptionPlans")
|
||||
// .leftJoin("subscriptionPlans.service", "service")
|
||||
// .addSelect(["service.id", "service.name", "service.title", "service.link", "service.icon", "service.description"])
|
||||
// .leftJoinAndSelect("discount.users", "users");
|
||||
|
||||
// if (queryDto.q) {
|
||||
// queryBuilder.andWhere("discount.code ILIKE :q", { q: `%${queryDto.q}%` });
|
||||
// if (endDateStr) {
|
||||
// updateData.endDate = new Date(endDateStr);
|
||||
// }
|
||||
|
||||
// queryBuilder.orderBy("discount.createdAt", "DESC").skip(skip).take(limit);
|
||||
// // Update the discount entity
|
||||
// const updatedDiscount = await this.discountRepository.update(id, updateData);
|
||||
// if (!updatedDiscount) {
|
||||
// throw new NotFoundException(DISCOUNT_ERROR_MESSAGES.NOT_FOUND);
|
||||
// }
|
||||
|
||||
// const [discounts, count] = await queryBuilder.getManyAndCount();
|
||||
// // Handle product relations if provided
|
||||
// if (targetProducts !== undefined) {
|
||||
// // Remove existing relations
|
||||
// await this.discountServicesRepository.deleteByDiscountId(id);
|
||||
|
||||
// return {
|
||||
// discounts,
|
||||
// count,
|
||||
// paginate: true,
|
||||
// };
|
||||
// // Add new relations if not empty
|
||||
// if (targetProducts.length > 0) {
|
||||
// await this.discountServicesRepository.createMany(id, targetProducts);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Return the updated discount with relations
|
||||
// return this.findById(id);
|
||||
// }
|
||||
|
||||
// /******************************************** */
|
||||
|
||||
// async findOne(id: string) {
|
||||
// const discount = await this.discountRepository.findOne({
|
||||
// where: { id },
|
||||
// relations: ["subscriptionPlans"],
|
||||
// });
|
||||
// if (!discount) throw new BadRequestException(DiscountMessage.NOT_FOUND);
|
||||
// return { discount };
|
||||
// async delete(id: string): Promise<void> {
|
||||
// const discount = await this.findById(id);
|
||||
// await this.discountRepository.delete(discount.id);
|
||||
// }
|
||||
|
||||
// /******************************************** */
|
||||
// async toggleActive(id: string, isActive: boolean): Promise<Discount> {
|
||||
// await this.findById(id);
|
||||
// const updatedDiscount = await this.discountRepository.update(id, { isActive });
|
||||
// if (!updatedDiscount) {
|
||||
// throw new NotFoundException(DISCOUNT_ERROR_MESSAGES.NOT_FOUND);
|
||||
// }
|
||||
// return updatedDiscount;
|
||||
// }
|
||||
|
||||
// async validateDiscount(code: string, user: User, productId?: string): Promise<Discount> {
|
||||
// const discount = await this.discountRepository.findValidByCode(code);
|
||||
|
||||
// async toggleActive(id: string) {
|
||||
// const discount = await this.discountRepository.findOne({ where: { id } });
|
||||
// if (!discount) {
|
||||
// throw new BadRequestException(DiscountMessage.NOT_FOUND);
|
||||
// throw new BadRequestException(DISCOUNT_ERROR_MESSAGES.INVALID);
|
||||
// }
|
||||
|
||||
// discount.isActive = !discount.isActive;
|
||||
// // Check if user has already used this discount
|
||||
// const usageCount = await this.usageDiscountRepository.countByUserAndDiscount(user.id, discount.id);
|
||||
// if (usageCount > 0) {
|
||||
// throw new BadRequestException(DISCOUNT_ERROR_MESSAGES.ALREADY_USED);
|
||||
// }
|
||||
|
||||
// const updatedDiscount = await this.discountRepository.save(discount);
|
||||
// // Check if product is valid for this discount
|
||||
// if (productId && discount.discountServices && discount.discountServices.length > 0) {
|
||||
// const hasProduct = await this.discountServicesRepository.hasProductForDiscount(discount.id, productId);
|
||||
// if (!hasProduct) {
|
||||
// throw new BadRequestException(DISCOUNT_ERROR_MESSAGES.INVALID_PRODUCT);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return {
|
||||
// message: CommonMessage.UPDATE_SUCCESS,
|
||||
// updatedDiscount,
|
||||
// };
|
||||
// return discount;
|
||||
// }
|
||||
|
||||
// /******************************************** */
|
||||
// async applyDiscount(amount: number, discountId: string, productId?: string): Promise<number> {
|
||||
// const discount = await this.findById(discountId);
|
||||
|
||||
// async generateUniqueCouponCode(): Promise<string> {
|
||||
// const code = this.generateCouponCode();
|
||||
// const exists = await this.discountRepository.findOne({ where: { code } });
|
||||
// if (exists) return this.generateUniqueCouponCode();
|
||||
// return code;
|
||||
// if (!discount.isValid) {
|
||||
// throw new BadRequestException(DISCOUNT_ERROR_MESSAGES.INVALID);
|
||||
// }
|
||||
|
||||
// // Check if product is valid for this discount
|
||||
// if (productId && discount.discountServices && discount.discountServices.length > 0) {
|
||||
// const hasProduct = await this.discountServicesRepository.hasProductForDiscount(discount.id, productId);
|
||||
// if (!hasProduct) {
|
||||
// throw new BadRequestException(DISCOUNT_ERROR_MESSAGES.INVALID_PRODUCT);
|
||||
// }
|
||||
// }
|
||||
|
||||
// let discountAmount = 0;
|
||||
|
||||
// if (discount.type === DiscountType.PERCENTAGE) {
|
||||
// discountAmount = (amount * discount.value) / 100;
|
||||
// } else if (discount.type === DiscountType.FIXED_AMOUNT) {
|
||||
// discountAmount = discount.value;
|
||||
// }
|
||||
|
||||
// // Ensure discount doesn't exceed the original amount
|
||||
// if (discountAmount > amount) {
|
||||
// discountAmount = amount;
|
||||
// }
|
||||
|
||||
// return discountAmount;
|
||||
// }
|
||||
|
||||
// /******************************************** */
|
||||
// async recordDiscountUsage(userId: string, discountId: string, appliedAmount: number): Promise<void> {
|
||||
// const discount = await this.findById(discountId);
|
||||
|
||||
// private generateCouponCode(length = 8) {
|
||||
// return randomBytes(length).toString("hex").slice(0, length).toUpperCase();
|
||||
// // Increment usage count
|
||||
// await this.discountRepository.update(discount.id, {
|
||||
// usedCount: discount.usedCount + 1,
|
||||
// });
|
||||
|
||||
// // Record usage
|
||||
// await this.usageDiscountRepository.create({
|
||||
// user: { id: userId } as User,
|
||||
// discount: { id: discountId } as Discount,
|
||||
// appliedAmount: new Decimal(appliedAmount),
|
||||
// usedAt: new Date(),
|
||||
// });
|
||||
// }
|
||||
|
||||
// async getProductsForDiscount(discountId: string): Promise<string[]> {
|
||||
// const discountServices = await this.discountServicesRepository.findByDiscountId(discountId);
|
||||
// return discountServices.map((service) => service.danakService.id);
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
import { DiscountService } from "../entities/discount-product.entity";
|
||||
@Injectable()
|
||||
export class DiscountServicesRepository extends Repository<DiscountService> {
|
||||
constructor(@InjectRepository(DiscountService) repository: Repository<DiscountService>) {
|
||||
super(repository.target, repository.manager, repository.queryRunner);
|
||||
}
|
||||
|
||||
async createMany(discountId: string, productIds: string[]): Promise<DiscountService[]> {
|
||||
const discountServices = productIds.map((productId) =>
|
||||
this.create({
|
||||
discount: { id: discountId },
|
||||
danakService: { id: productId },
|
||||
}),
|
||||
);
|
||||
return this.save(discountServices);
|
||||
}
|
||||
|
||||
async findByDiscountId(discountId: string): Promise<DiscountService[]> {
|
||||
return this.find({
|
||||
where: { discount: { id: discountId } },
|
||||
});
|
||||
}
|
||||
|
||||
async findByProductId(productId: string): Promise<DiscountService[]> {
|
||||
return this.find({
|
||||
where: { danakService: { id: productId } },
|
||||
relations: ["discount"],
|
||||
});
|
||||
}
|
||||
|
||||
async deleteByDiscountId(discountId: string): Promise<void> {
|
||||
await this.createQueryBuilder().delete().where("discountId = :discountId", { discountId }).execute();
|
||||
}
|
||||
|
||||
async hasProductForDiscount(discountId: string, productId: string): Promise<boolean> {
|
||||
const count = await this.count({
|
||||
where: {
|
||||
discount: { id: discountId },
|
||||
danakService: { id: productId },
|
||||
},
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async findDiscountIdsForProduct(productId: string): Promise<string[]> {
|
||||
const discountServices = await this.find({
|
||||
where: { danakService: { id: productId } },
|
||||
select: ["discount"],
|
||||
relations: ["discount"],
|
||||
});
|
||||
return discountServices.map((dp) => dp.discount.id);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
import { Discount } from "../entities/discount.entity";
|
||||
|
||||
@Injectable()
|
||||
export class DiscountRepository extends Repository<Discount> {
|
||||
constructor(@InjectRepository(Discount) discountRepository: Repository<Discount>) {
|
||||
super(discountRepository.target, discountRepository.manager, discountRepository.queryRunner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
import { Discount } from "../entities/discount.entity";
|
||||
|
||||
@Injectable()
|
||||
export class DiscountRepository extends Repository<Discount> {
|
||||
constructor(@InjectRepository(Discount) repository: Repository<Discount>) {
|
||||
super(repository.target, repository.manager, repository.queryRunner);
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<Discount | null> {
|
||||
return this.findOne({
|
||||
where: { id },
|
||||
relations: ["productRelations"],
|
||||
});
|
||||
}
|
||||
|
||||
async findByCode(code: string): Promise<Discount | null> {
|
||||
return this.findOne({
|
||||
where: { code },
|
||||
relations: ["productRelations"],
|
||||
});
|
||||
}
|
||||
|
||||
async findActiveByCode(code: string): Promise<Discount | null> {
|
||||
return this.findOne({
|
||||
where: {
|
||||
code,
|
||||
isActive: true,
|
||||
},
|
||||
relations: ["productRelations"],
|
||||
});
|
||||
}
|
||||
|
||||
async findAll(options?: any): Promise<[Discount[], number]> {
|
||||
const query = this.createQueryBuilder("discount").leftJoinAndSelect("discount.productRelations", "productRelations");
|
||||
|
||||
if (options?.status) {
|
||||
query.andWhere("discount.status = :status", { status: options.status });
|
||||
}
|
||||
|
||||
if (options?.isActive !== undefined) {
|
||||
query.andWhere("discount.isActive = :isActive", { isActive: options.isActive });
|
||||
}
|
||||
|
||||
if (options?.search) {
|
||||
query.andWhere("(discount.title ILIKE :search OR discount.code ILIKE :search)", {
|
||||
search: `%${options.search}%`,
|
||||
});
|
||||
}
|
||||
|
||||
if (options?.productId) {
|
||||
query.andWhere("EXISTS (SELECT 1 FROM discount_product dp WHERE dp.discountId = discount.id AND dp.productId = :productId)", {
|
||||
productId: options.productId,
|
||||
});
|
||||
}
|
||||
|
||||
if (options?.sort) {
|
||||
const [field, order] = options.sort.split(",");
|
||||
query.orderBy(`discount.${field}`, order.toUpperCase());
|
||||
} else {
|
||||
query.orderBy("discount.createdAt", "DESC");
|
||||
}
|
||||
|
||||
return query
|
||||
.skip(options?.skip || 0)
|
||||
.take(options?.take || 10)
|
||||
.getManyAndCount();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
import { UsageDiscount } from "../entities/usage-discount.entity";
|
||||
|
||||
@Injectable()
|
||||
export class UsageDiscountRepository extends Repository<UsageDiscount> {
|
||||
constructor(@InjectRepository(UsageDiscount) usageDiscountRepository: Repository<UsageDiscount>) {
|
||||
super(usageDiscountRepository.target, usageDiscountRepository.manager, usageDiscountRepository.queryRunner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
import { UsageDiscount } from "../entities/usage-discount.entity";
|
||||
|
||||
@Injectable()
|
||||
export class UsageDiscountRepository extends Repository<UsageDiscount> {
|
||||
constructor(@InjectRepository(UsageDiscount) repository: Repository<UsageDiscount>) {
|
||||
super(repository.target, repository.manager, repository.queryRunner);
|
||||
}
|
||||
|
||||
async findByUserAndDiscount(userId: string, discountId: string): Promise<UsageDiscount | null> {
|
||||
return this.findOne({
|
||||
where: {
|
||||
user: { id: userId },
|
||||
discount: { id: discountId },
|
||||
},
|
||||
relations: ["user", "discount"],
|
||||
});
|
||||
}
|
||||
|
||||
async countByDiscount(discountId: string): Promise<number> {
|
||||
return this.count({
|
||||
where: {
|
||||
discount: { id: discountId },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async countByUserAndDiscount(userId: string, discountId: string): Promise<number> {
|
||||
return this.count({
|
||||
where: {
|
||||
user: { id: userId },
|
||||
discount: { id: discountId },
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
|
||||
import { DiscountMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class ApplyDiscountDto {
|
||||
@ApiProperty({ description: "Discount code" })
|
||||
@IsString({ message: DiscountMessage.DISCOUNT_CODE_STRING })
|
||||
@IsNotEmpty({ message: DiscountMessage.DISCOUNT_CODE_REQUIRED })
|
||||
discountCode: string;
|
||||
}
|
||||
@@ -10,8 +10,8 @@ import { InvoicesService } from "./providers/invoices.service";
|
||||
import { InvoicesRepository } from "./repositories/invoices.repository";
|
||||
import { Discount } from "../discounts/entities/discount.entity";
|
||||
import { UsageDiscount } from "../discounts/entities/usage-discount.entity";
|
||||
import { DiscountRepository } from "../discounts/repositories/discount.repository";
|
||||
import { UsageDiscountRepository } from "../discounts/repositories/usage-discount.repository";
|
||||
import { DiscountRepository } from "../discounts/repositories/discounts.repository";
|
||||
import { UsageDiscountRepository } from "../discounts/repositories/usage-discounts.repository";
|
||||
import { NotificationModule } from "../notifications/notifications.module";
|
||||
import { UsersModule } from "../users/users.module";
|
||||
import { UtilsModule } from "../utils/utils.module";
|
||||
|
||||
@@ -16,6 +16,7 @@ import { ParamDto } from "../../common/DTO/param.dto";
|
||||
import { PermissionEnum } from "../users/enums/permission.enum";
|
||||
|
||||
@Controller("subscriptions")
|
||||
@AuthGuards()
|
||||
export class SubscriptionsController {
|
||||
constructor(
|
||||
private readonly subscriptionService: SubscriptionsService,
|
||||
@@ -23,7 +24,6 @@ export class SubscriptionsController {
|
||||
) {}
|
||||
|
||||
@ApiOperation({ summary: "Create a subscription plan" })
|
||||
@AuthGuards()
|
||||
@PermissionsDec(PermissionEnum.SERVICES)
|
||||
@Post()
|
||||
createSubscription(@Body() createDto: AddSubscriptionsToServiceDto) {
|
||||
@@ -31,7 +31,6 @@ export class SubscriptionsController {
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "get all subscription plans" })
|
||||
@AuthGuards()
|
||||
@PermissionsDec(PermissionEnum.SERVICES)
|
||||
@Get("service/:serviceId")
|
||||
getServiceSubscriptions(@Param() paramDto: ServiceIdParamDto, @Query() queryDto: ServiceSubsQueryDto) {
|
||||
@@ -39,21 +38,18 @@ export class SubscriptionsController {
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "get all user subscriptions ==> user route" })
|
||||
@AuthGuards()
|
||||
@Get("user")
|
||||
getUserSubscriptions(@UserDec("id") userId: string, @Query() queryDto: SearchUserSubsQueryDto) {
|
||||
return this.subscriptionService.getUserSubscriptions(userId, queryDto);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "get a user subscription by id ==> user route" })
|
||||
@AuthGuards()
|
||||
@Get("user/:id")
|
||||
getUserSubscriptionById(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
|
||||
return this.subscriptionService.getUserSubscriptionById(paramDto.id, userId);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "toggle status of service subs" })
|
||||
@AuthGuards()
|
||||
@PermissionsDec(PermissionEnum.SERVICES)
|
||||
@Post("toggle-status/:id")
|
||||
toggleStatusOfServiceSub(@Param() paramDto: ParamDto) {
|
||||
@@ -61,7 +57,6 @@ export class SubscriptionsController {
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "get a subscription plan by id" })
|
||||
@AuthGuards()
|
||||
@Get(":id")
|
||||
@PermissionsDec(PermissionEnum.SERVICES)
|
||||
getSubscription(@Param() paramDto: ParamDto) {
|
||||
@@ -69,7 +64,6 @@ export class SubscriptionsController {
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "update a subscription plan by id" })
|
||||
@AuthGuards()
|
||||
@Patch(":id")
|
||||
//TODO:fix this if needed
|
||||
@PermissionsDec(PermissionEnum.SERVICES)
|
||||
@@ -78,28 +72,24 @@ export class SubscriptionsController {
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "subscribe to a service ==> user route" })
|
||||
@AuthGuards()
|
||||
@Post(":serviceId/subscribe")
|
||||
subscribe(@Param() paramDto: ServiceIdParamDto, @Body() subscribeDto: SubscribeServiceDto, @UserDec("id") userId: string) {
|
||||
return this.subscriptionService.subscribeToPlan(paramDto.serviceId, subscribeDto, userId);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "add quick access a service ==> user route" })
|
||||
@AuthGuards()
|
||||
@Post(":id/add-quick-access")
|
||||
addQuickAccess(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
|
||||
return this.userQuickAccessService.addQuickAccess(userId, paramDto.id);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "get quick access a service ==> user route" })
|
||||
@AuthGuards()
|
||||
@Get("quick-access")
|
||||
getQuickAccess(@UserDec("id") userId: string) {
|
||||
return this.userQuickAccessService.getUserQuickAccess(userId);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "remove quick access a service ==> user route" })
|
||||
@AuthGuards()
|
||||
@Delete(":id/remove-quick-access")
|
||||
removeQuickAccess(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
|
||||
return this.userQuickAccessService.removeQuickAccess(userId, paramDto.id);
|
||||
|
||||
@@ -12,8 +12,6 @@ import { BlogComment } from "../../blogs/entities/blog-comment.entity";
|
||||
import { Blog } from "../../blogs/entities/blog.entity";
|
||||
import { Criticism } from "../../criticisms/entities/criticism.entity";
|
||||
import { DanakServiceReview } from "../../danak-services/entities/danak-service-review.entity";
|
||||
import { Discount } from "../../discounts/entities/discount.entity";
|
||||
import { UsageDiscount } from "../../discounts/entities/usage-discount.entity";
|
||||
import { Invoice } from "../../invoices/entities/invoice.entity";
|
||||
import { LearningProgress } from "../../learnings/entities/learning-progress.entity";
|
||||
import { Notification } from "../../notifications/entities/notification.entity";
|
||||
@@ -115,17 +113,14 @@ export class User extends BaseEntity {
|
||||
@OneToMany(() => LearningProgress, (learningProgress) => learningProgress.learning)
|
||||
learningProgress: LearningProgress[];
|
||||
|
||||
@ManyToMany(() => Discount, (discount) => discount.subscriptionPlans)
|
||||
discounts: Discount[];
|
||||
|
||||
@OneToOne(() => RealUser, (realUser) => realUser.user, { cascade: true, nullable: true })
|
||||
realUser: RealUser;
|
||||
|
||||
@OneToOne(() => LegalUser, (legalUser) => legalUser.user, { cascade: true, nullable: true })
|
||||
legalUser: LegalUser;
|
||||
|
||||
@ManyToMany(() => UsageDiscount, (usageDiscount) => usageDiscount.users)
|
||||
usageDiscounts: UsageDiscount[];
|
||||
// @ManyToMany(() => UsageDiscount, (usageDiscount) => usageDiscount.users)
|
||||
// usageDiscounts: UsageDiscount[];
|
||||
|
||||
@OneToMany(() => UserAnnouncement, (userAnnouncement) => userAnnouncement.user)
|
||||
userAnnouncements: UserAnnouncement[];
|
||||
|
||||
Reference in New Issue
Block a user