Files
trackgo-be/src/modules/sales/sales-requests/dto/sales-request.dto.ts
T
shancheas 34d4f2c120 Add products and sales management modules with database schema and validation
- Introduced `ProductsModule` to manage product data, including read and write controllers.
- Created database migrations for the `products`, `sales_requests`, `sales_orders`, `sales_invoices`, and related tables, including constraints and unique indexes.
- Implemented validation for product fields such as code, name, unit, and brand with corresponding utility functions.
- Developed service and repository layers for handling product and sales data operations.
- Added unit tests for the products and sales services, repositories, and controllers to ensure functionality and correctness.
- Updated application module to include the new `ProductsModule` and related sales modules for better organization.
2026-08-25 09:54:16 +07:00

299 lines
6.2 KiB
TypeScript

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
ArrayNotEmpty,
IsArray,
IsIn,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
IsUUID,
Matches,
Max,
MaxLength,
Min,
ValidateNested,
} from 'class-validator';
import { PaginationQueryDto } from '../../../../common/http/response';
import {
DOCUMENT_ADDRESS_MAX_LENGTH,
DOCUMENT_CODE_MAX_LENGTH,
DOCUMENT_CODE_PATTERN,
DOCUMENT_NOTES_MAX_LENGTH,
IMAGE_DESCRIPTION_MAX_LENGTH,
IMAGE_URL_MAX_LENGTH,
SALES_REQUEST_STATUSES,
} from '../../shared/sales-fields';
export class SalesLineDto {
@ApiProperty({ format: 'uuid' })
@IsUUID('4')
productId!: string;
@ApiProperty({ example: '2.0000' })
@IsString()
@IsNotEmpty()
quantity!: string;
@ApiPropertyOptional({ example: '12500.0000' })
@IsOptional()
@IsString()
price?: string;
}
export class SalesImageDto {
@ApiProperty({ example: 'https://cdn.example.com/a.png' })
@IsString()
@IsNotEmpty()
@MaxLength(IMAGE_URL_MAX_LENGTH)
url!: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(IMAGE_DESCRIPTION_MAX_LENGTH)
description?: string;
}
export class CreateSalesRequestDto {
@ApiPropertyOptional({ example: 'SR-20260824-0001' })
@IsOptional()
@IsString()
@MaxLength(DOCUMENT_CODE_MAX_LENGTH)
@Matches(DOCUMENT_CODE_PATTERN)
code?: string;
@ApiProperty({ example: '2026-08-24T10:00:00+07:00' })
@IsString()
@IsNotEmpty()
date!: string;
@ApiProperty({ format: 'uuid' })
@IsUUID('4')
salesPersonId!: string;
@ApiProperty({ format: 'uuid' })
@IsUUID('4')
branchId!: string;
@ApiProperty({ format: 'uuid' })
@IsUUID('4')
divisionId!: string;
@ApiProperty({ format: 'uuid' })
@IsUUID('4')
customerId!: string;
@ApiProperty({ example: 'Jl Sudirman 1' })
@IsString()
@IsNotEmpty()
@MaxLength(DOCUMENT_ADDRESS_MAX_LENGTH)
address!: string;
@ApiPropertyOptional()
@IsOptional()
@IsNumber()
@Min(-90)
@Max(90)
latitude?: number;
@ApiPropertyOptional()
@IsOptional()
@IsNumber()
@Min(-180)
@Max(180)
longitude?: number;
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(DOCUMENT_NOTES_MAX_LENGTH)
notes?: string;
@ApiProperty({ type: [SalesLineDto] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => SalesLineDto)
products!: SalesLineDto[];
@ApiPropertyOptional({ type: [SalesImageDto] })
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => SalesImageDto)
images?: SalesImageDto[];
@ApiPropertyOptional({ enum: SALES_REQUEST_STATUSES })
@IsOptional()
@IsIn([...SALES_REQUEST_STATUSES])
status?: string;
}
export class UpdateSalesRequestDto {
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(DOCUMENT_CODE_MAX_LENGTH)
@Matches(DOCUMENT_CODE_PATTERN)
code?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
date?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID('4')
salesPersonId?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID('4')
branchId?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID('4')
divisionId?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID('4')
customerId?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(DOCUMENT_ADDRESS_MAX_LENGTH)
address?: string;
@ApiPropertyOptional({ nullable: true })
@IsOptional()
@IsNumber()
latitude?: number | null;
@ApiPropertyOptional({ nullable: true })
@IsOptional()
@IsNumber()
longitude?: number | null;
@ApiPropertyOptional({ nullable: true })
@IsOptional()
@IsString()
notes?: string | null;
@ApiPropertyOptional({ type: [SalesLineDto] })
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => SalesLineDto)
products?: SalesLineDto[];
@ApiPropertyOptional({ type: [SalesImageDto] })
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => SalesImageDto)
images?: SalesImageDto[];
}
export class UpdateSalesRequestStatusDto {
@ApiProperty({ enum: SALES_REQUEST_STATUSES })
@IsIn([...SALES_REQUEST_STATUSES])
status!: string;
}
export class BulkIdsDto {
@ApiProperty({ type: [String], format: 'uuid' })
@IsArray()
@ArrayNotEmpty()
@IsUUID('4', { each: true })
ids!: string[];
}
export class BulkStatusDto {
@ApiProperty({ type: [String], format: 'uuid' })
@IsArray()
@ArrayNotEmpty()
@IsUUID('4', { each: true })
ids!: string[];
@ApiProperty({ enum: SALES_REQUEST_STATUSES })
@IsIn([...SALES_REQUEST_STATUSES])
status!: string;
}
export class ListSalesRequestsQueryDto extends PaginationQueryDto {
@ApiPropertyOptional()
@IsOptional()
@IsString()
code?: string;
@ApiPropertyOptional({ enum: SALES_REQUEST_STATUSES })
@IsOptional()
@IsIn([...SALES_REQUEST_STATUSES])
status?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID('4')
customerId?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID('4')
salesPersonId?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID('4')
branchId?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID('4')
divisionId?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
search?: string;
}
export class SalesRequestDto {
@ApiProperty({ format: 'uuid' })
id!: string;
@ApiProperty()
code!: string;
@ApiProperty()
date!: number;
@ApiProperty({ format: 'uuid' })
salesPersonId!: string;
@ApiProperty({ format: 'uuid' })
branchId!: string;
@ApiProperty({ format: 'uuid' })
divisionId!: string;
@ApiProperty({ format: 'uuid' })
customerId!: string;
@ApiProperty()
address!: string;
@ApiPropertyOptional({ nullable: true })
latitude!: number | null;
@ApiPropertyOptional({ nullable: true })
longitude!: number | null;
@ApiPropertyOptional({ nullable: true })
notes!: string | null;
@ApiProperty({ enum: SALES_REQUEST_STATUSES })
status!: string;
@ApiProperty()
createdAt!: number;
@ApiProperty()
updatedAt!: number;
@ApiProperty({ format: 'uuid' })
createdBy!: string;
@ApiProperty({ format: 'uuid' })
updatedBy!: string;
}