From 6c8de9867a66e3431cb20d80364b11afd497a83f Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 29 Nov 2025 09:30:12 +0330 Subject: [PATCH 1/8] product variant --- src/modules/category/category.service.ts | 13 ++++++++----- src/modules/product/providers/product.service.ts | 14 +++++++------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/modules/category/category.service.ts b/src/modules/category/category.service.ts index 4f3b391..9d57fd4 100644 --- a/src/modules/category/category.service.ts +++ b/src/modules/category/category.service.ts @@ -127,17 +127,20 @@ class CategoryService { createDto.title_en = slugify(createDto.title_en); const existTitle = await this.categoryRepository.findByTitle(createDto.title_en); - - const theme = await this.themeRepo.findById(createDto.themeId); - if (!theme) throw new BadRequestError(CommonMessage.NotFoundById); - if (existTitle) throw new BadRequestError(CategoryMessage.DuplicateTitle); + + let themeIdOrNull: any = null; + if (createDto.themeId) { + const themeDoc = await this.themeRepo.findById(createDto.themeId); + if (!themeDoc) throw new BadRequestError(CommonMessage.NotFoundById); + themeIdOrNull = themeDoc._id; + } //if parent exist first set parent leaf false if (createDto.parent) { await this.categoryRepository.model.findByIdAndUpdate(createDto.parent, { leaf: false }); } //we set the leaf to true - const category = await this.categoryRepository.model.create({ ...createDto, theme: theme._id }); + const category = await this.categoryRepository.model.create({ ...createDto, theme: themeIdOrNull }); return { message: CategoryMessage.Created, diff --git a/src/modules/product/providers/product.service.ts b/src/modules/product/providers/product.service.ts index 41b6433..912d777 100644 --- a/src/modules/product/providers/product.service.ts +++ b/src/modules/product/providers/product.service.ts @@ -564,14 +564,14 @@ class ProductService { // Check if a variant with the same themeValue already exists // const query: FilterQuery = { product: product._id }; - // if (addVariantDto.themeValueId) { - // const existingVariant = await this.productVariantRepo.model.findOne({ - // product: product._id, - // themeValue: addVariantDto.themeValueId, - // }); + if (addVariantDto.themeValueId) { + const existingVariant = await this.productVariantRepo.model.findOne({ + product: product._id, + themeValue: addVariantDto.themeValueId, + }); - // if (existingVariant) throw new BadRequestError(ProductMessage.VariantAlreadyExists); - // } + if (existingVariant) throw new BadRequestError(ProductMessage.VariantAlreadyExists); + } const productVariant = await this.createProductVariant(addVariantDto, product._id, shop._id.toString(), session); From a9283d352e1018505152596331008afe622fb499 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 29 Nov 2025 09:54:39 +0330 Subject: [PATCH 2/8] product verity --- src/modules/admin/controllers/category.controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/admin/controllers/category.controller.ts b/src/modules/admin/controllers/category.controller.ts index e66a704..d224e32 100644 --- a/src/modules/admin/controllers/category.controller.ts +++ b/src/modules/admin/controllers/category.controller.ts @@ -177,6 +177,7 @@ export class AdminCategoryController extends BaseController { @ApiOperation("Update a theme ==> need to login as admin") @ApiResponse("theme updated successfully", HttpStatus.Ok) @ApiAuth() + @ApiModel(UpdateThemeDTO) @httpPut( "/theme", Guard.authAdmin(), From 9f32dff2c0d79f250601203c4302b993620775bb Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 29 Nov 2025 10:14:48 +0330 Subject: [PATCH 3/8] product verity --- src/modules/admin/controllers/category.controller.ts | 1 + src/modules/category/DTO/CreateThemeValue.dto.ts | 8 ++++---- src/modules/category/models/Abstraction/IThemeValue.ts | 4 ++-- src/modules/category/models/themeValue.model.ts | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/modules/admin/controllers/category.controller.ts b/src/modules/admin/controllers/category.controller.ts index d224e32..84253b8 100644 --- a/src/modules/admin/controllers/category.controller.ts +++ b/src/modules/admin/controllers/category.controller.ts @@ -244,6 +244,7 @@ export class AdminCategoryController extends BaseController { @ApiOperation("Update a theme value ==> need to login as admin") @ApiResponse("theme value updated successfully", HttpStatus.Ok) @ApiAuth() + @ApiModel(UpdateThemeValueDTO) @httpPut( "/theme-value", Guard.authAdmin(), diff --git a/src/modules/category/DTO/CreateThemeValue.dto.ts b/src/modules/category/DTO/CreateThemeValue.dto.ts index a6cfb2e..1bc3ff4 100644 --- a/src/modules/category/DTO/CreateThemeValue.dto.ts +++ b/src/modules/category/DTO/CreateThemeValue.dto.ts @@ -1,5 +1,5 @@ import { Expose } from "class-transformer"; -import { IsNotEmpty, IsString } from "class-validator"; +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; import { ApiProperty } from "../../../common/decorator/swggerDocs"; import { IsValidId } from "../../../common/decorator/validation.decorator"; @@ -14,10 +14,10 @@ export class CreateThemeValueDTO { theme: string; @Expose() - @IsNotEmpty() + @IsOptional() @IsString() - @ApiProperty({ type: "string", description: "the name of theme value", example: "Red" }) - name: string; + @ApiProperty({ type: "string", description: "the name of theme value", example: "Red", required: false }) + name?: string; @Expose() @IsNotEmpty() diff --git a/src/modules/category/models/Abstraction/IThemeValue.ts b/src/modules/category/models/Abstraction/IThemeValue.ts index 88cd130..a358d31 100644 --- a/src/modules/category/models/Abstraction/IThemeValue.ts +++ b/src/modules/category/models/Abstraction/IThemeValue.ts @@ -3,6 +3,6 @@ import { Types } from "mongoose"; export interface IThemeValue { _id: Types.ObjectId; theme: Types.ObjectId; - name: string; - value: string; + name?: string; + value: string | number; } diff --git a/src/modules/category/models/themeValue.model.ts b/src/modules/category/models/themeValue.model.ts index 296e37b..1d7dfcd 100644 --- a/src/modules/category/models/themeValue.model.ts +++ b/src/modules/category/models/themeValue.model.ts @@ -5,7 +5,7 @@ import { IThemeValue } from "./Abstraction/IThemeValue"; const themeValueSchema = new Schema( { theme: { type: Schema.Types.ObjectId, ref: "Theme", required: true }, - name: { type: String, required: true }, + name: { type: String, required: false }, value: { type: Schema.Types.Mixed, required: true }, }, { timestamps: true, toJSON: { virtuals: true, versionKey: false }, id: false }, From 754496f25961c9612c7de9551237a45f17796996 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 29 Nov 2025 10:30:37 +0330 Subject: [PATCH 4/8] product variant --- src/modules/admin/controllers/category.controller.ts | 7 ++++--- src/modules/category/DTO/UpdateThemeValue.dto.ts | 12 +----------- src/modules/category/theme.service.ts | 10 ++++------ 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/modules/admin/controllers/category.controller.ts b/src/modules/admin/controllers/category.controller.ts index 84253b8..83db058 100644 --- a/src/modules/admin/controllers/category.controller.ts +++ b/src/modules/admin/controllers/category.controller.ts @@ -243,16 +243,17 @@ export class AdminCategoryController extends BaseController { @ApiOperation("Update a theme value ==> need to login as admin") @ApiResponse("theme value updated successfully", HttpStatus.Ok) + @ApiParam("id", "id of the theme value", true) @ApiAuth() @ApiModel(UpdateThemeValueDTO) @httpPut( - "/theme-value", + "/theme-value/:id", Guard.authAdmin(), Guard.checkAdminPermissions([PermissionEnum.ADMIN]), ValidationMiddleware.validateInput(UpdateThemeValueDTO), ) - public async updateThemeValue(@requestBody() updateDto: UpdateThemeValueDTO) { - const data = await this.themeService.updateThemeValue(updateDto); + public async updateThemeValue(@requestParam("id") id: string, @requestBody() updateDto: UpdateThemeValueDTO) { + const data = await this.themeService.updateThemeValue(id, updateDto); return this.response({ data }); } diff --git a/src/modules/category/DTO/UpdateThemeValue.dto.ts b/src/modules/category/DTO/UpdateThemeValue.dto.ts index de0a4ed..23d3787 100644 --- a/src/modules/category/DTO/UpdateThemeValue.dto.ts +++ b/src/modules/category/DTO/UpdateThemeValue.dto.ts @@ -1,19 +1,9 @@ import { Expose } from "class-transformer"; -import { IsNotEmpty, IsOptional, IsString, Length } from "class-validator"; +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; import { ApiProperty } from "../../../common/decorator/swggerDocs"; -import { IsValidId } from "../../../common/decorator/validation.decorator"; -import { ThemeValueModel } from "../models/themeValue.model"; export class UpdateThemeValueDTO { - @Expose() - @IsNotEmpty() - @IsString() - @Length(24, 24) - @IsValidId(ThemeValueModel) - @ApiProperty({ type: "string", description: "theme value id", example: "66f3bcaee566db722a044c62" }) - themeValueId: string; - @Expose() @IsOptional() @IsNotEmpty() diff --git a/src/modules/category/theme.service.ts b/src/modules/category/theme.service.ts index ad193ab..6422724 100644 --- a/src/modules/category/theme.service.ts +++ b/src/modules/category/theme.service.ts @@ -109,10 +109,10 @@ class ThemeService { }; } - async updateThemeValue(updateDto: UpdateThemeValueDTO) { - if (!isValidObjectId(updateDto.themeValueId)) throw new BadRequestError(CommonMessage.NotValidId); + async updateThemeValue(id: string, updateDto: UpdateThemeValueDTO) { + if (!isValidObjectId(id)) throw new BadRequestError(CommonMessage.NotValidId); - const existingThemeValue = await this.themeValueRepository.findById(updateDto.themeValueId); + const existingThemeValue = await this.themeValueRepository.findById(id); if (!existingThemeValue) throw new BadRequestError(CommonMessage.NotFoundById); const updateData: Partial<{ name: string; value: string | number }> = {}; @@ -125,9 +125,7 @@ class ThemeService { updateData.value = updateDto.value; } - const updatedThemeValue = await this.themeValueRepository.model - .findByIdAndUpdate(updateDto.themeValueId, updateData, { new: true }) - .populate("theme"); + const updatedThemeValue = await this.themeValueRepository.model.findByIdAndUpdate(id, updateData, { new: true }).populate("theme"); return { message: CommonMessage.Updated, From 488807278e545e0038956972aa4f18526a8a34d0 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 29 Nov 2025 13:14:03 +0330 Subject: [PATCH 5/8] them valkue --- src/modules/product/DTO/product.dto.ts | 20 ++++++++ src/modules/product/Repository/product.ts | 51 +++++-------------- .../product/providers/product.service.ts | 4 +- 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/src/modules/product/DTO/product.dto.ts b/src/modules/product/DTO/product.dto.ts index 1526bb6..20aa784 100644 --- a/src/modules/product/DTO/product.dto.ts +++ b/src/modules/product/DTO/product.dto.ts @@ -83,6 +83,22 @@ class SaleFormatDTO { //################################# +class ThemeValueDTO { + @Expose() + _id: string; + + @Expose() + theme: string; + + @Expose() + name?: string; + + @Expose() + value: string | number; +} + +//################################# + export class ProductDetailVariantDTO { @Expose() _id: string; @@ -136,6 +152,10 @@ export class ProductDetailVariantDTO { @Expose() @Type(() => Size_MeterageDTO) meterage?: Size_MeterageDTO; + + @Expose() + @Type(() => ThemeValueDTO) + themeValue?: ThemeValueDTO; } //################################# diff --git a/src/modules/product/Repository/product.ts b/src/modules/product/Repository/product.ts index a5c6e23..9852dc7 100644 --- a/src/modules/product/Repository/product.ts +++ b/src/modules/product/Repository/product.ts @@ -1320,13 +1320,13 @@ export class ProductRepository extends BaseRepository { }, }, }, - { - $match: { - $expr: { - $or: [{ $gt: [{ $size: "$variants" }, 0] }, { $eq: ["$category.theme", "noColor_noSize"] }], - }, - }, - }, + // { + // $match: { + // $expr: { + // $or: [{ $gt: [{ $size: "$variants" }, 0] }, { $eq: ["$category.theme", "noColor_noSize"] }], + // }, + // }, + // }, { $unwind: { path: "$variants", @@ -1371,46 +1371,19 @@ export class ProductRepository extends BaseRepository { }, { $lookup: { - from: "colors", - localField: "variants.color", + from: "themevalues", + localField: "variants.themeValue", foreignField: "_id", - as: "variants.color", + as: "variants.themeValue", }, }, { $unwind: { - path: "$variants.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "variants.size", - foreignField: "_id", - as: "variants.size", - }, - }, - { - $unwind: { - path: "$variants.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "variants.meterage", - foreignField: "_id", - as: "variants.meterage", - }, - }, - { - $unwind: { - path: "$variants.meterage", + path: "$variants.themeValue", preserveNullAndEmptyArrays: true, }, }, + { $group: { _id: "$_id", diff --git a/src/modules/product/providers/product.service.ts b/src/modules/product/providers/product.service.ts index 912d777..5089dc2 100644 --- a/src/modules/product/providers/product.service.ts +++ b/src/modules/product/providers/product.service.ts @@ -961,9 +961,7 @@ class ProductService { async getProductDetails(productId: number) { await this.validateProduct(productId); const docs = await this.productRepo.getProductDetails(productId); - if ((!docs?.[0]?.variants || docs?.[0]?.variants.length === 0) && docs?.[0]?.category.theme !== "noColor_noSize") { - throw new BadRequestError(CommonMessage.NotEnoughVariant); - } + const product = ProductDTO.transformProduct(docs[0]); const stats = await this.commentRepo.getProductStats(productId); const categoryPath = await this.categoryRepo.getCategoryPath(product.category._id.toString()); From c0b8fd3cbf50fe704c34ec5a1fcaf1754292db44 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 29 Nov 2025 14:37:48 +0330 Subject: [PATCH 6/8] populate shipping cost --- src/modules/cart/cart.repository.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/cart/cart.repository.ts b/src/modules/cart/cart.repository.ts index 8da7391..9cb2c8d 100644 --- a/src/modules/cart/cart.repository.ts +++ b/src/modules/cart/cart.repository.ts @@ -20,7 +20,11 @@ class CartRepository extends BaseRepository { }) .populate({ path: "items.variant", - populate: ["color", "size", "meterage", "warranty", "shop"], + populate: [ + { path: "warranty" }, + { path: "shop" }, + { path: "themeValue" }, + ], }); } From 1f16897065608864fb6a7f4228a08f7c249b3bcd Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 29 Nov 2025 15:04:24 +0330 Subject: [PATCH 7/8] product vriants bug --- src/IOC/ioc.types.ts | 4 +- .../admin/controllers/category.controller.ts | 4 +- src/modules/cart/cart.repository.ts | 14 +- .../category/DTO/CreateCategory.dto.ts | 2 +- .../category/DTO/UpdateCategory.dto.ts | 3 +- src/modules/category/DTO/category.dto.ts | 31 +-- .../category/DTO/createCategoryTheme.dto.ts | 67 +------ src/modules/category/category.repository.ts | 2 +- src/modules/category/category.service.ts | 6 +- src/modules/order/order.repository.ts | 144 ++------------ src/modules/product/DTO/product.dto.ts | 28 +-- .../product/Repository/incredibleOffers.ts | 72 +------ src/modules/product/Repository/product.ts | 185 +++--------------- .../product/Repository/productVarinat.ts | 114 ++--------- 14 files changed, 86 insertions(+), 590 deletions(-) diff --git a/src/IOC/ioc.types.ts b/src/IOC/ioc.types.ts index a328336..df912a0 100644 --- a/src/IOC/ioc.types.ts +++ b/src/IOC/ioc.types.ts @@ -66,9 +66,7 @@ export const IOCTYPES = { WarrantyRepository: Symbol.for("WarrantyRepository"), ShipmentRepository: Symbol.for("ShipmentRepository"), TokenRepository: Symbol.for("TokenRepository"), - ColorRepository: Symbol.for("ColorRepository"), - SizeRepository: Symbol.for("SizeRepository"), - MeterageRepository: Symbol.for("MeterageRepository"), + ThemeRepository: Symbol.for("ThemeRepository"), ThemeValueRepository: Symbol.for("ThemeValueRepository"), QuestionRepository: Symbol.for("QuestionRepository"), diff --git a/src/modules/admin/controllers/category.controller.ts b/src/modules/admin/controllers/category.controller.ts index 83db058..da04286 100644 --- a/src/modules/admin/controllers/category.controller.ts +++ b/src/modules/admin/controllers/category.controller.ts @@ -87,9 +87,7 @@ export class AdminCategoryController extends BaseController { @ApiResponse("successful", HttpStatus.Created) @ApiParam("id", "id of category", true) @ApiModel(CreateCategoryThemeDTO) - @ApiBody( - "the colors and sizes and meterage fields are optional and one of them should send and u can also send noColor_noSize too for no theme", - ) + @ApiBody("the ") @ApiAuth() @httpPost( "/:id/variants", diff --git a/src/modules/cart/cart.repository.ts b/src/modules/cart/cart.repository.ts index 9cb2c8d..a812e0f 100644 --- a/src/modules/cart/cart.repository.ts +++ b/src/modules/cart/cart.repository.ts @@ -20,11 +20,7 @@ class CartRepository extends BaseRepository { }) .populate({ path: "items.variant", - populate: [ - { path: "warranty" }, - { path: "shop" }, - { path: "themeValue" }, - ], + populate: [{ path: "warranty" }, { path: "shop" }, { path: "themeValue" }], }); } @@ -235,13 +231,9 @@ class CartRepository extends BaseRepository { { $lookup: { from: "productvariants", localField: "items.variant", foreignField: "_id", as: "variantDetails" } }, { $unwind: "$variantDetails" }, { $lookup: { from: "warranties", localField: "variantDetails.warranty", foreignField: "_id", as: "variantDetails.warranty" } }, - { $lookup: { from: "colors", localField: "variantDetails.color", foreignField: "_id", as: "variantDetails.color" } }, - { $lookup: { from: "sizes", localField: "variantDetails.size", foreignField: "_id", as: "variantDetails.size" } }, - { $lookup: { from: "meterages", localField: "variantDetails.meterage", foreignField: "_id", as: "variantDetails.meterage" } }, + { $lookup: { from: "themeValues", localField: "variantDetails.themeValue", foreignField: "_id", as: "variantDetails.themeValue" } }, { $unwind: { path: "$variantDetails.warranty", preserveNullAndEmptyArrays: true } }, - { $unwind: { path: "$variantDetails.color", preserveNullAndEmptyArrays: true } }, - { $unwind: { path: "$variantDetails.size", preserveNullAndEmptyArrays: true } }, - { $unwind: { path: "$variantDetails.meterage", preserveNullAndEmptyArrays: true } }, + { $unwind: { path: "$variantDetails.themeValue", preserveNullAndEmptyArrays: true } }, { $lookup: { from: "shops", localField: "variantDetails.shop", foreignField: "_id", as: "variantDetails.shop" } }, { $unwind: "$variantDetails.shop" }, { diff --git a/src/modules/category/DTO/CreateCategory.dto.ts b/src/modules/category/DTO/CreateCategory.dto.ts index 8d5b41d..e2e49c2 100644 --- a/src/modules/category/DTO/CreateCategory.dto.ts +++ b/src/modules/category/DTO/CreateCategory.dto.ts @@ -44,7 +44,7 @@ export class CreateCategoryDTO { @IsValidId(ThemeModel) @ApiProperty({ type: "string", - description: "the variation theme of a category ==> should be one of Color/Size/Meterage or noColor_noSize", + description: "the variation theme of a category ==> ", }) themeId: string; } diff --git a/src/modules/category/DTO/UpdateCategory.dto.ts b/src/modules/category/DTO/UpdateCategory.dto.ts index 6325609..7469701 100644 --- a/src/modules/category/DTO/UpdateCategory.dto.ts +++ b/src/modules/category/DTO/UpdateCategory.dto.ts @@ -57,8 +57,7 @@ export class UpdateCategoryDTO { @IsValidId(ThemeModel) @ApiProperty({ type: "string", - description: "the variation theme of a category ==> should be one of Color/Size/Meterage or noColor_noSize", - example: "Color", + description: "the variation theme of a category ==> ", }) themeId: string; } diff --git a/src/modules/category/DTO/category.dto.ts b/src/modules/category/DTO/category.dto.ts index 556c9da..d0fb53d 100644 --- a/src/modules/category/DTO/category.dto.ts +++ b/src/modules/category/DTO/category.dto.ts @@ -1,42 +1,17 @@ import { Expose, Transform, Type, plainToInstance } from "class-transformer"; +import { ThemeValueDTO } from "../../product/DTO/product.dto"; import { ICategory } from "../models/Abstraction/ICategory"; import { ICategoryAttribute } from "../models/Abstraction/ICategoryAttributes"; -export class Size_MeterageDTO { - @Expose() - _id: number; - @Expose() - value: string; -} -export class ColorDTO { - @Expose() - _id: number; - - @Expose() - name: string; - - @Expose() - hexColor: string; -} export class VariantDTO { @Expose() _id: string; @Expose() - @Type(() => ColorDTO) + @Type(() => ThemeValueDTO) @Transform(({ value }) => (value && value.length > 0 ? value : undefined) /*, { toClassOnly: true }*/) - colors: ColorDTO[]; - - @Expose() - @Type(() => Size_MeterageDTO) - @Transform(({ value }) => (value && value.length > 0 ? value : undefined) /*, { toClassOnly: true }*/) - sizes: Size_MeterageDTO[]; - - @Expose() - @Type(() => Size_MeterageDTO) - @Transform(({ value }) => (value && value.length > 0 ? value : undefined) /*, { toClassOnly: true }*/) - meterages: Size_MeterageDTO[]; + themeValues: ThemeValueDTO[]; } export class CategoryVariantDTO { diff --git a/src/modules/category/DTO/createCategoryTheme.dto.ts b/src/modules/category/DTO/createCategoryTheme.dto.ts index 23ffb53..d69070d 100644 --- a/src/modules/category/DTO/createCategoryTheme.dto.ts +++ b/src/modules/category/DTO/createCategoryTheme.dto.ts @@ -1,34 +1,11 @@ import { PartialType } from "@nestjs/mapped-types"; -import { Expose, Type } from "class-transformer"; -import { ArrayMinSize, IsHexColor, IsNotEmpty, IsOptional, ValidateNested } from "class-validator"; +import { Expose } from "class-transformer"; +import { IsOptional } from "class-validator"; import { ApiProperty } from "../../../common/decorator/swggerDocs"; import { IsValidId } from "../../../common/decorator/validation.decorator"; import { ThemeModel } from "../models/theme.model"; -export class CreateColorDTO { - @Expose() - @IsNotEmpty() - name: string; - - @Expose() - @IsNotEmpty() - @IsHexColor() - hexColor: string; -} - -export class CreateSizeDTO { - @Expose() - @IsNotEmpty() - value: string; -} - -export class CreateMeterageDTO { - @Expose() - @IsNotEmpty() - value: string; -} - export class CreateCategoryThemeDTO { @Expose() @IsOptional() @@ -38,46 +15,6 @@ export class CreateCategoryThemeDTO { description: "theme id", }) themeId?: string; - - @Expose() - @IsOptional() - @IsNotEmpty() - @ArrayMinSize(1) - @ValidateNested({ each: true }) - @Type(() => CreateColorDTO) - @ApiProperty({ - type: "Array", - required: true, - description: "the variation colors of a category theme", - example: [{ name: "مشکی", hexColor: "#000000" }], - }) - colors?: CreateColorDTO[]; - - @Expose() - @IsOptional() - @IsNotEmpty() - @ArrayMinSize(1) - @ValidateNested({ each: true }) - @Type(() => CreateSizeDTO) - @ApiProperty({ - type: "Array", - description: "the variation sizes of a category theme", - example: [{ value: "M" }, { value: "S" }, { value: "L" }], - }) - sizes?: CreateSizeDTO[]; - - @Expose() - @IsOptional() - @IsNotEmpty() - @ArrayMinSize(1) - @ValidateNested({ each: true }) - @Type(() => CreateSizeDTO) - @ApiProperty({ - type: "Array", - description: "the variation meterage of a category theme", - example: [{ value: "10" }, { value: "20" }, { value: "50" }], - }) - meterages?: CreateMeterageDTO[]; } export class UpdateCategoryVariantDTO extends PartialType(CreateCategoryThemeDTO) {} diff --git a/src/modules/category/category.repository.ts b/src/modules/category/category.repository.ts index f373c7f..a840903 100644 --- a/src/modules/category/category.repository.ts +++ b/src/modules/category/category.repository.ts @@ -149,7 +149,7 @@ class CategoryRepository extends BaseRepository { for (const category of categories) { if (!category?.theme || !category?.variants.length) continue; - const theme = `${category?.theme}s`.toLowerCase(); // e.g., 'colors', 'sizes', 'meterages' + const theme = `${category?.theme}s`.toLowerCase(); if (!themeVariantsMap.has(theme)) { themeVariantsMap.set(theme, new Set()); diff --git a/src/modules/category/category.service.ts b/src/modules/category/category.service.ts index 9d57fd4..ea3138c 100644 --- a/src/modules/category/category.service.ts +++ b/src/modules/category/category.service.ts @@ -391,14 +391,12 @@ class CategoryService { // get category variants const categoryVariants = await this.categoryRepository.getCategoryVariants(categoryIds); - const colors = categoryVariants?.colors; - const sizes = categoryVariants?.sizes; - const meterages = categoryVariants?.meterages; + const themeValues = categoryVariants?.themeValues; // fetch products from the category and its children const { docs, count } = await this.productRepo.getProductsWithCategory(categoryIds, queries, userId); const products = docs.map((doc: IProduct) => ProductDTO.transformProduct(doc)); - const filters = { attributes: catAttributes?.attributes, priceRange, colors, sizes, meterages }; + const filters = { attributes: catAttributes?.attributes, priceRange, themeValues }; // fetch category breadcrumb const breadcrumb = await this.getCategoryBreadcrumb(category._id.toString()); diff --git a/src/modules/order/order.repository.ts b/src/modules/order/order.repository.ts index c73d893..d8510bd 100644 --- a/src/modules/order/order.repository.ts +++ b/src/modules/order/order.repository.ts @@ -391,43 +391,15 @@ class OrderRepository extends BaseRepository { }, { $lookup: { - from: "colors", - localField: "orderItems.shipmentItems.variant.color", + from: "themeValues", + localField: "orderItems.shipmentItems.variant.themeValue", foreignField: "_id", - as: "orderItems.shipmentItems.variant.color", + as: "orderItems.shipmentItems.variant.themeValue", }, }, { $unwind: { - path: "$orderItems.shipmentItems.variant.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "orderItems.shipmentItems.variant.size", - foreignField: "_id", - as: "orderItems.shipmentItems.variant.size", - }, - }, - { - $unwind: { - path: "$orderItems.shipmentItems.variant.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "orderItems.shipmentItems.variant.meterage", - foreignField: "_id", - as: "orderItems.shipmentItems.variant.meterage", - }, - }, - { - $unwind: { - path: "$orderItems.shipmentItems.variant.meterage", + path: "$orderItems.shipmentItems.variant.themeValue", preserveNullAndEmptyArrays: true, }, }, @@ -566,43 +538,15 @@ class OrderItemRepo extends BaseRepository { }, { $lookup: { - from: "colors", - localField: "shipmentItems.variant.color", + from: "themeValues", + localField: "shipmentItems.variant.themeValue", foreignField: "_id", - as: "shipmentItems.variant.color", + as: "shipmentItems.variant.themeValue", }, }, { $unwind: { - path: "$shipmentItems.variant.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "shipmentItems.variant.size", - foreignField: "_id", - as: "shipmentItems.variant.size", - }, - }, - { - $unwind: { - path: "$shipmentItems.variant.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "shipmentItems.variant.meterage", - foreignField: "_id", - as: "shipmentItems.variant.meterage", - }, - }, - { - $unwind: { - path: "$shipmentItems.variant.meterage", + path: "$shipmentItems.variant.themeValue", preserveNullAndEmptyArrays: true, }, }, @@ -1521,43 +1465,15 @@ class OrderItemRepo extends BaseRepository { }, { $lookup: { - from: "colors", - localField: "shipmentItems.variant.color", + from: "themeValues", + localField: "shipmentItems.variant.themeValue", foreignField: "_id", - as: "shipmentItems.variant.color", + as: "shipmentItems.variant.themeValue", }, }, { $unwind: { - path: "$shipmentItems.variant.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "shipmentItems.variant.size", - foreignField: "_id", - as: "shipmentItems.variant.size", - }, - }, - { - $unwind: { - path: "$shipmentItems.variant.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "shipmentItems.variant.meterage", - foreignField: "_id", - as: "shipmentItems.variant.meterage", - }, - }, - { - $unwind: { - path: "$shipmentItems.variant.meterage", + path: "$shipmentItems.variant.themeValue", preserveNullAndEmptyArrays: true, }, }, @@ -1730,43 +1646,15 @@ class OrderItemRepo extends BaseRepository { }, { $lookup: { - from: "colors", - localField: "shipmentItems.variant.color", + from: "themeValues", + localField: "shipmentItems.variant.themeValue", foreignField: "_id", - as: "shipmentItems.variant.color", + as: "shipmentItems.variant.themeValue", }, }, { $unwind: { - path: "$shipmentItems.variant.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "shipmentItems.variant.size", - foreignField: "_id", - as: "shipmentItems.variant.size", - }, - }, - { - $unwind: { - path: "$shipmentItems.variant.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "shipmentItems.variant.meterage", - foreignField: "_id", - as: "shipmentItems.variant.meterage", - }, - }, - { - $unwind: { - path: "$shipmentItems.variant.meterage", + path: "$shipmentItems.variant.themeValue", preserveNullAndEmptyArrays: true, }, }, diff --git a/src/modules/product/DTO/product.dto.ts b/src/modules/product/DTO/product.dto.ts index 20aa784..4645f23 100644 --- a/src/modules/product/DTO/product.dto.ts +++ b/src/modules/product/DTO/product.dto.ts @@ -4,7 +4,7 @@ import { IsString } from "class-validator"; import { ApiProperty } from "../../../common/decorator/swggerDocs"; import { ProductStatus } from "../../../common/enums/product.enum"; import { BrandDTO } from "../../brand/DTO/brand.dto"; -import { CategoryTreeDTO, ColorDTO, Size_MeterageDTO } from "../../category/DTO/category.dto"; +import { CategoryTreeDTO } from "../../category/DTO/category.dto"; import { ShipmentMethodDTO } from "../../shipment/DTO/shipment.dto"; import { ShopDTO } from "../../shop/DTO/shop.dto"; import { WarrantyDTO } from "../../warranty/DTO/warranty.dto"; @@ -83,7 +83,7 @@ class SaleFormatDTO { //################################# -class ThemeValueDTO { +export class ThemeValueDTO { @Expose() _id: string; @@ -141,18 +141,6 @@ export class ProductDetailVariantDTO { @Type(() => WarrantyDTO) warranty: WarrantyDTO; - @Expose() - @Type(() => ColorDTO) - color?: ColorDTO; - - @Expose() - @Type(() => Size_MeterageDTO) - size?: Size_MeterageDTO; - - @Expose() - @Type(() => Size_MeterageDTO) - meterage?: Size_MeterageDTO; - @Expose() @Type(() => ThemeValueDTO) themeValue?: ThemeValueDTO; @@ -302,16 +290,8 @@ export class ProductVariantDTO { saleFormat: SaleFormatDTO; @Expose() - @Type(() => ColorDTO) - color?: ColorDTO; - - @Expose() - @Type(() => Size_MeterageDTO) - size?: Size_MeterageDTO; - - @Expose() - @Type(() => Size_MeterageDTO) - meterage?: Size_MeterageDTO; + @Type(() => ThemeValueDTO) + themeValue?: ThemeValueDTO; public static transformProduct(data: IProductVariant): ProductVariantDTO { const productVariantDTO = plainToInstance(ProductVariantDTO, data, { diff --git a/src/modules/product/Repository/incredibleOffers.ts b/src/modules/product/Repository/incredibleOffers.ts index 87d1016..f5f0611 100644 --- a/src/modules/product/Repository/incredibleOffers.ts +++ b/src/modules/product/Repository/incredibleOffers.ts @@ -117,43 +117,15 @@ export class IncredibleOffersRepo extends BaseRepository { }, { $lookup: { - from: "colors", - localField: "product.default_variant.color", + from: "themeValues", + localField: "product.default_variant.themeValue", foreignField: "_id", - as: "product.default_variant.color", + as: "product.default_variant.themeValue", }, }, { $unwind: { - path: "$product.default_variant.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "product.default_variant.size", - foreignField: "_id", - as: "product.default_variant.size", - }, - }, - { - $unwind: { - path: "$product.default_variant.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "product.default_variant.meterage", - foreignField: "_id", - as: "product.default_variant.meterage", - }, - }, - { - $unwind: { - path: "$product.variant.meterage", + path: "$product.default_variant.themeValue", preserveNullAndEmptyArrays: true, }, }, @@ -251,43 +223,15 @@ export class IncredibleOffersRepo extends BaseRepository { }, { $lookup: { - from: "colors", - localField: "variant.color", + from: "themeValues", + localField: "variant.themeValue", foreignField: "_id", - as: "variant.color", + as: "variant.themeValue", }, }, { $unwind: { - path: "$variant.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "variant.size", - foreignField: "_id", - as: "variant.size", - }, - }, - { - $unwind: { - path: "$variant.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "variant.meterage", - foreignField: "_id", - as: "variant.meterage", - }, - }, - { - $unwind: { - path: "$variant.meterage", + path: "$variant.themeValue", preserveNullAndEmptyArrays: true, }, }, diff --git a/src/modules/product/Repository/product.ts b/src/modules/product/Repository/product.ts index 9852dc7..5ffa3c1 100644 --- a/src/modules/product/Repository/product.ts +++ b/src/modules/product/Repository/product.ts @@ -1747,48 +1747,22 @@ export class ProductRepository extends BaseRepository { preserveNullAndEmptyArrays: true, }, }, + { $lookup: { - from: "colors", - localField: "variants.color", + from: "themeValues", + localField: "variants.themeValue", foreignField: "_id", - as: "variants.color", + as: "variants.themeValue", }, }, { $unwind: { - path: "$variants.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "variants.size", - foreignField: "_id", - as: "variants.size", - }, - }, - { - $unwind: { - path: "$variants.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "variants.meterage", - foreignField: "_id", - as: "variants.meterage", - }, - }, - { - $unwind: { - path: "$variants.meterage", + path: "$variants.themeValue", preserveNullAndEmptyArrays: true, }, }, + { $group: { _id: "$_id", @@ -2037,43 +2011,15 @@ export class ProductRepository extends BaseRepository { }, { $lookup: { - from: "colors", - localField: "variants.color", + from: "themeValues", + localField: "variants.themeValue", foreignField: "_id", - as: "variants.color", + as: "variants.themeValue", }, }, { $unwind: { - path: "$variants.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "variants.size", - foreignField: "_id", - as: "variants.size", - }, - }, - { - $unwind: { - path: "$variants.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "variants.meterage", - foreignField: "_id", - as: "variants.meterage", - }, - }, - { - $unwind: { - path: "$variants.meterage", + path: "$variants.themeValue", preserveNullAndEmptyArrays: true, }, }, @@ -2311,45 +2257,18 @@ export class ProductRepository extends BaseRepository { preserveNullAndEmptyArrays: true, }, }, + { $lookup: { - from: "colors", - localField: "variants.color", + from: "themeValues", + localField: "variants.themeValue", foreignField: "_id", - as: "variants.color", + as: "variants.themeValue", }, }, { $unwind: { - path: "$variants.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "variants.size", - foreignField: "_id", - as: "variants.size", - }, - }, - { - $unwind: { - path: "$variants.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "variants.meterage", - foreignField: "_id", - as: "variants.meterage", - }, - }, - { - $unwind: { - path: "$variants.meterage", + path: "$variants.themeValue", preserveNullAndEmptyArrays: true, }, }, @@ -2486,45 +2405,18 @@ export class ProductRepository extends BaseRepository { preserveNullAndEmptyArrays: true, }, }, + { $lookup: { - from: "colors", - localField: "variants.color", + from: "themeValues", + localField: "variants.themeValue", foreignField: "_id", - as: "variants.color", + as: "variants.themeValue", }, }, { $unwind: { - path: "$variants.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "variants.size", - foreignField: "_id", - as: "variants.size", - }, - }, - { - $unwind: { - path: "$variants.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "variants.meterage", - foreignField: "_id", - as: "variants.meterage", - }, - }, - { - $unwind: { - path: "$variants.meterage", + path: "$variants.themeValue", preserveNullAndEmptyArrays: true, }, }, @@ -2647,45 +2539,18 @@ export class ProductRepository extends BaseRepository { preserveNullAndEmptyArrays: true, }, }, + { $lookup: { - from: "colors", - localField: "variants.color", + from: "themeValues", + localField: "variants.themeValue", foreignField: "_id", - as: "variants.color", + as: "variants.themeValue", }, }, { $unwind: { - path: "$variants.color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "sizes", - localField: "variants.size", - foreignField: "_id", - as: "variants.size", - }, - }, - { - $unwind: { - path: "$variants.size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "meterages", - localField: "variants.meterage", - foreignField: "_id", - as: "variants.meterage", - }, - }, - { - $unwind: { - path: "$variants.meterage", + path: "$variants.themeValue", preserveNullAndEmptyArrays: true, }, }, diff --git a/src/modules/product/Repository/productVarinat.ts b/src/modules/product/Repository/productVarinat.ts index 2dc24b7..27a3969 100644 --- a/src/modules/product/Repository/productVarinat.ts +++ b/src/modules/product/Repository/productVarinat.ts @@ -86,28 +86,13 @@ export class ProductVariantRepository extends BaseRepository { { $unwind: "$category", }, + { $lookup: { - from: "colors", - localField: "color", + from: "themeValues", + localField: "themeValue", foreignField: "_id", - as: "color", - }, - }, - { - $lookup: { - from: "sizes", - localField: "size", - foreignField: "_id", - as: "size", - }, - }, - { - $lookup: { - from: "meterages", - localField: "meterage", - foreignField: "_id", - as: "meterage", + as: "themeValue", }, }, { @@ -120,22 +105,11 @@ export class ProductVariantRepository extends BaseRepository { }, { $unwind: { - path: "$color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $unwind: { - path: "$size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $unwind: { - path: "$meterage", + path: "$themeValue", preserveNullAndEmptyArrays: true, }, }, + { $addFields: { product_id: "$product._id", @@ -259,28 +233,13 @@ export class ProductVariantRepository extends BaseRepository { { $unwind: "$category", }, + { $lookup: { - from: "colors", - localField: "color", + from: "themeValues", + localField: "themeValue", foreignField: "_id", - as: "color", - }, - }, - { - $lookup: { - from: "sizes", - localField: "size", - foreignField: "_id", - as: "size", - }, - }, - { - $lookup: { - from: "meterages", - localField: "meterage", - foreignField: "_id", - as: "meterage", + as: "themeValue", }, }, { @@ -293,22 +252,11 @@ export class ProductVariantRepository extends BaseRepository { }, { $unwind: { - path: "$color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $unwind: { - path: "$size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $unwind: { - path: "$meterage", + path: "$themeValue", preserveNullAndEmptyArrays: true, }, }, + { $addFields: { product_id: "$product._id", @@ -400,28 +348,13 @@ export class ProductVariantRepository extends BaseRepository { { $unwind: "$category", }, + { $lookup: { - from: "colors", - localField: "color", + from: "themeValues", + localField: "themeValue", foreignField: "_id", - as: "color", - }, - }, - { - $lookup: { - from: "sizes", - localField: "size", - foreignField: "_id", - as: "size", - }, - }, - { - $lookup: { - from: "meterages", - localField: "meterage", - foreignField: "_id", - as: "meterage", + as: "themeValue", }, }, { @@ -434,22 +367,11 @@ export class ProductVariantRepository extends BaseRepository { }, { $unwind: { - path: "$color", - preserveNullAndEmptyArrays: true, - }, - }, - { - $unwind: { - path: "$size", - preserveNullAndEmptyArrays: true, - }, - }, - { - $unwind: { - path: "$meterage", + path: "$themeValue", preserveNullAndEmptyArrays: true, }, }, + { $addFields: { product_id: "$product._id", From e7fdc1f1adad48c2b4722313b83a7489b1e3c0b8 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 29 Nov 2025 19:09:27 +0330 Subject: [PATCH 8/8] them valkue populate --- src/modules/order/DTO/userOrder.dto.ts | 5 +++++ src/modules/order/order.repository.ts | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/modules/order/DTO/userOrder.dto.ts b/src/modules/order/DTO/userOrder.dto.ts index 52ee893..005fe92 100644 --- a/src/modules/order/DTO/userOrder.dto.ts +++ b/src/modules/order/DTO/userOrder.dto.ts @@ -4,6 +4,7 @@ import { OrderItemDTO } from "./order.dto"; import { OrdersStatus } from "../../../common/enums/order.enum"; import { TimeService } from "../../../utils/time.service"; import { PaymentDTO } from "../../payment/DTO/payment.dto"; +import { ThemeValueDTO } from "../../product/DTO/product.dto"; import { UserDTO } from "../../user/DTO/user.dto"; import { IOrder } from "../models/Abstraction/IOrder"; @@ -54,6 +55,10 @@ export class UserOrderDTO { @Transform(({ value }) => TimeService.convertGregorianToPersian(new Date(value), true)) createdAt: string; + @Expose() + @Type(() => ThemeValueDTO) + themeValue: ThemeValueDTO; + public static transformOrder(data: IOrder): UserOrderDTO { const OrderDTO = plainToClass(UserOrderDTO, data, { excludeExtraneousValues: true, diff --git a/src/modules/order/order.repository.ts b/src/modules/order/order.repository.ts index d8510bd..4a528ef 100644 --- a/src/modules/order/order.repository.ts +++ b/src/modules/order/order.repository.ts @@ -120,7 +120,7 @@ class OrderRepository extends BaseRepository { default: break; } - return this.model.aggregate([ + const orders = await this.model.aggregate([ { $match: matchQuery, }, @@ -207,6 +207,20 @@ class OrderRepository extends BaseRepository { { $unwind: "$shipmentItemVariant", }, + { + $lookup: { + from: "themevalues", + localField: "shipmentItemVariant.themeValue", + foreignField: "_id", + as: "shipmentItemVariant.themeValue", + }, + }, + { + $unwind: { + path: "$shipmentItemVariant.themeValue", + preserveNullAndEmptyArrays: true, + }, + }, { $group: { _id: "$orderItems._id", @@ -274,6 +288,7 @@ class OrderRepository extends BaseRepository { }, }, ]); + return orders; } async getUserOrderDetail(orderId: number, userId: string) {