chore: add business logic
This commit is contained in:
@@ -49,7 +49,6 @@
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/jwt": "^11.0.0",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/platform-fastify": "^11.1.0",
|
||||
"@nestjs/swagger": "^11.2.0",
|
||||
"@nestjs/throttler": "^6.4.0",
|
||||
@@ -80,7 +79,6 @@
|
||||
"@swc/cli": "^0.6.0",
|
||||
"@swc/core": "^1.10.7",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.10.7",
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
|
||||
Generated
+15
-10
@@ -65,9 +65,6 @@ importers:
|
||||
'@nestjs/passport':
|
||||
specifier: ^11.0.5
|
||||
version: 11.0.5(@nestjs/common@11.1.0(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)
|
||||
'@nestjs/platform-express':
|
||||
specifier: ^11.0.1
|
||||
version: 11.1.0(@nestjs/common@11.1.0(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.0)
|
||||
'@nestjs/platform-fastify':
|
||||
specifier: ^11.1.0
|
||||
version: 11.1.0(@fastify/static@8.1.1)(@nestjs/common@11.1.0(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.0)
|
||||
@@ -153,9 +150,6 @@ importers:
|
||||
'@types/bcrypt':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
'@types/express':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.1
|
||||
'@types/jest':
|
||||
specifier: ^29.5.14
|
||||
version: 29.5.14
|
||||
@@ -8048,6 +8042,7 @@ snapshots:
|
||||
tslib: 2.8.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
'@nestjs/platform-fastify@11.1.0(@fastify/static@8.1.1)(@nestjs/common@11.1.0(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.0)':
|
||||
dependencies:
|
||||
@@ -9423,6 +9418,7 @@ snapshots:
|
||||
busboy@1.6.0:
|
||||
dependencies:
|
||||
streamsearch: 1.1.0
|
||||
optional: true
|
||||
|
||||
bytes@3.1.2: {}
|
||||
|
||||
@@ -9642,6 +9638,7 @@ snapshots:
|
||||
inherits: 2.0.4
|
||||
readable-stream: 2.3.8
|
||||
typedarray: 0.0.6
|
||||
optional: true
|
||||
|
||||
concat-stream@2.0.0:
|
||||
dependencies:
|
||||
@@ -11165,7 +11162,8 @@ snapshots:
|
||||
is-docker: 2.2.1
|
||||
optional: true
|
||||
|
||||
isarray@1.0.0: {}
|
||||
isarray@1.0.0:
|
||||
optional: true
|
||||
|
||||
isarray@2.0.5: {}
|
||||
|
||||
@@ -12302,6 +12300,7 @@ snapshots:
|
||||
mkdirp@0.5.6:
|
||||
dependencies:
|
||||
minimist: 1.2.8
|
||||
optional: true
|
||||
|
||||
mkdirp@1.0.4: {}
|
||||
|
||||
@@ -12334,6 +12333,7 @@ snapshots:
|
||||
object-assign: 4.1.1
|
||||
type-is: 1.6.18
|
||||
xtend: 4.0.2
|
||||
optional: true
|
||||
|
||||
mute-stream@2.0.0: {}
|
||||
|
||||
@@ -12776,7 +12776,8 @@ snapshots:
|
||||
uuid: 9.0.1
|
||||
optional: true
|
||||
|
||||
process-nextick-args@2.0.1: {}
|
||||
process-nextick-args@2.0.1:
|
||||
optional: true
|
||||
|
||||
process-warning@4.0.1: {}
|
||||
|
||||
@@ -12930,6 +12931,7 @@ snapshots:
|
||||
safe-buffer: 5.1.2
|
||||
string_decoder: 1.1.1
|
||||
util-deprecate: 1.0.2
|
||||
optional: true
|
||||
|
||||
readable-stream@3.6.2:
|
||||
dependencies:
|
||||
@@ -13066,7 +13068,8 @@ snapshots:
|
||||
has-symbols: 1.1.0
|
||||
isarray: 2.0.5
|
||||
|
||||
safe-buffer@5.1.2: {}
|
||||
safe-buffer@5.1.2:
|
||||
optional: true
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
@@ -13290,7 +13293,8 @@ snapshots:
|
||||
|
||||
statuses@2.0.1: {}
|
||||
|
||||
streamsearch@1.1.0: {}
|
||||
streamsearch@1.1.0:
|
||||
optional: true
|
||||
|
||||
streamx@2.22.0:
|
||||
dependencies:
|
||||
@@ -13350,6 +13354,7 @@ snapshots:
|
||||
string_decoder@1.1.1:
|
||||
dependencies:
|
||||
safe-buffer: 5.1.2
|
||||
optional: true
|
||||
|
||||
string_decoder@1.3.0:
|
||||
dependencies:
|
||||
|
||||
@@ -15,6 +15,7 @@ import { databaseConfig } from "./configs/mikro-orm.config";
|
||||
import { rateLimitConfig } from "./configs/rateLimit.config";
|
||||
import { HTTPLogger } from "./core/middlewares/logger.middleware";
|
||||
import { AuthModule } from "./modules/auth/auth.module";
|
||||
import { BusinessesModule } from "./modules/businesses/businesses.module";
|
||||
import { CompaniesModule } from "./modules/companies/companies.module";
|
||||
import { IndustriesModule } from "./modules/industries/industries.module";
|
||||
import { InvoicesModule } from "./modules/invoices/invoices.module";
|
||||
@@ -23,6 +24,7 @@ import { TicketsModule } from "./modules/tickets/tickets.module";
|
||||
import { UploaderModule } from "./modules/uploader/uploader.module";
|
||||
import { UsersModule } from "./modules/users/users.module";
|
||||
import { UtilsModule } from "./modules/utils/utils.module";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true, cache: true }),
|
||||
@@ -50,7 +52,14 @@ import { UtilsModule } from "./modules/utils/utils.module";
|
||||
UploaderModule,
|
||||
TicketsModule,
|
||||
InvoicesModule,
|
||||
BusinessesModule,
|
||||
],
|
||||
// providers: [
|
||||
// {
|
||||
// provide: APP_INTERCEPTOR,
|
||||
// useClass: BusinessInterceptor,
|
||||
// },
|
||||
// ],
|
||||
})
|
||||
export class AppModule implements NestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ExecutionContext, createParamDecorator } from "@nestjs/common";
|
||||
import { FastifyRequest } from "fastify";
|
||||
|
||||
/**
|
||||
* Extracts the business ID from the request header
|
||||
* @example
|
||||
* ```typescript
|
||||
* @Get()
|
||||
* findAll(@BusinessIdHeader() businessId: string) {
|
||||
* // Use businessId
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export const BusinessIdHeader = createParamDecorator((headerName: string = "X-Business-Id", ctx: ExecutionContext) => {
|
||||
const req = ctx.switchToHttp().getRequest<FastifyRequest>();
|
||||
return req.headers[headerName.toLocaleLowerCase()];
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ExecutionContext, createParamDecorator } from "@nestjs/common";
|
||||
import { FastifyRequest } from "fastify";
|
||||
|
||||
import { Business } from "../../modules/businesses/entities/business.entity";
|
||||
|
||||
export const BusinessDec = createParamDecorator((data: keyof Business | undefined, ctx: ExecutionContext) => {
|
||||
const req = ctx.switchToHttp().getRequest<FastifyRequest>();
|
||||
const business = req.business;
|
||||
|
||||
return data ? business?.[data] : business;
|
||||
});
|
||||
@@ -597,3 +597,8 @@ export const enum CompanyMessage {
|
||||
COMPANY_ID_REQUIRED = "شرکت مورد نیاز است",
|
||||
COMPANY_ID_SHOULD_BE_A_UUID = "شناسه شرکت باید یک UUID معتبر باشد",
|
||||
}
|
||||
|
||||
export const enum BusinessMessage {
|
||||
NOT_FOUND = "کسب و کار یافت نشد",
|
||||
BUSINESS_ID_REQUIRED = "شناسه کسب و کار مورد نیاز است",
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export function bullMqConfig(): SharedBullAsyncConfiguration {
|
||||
},
|
||||
prefix: "dzone",
|
||||
defaultJobOptions: {
|
||||
removeOnComplete: false,
|
||||
removeOnComplete: true,
|
||||
removeOnFail: false,
|
||||
attempts: 5,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
ForbiddenException,
|
||||
Injectable,
|
||||
Logger,
|
||||
NestInterceptor,
|
||||
} from "@nestjs/common";
|
||||
import { FastifyRequest } from "fastify";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { BusinessMessage } from "../../common/enums/message.enum";
|
||||
import { Business } from "../../modules/businesses/entities/business.entity";
|
||||
import { BusinessService } from "../../modules/businesses/services/businesses.service";
|
||||
|
||||
declare module "fastify" {
|
||||
interface FastifyRequest {
|
||||
business?: Business;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class BusinessInterceptor implements NestInterceptor {
|
||||
private readonly headerName = "x-business-id";
|
||||
private readonly logger = new Logger(BusinessInterceptor.name);
|
||||
|
||||
constructor(private readonly businessService: BusinessService) {}
|
||||
|
||||
async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
|
||||
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
||||
|
||||
const businessId = request.headers[this.headerName.toLocaleLowerCase()] as string;
|
||||
|
||||
if (businessId) {
|
||||
try {
|
||||
const business = await this.businessService.getBusinessByDanakSubscriptionId(businessId);
|
||||
|
||||
request.business = business;
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
throw new BadRequestException(BusinessMessage.NOT_FOUND);
|
||||
}
|
||||
} else {
|
||||
throw new ForbiddenException(BusinessMessage.BUSINESS_ID_REQUIRED);
|
||||
}
|
||||
|
||||
return next.handle();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { MikroOrmModule } from "@mikro-orm/nestjs";
|
||||
import { BullModule } from "@nestjs/bullmq";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
import { SUBSCRIPTIONS } from "./constant";
|
||||
import { Business } from "./entities/business.entity";
|
||||
import { BusinessProvisioningProcessor } from "./queue/business-provisioning.processor";
|
||||
import { BusinessService } from "./services/businesses.service";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
MikroOrmModule.forFeature([Business]),
|
||||
BullModule.registerQueue({
|
||||
name: SUBSCRIPTIONS.PROVISIONING_QUEUE_NAME,
|
||||
prefix: SUBSCRIPTIONS.PROVISIONING_QUEUE_PREFIX,
|
||||
}),
|
||||
],
|
||||
providers: [BusinessService, BusinessProvisioningProcessor],
|
||||
exports: [BusinessService],
|
||||
})
|
||||
export class BusinessesModule {}
|
||||
@@ -0,0 +1,9 @@
|
||||
export const SUBSCRIPTIONS = Object.freeze({
|
||||
PROVISIONING_QUEUE_PREFIX: "provisioning",
|
||||
PROVISIONING_QUEUE_NAME: "provisioning",
|
||||
PROVISIONING_JOB_NAME: "business.created",
|
||||
|
||||
//
|
||||
SERVICE_SLUG: "Dzonea",
|
||||
SERVICE_ID: "7e3c2f08-b7e7-4402-ae5f-fea99cff56bd",
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Cascade, Collection, Entity, EntityRepositoryType, OneToMany, Property, Unique } from "@mikro-orm/core";
|
||||
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { Company } from "../../companies/entities/company.entity";
|
||||
import { Industry } from "../../industries/entities/industry.entity";
|
||||
import { BusinessRepository } from "../repositories/business.repository";
|
||||
|
||||
@Entity({ repository: () => BusinessRepository })
|
||||
export class Business extends BaseEntity {
|
||||
@Unique()
|
||||
@Property({ type: "uuid", nullable: false })
|
||||
danakSubscriptionId!: string;
|
||||
|
||||
@Property({ type: "varchar", length: 255, nullable: false })
|
||||
name!: string;
|
||||
|
||||
@Property({ type: "varchar", length: 255, nullable: true })
|
||||
logoUrl?: string;
|
||||
|
||||
@OneToMany(() => Company, (company) => company.business, { cascade: [Cascade.ALL] })
|
||||
companies = new Collection<Company>(this);
|
||||
|
||||
@OneToMany(() => Industry, (industry) => industry.business)
|
||||
industries = new Collection<Industry>(this);
|
||||
|
||||
[EntityRepositoryType]?: BusinessRepository;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface IBusinessProvisioningJob {
|
||||
subscriptionId: string;
|
||||
serviceId: string;
|
||||
serviceName: string;
|
||||
businessName: string;
|
||||
userId: string;
|
||||
slug: string;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// src/business/queue/business-provisioning.processor.ts
|
||||
import { EntityManager } from "@mikro-orm/postgresql";
|
||||
import { Processor } from "@nestjs/bullmq";
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { Job } from "bullmq";
|
||||
|
||||
import { WorkerProcessor } from "../../../common/queues/worker.processor";
|
||||
import { SUBSCRIPTIONS } from "../constant";
|
||||
import { Business } from "../entities/business.entity";
|
||||
import { IBusinessProvisioningJob } from "../interfaces/IBusiness";
|
||||
|
||||
@Processor(SUBSCRIPTIONS.PROVISIONING_QUEUE_NAME)
|
||||
export class BusinessProvisioningProcessor extends WorkerProcessor {
|
||||
protected readonly logger = new Logger(BusinessProvisioningProcessor.name);
|
||||
|
||||
constructor(private readonly em: EntityManager) {
|
||||
super();
|
||||
}
|
||||
|
||||
async process(job: Job) {
|
||||
switch (job.name) {
|
||||
case SUBSCRIPTIONS.PROVISIONING_JOB_NAME:
|
||||
await this.handleBusinessCreated(job);
|
||||
break;
|
||||
default:
|
||||
this.logger.warn(`Unknown job name: ${job.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleBusinessCreated(job: Job<IBusinessProvisioningJob>) {
|
||||
const em = this.em.fork();
|
||||
const { subscriptionId, serviceId, serviceName, businessName } = job.data;
|
||||
|
||||
// Only act if the event is for this service
|
||||
if (serviceId !== SUBSCRIPTIONS.SERVICE_ID) {
|
||||
this.logger.debug(`Skipping job for service: ${serviceName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if already exists
|
||||
let business = await em.findOne(Business, { danakSubscriptionId: subscriptionId });
|
||||
if (!business) {
|
||||
business = em.create(Business, {
|
||||
danakSubscriptionId: subscriptionId,
|
||||
name: businessName,
|
||||
// logoUrl: job.data.logoUrl, // Uncomment if logoUrl is provided in job data
|
||||
});
|
||||
await em.persistAndFlush(business);
|
||||
this.logger.log(`Created business for subscription ${subscriptionId}`);
|
||||
} else {
|
||||
this.logger.debug(`Business already exists for subscription ${subscriptionId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { EntityRepository } from "@mikro-orm/postgresql";
|
||||
|
||||
import { Business } from "../entities/business.entity";
|
||||
|
||||
export class BusinessRepository extends EntityRepository<Business> {}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
|
||||
import { BusinessMessage } from "../../../common/enums/message.enum";
|
||||
import { BusinessRepository } from "../repositories/business.repository";
|
||||
|
||||
@Injectable()
|
||||
export class BusinessService {
|
||||
constructor(private readonly businessRepository: BusinessRepository) {}
|
||||
|
||||
async getBusinessByDanakSubscriptionId(danakSubscriptionId: string) {
|
||||
const business = await this.businessRepository.findOne({ danakSubscriptionId, deletedAt: null });
|
||||
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
|
||||
|
||||
return business;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,52 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common";
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseInterceptors } from "@nestjs/common";
|
||||
import { ApiOperation } from "@nestjs/swagger";
|
||||
|
||||
import { CompanyListQueryDto } from "./DTO/company-list-query.dto";
|
||||
import { CreateCompanyDto } from "./DTO/create-company.dto";
|
||||
import { UpdateCompanyDto } from "./DTO/update-company.dto";
|
||||
import { CompaniesService } from "./services/companies.service";
|
||||
import { BusinessDec } from "../../common/decorators/business.decorator";
|
||||
import { ParamDto } from "../../common/DTO/param.dto";
|
||||
import { BusinessInterceptor } from "../../core/interceptors/business.interceptor";
|
||||
import { Business } from "../businesses/entities/business.entity";
|
||||
@Controller("companies")
|
||||
@UseInterceptors(BusinessInterceptor)
|
||||
export class CompaniesController {
|
||||
constructor(private readonly companiesService: CompaniesService) {}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: "create company (admin)" })
|
||||
create(@Body() createCompanyDto: CreateCompanyDto) {
|
||||
return this.companiesService.create(createCompanyDto);
|
||||
create(@Body() createCompanyDto: CreateCompanyDto, @BusinessDec() business: Business) {
|
||||
return this.companiesService.create(createCompanyDto, business);
|
||||
}
|
||||
|
||||
@Patch(":id")
|
||||
@ApiOperation({ summary: "update company (admin)" })
|
||||
update(@Param() paramDto: ParamDto, @Body() updateCompanyDto: UpdateCompanyDto) {
|
||||
return this.companiesService.updateCompanyById(paramDto.id, updateCompanyDto);
|
||||
update(@Param() paramDto: ParamDto, @Body() updateCompanyDto: UpdateCompanyDto, @BusinessDec("id") businessId: string) {
|
||||
return this.companiesService.updateCompanyById(paramDto.id, updateCompanyDto, businessId);
|
||||
}
|
||||
|
||||
@Delete(":id")
|
||||
@ApiOperation({ summary: "delete company (admin)" })
|
||||
delete(@Param() paramDto: ParamDto) {
|
||||
return this.companiesService.deleteCompanyById(paramDto.id);
|
||||
delete(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||
return this.companiesService.deleteCompanyById(paramDto.id, businessId);
|
||||
}
|
||||
|
||||
@Get("list")
|
||||
@ApiOperation({ summary: "get companies list (admin)" })
|
||||
getCompaniesList(@Query() query: CompanyListQueryDto) {
|
||||
return this.companiesService.getCompaniesList(query);
|
||||
getCompaniesList(@Query() query: CompanyListQueryDto, @BusinessDec("id") businessId: string) {
|
||||
return this.companiesService.getCompaniesList(query, businessId);
|
||||
}
|
||||
|
||||
@Get(":id")
|
||||
@ApiOperation({ summary: "get company by id" })
|
||||
get(@Param() paramDto: ParamDto) {
|
||||
return this.companiesService.getCompanyById(paramDto.id);
|
||||
get(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||
return this.companiesService.getCompanyById(paramDto.id, businessId);
|
||||
}
|
||||
|
||||
@Patch(":id/toggle-status")
|
||||
@ApiOperation({ summary: "toggle company status (admin)" })
|
||||
toggleStatus(@Param() paramDto: ParamDto) {
|
||||
return this.companiesService.toggleStatus(paramDto.id);
|
||||
toggleStatus(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||
return this.companiesService.toggleStatus(paramDto.id, businessId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ import { CompanyProduct } from "./entities/company-product.entity";
|
||||
import { CompanyService } from "./entities/company-service.entity";
|
||||
import { Company } from "./entities/company.entity";
|
||||
import { CompaniesService } from "./services/companies.service";
|
||||
import { BusinessesModule } from "../businesses/businesses.module";
|
||||
import { Industry } from "../industries/entities/industry.entity";
|
||||
import { UsersModule } from "../users/users.module";
|
||||
import { UtilsModule } from "../utils/utils.module";
|
||||
@Module({
|
||||
imports: [MikroOrmModule.forFeature([Company, Industry, CompanyProduct, CompanyService]), UsersModule, UtilsModule],
|
||||
imports: [MikroOrmModule.forFeature([Company, Industry, CompanyProduct, CompanyService]), UsersModule, UtilsModule, BusinessesModule],
|
||||
providers: [CompaniesService],
|
||||
controllers: [CompaniesController],
|
||||
exports: [CompaniesService],
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Cascade, Collection, Entity, EntityRepositoryType, Enum, Formula, ManyT
|
||||
import { CompanyProduct } from "./company-product.entity";
|
||||
import { CompanyService } from "./company-service.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { Business } from "../../businesses/entities/business.entity";
|
||||
import { Industry } from "../../industries/entities/industry.entity";
|
||||
import { Invoice } from "../../invoices/entities/invoice.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
@@ -66,5 +67,8 @@ export class Company extends BaseEntity {
|
||||
@ManyToOne(() => User, { deleteRule: "restrict" })
|
||||
user!: User;
|
||||
|
||||
@ManyToOne(() => Business, { deleteRule: "restrict" })
|
||||
business!: Business;
|
||||
|
||||
[EntityRepositoryType]?: CompanyRepository;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ import { Company } from "../entities/company.entity";
|
||||
|
||||
export class CompanyRepository extends EntityRepository<Company> {
|
||||
//
|
||||
async getCompaniesListForAdmin(queryDto: CompanyListQueryDto) {
|
||||
async getCompaniesListForAdmin(queryDto: CompanyListQueryDto, businessId: string) {
|
||||
const { limit, skip } = PaginationUtils(queryDto);
|
||||
|
||||
const where: FilterQuery<Company> = {
|
||||
deletedAt: null,
|
||||
business: { id: businessId },
|
||||
};
|
||||
|
||||
if (queryDto.status) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { CompanyMessage, IndustryMessage } from "../../../common/enums/message.enum";
|
||||
import { Business } from "../../businesses/entities/business.entity";
|
||||
import { Industry } from "../../industries/entities/industry.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { UsersService } from "../../users/services/users.service";
|
||||
@@ -25,7 +26,7 @@ export class CompaniesService {
|
||||
private readonly em: EntityManager,
|
||||
) {}
|
||||
|
||||
async create(createDto: CreateCompanyDto) {
|
||||
async create(createDto: CreateCompanyDto, business: Business) {
|
||||
const em = this.em.fork();
|
||||
try {
|
||||
await em.begin();
|
||||
@@ -34,8 +35,8 @@ export class CompaniesService {
|
||||
if (!industry) throw new BadRequestException(IndustryMessage.INDUSTRY_NOT_FOUND);
|
||||
|
||||
const [existingCompany, existIdentityNumber] = await Promise.all([
|
||||
em.findOne(Company, { name: createDto.name }),
|
||||
em.findOne(Company, { identificationNumber: createDto.identificationNumber }),
|
||||
em.findOne(Company, { name: createDto.name, business: { id: business.id } }),
|
||||
em.findOne(Company, { identificationNumber: createDto.identificationNumber, business: { id: business.id } }),
|
||||
]);
|
||||
|
||||
if (existingCompany) throw new BadRequestException(CompanyMessage.COMPANY_ALREADY_EXISTS_NAME);
|
||||
@@ -65,6 +66,7 @@ export class CompaniesService {
|
||||
products,
|
||||
services,
|
||||
user,
|
||||
business,
|
||||
});
|
||||
|
||||
await em.persistAndFlush(company);
|
||||
@@ -82,14 +84,18 @@ export class CompaniesService {
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
async updateCompanyById(id: string, updateDto: UpdateCompanyDto) {
|
||||
async updateCompanyById(id: string, updateDto: UpdateCompanyDto, businessId: string) {
|
||||
const em = this.em.fork();
|
||||
try {
|
||||
await em.begin();
|
||||
|
||||
const { services, products, ...rest } = updateDto;
|
||||
|
||||
const company = await em.findOne(Company, { id, deletedAt: null }, { populate: ["user", "services", "products"] });
|
||||
const company = await em.findOne(
|
||||
Company,
|
||||
{ id, deletedAt: null, business: { id: businessId } },
|
||||
{ populate: ["user", "services", "products"] },
|
||||
);
|
||||
if (!company) throw new BadRequestException(CompanyMessage.COMPANY_NOT_FOUND);
|
||||
|
||||
const updatedCompany = em.assign(company, rest);
|
||||
@@ -101,8 +107,14 @@ export class CompaniesService {
|
||||
}
|
||||
|
||||
if (updateDto.name) {
|
||||
const existingCompany = await em.findOne(Company, { name: updateDto.name, deletedAt: null, id: { $ne: company.id } });
|
||||
const existingCompany = await em.findOne(Company, {
|
||||
name: updateDto.name,
|
||||
deletedAt: null,
|
||||
id: { $ne: company.id },
|
||||
business: { id: businessId },
|
||||
});
|
||||
if (existingCompany) throw new BadRequestException(CompanyMessage.COMPANY_ALREADY_EXISTS_NAME);
|
||||
updatedCompany.name = updateDto.name;
|
||||
}
|
||||
|
||||
if (updateDto.identificationNumber) {
|
||||
@@ -110,8 +122,10 @@ export class CompaniesService {
|
||||
identificationNumber: updateDto.identificationNumber,
|
||||
deletedAt: null,
|
||||
id: { $ne: company.id },
|
||||
business: { id: businessId },
|
||||
});
|
||||
if (existingCompany) throw new BadRequestException(CompanyMessage.COMPANY_ALREADY_EXISTS_IDENTITY_NUMBER);
|
||||
updatedCompany.identificationNumber = updateDto.identificationNumber;
|
||||
}
|
||||
|
||||
const userUpdateData: Partial<User> = {};
|
||||
@@ -174,15 +188,15 @@ export class CompaniesService {
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
async getCompanyById(id: string) {
|
||||
const company = await this.findCompanyById(id);
|
||||
async getCompanyById(id: string, businessId: string) {
|
||||
const company = await this.findCompanyById(id, businessId);
|
||||
return { company };
|
||||
}
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
async getCompaniesList(queryDto: CompanyListQueryDto) {
|
||||
const [companies, count] = await this.companyRepository.getCompaniesListForAdmin(queryDto);
|
||||
async getCompaniesList(queryDto: CompanyListQueryDto, businessId: string) {
|
||||
const [companies, count] = await this.companyRepository.getCompaniesListForAdmin(queryDto, businessId);
|
||||
return {
|
||||
companies,
|
||||
count,
|
||||
@@ -192,8 +206,8 @@ export class CompaniesService {
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
async deleteCompanyById(id: string) {
|
||||
const company = await this.findCompanyById(id);
|
||||
async deleteCompanyById(id: string, businessId: string) {
|
||||
const company = await this.findCompanyById(id, businessId);
|
||||
|
||||
company.deletedAt = dayjs().toDate();
|
||||
|
||||
@@ -205,8 +219,8 @@ export class CompaniesService {
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
async toggleStatus(id: string) {
|
||||
const company = await this.findCompanyById(id);
|
||||
async toggleStatus(id: string, businessId: string) {
|
||||
const company = await this.findCompanyById(id, businessId);
|
||||
|
||||
company.isActive = !company.isActive;
|
||||
|
||||
@@ -231,9 +245,9 @@ export class CompaniesService {
|
||||
return company;
|
||||
}
|
||||
//-----------------------------------
|
||||
private async findCompanyById(id: string) {
|
||||
private async findCompanyById(id: string, businessId: string) {
|
||||
const company = await this.companyRepository.findOne(
|
||||
{ id, deletedAt: null },
|
||||
{ id, deletedAt: null, business: { id: businessId } },
|
||||
{ populate: ["user", "industry", "services", "products"] },
|
||||
);
|
||||
if (!company) throw new BadRequestException(CompanyMessage.COMPANY_NOT_FOUND);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Collection, Entity, EntityRepositoryType, Formula, OneToMany, Opt, Property } from "@mikro-orm/core";
|
||||
import { Collection, Entity, EntityRepositoryType, Formula, ManyToOne, OneToMany, Opt, Property } from "@mikro-orm/core";
|
||||
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { Business } from "../../businesses/entities/business.entity";
|
||||
import { Company } from "../../companies/entities/company.entity";
|
||||
import { IndustryRepository } from "../repositories/industry.repository";
|
||||
|
||||
@@ -21,5 +22,8 @@ export class Industry extends BaseEntity {
|
||||
@OneToMany(() => Company, (company) => company.industry)
|
||||
companies = new Collection<Company>(this);
|
||||
|
||||
@ManyToOne(() => Business, { deleteRule: "restrict" })
|
||||
business!: Business;
|
||||
|
||||
[EntityRepositoryType]?: IndustryRepository;
|
||||
}
|
||||
|
||||
@@ -1,49 +1,53 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common";
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseInterceptors } from "@nestjs/common";
|
||||
import { ApiOperation } from "@nestjs/swagger";
|
||||
|
||||
import { CreateIndustryDto } from "./DTO/create-industry.dto";
|
||||
import { IndustryListQueryDto } from "./DTO/industry-list-query.dto";
|
||||
import { UpdateIndustryDto } from "./DTO/update-industry.dto";
|
||||
import { IndustriesService } from "./services/industries.service";
|
||||
import { BusinessDec } from "../../common/decorators/business.decorator";
|
||||
import { ParamDto } from "../../common/DTO/param.dto";
|
||||
import { BusinessInterceptor } from "../../core/interceptors/business.interceptor";
|
||||
import { Business } from "../businesses/entities/business.entity";
|
||||
|
||||
@Controller("industries")
|
||||
@UseInterceptors(BusinessInterceptor)
|
||||
export class IndustriesController {
|
||||
constructor(private readonly industriesService: IndustriesService) {}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: "create industry" })
|
||||
create(@Body() createIndustryDto: CreateIndustryDto) {
|
||||
return this.industriesService.create(createIndustryDto);
|
||||
create(@Body() createIndustryDto: CreateIndustryDto, @BusinessDec() business: Business) {
|
||||
return this.industriesService.create(createIndustryDto, business);
|
||||
}
|
||||
|
||||
@Get(":id")
|
||||
@ApiOperation({ summary: "get industry by id" })
|
||||
getIndustryById(@Param() paramDto: ParamDto) {
|
||||
return this.industriesService.getIndustriesById(paramDto.id);
|
||||
getIndustryById(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||
return this.industriesService.getIndustriesById(paramDto.id, businessId);
|
||||
}
|
||||
|
||||
@Patch(":id")
|
||||
@ApiOperation({ summary: "update industry" })
|
||||
updateIndustry(@Param() paramDto: ParamDto, @Body() updateIndustryDto: UpdateIndustryDto) {
|
||||
return this.industriesService.updateIndustry(paramDto.id, updateIndustryDto);
|
||||
updateIndustry(@Param() paramDto: ParamDto, @Body() updateIndustryDto: UpdateIndustryDto, @BusinessDec("id") businessId: string) {
|
||||
return this.industriesService.updateIndustry(paramDto.id, updateIndustryDto, businessId);
|
||||
}
|
||||
|
||||
@Delete(":id")
|
||||
@ApiOperation({ summary: "delete industry" })
|
||||
deleteIndustry(@Param() paramDto: ParamDto) {
|
||||
return this.industriesService.deleteIndustry(paramDto.id);
|
||||
deleteIndustry(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||
return this.industriesService.deleteIndustry(paramDto.id, businessId);
|
||||
}
|
||||
|
||||
@Get("list")
|
||||
@ApiOperation({ summary: "get industries list for admin" })
|
||||
getIndustriesForAdmin(@Query() query: IndustryListQueryDto) {
|
||||
return this.industriesService.getIndustriesListForAdmin(query);
|
||||
getIndustriesForAdmin(@Query() query: IndustryListQueryDto, @BusinessDec("id") businessId: string) {
|
||||
return this.industriesService.getIndustriesListForAdmin(query, businessId);
|
||||
}
|
||||
|
||||
@Patch(":id/toggle-status")
|
||||
@ApiOperation({ summary: "toggle industry status" })
|
||||
toggleStatus(@Param() paramDto: ParamDto) {
|
||||
return this.industriesService.toggleStatus(paramDto.id);
|
||||
toggleStatus(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||
return this.industriesService.toggleStatus(paramDto.id, businessId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { MikroOrmModule } from "@mikro-orm/nestjs";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
import { BusinessesModule } from "../businesses/businesses.module";
|
||||
import { Industry } from "./entities/industry.entity";
|
||||
import { IndustriesController } from "./industries.controller";
|
||||
import { IndustriesService } from "./services/industries.service";
|
||||
|
||||
@Module({
|
||||
imports: [MikroOrmModule.forFeature([Industry])],
|
||||
imports: [MikroOrmModule.forFeature([Industry]), BusinessesModule],
|
||||
controllers: [IndustriesController],
|
||||
providers: [IndustriesService],
|
||||
exports: [IndustriesService],
|
||||
|
||||
@@ -6,10 +6,14 @@ import { Industry } from "../entities/industry.entity";
|
||||
|
||||
export class IndustryRepository extends EntityRepository<Industry> {
|
||||
//
|
||||
async getIndustriesListForAdmin(queryDto: IndustryListQueryDto) {
|
||||
async getIndustriesListForAdmin(queryDto: IndustryListQueryDto, businessId: string) {
|
||||
const { limit, skip } = PaginationUtils(queryDto);
|
||||
|
||||
const queryBuilder = this.createQueryBuilder("i").where({ deletedAt: null }).orderBy({ createdAt: "DESC" }).limit(limit).offset(skip);
|
||||
const queryBuilder = this.createQueryBuilder("i")
|
||||
.where({ deletedAt: null, business: { id: businessId } })
|
||||
.orderBy({ createdAt: "DESC" })
|
||||
.limit(limit)
|
||||
.offset(skip);
|
||||
|
||||
if (queryDto.q) {
|
||||
queryBuilder.andWhere({ title: { $ilike: `%${queryDto.q}%` } });
|
||||
|
||||
@@ -3,6 +3,7 @@ import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { IndustryMessage } from "../../../common/enums/message.enum";
|
||||
import { Business } from "../../businesses/entities/business.entity";
|
||||
import { CreateIndustryDto } from "../DTO/create-industry.dto";
|
||||
import { IndustryListQueryDto } from "../DTO/industry-list-query.dto";
|
||||
import { UpdateIndustryDto } from "../DTO/update-industry.dto";
|
||||
@@ -17,11 +18,14 @@ export class IndustriesService {
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
async create(createIndustryDto: CreateIndustryDto) {
|
||||
const existingIndustry = await this.industryRepository.findOne({ title: createIndustryDto.title });
|
||||
async create(createIndustryDto: CreateIndustryDto, business: Business) {
|
||||
const existingIndustry = await this.industryRepository.findOne({
|
||||
title: createIndustryDto.title,
|
||||
business: { id: business.id },
|
||||
});
|
||||
if (existingIndustry) throw new BadRequestException(IndustryMessage.INDUSTRY_ALREADY_EXISTS);
|
||||
|
||||
const industry = this.industryRepository.create(createIndustryDto);
|
||||
const industry = this.industryRepository.create({ ...createIndustryDto, business });
|
||||
this.em.persistAndFlush(industry);
|
||||
|
||||
return {
|
||||
@@ -31,16 +35,16 @@ export class IndustriesService {
|
||||
}
|
||||
//-----------------------------------
|
||||
|
||||
async getIndustriesById(id: string) {
|
||||
const industry = await this.industryRepository.findOne({ id, deletedAt: null });
|
||||
async getIndustriesById(id: string, businessId: string) {
|
||||
const industry = await this.industryRepository.findOne({ id, deletedAt: null, business: { id: businessId } });
|
||||
if (!industry) throw new BadRequestException(IndustryMessage.INDUSTRY_NOT_FOUND);
|
||||
|
||||
return { industry };
|
||||
}
|
||||
//-----------------------------------
|
||||
|
||||
async updateIndustry(id: string, updateIndustryDto: UpdateIndustryDto) {
|
||||
const industry = await this.industryRepository.findOne({ id, deletedAt: null });
|
||||
async updateIndustry(id: string, updateIndustryDto: UpdateIndustryDto, businessId: string) {
|
||||
const industry = await this.industryRepository.findOne({ id, deletedAt: null, business: { id: businessId } });
|
||||
if (!industry) throw new BadRequestException(IndustryMessage.INDUSTRY_NOT_FOUND);
|
||||
|
||||
this.industryRepository.assign(industry, updateIndustryDto);
|
||||
@@ -54,8 +58,8 @@ export class IndustriesService {
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
async deleteIndustry(id: string) {
|
||||
const industry = await this.industryRepository.findOne({ id, deletedAt: null });
|
||||
async deleteIndustry(id: string, businessId: string) {
|
||||
const industry = await this.industryRepository.findOne({ id, deletedAt: null, business: { id: businessId } });
|
||||
if (!industry) throw new BadRequestException(IndustryMessage.INDUSTRY_NOT_FOUND);
|
||||
|
||||
industry.deletedAt = dayjs().toDate();
|
||||
@@ -68,14 +72,14 @@ export class IndustriesService {
|
||||
}
|
||||
//-----------------------------------
|
||||
|
||||
async getIndustriesListForAdmin(queryDto: IndustryListQueryDto) {
|
||||
const [industries, count] = await this.industryRepository.getIndustriesListForAdmin(queryDto);
|
||||
async getIndustriesListForAdmin(queryDto: IndustryListQueryDto, businessId: string) {
|
||||
const [industries, count] = await this.industryRepository.getIndustriesListForAdmin(queryDto, businessId);
|
||||
return { industries, count, paginate: true };
|
||||
}
|
||||
//-----------------------------------
|
||||
|
||||
async toggleStatus(id: string) {
|
||||
const industry = await this.industryRepository.findOne({ id, deletedAt: null });
|
||||
async toggleStatus(id: string, businessId: string) {
|
||||
const industry = await this.industryRepository.findOne({ id, deletedAt: null, business: { id: businessId } });
|
||||
if (!industry) throw new BadRequestException(IndustryMessage.INDUSTRY_NOT_FOUND);
|
||||
|
||||
industry.isActive = !industry.isActive;
|
||||
|
||||
Reference in New Issue
Block a user