Files
edr-platform/apps/edr-freight-api/src/modules/bookings/dto/wagon-cancellation.dto.ts
Marshal d5a5085d6d feat: enhance booking and audit log functionalities
- Implemented read-only locking for customer-requested container sizes and billing currency in the GlCreateBookingForm component.
- Added functionality to lock partner quantities based on shipment requests in the ConsolidationPartnerPanel.
- Introduced a new Leave action in the LogPassYardWorkModal to unassign bookings from trains.
- Enhanced the AuditLogsPage to support filtering by action and added a Go button for direct navigation to entity detail pages.
- Updated WagonCancellationsPage to handle odd-20ft credits requiring partner selection during rebooking.
- Improved TrainScheduleV2DetailPage to allow manual loading of cargo and display warnings for unassigned bookings.
- Added a new reference field to the audit logs for better searchability and tracking of actions.
- Created a migration to add the reference column to the audit logs table and established an index for efficient querying.
- Defined a registry for audit reference sources to streamline the retrieval of human identifiers for various entities.
2026-08-24 23:49:20 +00:00

172 lines
4.2 KiB
TypeScript

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
ArrayNotEmpty,
IsArray,
IsDateString,
IsIn,
IsInt,
IsNumber,
IsOptional,
IsString,
IsUUID,
MaxLength,
Min,
ValidateNested,
} from 'class-validator';
import { WAGON_CANCELLATION_STATUSES } from '../entities/booking-wagon-cancellation.entity';
export class CancelContainerLineDto {
@ApiProperty({ description: 'Container size (ft) as stored on the booking line, e.g. "20", "40"' })
@IsString()
containerSize!: string;
@ApiProperty({ description: 'How many units of this size to cancel' })
@IsInt()
@Min(1)
quantity!: number;
}
export class RequestWagonCancellationDto {
@ApiPropertyOptional({
description:
'Cancel SPECIFIC allocated wagons: wagon_booking_allocation ids from GET /bookings/:id/wagons. ' +
'When set, wagons/containers are derived from the selected wagons and the other fields are ignored.',
type: [String],
})
@IsOptional()
@IsArray()
@ArrayNotEmpty()
@IsUUID('4', { each: true })
wagonAllocationIds?: string[];
@ApiPropertyOptional({
description: 'BULK bookings: number of wagons to cancel (tons derived proportionally)',
})
@IsOptional()
@IsNumber()
@Min(0.5)
wagons?: number;
@ApiPropertyOptional({
description: 'CONTAINER bookings: units to cancel per size (wagons derived per size)',
type: [CancelContainerLineDto],
})
@IsOptional()
@IsArray()
@ArrayNotEmpty()
@ValidateNested({ each: true })
@Type(() => CancelContainerLineDto)
containers?: CancelContainerLineDto[];
@ApiPropertyOptional({ description: 'Customer reason for the cancellation' })
@IsOptional()
@IsString()
@MaxLength(1000)
reason?: string;
}
export class RebookUnitDto {
@ApiProperty({ description: 'Container number for the rebooked unit' })
@IsString()
@MaxLength(64)
containerNumber!: string;
@ApiPropertyOptional({ description: 'Seal number' })
@IsOptional()
@IsString()
@MaxLength(64)
sealNumber?: string;
@ApiPropertyOptional({ description: 'VGM (tons) of the unit' })
@IsOptional()
@IsNumber()
@Min(0)
vgmTons?: number;
}
export class RebookContainerLineDto {
@ApiProperty({ description: 'Container size as stored on the credit, e.g. "20ft"' })
@IsString()
containerSize!: string;
@ApiProperty({
description:
'The rebooked units for this size — count MUST equal the cancelled quantity',
type: [RebookUnitDto],
})
@IsArray()
@ArrayNotEmpty()
@ValidateNested({ each: true })
@Type(() => RebookUnitDto)
units!: RebookUnitDto[];
}
export class RebookCancelledWagonsDto {
@ApiProperty({ description: 'Shipment day the credit is rebooked onto (ISO date)' })
@IsDateString()
scheduledDate!: string;
@ApiPropertyOptional({
description:
'Optional unit overrides: container number / seal / VGM may change, but ' +
'sizes and quantities must match the cancelled booking exactly. Sizes ' +
'omitted here keep their original units.',
type: [RebookContainerLineDto],
})
@IsOptional()
@IsArray()
@ArrayNotEmpty()
@ValidateNested({ each: true })
@Type(() => RebookContainerLineDto)
containers?: RebookContainerLineDto[];
@ApiPropertyOptional({
description:
'Required when the credit carries an odd 20ft count: the odd-20ft booking ' +
'GL picked to share the rebooked wagon (see the rebook-partners endpoint).',
})
@IsOptional()
@IsUUID()
partnerBookingId?: string;
}
export class FilterWagonCancellationsDto {
@ApiPropertyOptional({ enum: WAGON_CANCELLATION_STATUSES, isArray: true })
@IsOptional()
@IsArray()
@IsIn(WAGON_CANCELLATION_STATUSES as readonly string[], { each: true })
statuses?: string[];
@ApiPropertyOptional({ description: 'Booking reference / company name search' })
@IsOptional()
@IsString()
@MaxLength(120)
search?: string;
@ApiPropertyOptional()
@IsOptional()
@IsDateString()
from?: string;
@ApiPropertyOptional()
@IsOptional()
@IsDateString()
to?: string;
@ApiPropertyOptional({ default: 1 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number;
@ApiPropertyOptional({ default: 10 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
pageSize?: number;
}