add draft declaration

This commit is contained in:
Marshal
2026-08-23 19:02:33 +00:00
parent 58f88b74f8
commit 31fa313c66
10 changed files with 201 additions and 35 deletions

View File

@@ -301,8 +301,9 @@ export default function GlCreateBookingForm() {
const [trainScheduleId, setTrainScheduleId] = useState("");
const [contractRouteId, setContractRouteId] = useState<string | null>(null);
const [notes, setNotes] = useState("");
// ponytail: ETB-only for now — widen back to "USD" | "ETB" when multi-currency billing returns.
const [paymentCurrency, setPaymentCurrency] = useState<"USD" | "ETB">("ETB");
// IMPORT bookings pick ETB or USD — starts empty so the choice is
// deliberate (required before pricing). Everything else is forced to ETB.
const [paymentCurrency, setPaymentCurrency] = useState<"USD" | "ETB" | "">("");
// What the containers carry — captured per booking (moved off the contract).
const [cargoDescription, setCargoDescription] = useState("");
const [containerLines, setContainerLines] = useState<ContainerLineDraft[]>([]);
@@ -1022,8 +1023,21 @@ export default function GlCreateBookingForm() {
partnerCargoDescription,
]);
// Only IMPORT actually chooses — the rest bill ETB regardless of the state.
const effectiveCurrency: "USD" | "ETB" =
isImport && paymentCurrency ? paymentCurrency : "ETB";
const currencyError =
isImport && !paymentCurrency
? "Select the billing currency for this booking."
: undefined;
const formValid =
cargoValid && !oddBlocksSubmit && !dateError && !routeError && !partnerError;
cargoValid &&
!oddBlocksSubmit &&
!dateError &&
!routeError &&
!partnerError &&
!currencyError;
/** The create-booking DTO from the current form state — shared by the
* authoritative price preview and the actual submit so what GL confirms is
@@ -1033,7 +1047,7 @@ export default function GlCreateBookingForm() {
const payload: Freight.CreateBookingUnderContractDto = {
...(contractRouteId ? { contractRouteId } : {}),
paymentCurrency,
paymentCurrency: effectiveCurrency,
// Intercity bookings carry no date — staff assign a passing train later.
...(scheduledDate
? { scheduledDate: new Date(scheduledDate).toISOString() }
@@ -1100,7 +1114,7 @@ export default function GlCreateBookingForm() {
if (!partner || !consolidationActive) return null;
const payload: Freight.CreateBookingUnderContractDto = {
paymentCurrency,
paymentCurrency: effectiveCurrency,
...(scheduledDate
? { scheduledDate: new Date(scheduledDate).toISOString() }
: {}),
@@ -2138,10 +2152,11 @@ export default function GlCreateBookingForm() {
: "Shipments are invoiced in ETB."}
</Text>
<CurrencySelector
value={isIntercity ? "ETB" : paymentCurrency}
value={isImport ? paymentCurrency : "ETB"}
onChange={setPaymentCurrency}
disabled={isIntercity}
disabled={!isImport}
allowUsd={isImport}
error={currencyError}
/>
</Box>

View File

@@ -1835,13 +1835,59 @@ function DraftDeclarationStep({
);
const [currency, setCurrency] = useState(clearance.draftDeclaration?.currency ?? "ETB");
const [loading, setLoading] = useState(false);
// OFF = don't send the customer a draft: the step is skipped, staff file the
// real declaration directly, and duty & tax passes by default with it.
const [sendDraft, setSendDraft] = useState(true);
const changeRequest = clearance.draftDeclarationChangeRequest;
const existingFiles = clearance.draftDeclaration?.files ?? [];
const replaceMode = existingFiles.length > 0;
if (!sendDraft && !replaceMode) {
return (
<Stack gap="md">
<Switch
label="Send the customer a draft declaration"
description="Off: skip this step — upload the customs declaration directly. Duty & tax is passed by default."
checked={sendDraft}
onChange={(e) => setSendDraft(e.currentTarget.checked)}
/>
<Button
color="edr-green"
variant="light"
loading={loading}
fullWidth
onClick={async () => {
setLoading(true);
try {
await bookingsService.skipDraftDeclaration(bookingId);
toast.success(
"Draft declaration skipped — upload the customs declaration next. Duty & tax passed.",
);
onChanged?.();
} catch (e) {
toast.error(e instanceof Error ? e.message : "Failed");
} finally {
setLoading(false);
}
}}
>
Skip draft declaration
</Button>
</Stack>
);
}
return (
<Stack gap="md">
{!replaceMode ? (
<Switch
label="Send the customer a draft declaration"
description="Off: skip this step — upload the customs declaration directly. Duty & tax is passed by default."
checked={sendDraft}
onChange={(e) => setSendDraft(e.currentTarget.checked)}
/>
) : null}
{/* The customer sent this draft back — their words drive the
correction, so they lead the step. */}
{changeRequest ? (