diff --git a/src/modules/order/interface/order.interface.ts b/src/modules/order/interface/order.interface.ts index 32ec4e1..3a51499 100644 --- a/src/modules/order/interface/order.interface.ts +++ b/src/modules/order/interface/order.interface.ts @@ -37,7 +37,7 @@ export enum OrderItemStatusEnum { CANCELED = 'canceled', } -export interface IField { fieldId: number, value: string } + export interface IAttachment { type: string, url: string } diff --git a/src/modules/request/dto/create-request.dto.ts b/src/modules/request/dto/create-request.dto.ts index 0606908..8fc9313 100644 --- a/src/modules/request/dto/create-request.dto.ts +++ b/src/modules/request/dto/create-request.dto.ts @@ -1 +1,59 @@ -export class CreateRequestDto {} +import { + IsString, + IsInt, + IsArray, + IsNumber, + IsNotEmpty, + ArrayMinSize, + IsOptional, +} from 'class-validator'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IAttachment, IField } from '../interface/request.interface'; + +export class CreateRequestItemDto { + @IsString() + @IsNotEmpty() + @ApiProperty() + productId: string; + + @IsNumber() + @ApiProperty() + quantity: number; + + @ApiPropertyOptional({ type: 'array', items: { type: 'object', properties: { fieldId: { type: 'number' }, value: { type: 'string' } } } }) + @IsOptional() + @IsArray() + attributes?: IField[]; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + description?: string; + + @ApiPropertyOptional({ type: 'array', items: { type: 'object', properties: { type: { type: 'string' }, url: { type: 'string' } } } }) + @IsOptional() + @IsArray() + attachments?: IAttachment[]; +} + +export class CreateRequestDto { + @IsArray() + @ApiProperty({ + isArray: true, + type: [CreateRequestItemDto], + example: [ + { + productId: '01ARZ3NDEKTSV4RRFFQ69G5FAV', + quantity: 100, + attributes: [{ fieldId: 1, value: 'blue' }], + attachments: [{ url: '', type: 'image' }], + description: 'توضیحات', + }, + ], + }) + @IsNotEmpty() + @ArrayMinSize(1, { message: 'At least one item is required' }) + @Type(() => CreateRequestItemDto) + items: CreateRequestItemDto[]; +} diff --git a/src/modules/request/entities/request-item.entity.ts b/src/modules/request/entities/request-item.entity.ts index 47b09f0..504f882 100644 --- a/src/modules/request/entities/request-item.entity.ts +++ b/src/modules/request/entities/request-item.entity.ts @@ -2,9 +2,8 @@ import { Entity, ManyToOne, OptionalProps, Property } from '@mikro-orm/core'; import { Product } from 'src/modules/product/entities/product.entity'; import { BaseEntity } from 'src/common/entities/base.entity'; import { Request } from './request.entity'; -import { IAttachment } from '../interface/request.interface'; - - +import { IAttachment, IField } from '../interface/request.interface'; + @Entity({ tableName: 'request_items' }) export class RequestItem extends BaseEntity { [OptionalProps]?: 'createdAt' | 'deletedAt'; @@ -12,10 +11,12 @@ export class RequestItem extends BaseEntity { @ManyToOne(() => Product) product!: Product; + @Property({ type: 'json', nullable: true }) + attributes?: IField[] + @ManyToOne(() => Request) request!: Request; - @Property({ type: 'int' }) quantity!: number; diff --git a/src/modules/request/entities/request.entity.ts b/src/modules/request/entities/request.entity.ts index 5fc5e4f..7bfa654 100644 --- a/src/modules/request/entities/request.entity.ts +++ b/src/modules/request/entities/request.entity.ts @@ -19,7 +19,7 @@ import { RequestStatusEnum } from '../enum/request'; @Entity({ tableName: 'requests' }) @Index({ properties: ['user'] }) export class Request extends BaseEntity { - [OptionalProps]?: 'createdAt' | 'deletedAt' + [OptionalProps]?: 'createdAt' | 'deletedAt'|'requestNumber' @PrimaryKey({ type: 'string', columnType: 'char(26)' }) id: string = ulid() diff --git a/src/modules/request/interface/request.interface.ts b/src/modules/request/interface/request.interface.ts index 3e81846..d008669 100644 --- a/src/modules/request/interface/request.interface.ts +++ b/src/modules/request/interface/request.interface.ts @@ -1 +1,3 @@ export interface IAttachment { type: string, url: string } + +export interface IField { fieldId: number, value: string } \ No newline at end of file diff --git a/src/modules/request/request.controller.ts b/src/modules/request/request.controller.ts index 4794890..ce85899 100644 --- a/src/modules/request/request.controller.ts +++ b/src/modules/request/request.controller.ts @@ -1,33 +1,40 @@ -import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; +import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards } from '@nestjs/common'; import { RequestService } from './request.service'; import { CreateRequestDto } from './dto/create-request.dto'; import { UpdateRequestDto } from './dto/update-request.dto'; +import { AuthGuard } from '../auth/guards/auth.guard'; +import { UserId } from 'src/common/decorators'; -@Controller('request') +@Controller() export class RequestController { - constructor(private readonly requestService: RequestService) {} + constructor(private readonly requestService: RequestService) { } - @Post() - create(@Body() createRequestDto: CreateRequestDto) { - return this.requestService.create(createRequestDto); + @Post('public/request') + @UseGuards(AuthGuard) + create(@Body() dto: CreateRequestDto, @UserId() userId: string) { + return this.requestService.create(dto, userId); } - @Get() + @Get('public/request') + @UseGuards(AuthGuard) findAll() { return this.requestService.findAll(); } - @Get(':id') + @Get('public/request:id') + @UseGuards(AuthGuard) findOne(@Param('id') id: string) { return this.requestService.findOne(+id); } - @Patch(':id') + @Patch('public/request:id') + @UseGuards(AuthGuard) update(@Param('id') id: string, @Body() updateRequestDto: UpdateRequestDto) { return this.requestService.update(+id, updateRequestDto); } - @Delete(':id') + @Delete('public/request:id') + @UseGuards(AuthGuard) remove(@Param('id') id: string) { return this.requestService.remove(+id); } diff --git a/src/modules/request/request.module.ts b/src/modules/request/request.module.ts index a7e90db..d764812 100644 --- a/src/modules/request/request.module.ts +++ b/src/modules/request/request.module.ts @@ -1,8 +1,18 @@ import { Module } from '@nestjs/common'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; import { RequestService } from './request.service'; import { RequestController } from './request.controller'; +import { Request } from './entities/request.entity'; +import { RequestItem } from './entities/request-item.entity'; +import { UserModule } from '../user/user.module'; +import { ProductModule } from '../product/product.module'; @Module({ + imports: [ + MikroOrmModule.forFeature([Request, RequestItem]), + UserModule, + ProductModule, + ], controllers: [RequestController], providers: [RequestService], }) diff --git a/src/modules/request/request.service.ts b/src/modules/request/request.service.ts index 522f38c..f59e82e 100644 --- a/src/modules/request/request.service.ts +++ b/src/modules/request/request.service.ts @@ -1,11 +1,49 @@ import { Injectable } from '@nestjs/common'; +import { EntityManager } from '@mikro-orm/postgresql'; import { CreateRequestDto } from './dto/create-request.dto'; import { UpdateRequestDto } from './dto/update-request.dto'; +import { Request } from './entities/request.entity'; +import { RequestItem } from './entities/request-item.entity'; +import { RequestStatusEnum } from './enum/request'; +import { UserService } from '../user/providers/user.service'; +import { ProductService } from '../product/providers/product.service'; @Injectable() export class RequestService { - create(createRequestDto: CreateRequestDto) { - return 'This action adds a new request'; + constructor( + private readonly em: EntityManager, + private readonly userService: UserService, + private readonly productService: ProductService, + ) {} + + async create(createRequestDto: CreateRequestDto, userId: string) { + const { items } = createRequestDto; + + const user = await this.userService.findOrFail(userId); + + return this.em.transactional(async (em) => { + const request = em.create(Request, { + user, + status: RequestStatusEnum.PENDING, + }); + em.persist(request); + + for (const item of items) { + const product = await this.productService.findOneOrFail(item.productId); + const requestItem = em.create(RequestItem, { + request, + product, + quantity: item.quantity, + attributes: item.attributes, + description: item.description, + attachments: item.attachments, + }); + request.items.add(requestItem); + } + + await em.flush(); + return request; + }); } findAll() {