chore: company and industy related logix
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { IsArray, IsNotEmpty, IsString, IsUrl } from "class-validator";
|
import { IsNotEmpty, IsString, IsUrl } from "class-validator";
|
||||||
|
|
||||||
import { CompanyMessage } from "../../../common/enums/message.enum";
|
import { CompanyMessage } from "../../../common/enums/message.enum";
|
||||||
|
|
||||||
@@ -10,8 +10,7 @@ export class CreateCompanyProductDto {
|
|||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
@IsNotEmpty({ message: CompanyMessage.PRODUCT_IMAGES_REQUIRED })
|
@IsNotEmpty({ message: CompanyMessage.PRODUCT_IMAGES_REQUIRED })
|
||||||
@IsArray({ message: CompanyMessage.PRODUCT_IMAGES_MUST_BE_ARRAY })
|
@IsUrl({ protocols: ["http", "https"] }, { message: CompanyMessage.PRODUCT_IMAGES_MUST_BE_URL })
|
||||||
@IsUrl({ protocols: ["http", "https"] }, { each: true, message: CompanyMessage.PRODUCT_IMAGES_MUST_BE_URL })
|
@ApiProperty({ description: "تصاویر محصول", example: "https://example.com/image1.jpg" })
|
||||||
@ApiProperty({ description: "تصاویر محصول", example: ["https://example.com/image1.jpg", "https://example.com/image2.jpg"] })
|
imageUrl: string;
|
||||||
imagesUrl: string[];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { IsArray, IsNotEmpty, IsString, IsUrl } from "class-validator";
|
import { IsNotEmpty, IsString, IsUrl } from "class-validator";
|
||||||
|
|
||||||
import { CompanyMessage } from "../../../common/enums/message.enum";
|
import { CompanyMessage } from "../../../common/enums/message.enum";
|
||||||
|
|
||||||
@@ -10,8 +10,7 @@ export class CreateCompanyServiceDto {
|
|||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
@IsNotEmpty({ message: CompanyMessage.SERVICE_IMAGES_REQUIRED })
|
@IsNotEmpty({ message: CompanyMessage.SERVICE_IMAGES_REQUIRED })
|
||||||
@IsArray({ message: CompanyMessage.SERVICE_IMAGES_MUST_BE_ARRAY })
|
@IsUrl({ protocols: ["http", "https"] }, { message: CompanyMessage.SERVICE_IMAGES_MUST_BE_URL })
|
||||||
@IsUrl({ protocols: ["http", "https"] }, { each: true, message: CompanyMessage.SERVICE_IMAGES_MUST_BE_URL })
|
@ApiProperty({ description: "تصاویر خدمت", example: "https://example.com/image1.jpg" })
|
||||||
@ApiProperty({ description: "تصاویر خدمت", example: ["https://example.com/image1.jpg", "https://example.com/image2.jpg"] })
|
imageUrl: string;
|
||||||
imagesUrl: string[];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ export class CompanyProduct extends BaseEntity {
|
|||||||
@Property({ type: "varchar", length: 250, nullable: false })
|
@Property({ type: "varchar", length: 250, nullable: false })
|
||||||
title!: string;
|
title!: string;
|
||||||
|
|
||||||
@Property({ type: "array", nullable: false })
|
@Property({ type: "varchar", length: 250, nullable: false })
|
||||||
imagesUrl!: string[];
|
imageUrl!: string;
|
||||||
|
|
||||||
@ManyToOne(() => Company, { deleteRule: "cascade" })
|
@ManyToOne(() => Company, { deleteRule: "cascade" })
|
||||||
company!: Company;
|
company!: Company;
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ export class CompanyService extends BaseEntity {
|
|||||||
@Property({ type: "varchar", length: 250, nullable: false })
|
@Property({ type: "varchar", length: 250, nullable: false })
|
||||||
title!: string;
|
title!: string;
|
||||||
|
|
||||||
@Property({ type: "array", nullable: false })
|
@Property({ type: "varchar", length: 250, nullable: false })
|
||||||
imagesUrl!: string[];
|
imageUrl!: string;
|
||||||
|
|
||||||
@ManyToOne(() => Company, { deleteRule: "cascade" })
|
@ManyToOne(() => Company, { deleteRule: "cascade" })
|
||||||
company!: Company;
|
company!: Company;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Cascade, Collection, Entity, EntityRepositoryType, Enum, ManyToOne, OneToMany, Opt, Property } from "@mikro-orm/core";
|
import { Cascade, Collection, Entity, EntityRepositoryType, Enum, Formula, ManyToOne, OneToMany, Opt, Property } from "@mikro-orm/core";
|
||||||
|
|
||||||
import { CompanyProduct } from "./company-product.entity";
|
import { CompanyProduct } from "./company-product.entity";
|
||||||
import { CompanyService } from "./company-service.entity";
|
import { CompanyService } from "./company-service.entity";
|
||||||
@@ -52,9 +52,12 @@ export class Company extends BaseEntity {
|
|||||||
@OneToMany(() => CompanyService, (service) => service.company, { cascade: [Cascade.ALL] })
|
@OneToMany(() => CompanyService, (service) => service.company, { cascade: [Cascade.ALL] })
|
||||||
services = new Collection<CompanyService>(this);
|
services = new Collection<CompanyService>(this);
|
||||||
|
|
||||||
@OneToMany(() => Invoice, (invoice) => invoice.company)
|
@OneToMany(() => Invoice, (invoice) => invoice.company, { cascade: [Cascade.ALL] })
|
||||||
invoices = new Collection<Invoice>(this);
|
invoices = new Collection<Invoice>(this);
|
||||||
|
|
||||||
|
@Formula((alias) => `(SELECT COUNT(*)::int FROM invoice i WHERE i.company_id = ${alias}.id)`, { persist: false })
|
||||||
|
invoiceCount?: number;
|
||||||
|
|
||||||
//-----------------------------------
|
//-----------------------------------
|
||||||
|
|
||||||
@ManyToOne(() => Industry, { deleteRule: "restrict" })
|
@ManyToOne(() => Industry, { deleteRule: "restrict" })
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EntityRepository } from "@mikro-orm/postgresql";
|
import { EntityRepository, FilterQuery } from "@mikro-orm/postgresql";
|
||||||
|
|
||||||
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||||
import { CompanyListQueryDto } from "../DTO/company-list-query.dto";
|
import { CompanyListQueryDto } from "../DTO/company-list-query.dto";
|
||||||
@@ -9,57 +9,52 @@ export class CompanyRepository extends EntityRepository<Company> {
|
|||||||
async getCompaniesListForAdmin(queryDto: CompanyListQueryDto) {
|
async getCompaniesListForAdmin(queryDto: CompanyListQueryDto) {
|
||||||
const { limit, skip } = PaginationUtils(queryDto);
|
const { limit, skip } = PaginationUtils(queryDto);
|
||||||
|
|
||||||
const qb = this.createQueryBuilder("company")
|
const where: FilterQuery<Company> = {
|
||||||
.select([
|
deletedAt: null,
|
||||||
"company.id",
|
};
|
||||||
"company.name",
|
|
||||||
"company.status",
|
if (queryDto.status) {
|
||||||
"company.isActive",
|
where.status = queryDto.status;
|
||||||
"company.createdAt",
|
}
|
||||||
|
if (queryDto.isActive !== undefined) {
|
||||||
|
where.isActive = queryDto.isActive === 1;
|
||||||
|
}
|
||||||
|
if (queryDto.q) {
|
||||||
|
where.name = { $ilike: `%${queryDto.q}%` };
|
||||||
|
}
|
||||||
|
if (queryDto.industryId) {
|
||||||
|
where.industry = queryDto.industryId;
|
||||||
|
}
|
||||||
|
if (queryDto.registrationDate) {
|
||||||
|
where.createdAt = {
|
||||||
|
$gte: queryDto.registrationDate,
|
||||||
|
$lte: queryDto.registrationDate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (queryDto.name) {
|
||||||
|
where.name = { $ilike: `%${queryDto.name}%` };
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.findAndCount(where, {
|
||||||
|
populate: ["industry", "user"],
|
||||||
|
limit,
|
||||||
|
offset: skip,
|
||||||
|
orderBy: { createdAt: "DESC" },
|
||||||
|
fields: [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"isActive",
|
||||||
|
"dateOfEstablishment",
|
||||||
|
"status",
|
||||||
|
"invoiceCount",
|
||||||
|
"createdAt",
|
||||||
"industry.id",
|
"industry.id",
|
||||||
"industry.title",
|
"industry.title",
|
||||||
"user.id",
|
"user.id",
|
||||||
"user.firstName",
|
"user.firstName",
|
||||||
"user.lastName",
|
"user.lastName",
|
||||||
"(SELECT COUNT(*) FROM invoice WHERE invoice.company_id = company.id) as invoiceCount",
|
"user.fullName",
|
||||||
])
|
],
|
||||||
.leftJoin("company.industry", "industry")
|
|
||||||
.leftJoin("company.user", "user")
|
|
||||||
.where({
|
|
||||||
deletedAt: null,
|
|
||||||
...(queryDto.status && { status: queryDto.status }),
|
|
||||||
...(queryDto.isActive !== undefined && { isActive: queryDto.isActive === 1 }),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (queryDto.q) {
|
|
||||||
qb.andWhere({ name: { $ilike: `%${queryDto.q}%` } });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryDto.industryId) {
|
|
||||||
qb.andWhere({ industry: { id: queryDto.industryId } });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryDto.registrationDate) {
|
|
||||||
qb.andWhere({
|
|
||||||
createdAt: {
|
|
||||||
$gte: queryDto.registrationDate,
|
|
||||||
$lte: queryDto.registrationDate,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryDto.name) {
|
|
||||||
qb.andWhere({ name: { $ilike: `%${queryDto.name}%` } });
|
|
||||||
}
|
|
||||||
|
|
||||||
qb.orderBy({ createdAt: "DESC" }).limit(limit).offset(skip);
|
|
||||||
|
|
||||||
return qb.getResultAndCount();
|
|
||||||
|
|
||||||
// // Transform the result to include invoiceCount as a number
|
|
||||||
// const transformedCompanies = companies.map((company) => ({
|
|
||||||
// ...company,
|
|
||||||
// invoiceCount: parseInt(company.invoiceCount, 10),
|
|
||||||
// }));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ export class CompaniesService {
|
|||||||
const products = createDto.products.map((productData) => {
|
const products = createDto.products.map((productData) => {
|
||||||
const product = new CompanyProduct();
|
const product = new CompanyProduct();
|
||||||
product.title = productData.title;
|
product.title = productData.title;
|
||||||
product.imagesUrl = productData.imagesUrl;
|
product.imageUrl = productData.imageUrl;
|
||||||
return product;
|
return product;
|
||||||
});
|
});
|
||||||
|
|
||||||
const services = createDto.services.map((serviceData) => {
|
const services = createDto.services.map((serviceData) => {
|
||||||
const service = new CompanyService();
|
const service = new CompanyService();
|
||||||
service.title = serviceData.title;
|
service.title = serviceData.title;
|
||||||
service.imagesUrl = serviceData.imagesUrl;
|
service.imageUrl = serviceData.imageUrl;
|
||||||
return service;
|
return service;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,10 +87,12 @@ export class CompaniesService {
|
|||||||
try {
|
try {
|
||||||
await em.begin();
|
await em.begin();
|
||||||
|
|
||||||
const company = await em.findOne(Company, { id, deletedAt: null }, { populate: ["user"] });
|
const { services, products, ...rest } = updateDto;
|
||||||
|
|
||||||
|
const company = await em.findOne(Company, { id, deletedAt: null }, { populate: ["user", "services", "products"] });
|
||||||
if (!company) throw new BadRequestException(CompanyMessage.COMPANY_NOT_FOUND);
|
if (!company) throw new BadRequestException(CompanyMessage.COMPANY_NOT_FOUND);
|
||||||
|
|
||||||
const updatedCompany = em.assign(company, updateDto);
|
const updatedCompany = em.assign(company, rest);
|
||||||
|
|
||||||
if (updateDto.industryId) {
|
if (updateDto.industryId) {
|
||||||
const industry = await em.findOne(Industry, { id: updateDto.industryId, deletedAt: null });
|
const industry = await em.findOne(Industry, { id: updateDto.industryId, deletedAt: null });
|
||||||
@@ -121,12 +123,48 @@ export class CompaniesService {
|
|||||||
|
|
||||||
if (Object.keys(userUpdateData).length > 0) await this.usersService.updateUser(company.user.id, userUpdateData, em);
|
if (Object.keys(userUpdateData).length > 0) await this.usersService.updateUser(company.user.id, userUpdateData, em);
|
||||||
|
|
||||||
|
if (services && services.length > 0) {
|
||||||
|
// Remove all old services from the database
|
||||||
|
for (const oldService of company.services.getItems()) {
|
||||||
|
em.remove(oldService);
|
||||||
|
}
|
||||||
|
await em.flush(); // flush removals before adding new
|
||||||
|
|
||||||
|
// Add new services
|
||||||
|
for (const service of services) {
|
||||||
|
const serviceEntity = em.create(CompanyService, {
|
||||||
|
title: service.title,
|
||||||
|
imageUrl: service.imageUrl,
|
||||||
|
company: company,
|
||||||
|
});
|
||||||
|
updatedCompany.services.add(serviceEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (products && products.length > 0) {
|
||||||
|
// Remove all old products from the database
|
||||||
|
for (const oldProduct of company.products.getItems()) {
|
||||||
|
em.remove(oldProduct);
|
||||||
|
}
|
||||||
|
await em.flush(); // flush removals before adding new
|
||||||
|
|
||||||
|
// Add new products
|
||||||
|
for (const product of products) {
|
||||||
|
const productEntity = em.create(CompanyProduct, {
|
||||||
|
title: product.title,
|
||||||
|
imageUrl: product.imageUrl,
|
||||||
|
company: company,
|
||||||
|
});
|
||||||
|
updatedCompany.products.add(productEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await em.flush();
|
await em.flush();
|
||||||
await em.commit();
|
await em.commit();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: CompanyMessage.UPDATED_SUCCESSFULLY,
|
message: CompanyMessage.UPDATED_SUCCESSFULLY,
|
||||||
updatedCompany,
|
company: updatedCompany.name,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await em.rollback();
|
await em.rollback();
|
||||||
@@ -194,7 +232,10 @@ export class CompaniesService {
|
|||||||
}
|
}
|
||||||
//-----------------------------------
|
//-----------------------------------
|
||||||
private async findCompanyById(id: string) {
|
private async findCompanyById(id: string) {
|
||||||
const company = await this.companyRepository.findOne({ id, deletedAt: null });
|
const company = await this.companyRepository.findOne(
|
||||||
|
{ id, deletedAt: null },
|
||||||
|
{ populate: ["user", "industry", "services", "products"] },
|
||||||
|
);
|
||||||
if (!company) throw new BadRequestException(CompanyMessage.COMPANY_NOT_FOUND);
|
if (!company) throw new BadRequestException(CompanyMessage.COMPANY_NOT_FOUND);
|
||||||
return company;
|
return company;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Collection, Entity, EntityRepositoryType, OneToMany, Opt, Property } from "@mikro-orm/core";
|
import { Collection, Entity, EntityRepositoryType, Formula, OneToMany, Opt, Property } from "@mikro-orm/core";
|
||||||
|
|
||||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
import { Company } from "../../companies/entities/company.entity";
|
import { Company } from "../../companies/entities/company.entity";
|
||||||
@@ -15,6 +15,9 @@ export class Industry extends BaseEntity {
|
|||||||
@Property({ type: "boolean", default: true })
|
@Property({ type: "boolean", default: true })
|
||||||
isActive!: boolean & Opt;
|
isActive!: boolean & Opt;
|
||||||
|
|
||||||
|
@Formula((alias) => `(SELECT COUNT(*)::int FROM company c WHERE c.industry_id = ${alias}.id)`, { persist: false })
|
||||||
|
companiesCount?: number;
|
||||||
|
|
||||||
@OneToMany(() => Company, (company) => company.industry)
|
@OneToMany(() => Company, (company) => company.industry)
|
||||||
companies = new Collection<Company>(this);
|
companies = new Collection<Company>(this);
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,7 @@ export class IndustryRepository extends EntityRepository<Industry> {
|
|||||||
async getIndustriesListForAdmin(queryDto: IndustryListQueryDto) {
|
async getIndustriesListForAdmin(queryDto: IndustryListQueryDto) {
|
||||||
const { limit, skip } = PaginationUtils(queryDto);
|
const { limit, skip } = PaginationUtils(queryDto);
|
||||||
|
|
||||||
const queryBuilder = this.createQueryBuilder("i")
|
const queryBuilder = this.createQueryBuilder("i").where({ deletedAt: null }).orderBy({ createdAt: "DESC" }).limit(limit).offset(skip);
|
||||||
.leftJoinAndSelect("i.companies", "c")
|
|
||||||
.where({ deletedAt: null })
|
|
||||||
.orderBy({ createdAt: "DESC" })
|
|
||||||
.limit(limit)
|
|
||||||
.offset(skip);
|
|
||||||
|
|
||||||
if (queryDto.q) {
|
if (queryDto.q) {
|
||||||
queryBuilder.andWhere({ title: { $ilike: `%${queryDto.q}%` } });
|
queryBuilder.andWhere({ title: { $ilike: `%${queryDto.q}%` } });
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ export class User extends BaseEntity {
|
|||||||
@Property({ type: "boolean", default: false })
|
@Property({ type: "boolean", default: false })
|
||||||
emailVerified!: boolean & Opt;
|
emailVerified!: boolean & Opt;
|
||||||
|
|
||||||
|
@Property({ persist: false })
|
||||||
|
get fullName(): string | null {
|
||||||
|
return `${this.firstName} ${this.lastName}`;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------
|
//-----------------------------------
|
||||||
|
|
||||||
@ManyToOne(() => Role, { deleteRule: "restrict" })
|
@ManyToOne(() => Role, { deleteRule: "restrict" })
|
||||||
|
|||||||
Reference in New Issue
Block a user