import { BaseEntity } from '@edr/api-common'; import { Column, Entity } from 'typeorm'; /** * numeric comes back from pg as a string. Every value here is arithmetic in a * report expression, so convert on read rather than making each caller do it. */ const asNumber = { to: (value: number) => value, from: (value: string | null) => (value === null ? null : Number(value)), }; /** * Single-row table holding the railway's operating standards — the numbers the * operations reports measure actual performance against. Same single-row shape * as `logo_settings` and `exchange_settings`; the app never inserts a second row. * * These live in the database rather than in a constants file because the * business treats them as tunable (the corridor standard is explicitly * described as "flexible"), and a planner must be able to change one without a * deployment. */ @Entity({ schema: 'freight', name: 'operations_standards' }) export class OperationsStandard extends BaseEntity { /** Standard time a train may stand at an Ethiopian station, in hours. */ @Column({ name: 'station_standard_hours_ethiopia', type: 'numeric', precision: 6, scale: 2, default: 10, transformer: asNumber, }) stationStandardHoursEthiopia!: number; /** Standard time a train may stand at a Djibouti station, in hours. */ @Column({ name: 'station_standard_hours_djibouti', type: 'numeric', precision: 6, scale: 2, default: 13, transformer: asNumber, }) stationStandardHoursDjibouti!: number; /** Container turn-around cycle: 10 + 21 + 13 + 21. */ @Column({ name: 'cycle_standard_hours_container', type: 'numeric', precision: 6, scale: 2, default: 65, transformer: asNumber, }) cycleStandardHoursContainer!: number; /** Bulk cycle via DMP: 13 + 21 + 33 + 21. */ @Column({ name: 'cycle_standard_hours_bulk_dmp', type: 'numeric', precision: 6, scale: 2, default: 88, transformer: asNumber, }) cycleStandardHoursBulkDmp!: number; /** Bulk cycle via Negad freight yard: 13 + 21 + 41 + 21. */ @Column({ name: 'cycle_standard_hours_bulk_nagad', type: 'numeric', precision: 6, scale: 2, default: 96, transformer: asNumber, }) cycleStandardHoursBulkNagad!: number; /** Bulk cycle via BCC: 13 + 21 + 41 + 21. */ @Column({ name: 'cycle_standard_hours_bulk_bcc', type: 'numeric', precision: 6, scale: 2, default: 96, transformer: asNumber, }) cycleStandardHoursBulkBcc!: number; /** * Standard running time for one corridor leg, used when the yard pair has no * `yard_distances.standard_hours` of its own. */ @Column({ name: 'default_leg_standard_hours', type: 'numeric', precision: 6, scale: 2, default: 21, transformer: asNumber, }) defaultLegStandardHours!: number; /** Grace on top of the leg standard before a train counts as delayed. */ @Column({ name: 'delay_tolerance_minutes', type: 'int', default: 30 }) delayToleranceMinutes!: number; /** Charged tonnage per laden 20ft container. */ @Column({ name: 'charged_tons_full_20ft', type: 'numeric', precision: 8, scale: 2, default: 20, transformer: asNumber, }) chargedTonsFull20ft!: number; /** Charged tonnage per laden 40ft container. */ @Column({ name: 'charged_tons_full_40ft', type: 'numeric', precision: 8, scale: 2, default: 40, transformer: asNumber, }) chargedTonsFull40ft!: number; /** Charged tonnage per empty 20ft container. */ @Column({ name: 'charged_tons_empty_20ft', type: 'numeric', precision: 8, scale: 2, default: 2.24, transformer: asNumber, }) chargedTonsEmpty20ft!: number; /** Charged tonnage per empty 40ft container. */ @Column({ name: 'charged_tons_empty_40ft', type: 'numeric', precision: 8, scale: 2, default: 3.88, transformer: asNumber, }) chargedTonsEmpty40ft!: number; /** Charged tonnage per wagon of steel, fertilizer, rice, sugar, livestock. */ @Column({ name: 'charged_tons_per_wagon_general', type: 'numeric', precision: 8, scale: 2, default: 70, transformer: asNumber, }) chargedTonsPerWagonGeneral!: number; /** Charged tonnage per wagon of vegetables, milk, meat and other perishables. */ @Column({ name: 'charged_tons_per_wagon_perishable', type: 'numeric', precision: 8, scale: 2, default: 38, transformer: asNumber, }) chargedTonsPerWagonPerishable!: number; /** * Wagons in a full trainset when the cargo type has no * `cargo_types.full_trainset_wagons` of its own. */ @Column({ name: 'default_full_trainset_wagons', type: 'int', default: 50 }) defaultFullTrainsetWagons!: number; /** * Standard loading-and-unloading time for a container train's stop, in hours. * * Null until a planner sets it, and deliberately so: the reporting spec names * no handling standard, so an unset value reports no rate rather than judging * a train against a guess. Same for the bulk figure below. */ @Column({ name: 'handling_standard_hours_container', type: 'numeric', precision: 6, scale: 2, nullable: true, transformer: asNumber, }) handlingStandardHoursContainer?: number | null; /** Standard loading-and-unloading time for a bulk train's stop, in hours. */ @Column({ name: 'handling_standard_hours_bulk', type: 'numeric', precision: 6, scale: 2, nullable: true, transformer: asNumber, }) handlingStandardHoursBulk?: number | null; /** IAM user id of the last operator to change a standard. */ @Column({ name: 'updated_by_id', type: 'uuid', nullable: true }) updatedById?: string | null; }