mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 17:08:18 +00:00
Extends the warehouse hierarchy below zone with ground stacks and vertical slots, so a container's exact position is recorded rather than only its zone. - freight.warehouse_zone_stacks / warehouse_zone_slots, plus nullable stack_id / slot_id on warehouse_inventory (existing rows stay valid) - slot occupancy is derived from inventory status, guarded by a partial unique index, so no exit path has to remember to free a slot - placement service: hierarchy validation, bottom-up stacking rules, accessibility/blocking-container reads, capacity vs slot summaries - stack CRUD with auto-generated slots; reuses warehouse-zone permissions - slot support folded into the existing move()/store() paths - fix: validateLocation now rejects a mismatched warehouse/yard/zone triple - seed:warehouse-layout builds the layout from a JSON config
121 lines
2.8 KiB
TypeScript
121 lines
2.8 KiB
TypeScript
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
import { IsBoolean, IsEnum, IsInt, IsOptional, IsString, IsUUID, Max, MaxLength, Min } from 'class-validator';
|
|
|
|
import {
|
|
DEFAULT_MAX_STACK_HEIGHT,
|
|
WAREHOUSE_ZONE_STACK_STATUSES,
|
|
WarehouseZoneStackStatus,
|
|
} from '../entities/warehouse-zone-stack.entity';
|
|
import {
|
|
WAREHOUSE_ZONE_SLOT_STATUSES,
|
|
WarehouseZoneSlotStatus,
|
|
} from '../entities/warehouse-zone-slot.entity';
|
|
|
|
/** Nobody stacks boxes this high; the cap is here to catch a typo'd 30. */
|
|
const MAX_SUPPORTED_STACK_HEIGHT = 10;
|
|
|
|
export class CreateWarehouseZoneStackDto {
|
|
@ApiPropertyOptional({ format: 'uuid', description: 'Optional — taken from the route param when omitted' })
|
|
@IsOptional()
|
|
@IsUUID()
|
|
zoneId?: string;
|
|
|
|
@ApiProperty({ example: 'ZA-001' })
|
|
@IsString()
|
|
@MaxLength(40)
|
|
code!: string;
|
|
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@IsString()
|
|
@MaxLength(160)
|
|
name?: string;
|
|
|
|
@ApiPropertyOptional({ description: 'Physical row label' })
|
|
@IsOptional()
|
|
@IsString()
|
|
@MaxLength(20)
|
|
row?: string;
|
|
|
|
@ApiPropertyOptional({ description: 'Physical bay label' })
|
|
@IsOptional()
|
|
@IsString()
|
|
@MaxLength(20)
|
|
bay?: string;
|
|
|
|
@ApiPropertyOptional({ description: 'Physical position label' })
|
|
@IsOptional()
|
|
@IsString()
|
|
@MaxLength(20)
|
|
position?: string;
|
|
|
|
@ApiPropertyOptional({
|
|
default: DEFAULT_MAX_STACK_HEIGHT,
|
|
description: 'One slot is generated per level, 1 to this height.',
|
|
})
|
|
@IsOptional()
|
|
@IsInt()
|
|
@Min(1)
|
|
@Max(MAX_SUPPORTED_STACK_HEIGHT)
|
|
maxStackHeight?: number;
|
|
}
|
|
|
|
export class UpdateWarehouseZoneStackDto {
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@IsString()
|
|
@MaxLength(40)
|
|
code?: string;
|
|
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@IsString()
|
|
@MaxLength(160)
|
|
name?: string;
|
|
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@IsString()
|
|
@MaxLength(20)
|
|
row?: string;
|
|
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@IsString()
|
|
@MaxLength(20)
|
|
bay?: string;
|
|
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@IsString()
|
|
@MaxLength(20)
|
|
position?: string;
|
|
|
|
@ApiPropertyOptional({ description: 'Raising it adds slots; lowering it removes the empty top levels.' })
|
|
@IsOptional()
|
|
@IsInt()
|
|
@Min(1)
|
|
@Max(MAX_SUPPORTED_STACK_HEIGHT)
|
|
maxStackHeight?: number;
|
|
|
|
@ApiPropertyOptional({ enum: WAREHOUSE_ZONE_STACK_STATUSES })
|
|
@IsOptional()
|
|
@IsEnum(WAREHOUSE_ZONE_STACK_STATUSES)
|
|
status?: WarehouseZoneStackStatus;
|
|
}
|
|
|
|
export class UpdateWarehouseZoneSlotDto {
|
|
@ApiPropertyOptional({
|
|
enum: WAREHOUSE_ZONE_SLOT_STATUSES,
|
|
description: 'Operator intent only. OCCUPIED is derived from inventory and cannot be set here.',
|
|
})
|
|
@IsOptional()
|
|
@IsEnum(WAREHOUSE_ZONE_SLOT_STATUSES)
|
|
status?: WarehouseZoneSlotStatus;
|
|
|
|
@ApiPropertyOptional()
|
|
@IsOptional()
|
|
@IsBoolean()
|
|
isActive?: boolean;
|
|
}
|