# Catalog Module

## Overview
The catalog module manages all merchandising entities: products, categories, product variants, and product reviews. Each resource exposes CRUD endpoints, rich filtering, and pagination helpers. All identifiers are now auto-increment integers.

## Products
- **Endpoints**
  - `POST /catalog/products` — create a product.
  - `GET /catalog/products` — list products with pagination, filtering, and sorting.
  - `GET /catalog/products/:id` — fetch a single product with related variants, categories, and reviews.
  - `PATCH /catalog/products/:id` — update product fields; also re-sync category assignments when `categoryIds` are provided.
  - `DELETE /catalog/products/:id` — soft-delete a product.
- **Key Fields** (see `CreateProductDto`)
  - Required: `title`, `basePrice`, `discount`, `price`.
  - Optional metadata: slug (auto-generated from title when omitted), `subtitle`, `descriptionHtml`, `productCode`, `brandId`, `currency`, `stockQty`, `shippingNote`, `returnPolicyNote`, media/SEO helpers, `metaJson`.
  - Relationships: `categoryIds` (integer array).
- **Filtering & Sorting** (`ProductListQueryDto`)
  - `search` (title/slug/code LIKE)
  - `status`
  - `categoryId` (integer FK to categories)
  - Price range: `minPrice`, `maxPrice`
  - `minRating`
  - Sorting via `sortBy` (`createdAt`, `updatedAt`, `price`, `basePrice`, `soldCount`, `ratingAvg`, `title`) and `sortOrder` (`ASC`/`DESC`).
- **Notes**
  - Category synchronization validates supplied IDs exist before inserting join rows.
  - Products must reference an `active` brand when `brandId` is supplied.
  - Products are soft-deleted via `deletedAt`; cascading relations honor this state.

## Categories
- **Endpoints**
  - `POST /catalog/categories`
  - `GET /catalog/categories`
  - `GET /catalog/categories/:id`
  - `PATCH /catalog/categories/:id`
  - `DELETE /catalog/categories/:id`
- **Key Fields**: `name`, `slug`, optional `description`, `thumbnailUrl`, flags `isActive`, `isMenu`, `isSidebar`, `isFeature`, `isSlider`, and hierarchical `parentId`.
- **Filtering** (`CategoryListQueryDto`)
  - `search` (name/slug), `parentId`, boolean flags for menu/sidebar/feature/slider/active.
- **Behavior**
  - Parent assignment validates the referenced category (and prevents self-parenting).
  - Removing a category cascades through join tables (`product_categories`).

## Variants
- **Endpoints**
  - `POST /catalog/variants`
  - `GET /catalog/variants`
  - `GET /catalog/variants/:id`
  - `PATCH /catalog/variants/:id`
  - `DELETE /catalog/variants/:id`
- **Key Fields**: `productId` (required), `sku`, `optionValues` (JSON map), pricing/cost/stock metrics, weight & dimensions, `imageUrl`, `status`.
- **Filtering**
  - `productId` and `status` via `VariantListQueryDto`.

## Reviews
- **Endpoints**
  - `POST /catalog/reviews`
  - `GET /catalog/reviews`
  - `GET /catalog/reviews/:id`
  - `PATCH /catalog/reviews/:id`
  - `DELETE /catalog/reviews/:id`
- **Key Fields**: `productId`, `userId`, `rating` (1–5), optional `title`, `comment`, moderation `status`.
- **Filtering**
  - Query by `productId`, `userId`, `status`, and `minRating`.
- **Behavior**
  - Creation/update validates referenced product and user records before persisting.

## Brands
- **Endpoints**
  - `POST /catalog/brands`
  - `GET /catalog/brands`
  - `GET /catalog/brands/:id`
  - `PATCH /catalog/brands/:id`
  - `DELETE /catalog/brands/:id`
- **Key Fields**: `name` (unique), optional `description`, `status` (`active` or `inactive`).
- **Filtering**
  - `status` and `search` (name LIKE) via `BrandListQueryDto`.
- **Behavior**
  - Brand names are enforced to be unique.
  - Inactive brands cannot be attached to new products.

## Shared Details
- Pagination helpers (`page`, `limit`) and metadata responses follow the common `PaginatedResult<T>` shape.
- DTO validation uses `class-validator`; numeric identifiers leverage `@Type(() => Number)` plus `@IsInt`/`@Min(1)`.
- Mapper utilities in `catalog.mappers.ts` normalize relation IDs to numbers and compose nested DTOs.
- Join tables (`product_categories`, `product_tags`, `related_products`) now store integer foreign keys, replacing former UUIDs.

## Integration Tips
- Always send numeric IDs in request payloads; passing strings will fail validation.
- When seeding data, insert base records first (users, categories, products) to satisfy FK constraints enforced by the updated migrations.
