mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 13:28:11 +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
103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
import {
|
|
BadRequestException,
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
Get,
|
|
Param,
|
|
ParseUUIDPipe,
|
|
Patch,
|
|
Post,
|
|
Query,
|
|
} from '@nestjs/common';
|
|
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
|
|
|
import { BookingStaff } from '../../common/booking-guards';
|
|
import { FREIGHT_PERMS } from '../../seed/freight-permissions.registry';
|
|
import {
|
|
CreateWarehouseZoneStackDto,
|
|
UpdateWarehouseZoneSlotDto,
|
|
UpdateWarehouseZoneStackDto,
|
|
} from './dto/warehouse-zone-stack.dto';
|
|
import { WarehouseZoneStacksService } from './warehouse-zone-stacks.service';
|
|
|
|
/**
|
|
* Stacks and slots are zone configuration, so they ride the warehouse-zone
|
|
* permissions rather than introducing new keys — a new key needs a matching
|
|
* `iam.permissions` row in every environment or boot fails.
|
|
*/
|
|
@ApiTags('warehouse-zone-stacks')
|
|
@ApiBearerAuth()
|
|
@Controller('warehouse-zone-stacks')
|
|
// Class gate lists every key its routes use: Nest runs class AND method guards.
|
|
@BookingStaff([
|
|
FREIGHT_PERMS.warehouseZones.view,
|
|
FREIGHT_PERMS.warehouseInventory.view,
|
|
FREIGHT_PERMS.warehouseZones.create,
|
|
FREIGHT_PERMS.warehouseZones.update,
|
|
FREIGHT_PERMS.warehouseZones.delete,
|
|
])
|
|
export class WarehouseZoneStacksController {
|
|
constructor(private readonly stacksService: WarehouseZoneStacksService) {}
|
|
|
|
@Get()
|
|
@ApiOperation({ summary: 'List the ground stacks configured in a zone' })
|
|
findByZone(@Query('zoneId', ParseUUIDPipe) zoneId: string) {
|
|
return this.stacksService.findByZone(zoneId);
|
|
}
|
|
|
|
@Post()
|
|
@BookingStaff(FREIGHT_PERMS.warehouseZones.create)
|
|
@ApiOperation({
|
|
summary: 'Create a ground stack',
|
|
description: 'One slot per level is generated automatically, from 1 to maxStackHeight (default 3).',
|
|
})
|
|
create(@Body() dto: CreateWarehouseZoneStackDto, @Query('zoneId') zoneIdQuery?: string) {
|
|
const zoneId = dto.zoneId ?? zoneIdQuery;
|
|
if (!zoneId) {
|
|
throw new BadRequestException('zoneId is required');
|
|
}
|
|
return this.stacksService.create(zoneId, dto);
|
|
}
|
|
|
|
// Declared before ':id' so 'slots' is never swallowed as a stack id.
|
|
@Patch('slots/:slotId')
|
|
@BookingStaff(FREIGHT_PERMS.warehouseZones.update)
|
|
@ApiOperation({
|
|
summary: 'Block, reserve, or reactivate one slot',
|
|
description: 'Occupancy is derived from inventory and cannot be set here.',
|
|
})
|
|
updateSlot(@Param('slotId', ParseUUIDPipe) slotId: string, @Body() dto: UpdateWarehouseZoneSlotDto) {
|
|
return this.stacksService.updateSlot(slotId, dto);
|
|
}
|
|
|
|
@Get(':id')
|
|
@ApiOperation({ summary: 'Get one stack with its slots' })
|
|
findOne(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.stacksService.findById(id);
|
|
}
|
|
|
|
@Get(':id/occupancy')
|
|
@ApiOperation({ summary: 'Level-by-level occupancy of one stack' })
|
|
occupancy(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.stacksService.slotOccupancy(id);
|
|
}
|
|
|
|
@Patch(':id')
|
|
@BookingStaff(FREIGHT_PERMS.warehouseZones.update)
|
|
@ApiOperation({
|
|
summary: 'Update a stack',
|
|
description: 'Raising maxStackHeight adds slots; lowering it trims the empty top levels.',
|
|
})
|
|
update(@Param('id', ParseUUIDPipe) id: string, @Body() dto: UpdateWarehouseZoneStackDto) {
|
|
return this.stacksService.update(id, dto);
|
|
}
|
|
|
|
@Delete(':id')
|
|
@BookingStaff(FREIGHT_PERMS.warehouseZones.delete)
|
|
@ApiOperation({ summary: 'Delete a stack', description: 'Refused while containers still stand in it.' })
|
|
remove(@Param('id', ParseUUIDPipe) id: string) {
|
|
return this.stacksService.remove(id);
|
|
}
|
|
}
|