Refactor wagon type handling and container wagon calculations

- Removed maxWagonsPerTrain from WagonType entity and related DTOs.
- Updated containerWagonsForLines function to calculate required wagons based on container lines more accurately.
- Added unit tests for containerWagonsForLines to ensure correct calculations.
- Adjusted related services and scripts to reflect the removal of maxWagonsPerTrain.
- Enhanced booking and contract components to use new status labels for better user experience.
- Implemented validation for unique container numbers in shipment forms.
This commit is contained in:
Marshal
2026-07-09 03:36:35 +00:00
parent addee34d5d
commit ddddcfb71f
39 changed files with 753 additions and 121 deletions

View File

@@ -7,6 +7,10 @@ export type WagonTypeDimensions = {
export type LocomotiveLimits = {
maxPullWeightTons: number;
maxTrainLengthMeters: number;
/** Allowed deviation above maxPullWeightTons before scheduling blocks the train. */
overageToleranceTons?: number | null;
/** Allowed deviation above maxTrainLengthMeters before scheduling blocks the train. */
overageToleranceMeters?: number | null;
};
export type DerivedTrainCapacity = {
@@ -29,14 +33,19 @@ export function deriveTrainCapacityFromLocomotive(
wagonTypes: WagonTypeDimensions[],
ruleCaps?: { maxTrainWeightTons?: number; maxTrainLengthMeters?: number },
): DerivedTrainCapacity {
const maxWeightTons = Math.min(
Number(locomotive.maxPullWeightTons) || Infinity,
ruleCaps?.maxTrainWeightTons ?? Infinity,
);
const maxLengthMeters = Math.min(
Number(locomotive.maxTrainLengthMeters) || Infinity,
ruleCaps?.maxTrainLengthMeters ?? Infinity,
);
const overageTons = Number(locomotive.overageToleranceTons) || 0;
const overageMeters = Number(locomotive.overageToleranceMeters) || 0;
const maxWeightTons =
Math.min(
Number(locomotive.maxPullWeightTons) || Infinity,
ruleCaps?.maxTrainWeightTons ?? Infinity,
) + overageTons;
const maxLengthMeters =
Math.min(
Number(locomotive.maxTrainLengthMeters) || Infinity,
ruleCaps?.maxTrainLengthMeters ?? Infinity,
) + overageMeters;
const types =
wagonTypes.length > 0
@@ -75,7 +84,10 @@ export const MAX_FALLBACK_LENGTH = 760;
* across all assigned locomotives. Returns null when no locomotives are given.
*/
export function minLocomotiveLimits(
locomotives: Array<Pick<LocomotiveLimits, 'maxPullWeightTons' | 'maxTrainLengthMeters'>>,
locomotives: Array<
Pick<LocomotiveLimits, 'maxPullWeightTons' | 'maxTrainLengthMeters'> &
Partial<Pick<LocomotiveLimits, 'overageToleranceTons' | 'overageToleranceMeters'>>
>,
): LocomotiveLimits | null {
if (!locomotives.length) return null;
return {
@@ -85,6 +97,13 @@ export function minLocomotiveLimits(
maxTrainLengthMeters: Math.min(
...locomotives.map((l) => Number(l.maxTrainLengthMeters) || Infinity),
),
// Weakest locomotive's tolerance governs the set, same as its caps.
overageToleranceTons: Math.min(
...locomotives.map((l) => Number(l.overageToleranceTons) || 0),
),
overageToleranceMeters: Math.min(
...locomotives.map((l) => Number(l.overageToleranceMeters) || 0),
),
};
}