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; }