update booking logic for train schedules and customs contracts

This commit is contained in:
Marshal
2026-07-10 12:13:08 +00:00
parent 10111c9e01
commit a58650bdfb
4 changed files with 39 additions and 40 deletions

View File

@@ -241,11 +241,10 @@ export default function ContractDetailPage() {
});
// Intercity contracts are never window-gated: the shipment rides a passing
// import/export train that staff assign later, so booking is always open.
// GENERAL contracts are also not gated at creation — the booking enters the
// per-booking clearance gate first and picks its shipment day at proceed time.
// ONE_TIME and GENERAL contracts are both gated — booking is only possible
// while a window on the contract's lane is open.
const bookingWindowOpen =
contract?.tradeDirection === "DOMESTIC" ||
contract?.contractKind === "GENERAL" ||
hasOpenWindow(bookingWindows);
// Draw-down capacity per cargo line (GENERAL contracts only). The backend

View File

@@ -146,14 +146,12 @@ export default function NewShipmentPage() {
// Coarse gate: if the customer deep-links here while no booking window is
// open, show the same closed-state notice as the contract page instead of the
// form. Still allowed the moment any window isOpenNow. Intercity contracts
// are never window-gated — the shipment rides a passing train that staff
// pick at finalize time, so booking is always open. GENERAL contracts are not
// gated at creation either: the booking enters per-booking clearance first
// and picks its shipment day at proceed time.
// form. Still allowed the moment any window isOpenNow. Applies to ONE_TIME
// and GENERAL alike. Intercity contracts are never window-gated — the
// shipment rides a passing train that staff pick at finalize time, so
// booking is always open.
if (
contract.tradeDirection !== "DOMESTIC" &&
contract.contractKind !== "GENERAL" &&
!hasOpenWindow(bookingWindows)
) {
return (

View File

@@ -61,11 +61,16 @@ export default function NewShipmentRequestPage() {
const isContainer = contract.freightType === "CONTAINER";
const route = contract.routes?.[0];
// GENERAL customs contracts: GL schedules the shipment during clearance —
// the customer only states the quantity, never picks a date.
const hasCustoms =
contract.contractKind === "GENERAL" &&
(contract.serviceType?.includesCustoms ?? contract.customsClearingEnabled);
const handleSubmit = () => {
const dto: Freight.CreateBookingRequestDto = {
contractRouteId: route?.id,
scheduledDate: scheduledDate || undefined,
scheduledDate: hasCustoms ? undefined : scheduledDate || undefined,
notes: notes.trim() || undefined,
};
@@ -104,19 +109,26 @@ export default function NewShipmentRequestPage() {
<Paper withBorder radius="lg" p="xl" style={{ borderColor: BORDER }}>
<Stack gap="md">
<DatePickerInput
label="Preferred shipment date"
placeholder="Pick a date"
leftSection={<CalendarDays size={16} />}
minDate={new Date().toISOString().slice(0, 10)}
value={scheduledDate || null}
onChange={(v) => setScheduledDate(v ?? "")}
radius="md"
popoverProps={{ withinPortal: true }}
/>
{!hasCustoms && (
<DatePickerInput
label="Preferred shipment date"
placeholder="Pick a date"
leftSection={<CalendarDays size={16} />}
minDate={new Date().toISOString().slice(0, 10)}
value={scheduledDate || null}
onChange={(v) => setScheduledDate(v ?? "")}
radius="md"
popoverProps={{ withinPortal: true }}
/>
)}
<NumberInput
label={isContainer ? "Number of containers" : "Cargo weight (tons)"}
description={
hasCustoms
? "Global Logistics schedules the shipment date during customs clearance — you only state the quantity."
: undefined
}
value={quantity}
onChange={setQuantity}
min={1}