diff --git a/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx b/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx index 5502886d3..67e84a09f 100644 --- a/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx +++ b/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx @@ -348,6 +348,9 @@ export default function GlCreateBookingForm() { // Intercity shipments ride a passing import/export train staff pick at // finalize time — no shipment day is chosen and no window gate applies. const isIntercity = contract?.tradeDirection === "DOMESTIC"; + // USD billing is offered on import traffic only — export and domestic + // shipments are always invoiced in ETB. + const isImport = contract?.tradeDirection === "IMPORT"; // ONE_TIME split-remainder mode: a previous booking on this contract was // split on train capacity, so the capacity endpoint reports the outstanding @@ -1757,12 +1760,15 @@ export default function GlCreateBookingForm() { Billing currency - Shipments are invoiced in ETB. + {isImport + ? "Import shipments may be invoiced in ETB or USD. USD is paid by bank transfer, not online." + : "Shipments are invoiced in ETB."} diff --git a/apps/edr-freight-web/portal/src/pages/bookings/EditBookingPage.tsx b/apps/edr-freight-web/portal/src/pages/bookings/EditBookingPage.tsx index 85709f1d4..26a59b405 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/EditBookingPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/EditBookingPage.tsx @@ -683,7 +683,11 @@ export default function EditBookingPage() { /> - + {/* USD billing is import-only; export/intercity stay ETB. */} + {(selectedService?.includesFirstMile || selectedService?.includesLastMile || diff --git a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/payment-currency-field.tsx b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/payment-currency-field.tsx index d874fb783..fb91b515b 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/payment-currency-field.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/payment-currency-field.tsx @@ -19,14 +19,25 @@ const CURRENCY_ICONS: Record< export function PaymentCurrencyField({ control, + allowUsd = false, }: { control: Control; + /** + * Offer USD alongside ETB. Import shipments only — export and domestic + * traffic is always invoiced in ETB. + */ + allowUsd?: boolean; }) { + const options = allowUsd + ? PAYMENT_CURRENCY_OPTIONS + : PAYMENT_CURRENCY_OPTIONS.filter((o) => o.value !== "USD"); return ( Payment currency - Choose the currency for your freight quote and invoices. + {allowUsd + ? "Choose the currency for your freight quote and invoices. USD is paid by bank transfer, not online." + : "Choose the currency for your freight quote and invoices."} - {PAYMENT_CURRENCY_OPTIONS.map((option) => { + {options.map((option) => { const Icon = CURRENCY_ICONS[option.value].icon; const selected = field.value === option.value; return ( @@ -97,10 +108,7 @@ export function PaymentCurrencyField({ {/* Description for the active currency, kept subtle. */} - { - PAYMENT_CURRENCY_OPTIONS.find((o) => o.value === field.value) - ?.description - } + {options.find((o) => o.value === field.value)?.description} diff --git a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/schema.ts b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/schema.ts index 08bb5236d..27b669d72 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/schema.ts +++ b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/schema.ts @@ -81,11 +81,16 @@ export const PAYMENT_CURRENCY_OPTIONS: Array<{ label: string; description: string; }> = [ - // ponytail: ETB-only for now — re-add the USD option when multi-currency billing returns. { value: "ETB", label: "ETB", - description: "Ethiopian Birr — local pricing and invoicing.", + description: "Ethiopian Birr — pay online through the payment gateway.", + }, + // Import shipments only; the field is hidden on export/domestic traffic. + { + value: "USD", + label: "USD", + description: "US Dollar — paid by bank transfer, slip sent to Finance.", }, ]; diff --git a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step2-service-type.tsx b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step2-service-type.tsx index 47e80e7e2..2c51cbb62 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step2-service-type.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step2-service-type.tsx @@ -133,7 +133,11 @@ export function Step2ServiceType({ )} /> - + {/* USD billing is import-only; export/intercity stay ETB. */} + {showServiceSections && ( diff --git a/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx b/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx index c65ccea2a..a5b29a299 100644 --- a/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx @@ -1215,6 +1215,9 @@ function ScheduleStep({ })(); const isIntercity = contract.tradeDirection === "DOMESTIC"; + // USD billing is offered on import traffic only — export and domestic + // shipments are always invoiced in ETB. + const isImport = contract.tradeDirection === "IMPORT"; const { data: availableDays, isLoading } = useQuery({ ...api.bookings.getAvailableDaysForCargo.queryOptions({ input: @@ -1310,12 +1313,15 @@ function ScheduleStep({ Billing currency * - Shipments are invoiced in ETB. + {isImport + ? "Import shipments may be invoiced in ETB or USD. USD is paid by bank transfer, not online." + : "Shipments are invoiced in ETB."} field.onChange(v)} error={fieldState.error?.message} + allowUsd={isImport} /> )} diff --git a/packages/ui-common/src/components/CurrencySelector/CurrencySelector.tsx b/packages/ui-common/src/components/CurrencySelector/CurrencySelector.tsx index 5ce3d1272..f25a7b015 100644 --- a/packages/ui-common/src/components/CurrencySelector/CurrencySelector.tsx +++ b/packages/ui-common/src/components/CurrencySelector/CurrencySelector.tsx @@ -8,17 +8,27 @@ export interface CurrencySelectorProps { disabled?: boolean; /** Validation error shown under the cards. */ error?: string; + /** + * Offer USD alongside ETB. Import shipments only — export and domestic + * traffic is invoiced in ETB, so the option stays hidden everywhere else. + * USD is settled by bank transfer, never through the online gateway. + */ + allowUsd?: boolean; } -// ponytail: ETB-only for now — restore the USD entry when multi-currency billing returns. -const OPTIONS = [ - { - code: "ETB", - symbol: "Br", - name: "Ethiopian Birr", - hint: "All shipments are invoiced in ETB", - }, -] as const; +const ETB_OPTION = { + code: "ETB", + symbol: "Br", + name: "Ethiopian Birr", + hint: "Pay online through the payment gateway", +} as const; + +const USD_OPTION = { + code: "USD", + symbol: "$", + name: "US Dollar", + hint: "Paid by bank transfer — send the slip to Finance", +} as const; /** * Card-style USD/ETB billing-currency picker. Renders unselected when `value` @@ -29,7 +39,9 @@ export function CurrencySelector({ onChange, disabled = false, error, + allowUsd = false, }: CurrencySelectorProps) { + const options = allowUsd ? [ETB_OPTION, USD_OPTION] : [ETB_OPTION]; return ( - {OPTIONS.map((o) => { + {options.map((o) => { const selected = value === o.code; return (