[200~feat: add CustomsPaymentsCard and PaymentsTab components for handling customs payments and payment summaries

This commit is contained in:
Marshal
2026-08-20 15:45:42 +00:00
parent 8d7551bb8e
commit 9e1d5ee9f2
58 changed files with 3324 additions and 810 deletions

View File

@@ -59,6 +59,7 @@ import {
import {
DEFAULT_CONFIGURATION_SLUG,
DEFAULT_RULES_SLUG,
ROUTE_SCOPED_TRIGGERS,
RULE_ENGINE_CATEGORY_BASE_PATH,
RULE_ENGINE_SELECT_NONE,
getRuleEngineResource,
@@ -120,9 +121,7 @@ const yardOptionsForLegEnd = (
// direction + route, so their yard dropdowns narrow exactly like base
// freight.
(appliesTo === "OTHER" &&
["CUSTOMS_CLEARANCE", "WITH_RETURN", "FUEL"].includes(
String(values.trigger ?? ""),
))
ROUTE_SCOPED_TRIGGERS.includes(String(values.trigger ?? "")))
) {
const direction = String(values.tradeDirection ?? "");
// Direction is what decides the countries, so offer nothing until it is set

View File

@@ -190,6 +190,10 @@ const RATE_TRIGGERS = [
{ label: "Cancellation (per wagon, per direction + cargo type)", value: "CANCELLATION" },
{ label: "Shipping line extra fee (PIL)", value: "PIL_EXTRA_FEE" },
{ label: "Customs clearance service fee (billed with the booking)", value: "CUSTOMS_CLEARANCE" },
{
label: "Ethiopian customs clearance service fee (Ethiopian-side-only services)",
value: "ETHIOPIAN_CUSTOMS_CLEARANCE",
},
{ label: "Fuel (per lane + cargo type)", value: "FUEL" },
];
@@ -248,8 +252,16 @@ const SHIPPING_LINE_CARGO_KINDS = [
const isBaseFreightRate = (values: Record<string, unknown>) =>
["BULK", "CONTAINER", "INTERCITY"].includes(String(values.appliesTo ?? ""));
/** Surcharges sold per origin → destination leg (mirrors RatesService.isRouteScoped). */
export const ROUTE_SCOPED_TRIGGERS = [
"CUSTOMS_CLEARANCE",
"ETHIOPIAN_CUSTOMS_CLEARANCE",
"WITH_RETURN",
"FUEL",
];
/**
* Rates priced per leg: base rail freight, plus the customs clearance fee and
* Rates priced per leg: base rail freight, plus the customs clearance fees and
* the empty-container return surcharge (sold per route + container type).
*/
const isRouteScopedRate = (values: Record<string, unknown>) =>
@@ -258,19 +270,19 @@ const isRouteScopedRate = (values: Record<string, unknown>) =>
(isShippingLineRate(values)
? hasShippingLine(values) &&
(values.shippingLineRateKind === "BASE" ||
["CUSTOMS_CLEARANCE", "WITH_RETURN", "FUEL"].includes(
String(values.trigger ?? ""),
))
ROUTE_SCOPED_TRIGGERS.includes(String(values.trigger ?? "")))
: isBaseFreightRate(values)) ||
(String(values.appliesTo ?? "") === "OTHER" &&
["CUSTOMS_CLEARANCE", "WITH_RETURN", "FUEL"].includes(String(values.trigger ?? "")));
ROUTE_SCOPED_TRIGGERS.includes(String(values.trigger ?? "")));
/**
* Surcharges sold per cargo kind: the admin says container or bulk, then names
* the container type or bulk commodity the fee covers.
*/
const isCargoKindTrigger = (values: Record<string, unknown>) =>
["CUSTOMS_CLEARANCE", "CANCELLATION"].includes(String(values.trigger ?? ""));
["CUSTOMS_CLEARANCE", "ETHIOPIAN_CUSTOMS_CLEARANCE", "CANCELLATION"].includes(
String(values.trigger ?? ""),
);
const unitOption = (value: string) => ({ label: value.replace(/_/g, " "), value });
@@ -314,6 +326,7 @@ const unitsForShape = (
// Wagon cancellation fee — scales with the cancelled wagons only.
return ["PER_WAGON"];
case "CUSTOMS_CLEARANCE":
case "ETHIOPIAN_CUSTOMS_CLEARANCE":
// Per cargo kind: container fees per box/wagon, bulk per ton/wagon.
return cargoKind === "BULK"
? ["PER_TON", "PER_WAGON"]
@@ -726,6 +739,14 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
{ name: "includesFirstMile", label: "Includes first mile", type: "boolean" },
{ name: "includesLastMile", label: "Includes last mile", type: "boolean" },
{ name: "includesCustoms", label: "Includes customs", type: "boolean" },
{
name: "includesEthiopianCustomsOnly",
label: "Ethiopian customs only",
type: "boolean",
description:
"EDR clears customs on the Ethiopian side only. Same clearance flow; contracts and bookings price off the Ethiopian customs clearance rate instead of the standard one.",
showIf: (v) => v.includesCustoms === true,
},
{ name: "isActive", label: "Active", type: "boolean" },
],
},
@@ -931,6 +952,11 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
label: "Customs clearance",
filters: { trigger: "CUSTOMS_CLEARANCE", isShippingLineRate: "false" },
},
{
key: "ethiopian-customs",
label: "Ethiopian customs",
filters: { trigger: "ETHIOPIAN_CUSTOMS_CLEARANCE", isShippingLineRate: "false" },
},
{
key: "return",
label: "Container return",
@@ -1085,6 +1111,7 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
(String(v.appliesTo ?? "") === "OTHER" &&
[
"CUSTOMS_CLEARANCE",
"ETHIOPIAN_CUSTOMS_CLEARANCE",
"CANCELLATION",
"WITH_RETURN",
"LASHING",