import { Body, Controller, Delete, Get, HttpCode, 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 { AllocationPreviewDto, CreateAllocationRuleDto, UpdateAllocationRuleDto, } from './dto/allocation-rule.dto'; import { CreateFeeRuleDto, UpdateFeeRuleDto } from './dto/fee-rule.dto'; import { AcknowledgeAccrualDto } from './dto/acknowledge-accrual.dto'; import { WarehouseAllocationService } from './warehouse-allocation.service'; import { WarehouseFeeService } from './warehouse-fee.service'; @ApiTags('warehouse-rules') @ApiBearerAuth() @Controller() export class WarehouseRulesController { constructor( private readonly allocationService: WarehouseAllocationService, private readonly feeService: WarehouseFeeService, ) {} // ── Allocation rules ─────────────────────────────────────────────────────── @Get('warehouse-allocation-rules') @BookingStaff(FREIGHT_PERMS.warehouseAllocationRules.view) @ApiOperation({ summary: 'List warehouse allocation rules' }) listAllocationRules() { return this.allocationService.listRules(); } @Post('warehouse-allocation-rules') @BookingStaff(FREIGHT_PERMS.warehouseAllocationRules.create) @ApiOperation({ summary: 'Create a warehouse allocation rule' }) createAllocationRule(@Body() dto: CreateAllocationRuleDto) { return this.allocationService.createRule(dto); } @Patch('warehouse-allocation-rules/:id') @BookingStaff(FREIGHT_PERMS.warehouseAllocationRules.update) @ApiOperation({ summary: 'Update a warehouse allocation rule' }) updateAllocationRule(@Param('id', ParseUUIDPipe) id: string, @Body() dto: UpdateAllocationRuleDto) { return this.allocationService.updateRule(id, dto); } @Delete('warehouse-allocation-rules/:id') @HttpCode(204) @BookingStaff(FREIGHT_PERMS.warehouseAllocationRules.delete) @ApiOperation({ summary: 'Delete a warehouse allocation rule' }) deleteAllocationRule(@Param('id', ParseUUIDPipe) id: string) { return this.allocationService.deleteRule(id); } @Post('warehouse-allocation/preview') @BookingStaff(FREIGHT_PERMS.warehouseAllocationRules.view) @ApiOperation({ summary: 'Preview the yard/warehouse/zone a booking would be allocated to' }) previewAllocation(@Body() dto: AllocationPreviewDto) { return this.allocationService.resolveLocation(dto); } // ── Fee rules ──────────────────────────────────────────────────────────────── @Get('warehouse-fee-rules') @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.view) @ApiOperation({ summary: 'List storage / demurrage fee rules' }) listFeeRules() { return this.feeService.listRules(); } @Post('warehouse-fee-rules') @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.create) @ApiOperation({ summary: 'Create a storage / demurrage fee rule' }) createFeeRule(@Body() dto: CreateFeeRuleDto) { return this.feeService.createRule(dto); } @Patch('warehouse-fee-rules/:id') @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.update) @ApiOperation({ summary: 'Update a fee rule' }) updateFeeRule(@Param('id', ParseUUIDPipe) id: string, @Body() dto: UpdateFeeRuleDto) { return this.feeService.updateRule(id, dto); } @Delete('warehouse-fee-rules/:id') @HttpCode(204) @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.delete) @ApiOperation({ summary: 'Delete a fee rule' }) deleteFeeRule(@Param('id', ParseUUIDPipe) id: string) { return this.feeService.deleteRule(id); } @Get('warehouse-fees/accrual-dashboard') @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.view) @ApiOperation({ summary: 'Live per-item fee accrual (storage/demurrage) with alerts' }) accrualDashboard(@Query('billingCurrency') billingCurrency?: string) { return this.feeService.accrualDashboard(billingCurrency); } @Get('warehouse-fees/on-time-dispatch') @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.view) @ApiOperation({ summary: 'On-time dispatch rate — items that left before storage free-days expired' }) onTimeDispatch() { return this.feeService.onTimeDispatchStats(); } @Post('warehouse-fees/accrual/:inventoryId/acknowledge') @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.update) @ApiOperation({ summary: 'Acknowledge / snooze an item fee-accrual alert' }) acknowledgeAccrual( @Param('inventoryId', ParseUUIDPipe) inventoryId: string, @Body() dto: AcknowledgeAccrualDto, ) { return this.feeService.acknowledgeAccrual(inventoryId, { snoozeDays: dto.snoozeDays, note: dto.note, }); } @Delete('warehouse-fees/accrual/:inventoryId/acknowledge') @HttpCode(204) @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.update) @ApiOperation({ summary: 'Remove an accrual acknowledgement (re-surface for alerts)' }) unacknowledgeAccrual(@Param('inventoryId', ParseUUIDPipe) inventoryId: string) { return this.feeService.unacknowledgeAccrual(inventoryId); } @Get('warehouse-inventory/:id/fee-preview') @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.view) @ApiOperation({ summary: 'Preview demurrage + storage fees for an inventory item' }) feePreview( @Param('id', ParseUUIDPipe) id: string, @Query('billingCurrency') billingCurrency?: string, ) { return this.feeService.previewForInventory(id, billingCurrency); } @Get('last-mile/:id/truck-detention-preview') @BookingStaff(FREIGHT_PERMS.warehouseFeeRules.view) @ApiOperation({ summary: 'Preview truck detention for a last-mile leg (per truck per day after grace)' }) truckDetentionPreview( @Param('id', ParseUUIDPipe) id: string, @Query('billingCurrency') billingCurrency?: string, ) { return this.feeService.previewTruckDetention(id, billingCurrency); } }