chore: company and industy related logix
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
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";
|
||||
|
||||
@@ -10,8 +10,7 @@ export class CreateCompanyProductDto {
|
||||
title: string;
|
||||
|
||||
@IsNotEmpty({ message: CompanyMessage.PRODUCT_IMAGES_REQUIRED })
|
||||
@IsArray({ message: CompanyMessage.PRODUCT_IMAGES_MUST_BE_ARRAY })
|
||||
@IsUrl({ protocols: ["http", "https"] }, { each: true, message: CompanyMessage.PRODUCT_IMAGES_MUST_BE_URL })
|
||||
@ApiProperty({ description: "تصاویر محصول", example: ["https://example.com/image1.jpg", "https://example.com/image2.jpg"] })
|
||||
imagesUrl: string[];
|
||||
@IsUrl({ protocols: ["http", "https"] }, { message: CompanyMessage.PRODUCT_IMAGES_MUST_BE_URL })
|
||||
@ApiProperty({ description: "تصاویر محصول", example: "https://example.com/image1.jpg" })
|
||||
imageUrl: string;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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";
|
||||
|
||||
@@ -10,8 +10,7 @@ export class CreateCompanyServiceDto {
|
||||
title: string;
|
||||
|
||||
@IsNotEmpty({ message: CompanyMessage.SERVICE_IMAGES_REQUIRED })
|
||||
@IsArray({ message: CompanyMessage.SERVICE_IMAGES_MUST_BE_ARRAY })
|
||||
@IsUrl({ protocols: ["http", "https"] }, { each: true, message: CompanyMessage.SERVICE_IMAGES_MUST_BE_URL })
|
||||
@ApiProperty({ description: "تصاویر خدمت", example: ["https://example.com/image1.jpg", "https://example.com/image2.jpg"] })
|
||||
imagesUrl: string[];
|
||||
@IsUrl({ protocols: ["http", "https"] }, { message: CompanyMessage.SERVICE_IMAGES_MUST_BE_URL })
|
||||
@ApiProperty({ description: "تصاویر خدمت", example: "https://example.com/image1.jpg" })
|
||||
imageUrl: string;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ export class CompanyProduct extends BaseEntity {
|
||||
@Property({ type: "varchar", length: 250, nullable: false })
|
||||
title!: string;
|
||||
|
||||
@Property({ type: "array", nullable: false })
|
||||
imagesUrl!: string[];
|
||||
@Property({ type: "varchar", length: 250, nullable: false })
|
||||
imageUrl!: string;
|
||||
|
||||
@ManyToOne(() => Company, { deleteRule: "cascade" })
|
||||
company!: Company;
|
||||
|
||||
@@ -8,8 +8,8 @@ export class CompanyService extends BaseEntity {
|
||||
@Property({ type: "varchar", length: 250, nullable: false })
|
||||
title!: string;
|
||||
|
||||
@Property({ type: "array", nullable: false })
|
||||
imagesUrl!: string[];
|
||||
@Property({ type: "varchar", length: 250, nullable: false })
|
||||
imageUrl!: string;
|
||||
|
||||
@ManyToOne(() => Company, { deleteRule: "cascade" })
|
||||
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 { CompanyService } from "./company-service.entity";
|
||||
@@ -52,9 +52,12 @@ export class Company extends BaseEntity {
|
||||
@OneToMany(() => CompanyService, (service) => service.company, { cascade: [Cascade.ALL] })
|
||||
services = new Collection<CompanyService>(this);
|
||||
|
||||
@OneToMany(() => Invoice, (invoice) => invoice.company)
|
||||
@OneToMany(() => Invoice, (invoice) => invoice.company, { cascade: [Cascade.ALL] })
|
||||
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" })
|
||||
|
||||
@@ -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 { CompanyListQueryDto } from "../DTO/company-list-query.dto";
|
||||
@@ -9,57 +9,52 @@ export class CompanyRepository extends EntityRepository<Company> {
|
||||
async getCompaniesListForAdmin(queryDto: CompanyListQueryDto) {
|
||||
const { limit, skip } = PaginationUtils(queryDto);
|
||||
|
||||
const qb = this.createQueryBuilder("company")
|
||||
.select([
|
||||
"company.id",
|
||||
"company.name",
|
||||
"company.status",
|
||||
"company.isActive",
|
||||
"company.createdAt",
|
||||
const where: FilterQuery<Company> = {
|
||||
deletedAt: null,
|
||||
};
|
||||
|
||||
if (queryDto.status) {
|
||||
where.status = queryDto.status;
|
||||
}
|
||||
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.title",
|
||||
"user.id",
|
||||
"user.firstName",
|
||||
"user.lastName",
|
||||
"(SELECT COUNT(*) FROM invoice WHERE invoice.company_id = company.id) as invoiceCount",
|
||||
])
|
||||
.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),
|
||||
// }));
|
||||
"user.fullName",
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +47,14 @@ export class CompaniesService {
|
||||
const products = createDto.products.map((productData) => {
|
||||
const product = new CompanyProduct();
|
||||
product.title = productData.title;
|
||||
product.imagesUrl = productData.imagesUrl;
|
||||
product.imageUrl = productData.imageUrl;
|
||||
return product;
|
||||
});
|
||||
|
||||
const services = createDto.services.map((serviceData) => {
|
||||
const service = new CompanyService();
|
||||
service.title = serviceData.title;
|
||||
service.imagesUrl = serviceData.imagesUrl;
|
||||
service.imageUrl = serviceData.imageUrl;
|
||||
return service;
|
||||
});
|
||||
|
||||
@@ -87,10 +87,12 @@ export class CompaniesService {
|
||||
try {
|
||||
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);
|
||||
|
||||
const updatedCompany = em.assign(company, updateDto);
|
||||
const updatedCompany = em.assign(company, rest);
|
||||
|
||||
if (updateDto.industryId) {
|
||||
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 (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.commit();
|
||||
|
||||
return {
|
||||
message: CompanyMessage.UPDATED_SUCCESSFULLY,
|
||||
updatedCompany,
|
||||
company: updatedCompany.name,
|
||||
};
|
||||
} catch (error) {
|
||||
await em.rollback();
|
||||
@@ -194,7 +232,10 @@ export class CompaniesService {
|
||||
}
|
||||
//-----------------------------------
|
||||
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);
|
||||
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 { Company } from "../../companies/entities/company.entity";
|
||||
@@ -15,6 +15,9 @@ export class Industry extends BaseEntity {
|
||||
@Property({ type: "boolean", default: true })
|
||||
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)
|
||||
companies = new Collection<Company>(this);
|
||||
|
||||
|
||||
@@ -9,12 +9,7 @@ export class IndustryRepository extends EntityRepository<Industry> {
|
||||
async getIndustriesListForAdmin(queryDto: IndustryListQueryDto) {
|
||||
const { limit, skip } = PaginationUtils(queryDto);
|
||||
|
||||
const queryBuilder = this.createQueryBuilder("i")
|
||||
.leftJoinAndSelect("i.companies", "c")
|
||||
.where({ deletedAt: null })
|
||||
.orderBy({ createdAt: "DESC" })
|
||||
.limit(limit)
|
||||
.offset(skip);
|
||||
const queryBuilder = this.createQueryBuilder("i").where({ deletedAt: null }).orderBy({ createdAt: "DESC" }).limit(limit).offset(skip);
|
||||
|
||||
if (queryDto.q) {
|
||||
queryBuilder.andWhere({ title: { $ilike: `%${queryDto.q}%` } });
|
||||
|
||||
@@ -38,6 +38,11 @@ export class User extends BaseEntity {
|
||||
@Property({ type: "boolean", default: false })
|
||||
emailVerified!: boolean & Opt;
|
||||
|
||||
@Property({ persist: false })
|
||||
get fullName(): string | null {
|
||||
return `${this.firstName} ${this.lastName}`;
|
||||
}
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
@ManyToOne(() => Role, { deleteRule: "restrict" })
|
||||
|
||||
Reference in New Issue
Block a user