import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray, IsIn, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID, Matches, Max, MaxLength, Min, ValidateNested, } from 'class-validator'; import { CodeRelationDto, DefaultRelationDto, PaginationQueryDto, parseQueryIdList, UserRelationDto, } from '../../../../common/http/response'; import { DOCUMENT_ADDRESS_MAX_LENGTH, DOCUMENT_CODE_MAX_LENGTH, DOCUMENT_CODE_PATTERN, DOCUMENT_NOTES_MAX_LENGTH, PACKING_SLIP_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 CreatePackingSlipDto { @ApiPropertyOptional({ example: 'PS-20260824-0001' }) @IsOptional() @IsString() @MaxLength(DOCUMENT_CODE_MAX_LENGTH) @Matches(DOCUMENT_CODE_PATTERN) code?: string; @ApiPropertyOptional({ format: 'uuid' }) @IsOptional() @IsUUID('4') salesOrderId?: string; @ApiPropertyOptional() @IsOptional() @IsString() salesOrderNumber?: string; @ApiPropertyOptional({ example: '2026-08-24T10:00:00+07:00' }) @IsOptional() @IsString() date?: string; @ApiPropertyOptional({ format: 'uuid' }) @IsOptional() @IsUUID('4') customerId?: string; @ApiPropertyOptional({ example: 'Jl Sudirman 1' }) @IsOptional() @IsString() @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; @ApiPropertyOptional({ type: [SalesLineDto] }) @IsOptional() @IsArray() @ValidateNested({ each: true }) @Type(() => SalesLineDto) products?: SalesLineDto[]; @ApiPropertyOptional({ enum: PACKING_SLIP_STATUSES }) @IsOptional() @IsIn([...PACKING_SLIP_STATUSES]) status?: string; } export class UpdatePackingSlipDto { @ApiPropertyOptional() @IsOptional() @IsString() @MaxLength(DOCUMENT_CODE_MAX_LENGTH) @Matches(DOCUMENT_CODE_PATTERN) code?: string; @ApiPropertyOptional({ format: 'uuid', nullable: true }) @IsOptional() @IsUUID('4') salesOrderId?: string | null; @ApiPropertyOptional() @IsOptional() @IsString() date?: 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[]; } export class UpdatePackingSlipStatusDto { @ApiProperty({ enum: PACKING_SLIP_STATUSES }) @IsIn([...PACKING_SLIP_STATUSES]) status!: string; @ApiPropertyOptional({ type: [SalesLineDto] }) @IsOptional() @IsArray() @ValidateNested({ each: true }) @Type(() => SalesLineDto) products?: SalesLineDto[]; } 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: PACKING_SLIP_STATUSES }) @IsIn([...PACKING_SLIP_STATUSES]) status!: string; } export class ListPackingSlipsQueryDto extends PaginationQueryDto { @ApiPropertyOptional() @IsOptional() @IsString() code?: string; @ApiPropertyOptional({ enum: PACKING_SLIP_STATUSES }) @IsOptional() @IsIn([...PACKING_SLIP_STATUSES]) status?: string; @ApiPropertyOptional({ format: 'uuid' }) @IsOptional() @IsUUID('4') customerId?: string; @ApiPropertyOptional({ type: [String], format: 'uuid' }) @IsOptional() @Transform(({ value }) => parseQueryIdList(value)) @IsArray() @IsUUID('4', { each: true }) customerIds?: string[]; @ApiPropertyOptional({ format: 'uuid' }) @IsOptional() @IsUUID('4') salesOrderId?: string; @ApiPropertyOptional() @IsOptional() @IsString() search?: string; } export class PackingSlipDto { @ApiProperty({ format: 'uuid' }) id!: string; @ApiProperty() code!: string; @ApiPropertyOptional({ type: CodeRelationDto, nullable: true }) salesOrder!: CodeRelationDto | null; @ApiProperty() date!: number; @ApiProperty({ type: DefaultRelationDto, nullable: true }) customer!: DefaultRelationDto | null; @ApiProperty() address!: string; @ApiPropertyOptional({ nullable: true }) latitude!: number | null; @ApiPropertyOptional({ nullable: true }) longitude!: number | null; @ApiPropertyOptional({ nullable: true }) notes!: string | null; @ApiProperty({ enum: PACKING_SLIP_STATUSES }) status!: string; @ApiProperty() createdAt!: number; @ApiProperty() updatedAt!: number; @ApiProperty({ type: UserRelationDto }) createdBy!: UserRelationDto; @ApiProperty({ type: UserRelationDto }) updatedBy!: UserRelationDto; }