diff --git a/apps/edr-freight-web/portal/src/pages/contracts/ContractDetailPage.tsx b/apps/edr-freight-web/portal/src/pages/contracts/ContractDetailPage.tsx
index 6ceb1fb69..ded69afb1 100644
--- a/apps/edr-freight-web/portal/src/pages/contracts/ContractDetailPage.tsx
+++ b/apps/edr-freight-web/portal/src/pages/contracts/ContractDetailPage.tsx
@@ -64,6 +64,7 @@ import {
} from "@/pages/bookings/booking-display";
import { InitiateBookingButton } from "@/components/customer-actions/ContractCustomerAction";
import { formatRateUnit } from "./new-contract-form/unit-rates";
+import { formatAmount } from "./new-shipment-form/total";
import { bookingDocNoun } from "@/pages/bookings/clearance/bookingNextAction";
import { getContractBookingAction } from "./contract-booking-action";
import { closedWindowMessage, hasOpenWindow } from "./booking-window";
@@ -719,7 +720,7 @@ export default function ContractDetailPage() {
)}
- {(item.unitPrice ?? 0).toLocaleString()} {pricing.currency}{" "}
+ {formatAmount(item.unitPrice)} {pricing.currency}{" "}
/ {formatRateUnit(item.unit)}
@@ -1336,9 +1337,7 @@ export default function ContractDetailPage() {
whiteSpace: "nowrap",
}}
>
- {amount > 0
- ? `ETB ${amount.toLocaleString()}`
- : "—"}
+ {amount > 0 ? `ETB ${formatAmount(amount)}` : "—"}
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 a5b29a299..347cdd2bb 100644
--- a/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx
+++ b/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx
@@ -75,7 +75,7 @@ import {
createShipmentFormSchema,
initialShipmentFormValues,
} from "./new-shipment-form/schema";
-import { computeShipmentTotal } from "./new-shipment-form/total";
+import { computeShipmentTotal, formatAmount } from "./new-shipment-form/total";
import {
downloadContainerImportTemplate,
parseContainerExcel,
@@ -1014,7 +1014,7 @@ function PriceConfirmModal({
))}
{overweightSurchargeAmount > 0
- ? `An overweight surcharge of ${overweightSurchargeAmount.toLocaleString()} ${
+ ? `An overweight surcharge of ${formatAmount(overweightSurchargeAmount)} ${
validation?.currency ?? total?.currency ?? ""
} applies (included in the total below). You can still submit, or go back and adjust weights.`
: "An overweight surcharge applies. You can still submit, or go back and adjust weights."}
@@ -1038,7 +1038,7 @@ function PriceConfirmModal({
{line.quantity.toLocaleString()} ×{" "}
- {line.unitPrice.toLocaleString()} {total.currency} ·{" "}
+ {formatAmount(line.unitPrice)} {total.currency} ·{" "}
{formatRateUnit(line.unit)}
@@ -1048,7 +1048,7 @@ function PriceConfirmModal({
c="#10202F"
style={{ whiteSpace: "nowrap" }}
>
- {line.amount.toLocaleString()} {total.currency}
+ {formatAmount(line.amount)} {total.currency}
))}
@@ -1070,7 +1070,7 @@ function PriceConfirmModal({
Total
- {total.total.toLocaleString()}{" "}
+ {formatAmount(total.total)}{" "}
{total.currency}
diff --git a/apps/edr-freight-web/portal/src/pages/contracts/new-shipment-form/total.ts b/apps/edr-freight-web/portal/src/pages/contracts/new-shipment-form/total.ts
index cbe9ae083..7c80ca1d6 100644
--- a/apps/edr-freight-web/portal/src/pages/contracts/new-shipment-form/total.ts
+++ b/apps/edr-freight-web/portal/src/pages/contracts/new-shipment-form/total.ts
@@ -15,6 +15,19 @@ export interface ShipmentTotal {
total: number;
}
+/**
+ * Money always prints its cents. Bare toLocaleString() defaults to
+ * maximumFractionDigits: 0, which rounded the total away from the line items it
+ * sums (118,171.21 shown as 118,171) — and the customer is billed the exact
+ * amount, so the shown figure must match to the cent.
+ */
+export function formatAmount(amount: number | string | null | undefined) {
+ return Number(amount ?? 0).toLocaleString(undefined, {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ });
+}
+
/**
* Quantity a bulk rate bills, in ITS OWN unit. PER_ITEM cargo carries both
* figures — the item count prices the booking, the tonnage sizes the wagons —