import { IsString, IsOptional, IsArray, IsInt, Min, IsUUID, ValidateNested, ArrayMaxSize, IsNotEmpty, MaxLength } from 'class-validator'; import { Type } from 'class-transformer'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; export class OpenTabItemDto { @ApiProperty({ example: 'Cola' }) @IsString() @IsNotEmpty() @MaxLength(200) productName: string; @ApiProperty({ example: 10 }) @IsInt() @Min(0) quantity: number; @ApiProperty({ example: 250 }) @IsInt() @Min(0) unitPriceInCents: number; } export class OpenTabDto { @ApiProperty({ example: 'local-tab-uuid' }) @IsString() @IsNotEmpty() id: string; @ApiProperty({ example: 'Jan' }) @IsString() customerName: string; @ApiProperty({ example: 5 }) @IsInt() @Min(0) version: number; @ApiProperty({ example: 2450 }) @IsInt() @Min(0) totalInCents: number; @ApiProperty({ example: 3 }) @IsInt() @Min(0) itemCount: number; @ApiPropertyOptional({ type: [OpenTabItemDto], description: 'Lines on the tab; omitted by older app versions' }) @IsOptional() @IsArray() @ArrayMaxSize(500) @ValidateNested({ each: true }) @Type(() => OpenTabItemDto) items?: OpenTabItemDto[]; } export class SyncDto { @ApiProperty({ example: 'uuid' }) @IsUUID() deviceId: string; @ApiPropertyOptional({ example: 'cursor-value' }) @IsOptional() @IsString() cursor?: string; @ApiPropertyOptional({ description: 'Where the settlements feed left off; absent on the first sync after pairing' }) @IsOptional() @IsString() @MaxLength(100) settlementsCursor?: string; // The server holds the tabs: only used once, to take over the terminal's tabs on its first sync @ApiProperty({ type: [OpenTabDto] }) @IsArray() @ArrayMaxSize(1000) @ValidateNested({ each: true }) @Type(() => OpenTabDto) openTabs: OpenTabDto[]; // Validated one by one in the service: a bad event is rejected on its own instead of failing the sync @ApiPropertyOptional({ description: 'TabEventDto[]: what happened on tabs since the last acknowledged sync' }) @IsOptional() @IsArray() @ArrayMaxSize(1000) events?: unknown[]; // Validated one by one in the service, like events @ApiPropertyOptional({ description: "ReportedProductDto[]: the terminal's complete product catalog with stock. Omitted by app versions without stock", }) @IsOptional() @IsArray() @ArrayMaxSize(2000) products?: unknown[]; @ApiPropertyOptional({ description: 'StockAdjustmentResultDto[]: outcomes of stock adjustments the terminal received' }) @IsOptional() @IsArray() @ArrayMaxSize(1000) stockAdjustmentResults?: unknown[]; }