chore: add new service to add subs to danak services in array

This commit is contained in:
mahyargdz
2025-02-13 13:27:21 +03:30
parent 95b9c38031
commit caa968ab4a
24 changed files with 317 additions and 98 deletions
@@ -1,9 +1,10 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsBoolean, IsInt, IsNotEmpty, IsString, IsUUID, Length, Min } from "class-validator";
import { Type } from "class-transformer";
import { ArrayMinSize, IsArray, IsBoolean, IsInt, IsNotEmpty, IsString, IsUUID, Length, Min, ValidateNested } from "class-validator";
import { SubscriptionMessage } from "../../../common/enums/message.enum";
export class CreateSubscriptionPlanDto {
export class SubscriptionPlanDto {
@IsNotEmpty({ message: SubscriptionMessage.NAME_REQUIRED })
@IsString({ message: SubscriptionMessage.NAME_SHOULD_BE_STRING })
@Length(3, 100, { message: SubscriptionMessage.NAME_LENGTH })
@@ -25,6 +26,18 @@ export class CreateSubscriptionPlanDto {
@IsBoolean({ message: SubscriptionMessage.IS_ACTIVE_SHOULD_BE_BOOLEAN })
@ApiProperty({ description: "the status of the subscription plan", example: true })
isActive: boolean;
}
export class AddSubscriptionsToServiceDto {
@IsNotEmpty({ message: SubscriptionMessage.SUBS_REQUIRED })
@IsArray({ message: SubscriptionMessage.SUBS_SHOULD_BE_ARRAY })
@ArrayMinSize(1, { message: SubscriptionMessage.SUBS_MIN_SIZE })
@ValidateNested({ each: true })
@Type(() => SubscriptionPlanDto)
@ApiProperty({
type: [SubscriptionPlanDto],
})
subs: SubscriptionPlanDto[];
@IsNotEmpty({ message: SubscriptionMessage.SERVICE_REQUIRED })
@IsUUID("4", { message: SubscriptionMessage.SERVICE_MUST_BE_UUID })
@@ -0,0 +1,19 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsOptional, IsString } from "class-validator";
import { PaginationDto } from "../../../common/DTO/pagination.dto";
import { SubscriptionMessage } from "../../../common/enums/message.enum";
export class ServiceSubsQueryDto extends PaginationDto {
@IsOptional()
@Type(() => Number)
// @IsBoolean({ message: CategoryMessage.IS_ACTIVE_SHOULD_BE_BOOLEAN })
@ApiPropertyOptional({ description: "Category status", example: 1 })
isActive?: number;
@IsOptional()
@IsString({ message: SubscriptionMessage.SEARCH_QUERY_STRING })
@ApiPropertyOptional({ description: "Search query", example: "search query" })
q?: string;
}
@@ -1,5 +1,12 @@
import { PartialType } from "@nestjs/swagger";
import { ApiProperty, PartialType } from "@nestjs/swagger";
import { IsNotEmpty, IsUUID } from "class-validator";
import { CreateSubscriptionPlanDto } from "./create-subscription.dto";
import { SubscriptionPlanDto } from "./create-subscription.dto";
import { SubscriptionMessage } from "../../../common/enums/message.enum";
export class UpdateSubscriptionPlanDto extends PartialType(CreateSubscriptionPlanDto) {}
export class UpdateSubscriptionPlanDto extends PartialType(SubscriptionPlanDto) {
@IsNotEmpty({ message: SubscriptionMessage.SERVICE_REQUIRED })
@IsUUID("4", { message: SubscriptionMessage.SERVICE_MUST_BE_UUID })
@ApiProperty({ description: "Service ID", example: "d290f1ee-6c54-4b01-90e6-d701748f0851" })
serviceId: string;
}
@@ -1,14 +1,15 @@
// eslint-disable-next-line import/no-named-as-default
import Decimal from "decimal.js";
import { Column, Entity, ManyToOne } from "typeorm";
import { Column, Entity, Index, ManyToOne } from "typeorm";
import { BaseEntity } from "../../../common/entities/base.entity";
import { DecimalTransformer } from "../../../common/transformers/decimal.transformer";
import { DanakService } from "../../danak-services/entities/danak-service.entity";
@Entity()
@Index(["service", "name"], { unique: true })
export class SubscriptionPlan extends BaseEntity {
@Column({ type: "varchar", length: 150, nullable: false, unique: true })
@Column({ type: "varchar", length: 150, nullable: false })
name: string;
@Column({ type: "int", nullable: false })
@@ -1,15 +1,17 @@
import { BadRequestException, Injectable } from "@nestjs/common";
// eslint-disable-next-line import/no-named-as-default
import Decimal from "decimal.js";
import { DataSource } from "typeorm";
import { DataSource, In } from "typeorm";
import { ServiceMessage, SubscriptionMessage, WalletMessage } from "../../../common/enums/message.enum";
import { DanakServicesService } from "../../danak-services/providers/danak-services.service";
import { InvoicesService } from "../../invoices/providers/invoices.service";
import { UsersService } from "../../users/providers/users.service";
import { PaginationUtils } from "../../utils/providers/pagination.utils";
import { Wallet } from "../../wallets/entities/wallet.entity";
import { WalletsService } from "../../wallets/providers/wallets.service";
import { CreateSubscriptionPlanDto } from "../DTO/create-subscription.dto";
import { AddSubscriptionsToServiceDto } from "../DTO/create-subscription.dto";
import { ServiceSubsQueryDto } from "../DTO/service-subs-query.dto";
import { SubscribeServiceDto } from "../DTO/subscribe-service.dto";
import { UpdateSubscriptionPlanDto } from "../DTO/update-subscription.dto";
import { SubscriptionPlan } from "../entities/subscription.entity";
@@ -31,18 +33,29 @@ export class SubscriptionsService {
) {}
//************************************ */
async createSubscriptionsPlan(createDto: CreateSubscriptionPlanDto) {
async createSubscriptionsPlan(createDto: AddSubscriptionsToServiceDto) {
const danakService = await this.danakServices.findServiceById(createDto.serviceId);
if (!danakService) throw new BadRequestException(ServiceMessage.SERVICE_NOT_FOUND_BY_ID);
const existSubscription = await this.subscriptionsPlanRepository.findOneByName(createDto.name);
if (existSubscription) throw new BadRequestException(SubscriptionMessage.NAME_EXIST);
const subscriptions = createDto.subs.map((sub) => ({ ...sub, service: { id: createDto.serviceId } }));
const subscriptionNames = createDto.subs.map((sub) => sub.name);
const existingSubscriptions = await this.subscriptionsPlanRepository.find({
where: {
name: In(subscriptionNames),
service: { id: createDto.serviceId },
},
});
if (existingSubscriptions.length > 0)
throw new BadRequestException({ message: [SubscriptionMessage.NAME_EXIST, existingSubscriptions] });
const createdSubscriptions = this.subscriptionsPlanRepository.create(subscriptions);
await this.subscriptionsPlanRepository.save(createdSubscriptions);
const subscription = this.subscriptionsPlanRepository.create({ ...createDto, service: { id: createDto.serviceId } });
await this.subscriptionsPlanRepository.save(subscription);
return {
message: SubscriptionMessage.CREATED,
subscription,
subscriptions: createdSubscriptions,
};
}
//************************************ */
@@ -82,15 +95,51 @@ export class SubscriptionsService {
}
//************************************ */
async getSubscriptionsPlans() {
const subscriptions = await this.subscriptionsPlanRepository.find({ relations: ["service"] });
async getServiceSubscriptions(serviceId: string, queryDto: ServiceSubsQueryDto) {
const service = await this.danakServices.findServiceById(serviceId);
const { limit, skip } = PaginationUtils(queryDto);
const queryBuilder = this.subscriptionsPlanRepository
.createQueryBuilder("subscription")
.leftJoin("subscription.service", "service")
.addSelect(["service.id"])
.where("service.id = :serviceId", { serviceId });
if (queryDto.q) {
queryBuilder.andWhere("subscription.name ILIKE :query", { query: `%${queryDto.q}%` });
}
if (queryDto.isActive !== undefined) {
queryBuilder.andWhere("subscription.isActive = :isActive", { isActive: queryDto.isActive === 1 });
}
const [subscriptions, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount();
return {
service,
subscriptions,
count,
pagination: true,
};
}
//************************************ */
async toggleSubStatus(subId: string) {
const subscription = await this.subscriptionsPlanRepository.findOneBy({ id: subId });
if (!subscription) throw new BadRequestException(SubscriptionMessage.NOT_FOUND);
subscription.isActive = !subscription.isActive;
await this.subscriptionsPlanRepository.save(subscription);
return {
message: SubscriptionMessage.STATUS_UPDATED,
isActive: subscription.isActive,
};
}
//************************************ */
async subscribeToPlan(serviceId: string, subscribeDto: SubscribeServiceDto, userId: string) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
@@ -1,8 +1,9 @@
import { Body, Controller, Get, Param, Patch, Post } from "@nestjs/common";
import { Body, Controller, Get, Param, Patch, Post, Query } from "@nestjs/common";
import { ApiOperation } from "@nestjs/swagger";
import { CreateSubscriptionPlanDto } from "./DTO/create-subscription.dto";
import { AddSubscriptionsToServiceDto } from "./DTO/create-subscription.dto";
import { ServiceIdParamDto } from "./DTO/service-id.param.dto";
import { ServiceSubsQueryDto } from "./DTO/service-subs-query.dto";
import { SubscribeServiceDto } from "./DTO/subscribe-service.dto";
import { UpdateSubscriptionPlanDto } from "./DTO/update-subscription.dto";
import { SubscriptionsService } from "./providers/subscriptions.service";
@@ -20,16 +21,24 @@ export class SubscriptionsController {
@AuthGuards()
@Roles(RoleEnum.ADMIN)
@Post()
createSubscription(@Body() createDto: CreateSubscriptionPlanDto) {
createSubscription(@Body() createDto: AddSubscriptionsToServiceDto) {
return this.subscriptionService.createSubscriptionsPlan(createDto);
}
@ApiOperation({ summary: "get all subscription plans" })
@AuthGuards()
@Roles(RoleEnum.ADMIN)
@Get()
getAllSubscriptions() {
return this.subscriptionService.getSubscriptionsPlans();
@Get("service/:serviceId")
getServiceSubscriptions(@Param() paramDto: ServiceIdParamDto, @Query() queryDto: ServiceSubsQueryDto) {
return this.subscriptionService.getServiceSubscriptions(paramDto.serviceId, queryDto);
}
@ApiOperation({ summary: "toggle status of service subs" })
@AuthGuards()
@Roles(RoleEnum.ADMIN)
@Post("toggle-status/:id")
toggleStatusOfServiceSub(@Param() paramDto: ParamDto) {
return this.subscriptionService.toggleSubStatus(paramDto.id);
}
@ApiOperation({ summary: "get a subscription plan by id" })