import { BaseEntity } from '@edr/api-common'; import { TrainSetWagonStatus } from '@edr/types'; import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; import { WagonBookingAllocation } from '../../train-schedules/entities/wagon-booking-allocation.entity'; import { Wagon } from '../../wagons/entities/wagon.entity'; import { WagonType } from '../../wagon-types/entities/wagon-type.entity'; import { TrainSet } from './train-set.entity'; export const TRAIN_SET_WAGON_STATUSES = [ TrainSetWagonStatus.Planned, TrainSetWagonStatus.Reserved, TrainSetWagonStatus.Loaded, TrainSetWagonStatus.Departed, ] as const; export type TrainSetWagonStatusType = (typeof TRAIN_SET_WAGON_STATUSES)[number]; @Entity({ schema: 'freight', name: 'train_set_wagons' }) @Index(['trainSetId', 'sequenceNo'], { unique: true }) export class TrainSetWagon extends BaseEntity { @Column({ name: 'train_set_id', type: 'uuid' }) trainSetId!: string; @ManyToOne(() => TrainSet, (trainSet) => trainSet.wagons, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'train_set_id' }) trainSet?: TrainSet; @Column({ name: 'wagon_type_id', type: 'uuid' }) wagonTypeId!: string; @Column({ name: 'physical_wagon_id', type: 'uuid', nullable: true }) physicalWagonId!: string | null; @ManyToOne(() => Wagon, { nullable: true, onDelete: 'SET NULL' }) @JoinColumn({ name: 'physical_wagon_id' }) physicalWagon?: Wagon | null; @ManyToOne(() => WagonType, (wagonType) => wagonType.trainSetWagons) @JoinColumn({ name: 'wagon_type_id' }) wagonType?: WagonType; @Column({ name: 'sequence_no', type: 'int' }) sequenceNo!: number; @Column({ name: 'capacity_tons', type: 'numeric', precision: 10, scale: 3 }) capacityTons!: number; @Column({ name: 'length_meters', type: 'numeric', precision: 10, scale: 3 }) lengthMeters!: number; @Column({ name: 'assigned_weight_tons', type: 'numeric', precision: 10, scale: 3, default: 0 }) assignedWeightTons!: number; @Column({ name: 'status', type: 'varchar', length: 20, default: 'PLANNED' }) status!: string; // ── Leg occupancy (segment corridor bookings) ────────────────────────────── // A slot may occupy only part of the route: it boards (attaches/loads) at // board_yard_id and alights (unloads/detaches) at alight_yard_id. NULL on both // means the slot rides the whole route (legacy full-route bookings). Slots // whose legs don't overlap coexist without consuming each other's capacity. @Column({ name: 'board_yard_id', type: 'uuid', nullable: true }) boardYardId?: string | null; @Column({ name: 'alight_yard_id', type: 'uuid', nullable: true }) alightYardId?: string | null; @OneToMany(() => WagonBookingAllocation, (allocation) => allocation.trainSetWagon) allocations?: WagonBookingAllocation[]; }