mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 23:35:42 +00:00
feat: enhance train scheduling and contract management features
- Added StationWorkControls to manage loading/unloading phases in TrainScheduleV2DetailPage. - Implemented API endpoints for recording station work and managing wagon detach requests. - Updated contract templates to include Ethiopian customs handling options. - Enhanced shipment forms to collect customs clearing agent details for without-customs bookings. - Introduced NUMBER_OF_WAGONS as a unit of measure for bulk cargo, allowing customers to specify wagon counts. - Improved validation for customs clearing agent information in shipment forms. - Updated various components and services to accommodate new features and ensure data integrity.
This commit is contained in:
@@ -130,6 +130,7 @@ interface LineErrors {
|
||||
|
||||
interface BulkErrors {
|
||||
quantity?: string;
|
||||
wagons?: string;
|
||||
hazardous?: string;
|
||||
reefer?: string;
|
||||
}
|
||||
@@ -175,6 +176,8 @@ interface ContainerLineDraft {
|
||||
interface BulkDraft {
|
||||
cargoWeightTons: string;
|
||||
itemCount: string;
|
||||
/** NUMBER_OF_WAGONS cargo only: wagons this shipment needs. */
|
||||
requestedWagons: string;
|
||||
hazardousQuantity: string;
|
||||
reeferQuantity: string;
|
||||
}
|
||||
@@ -203,7 +206,19 @@ function emptyLine(size: string): ContainerLineDraft {
|
||||
|
||||
function bulkUnitOfMeasure(
|
||||
contract: Freight.IContract,
|
||||
): "PER_TON" | "PER_ITEM" {
|
||||
): "PER_TON" | "PER_ITEM" | "NUMBER_OF_WAGONS" {
|
||||
// The cargo type's own configured unit wins; the pricing-line sniff below is
|
||||
// the legacy fallback for contracts loaded without the cargoScope relation.
|
||||
const configured = contract.cargoScope?.find(
|
||||
(scope) => scope.cargoType?.unitOfMeasure,
|
||||
)?.cargoType?.unitOfMeasure;
|
||||
if (
|
||||
configured === "PER_TON" ||
|
||||
configured === "PER_ITEM" ||
|
||||
configured === "NUMBER_OF_WAGONS"
|
||||
) {
|
||||
return configured;
|
||||
}
|
||||
const hasPerItem = contract.pricingBreakdown?.lineItems?.some(
|
||||
(li) => li.unit === "per_item",
|
||||
);
|
||||
@@ -322,6 +337,7 @@ export default function GlCreateBookingForm() {
|
||||
const [bulk, setBulk] = useState<BulkDraft>({
|
||||
cargoWeightTons: "",
|
||||
itemCount: "",
|
||||
requestedWagons: "",
|
||||
hazardousQuantity: "0",
|
||||
reeferQuantity: "0",
|
||||
});
|
||||
@@ -521,16 +537,18 @@ export default function GlCreateBookingForm() {
|
||||
})),
|
||||
);
|
||||
} else if (lines.bulk) {
|
||||
setBulk({
|
||||
setBulk((b) => ({
|
||||
cargoWeightTons:
|
||||
lines.bulk.cargoWeightTons != null
|
||||
? String(lines.bulk.cargoWeightTons)
|
||||
lines.bulk!.cargoWeightTons != null
|
||||
? String(lines.bulk!.cargoWeightTons)
|
||||
: "",
|
||||
itemCount:
|
||||
lines.bulk.itemCount != null ? String(lines.bulk.itemCount) : "",
|
||||
hazardousQuantity: String(lines.bulk.hazardousQuantity ?? 0),
|
||||
lines.bulk!.itemCount != null ? String(lines.bulk!.itemCount) : "",
|
||||
// The request never carries a wagon count — GL enters it here.
|
||||
requestedWagons: b.requestedWagons,
|
||||
hazardousQuantity: String(lines.bulk!.hazardousQuantity ?? 0),
|
||||
reeferQuantity: "0",
|
||||
});
|
||||
}));
|
||||
}
|
||||
if (bookingRequest.contractRouteId)
|
||||
setContractRouteId(bookingRequest.contractRouteId);
|
||||
@@ -618,6 +636,7 @@ export default function GlCreateBookingForm() {
|
||||
returnQuantity: Number(l.returnQuantity || 0),
|
||||
})),
|
||||
bulkQuantity: Number(bulk.cargoWeightTons || bulk.itemCount || 0),
|
||||
bulkRequestedWagons: Number(bulk.requestedWagons || 0),
|
||||
bulkHazardousQuantity: Number(bulk.hazardousQuantity || 0),
|
||||
bulkReeferQuantity: Number(bulk.reeferQuantity || 0),
|
||||
}),
|
||||
@@ -979,6 +998,12 @@ export default function GlCreateBookingForm() {
|
||||
if (Number.isNaN(qty) || qty <= 0) {
|
||||
errs.quantity = "Enter a quantity greater than 0.";
|
||||
}
|
||||
if (bulkUom === "NUMBER_OF_WAGONS") {
|
||||
const wagons = Number(bulk.requestedWagons || 0);
|
||||
if (!Number.isInteger(wagons) || wagons < 1) {
|
||||
errs.wagons = "Enter the number of wagons needed (at least 1).";
|
||||
}
|
||||
}
|
||||
const h = Number(bulk.hazardousQuantity || 0);
|
||||
if (Number.isNaN(h) || h < 0) {
|
||||
errs.hazardous = "Enter a valid hazardous quantity.";
|
||||
@@ -1017,7 +1042,10 @@ export default function GlCreateBookingForm() {
|
||||
line.every((e) => !e.containerNumber && !e.vgmTons),
|
||||
) &&
|
||||
!cargoDescriptionError
|
||||
: !bulkErrors.quantity && !bulkErrors.hazardous && !bulkErrors.reefer;
|
||||
: !bulkErrors.quantity &&
|
||||
!bulkErrors.wagons &&
|
||||
!bulkErrors.hazardous &&
|
||||
!bulkErrors.reefer;
|
||||
|
||||
// COMPLETION never blocks on an odd 20ft total: a customs instance can share
|
||||
// the wagon via the manual pair (consolidationActive), and anything else is
|
||||
@@ -1152,6 +1180,9 @@ export default function GlCreateBookingForm() {
|
||||
reeferQuantity: Number(bulk.reeferQuantity || 0) || undefined,
|
||||
},
|
||||
];
|
||||
if (bulkUom === "NUMBER_OF_WAGONS" && bulk.requestedWagons !== "") {
|
||||
payload.requestedWagons = Number(bulk.requestedWagons);
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
@@ -2049,6 +2080,27 @@ export default function GlCreateBookingForm() {
|
||||
radius={10}
|
||||
styles={fieldStyles}
|
||||
/>
|
||||
{bulkUom === "NUMBER_OF_WAGONS" && (
|
||||
<TextInput
|
||||
type="number"
|
||||
onKeyDown={blockNegative}
|
||||
label="Number of wagons needed"
|
||||
placeholder="e.g. 40"
|
||||
description="The cargo weight is spread evenly across these wagons; a per-wagon rate bills this count."
|
||||
min={1}
|
||||
step={1}
|
||||
value={bulk.requestedWagons}
|
||||
error={showErrors ? bulkErrors.wagons : undefined}
|
||||
onChange={(e) =>
|
||||
setBulk((b) => ({
|
||||
...b,
|
||||
requestedWagons: e.currentTarget.value,
|
||||
}))
|
||||
}
|
||||
radius={10}
|
||||
styles={fieldStyles}
|
||||
/>
|
||||
)}
|
||||
{contract.isHazardous && (
|
||||
<TextInput
|
||||
type="number"
|
||||
|
||||
Reference in New Issue
Block a user