Merge pull request #1188 from Tria-plc/freight_feature/usermanagement

add usd for import
This commit is contained in:
marshal
2026-08-08 18:15:23 +03:00
committed by GitHub
7 changed files with 67 additions and 22 deletions

View File

@@ -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
</Text>
<Text size="xs" c="dimmed" mb={8}>
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."}
</Text>
<CurrencySelector
value={isIntercity ? "ETB" : paymentCurrency}
onChange={setPaymentCurrency}
disabled={isIntercity}
allowUsd={isImport}
/>
</Box>

View File

@@ -683,7 +683,11 @@ export default function EditBookingPage() {
/>
</SimpleGrid>
<PaymentCurrencyField control={form.control} />
{/* USD billing is import-only; export/intercity stay ETB. */}
<PaymentCurrencyField
control={form.control}
allowUsd={operationType === "import"}
/>
{(selectedService?.includesFirstMile ||
selectedService?.includesLastMile ||

View File

@@ -19,14 +19,25 @@ const CURRENCY_ICONS: Record<
export function PaymentCurrencyField({
control,
allowUsd = false,
}: {
control: Control<BookingFormInputValues, any, BookingFormValues>;
/**
* 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 (
<Box mt={24}>
<StepLabel>Payment currency</StepLabel>
<Text fz={12} c="#6B7C8E" mt={4} mb={12}>
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."}
</Text>
<Controller
name="paymentCurrency"
@@ -44,7 +55,7 @@ export function PaymentCurrencyField({
border: "1px solid #E6ECF2",
}}
>
{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({
</Group>
{/* Description for the active currency, kept subtle. */}
<Text fz={11.5} c="#6B7C8E" mt={8}>
{
PAYMENT_CURRENCY_OPTIONS.find((o) => o.value === field.value)
?.description
}
{options.find((o) => o.value === field.value)?.description}
</Text>
<OptionFieldError error={fieldState.error} />
</div>

View File

@@ -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.",
},
];

View File

@@ -133,7 +133,11 @@ export function Step2ServiceType({
)}
/>
<PaymentCurrencyField control={form.control} />
{/* USD billing is import-only; export/intercity stay ETB. */}
<PaymentCurrencyField
control={form.control}
allowUsd={operationType === "import"}
/>
{showServiceSections && (
<Stack gap={12} mt={24}>

View File

@@ -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({
<Box mb="lg">
<StepLabel>Billing currency *</StepLabel>
<Text fz={12.5} c="dimmed" mt={4} mb={10}>
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."}
</Text>
<CurrencySelector
value={field.value || ""}
onChange={(v) => field.onChange(v)}
error={fieldState.error?.message}
allowUsd={isImport}
/>
</Box>
)}

View File

@@ -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 (
<Box>
<Box
@@ -39,7 +51,7 @@ export function CurrencySelector({
gap: 10,
}}
>
{OPTIONS.map((o) => {
{options.map((o) => {
const selected = value === o.code;
return (
<button