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

feat(freight-web): restrict booking currency to ETB and portal ETB pa…
This commit is contained in:
marshal
2026-08-08 13:49:16 +03:00
committed by GitHub
9 changed files with 31 additions and 52 deletions

View File

@@ -276,9 +276,8 @@ export default function GlCreateBookingForm() {
const [trainScheduleId, setTrainScheduleId] = useState("");
const [contractRouteId, setContractRouteId] = useState<string | null>(null);
const [notes, setNotes] = useState("");
// The customer states the billing currency on their shipment request — GL
// books in it. Intercity is always ETB (the API enforces this too).
const [paymentCurrency, setPaymentCurrency] = useState<"USD" | "ETB">("USD");
// ponytail: ETB-only for now — widen back to "USD" | "ETB" when multi-currency billing returns.
const [paymentCurrency, setPaymentCurrency] = useState<"USD" | "ETB">("ETB");
// What the containers carry — captured per booking (moved off the contract).
const [cargoDescription, setCargoDescription] = useState("");
const [containerLines, setContainerLines] = useState<ContainerLineDraft[]>([]);
@@ -449,9 +448,6 @@ export default function GlCreateBookingForm() {
}
if (bookingRequest.contractRouteId)
setContractRouteId(bookingRequest.contractRouteId);
if (bookingRequest.paymentCurrency === "USD" || bookingRequest.paymentCurrency === "ETB") {
setPaymentCurrency(bookingRequest.paymentCurrency);
}
if (bookingRequest.notes) setNotes(bookingRequest.notes);
}, [bookingRequest, prefilled]);
@@ -1761,11 +1757,7 @@ export default function GlCreateBookingForm() {
Billing currency
</Text>
<Text size="xs" c="dimmed" mb={8}>
{isIntercity
? "Intercity shipments are invoiced in ETB."
: bookingRequest?.paymentCurrency
? "Requested by the customer on their shipment request."
: "The contract is quoted in USD — pick the currency this shipment is invoiced in."}
Shipments are invoiced in ETB.
</Text>
<CurrencySelector
value={isIntercity ? "ETB" : paymentCurrency}

View File

@@ -529,10 +529,7 @@ export default function NewBookingPage() {
/>
<Select
label="Payment currency"
data={[
{ value: "ETB", label: "ETB — Birr" },
{ value: "USD", label: "USD — Dollar" },
]}
data={[{ value: "ETB", label: "ETB — Birr" }]}
value={paymentCurrency}
onChange={(v) => setPaymentCurrency(v ?? "ETB")}
/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

View File

@@ -25,17 +25,20 @@ interface ProviderOption {
currencies: string[];
accent: string;
}
// Only Telebirr, Waafi, CAC Bank and CBE bill payment are enabled for now.
// ponytail: ETB pays via CBE bill only for now — restore the Telebirr entry
// ({ method: "TELEBIRR", currencies: ["ETB"] }) when mobile money returns.
const PROVIDERS: ProviderOption[] = [
{
method: "TELEBIRR",
label: "telebirr",
description: "Ethiopian mobile money · ETB",
logo: "/assets/telebirr.jpeg",
currencies: ["ETB"],
accent: "#0A6F4D",
},
// {
// method: "TELEBIRR",
// label: "telebirr",
// description: "Ethiopian mobile money · ETB",
// logo: "/assets/telebirr.jpeg",
// currencies: ["ETB"],
// accent: "#0A6F4D",
// },
{
method: "WAAFI",
label: "Waafi",
@@ -55,7 +58,7 @@ const PROVIDERS: ProviderOption[] = [
method: "CBE_BILL",
label: "CBE bill payment",
description: "Pay at any CBE branch, app or USSD · ETB",
logo: "/assets/edr-logo.png",
logo: "/assets/cbe.png",
currencies: ["ETB"],
accent: "#5B2D8C",
},
@@ -69,8 +72,8 @@ const OTP_LENGTH = 4;
const isBillMethod = (method: PaymentMethod) => method === "CBE_BILL";
/**
* Pick the provider that settles in the booking's currency. USD → Waafi,
* ETB → Telebirr. Falls back to the first provider when unknown.
* Pick the provider that settles in the booking's currency. USD → Waafi/CAC,
* ETB → CBE bill. Falls back to the full list when unknown.
*/
function providersForCurrency(currency?: string | null): ProviderOption[] {
const cur = currency?.trim().toUpperCase();
@@ -190,7 +193,7 @@ export function PaymentMethodModal({
onClose: () => void;
/** Human-readable total, e.g. "ETB 12,500". */
amountLabel?: string;
/** Booking payment currency — drives which provider is shown (USD → Waafi, ETB → Telebirr). */
/** Booking payment currency — drives which provider is shown (USD → Waafi/CAC, ETB → CBE bill). */
currency?: string | null;
onConfirm: (method: PaymentMethod, payerAccount?: string) => void;
processing?: boolean;

View File

@@ -157,8 +157,8 @@ function mapBookingToFormValues(
isRefrigerated: booking.isRefrigerated ?? false,
bulkHazardousQty: String(Number(booking.bulkHazardousQuantity ?? 0)),
bulkReeferQty: String(Number(booking.bulkReeferQuantity ?? 0)),
paymentCurrency:
booking.paymentCurrency === "ETB" ? "ETB" : "USD",
// ponytail: ETB-only for now — old USD drafts are re-billed in ETB on edit.
paymentCurrency: "ETB",
scheduledDate: booking.scheduledDate
? new Date(booking.scheduledDate).toISOString().slice(0, 10)
: "",

View File

@@ -81,11 +81,7 @@ export const PAYMENT_CURRENCY_OPTIONS: Array<{
label: string;
description: string;
}> = [
{
value: "USD",
label: "USD",
description: "US Dollar — international pricing and invoicing.",
},
// ponytail: ETB-only for now — re-add the USD option when multi-currency billing returns.
{
value: "ETB",
label: "ETB",
@@ -373,7 +369,7 @@ export const initialBookingFormValues: DeepPartial<BookingFormValues> = {
previousContractRef: "",
serviceTypeId: "",
paymentCurrency: "USD",
paymentCurrency: "ETB",
firstMile: {
enabled: false,
pickUpAddress: "",

View File

@@ -299,7 +299,7 @@ export function Step8Review({
<DetailRow label="Service" value={serviceType?.name ?? ""} />
<DetailRow
label="Payment currency"
value={values.paymentCurrency ?? "USD"}
value={values.paymentCurrency ?? "ETB"}
/>
<Button
type="button"

View File

@@ -281,7 +281,7 @@ function mapBookingToShipmentValues(
}>;
};
const values: Partial<ShipmentFormInputValues> = {
paymentCurrency: booking.paymentCurrency === "ETB" ? "ETB" : "USD",
paymentCurrency: "ETB",
withReturn: booking.equipmentReturn === "WITH_RETURN",
cargoDescription: b.cargoFreeText ?? "",
...(b.contractRouteId ? { contractRouteId: b.contractRouteId } : {}),
@@ -390,11 +390,8 @@ function NewShipmentBookingForm({
// Seed the equipment-return toggle from the contract; the customer can
// still flip it per shipment.
withReturn: contract.equipmentReturn === "WITH_RETURN",
// The contract quotes USD; the customer bills this shipment in the
// currency they pick here. Intercity is always ETB, so it is preset;
// everything else starts empty so the customer picks deliberately
// instead of silently inheriting USD.
paymentCurrency: contract.tradeDirection === "DOMESTIC" ? "ETB" : "",
// ponytail: ETB-only for now — preset since there is no other choice.
paymentCurrency: "ETB",
},
resolver: zodResolver(
createShipmentFormSchema({
@@ -1313,8 +1310,7 @@ function ScheduleStep({
<Box mb="lg">
<StepLabel>Billing currency *</StepLabel>
<Text fz={12.5} c="dimmed" mt={4} mb={10}>
Your contract is quoted in USD. Pick the currency this shipment is
invoiced in the total is converted for you.
Shipments are invoiced in ETB.
</Text>
<CurrencySelector
value={field.value || ""}

View File

@@ -10,18 +10,13 @@ export interface CurrencySelectorProps {
error?: string;
}
// ponytail: ETB-only for now — restore the USD entry when multi-currency billing returns.
const OPTIONS = [
{
code: "USD",
symbol: "$",
name: "US Dollar",
hint: "As quoted on the contract",
},
{
code: "ETB",
symbol: "Br",
name: "Ethiopian Birr",
hint: "Converted from the USD total",
hint: "All shipments are invoiced in ETB",
},
] as const;