mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 08:48:11 +00:00
Merge pull request #942 from Tria-plc/freight_feature/usermanagement
revert back the clerance payment
This commit is contained in:
@@ -25,6 +25,8 @@ const FIELD_LABELS: Record<string, string> = {
|
||||
tradeDirection: "Direction",
|
||||
containerTypeId: "Container type",
|
||||
cargoTypeId: "Cargo type",
|
||||
originYardId: "Origin yard",
|
||||
destinationYardId: "Destination yard",
|
||||
};
|
||||
|
||||
const fmtDateTime = (iso: string) =>
|
||||
@@ -36,12 +38,20 @@ const fmtDateTime = (iso: string) =>
|
||||
hour12: false,
|
||||
});
|
||||
|
||||
const fmtValue = (field: string, value: unknown): string => {
|
||||
const fmtValue = (
|
||||
field: string,
|
||||
value: unknown,
|
||||
labels?: Record<string, string>,
|
||||
): string => {
|
||||
if (value === null || value === undefined || value === "") return "—";
|
||||
if (field === "rateValue") {
|
||||
const num = Number(value);
|
||||
return Number.isNaN(num) ? String(value) : num.toLocaleString();
|
||||
}
|
||||
// Yard ids are unreadable — an approver decides on the route, not a UUID.
|
||||
if (field === "originYardId" || field === "destinationYardId") {
|
||||
return labels?.[String(value)] ?? String(value);
|
||||
}
|
||||
return String(value).replace(/_/g, " ");
|
||||
};
|
||||
|
||||
@@ -77,6 +87,8 @@ interface RateApprovalsSectionProps {
|
||||
canDecide: boolean;
|
||||
approve: Decide;
|
||||
reject: Decide;
|
||||
/** yardId → label, so a re-routed rate reads as yards, not UUIDs. */
|
||||
yardLabels?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,6 +101,7 @@ const RateApprovalsSection = ({
|
||||
canDecide,
|
||||
approve,
|
||||
reject,
|
||||
yardLabels,
|
||||
}: RateApprovalsSectionProps) => {
|
||||
const [openId, setOpenId] = useState<string | null>(null);
|
||||
const [notes, setNotes] = useState<Record<string, string>>({});
|
||||
@@ -212,11 +225,11 @@ const RateApprovalsSection = ({
|
||||
{FIELD_LABELS[field] ?? field}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" td="line-through">
|
||||
{fmtValue(field, r.previousValues[field])}
|
||||
{fmtValue(field, r.previousValues[field], yardLabels)}
|
||||
</Text>
|
||||
<ArrowRight size={13} />
|
||||
<Text size="sm" fw={600}>
|
||||
{fmtValue(field, r.payload[field])}
|
||||
{fmtValue(field, r.payload[field], yardLabels)}
|
||||
</Text>
|
||||
</Group>
|
||||
))}
|
||||
|
||||
@@ -269,6 +269,10 @@ const RuleEngineResourcePage = () => {
|
||||
);
|
||||
const { data: yardOptions, isLoading: yardOptionsLoading } =
|
||||
useYardOptions(usesYardField);
|
||||
const yardLabelById = useMemo(
|
||||
() => Object.fromEntries((yardOptions ?? []).map((y) => [y.value, y.label])),
|
||||
[yardOptions],
|
||||
);
|
||||
const usesApprovalRoleField = Boolean(
|
||||
config?.formFields.some(
|
||||
(f) => f.name === "requiredRole" || f.name === "blocksRole",
|
||||
@@ -661,6 +665,7 @@ const RuleEngineResourcePage = () => {
|
||||
canDecide={canApproveRates}
|
||||
approve={rateChangeWorkflow.approve}
|
||||
reject={rateChangeWorkflow.reject}
|
||||
yardLabels={yardLabelById}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -171,12 +171,11 @@ const RATE_TRIGGERS = [
|
||||
value: "WITH_RETURN",
|
||||
},
|
||||
{ label: "Shipping line mapped", value: "SHIPPING_LINE" },
|
||||
{ label: "Consolidation", value: "CONSOLIDATION" },
|
||||
{ label: "Lashing (flat, per booking)", value: "LASHING" },
|
||||
{ label: "Penalty", value: "CONSOLIDATION" },
|
||||
{ label: "Lashing (per container type / bulk)", value: "LASHING" },
|
||||
{ label: "Cancellation", value: "CANCELLATION" },
|
||||
{ label: "Demurrage", value: "DEMURRAGE" },
|
||||
{ label: "Shipping line extra fee (PIL)", value: "PIL_EXTRA_FEE" },
|
||||
{ label: "Customs clearance service fee (prepaid)", value: "CUSTOMS_CLEARANCE" },
|
||||
{ label: "Customs clearance service fee (billed with the booking)", value: "CUSTOMS_CLEARANCE" },
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -211,7 +210,11 @@ const unitOption = (value: string) => ({ label: value.replace(/_/g, " "), value
|
||||
* bill per container, bulk per ton, overweight always per excess ton, etc. Kept
|
||||
* in sync with apps/edr-freight-api/.../entities/rate-unit.util.ts.
|
||||
*/
|
||||
const allowedRateUnits = (appliesTo: string, trigger: string): string[] => {
|
||||
const allowedRateUnits = (
|
||||
appliesTo: string,
|
||||
trigger: string,
|
||||
cargoKind = "",
|
||||
): string[] => {
|
||||
if (appliesTo === "OTHER") {
|
||||
switch (trigger) {
|
||||
case "OVERWEIGHT":
|
||||
@@ -221,16 +224,16 @@ const allowedRateUnits = (appliesTo: string, trigger: string): string[] => {
|
||||
case "DEMURRAGE":
|
||||
return ["PER_CONTAINER", "PER_TON"];
|
||||
case "WITH_RETURN":
|
||||
// Container-only service — bills per returned container.
|
||||
return ["PER_CONTAINER", "FLAT"];
|
||||
// Container-only service — per returned container, per wagon, or flat.
|
||||
return ["PER_CONTAINER", "PER_WAGON", "FLAT"];
|
||||
case "CANCELLATION":
|
||||
return ["FLAT", "PER_INVOICE"];
|
||||
case "CUSTOMS_CLEARANCE":
|
||||
// Flat per clearance (ONE_TIME) / per shipment request (GENERAL).
|
||||
return ["FLAT"];
|
||||
case "LASHING":
|
||||
// Flat cargo-securing fee, billed once per booking.
|
||||
return ["FLAT"];
|
||||
// Per cargo kind: container fees per box/wagon, bulk per ton/wagon.
|
||||
return cargoKind === "BULK"
|
||||
? ["PER_TON", "PER_WAGON"]
|
||||
: ["PER_CONTAINER", "PER_WAGON"];
|
||||
case "CONSOLIDATION":
|
||||
case "SHIPPING_LINE":
|
||||
case "PIL_EXTRA_FEE":
|
||||
@@ -258,7 +261,11 @@ const rateUnitOptions = (values: Record<string, unknown>) => {
|
||||
const appliesTo = String(values.appliesTo ?? "");
|
||||
const trigger = appliesTo === "OTHER" ? String(values.trigger ?? "") : "ALWAYS";
|
||||
if (!appliesTo) return [];
|
||||
return allowedRateUnits(appliesTo, trigger).map(unitOption);
|
||||
return allowedRateUnits(
|
||||
appliesTo,
|
||||
trigger,
|
||||
String(values.cargoKind ?? ""),
|
||||
).map(unitOption);
|
||||
};
|
||||
|
||||
const CURRENCIES = [
|
||||
@@ -659,7 +666,7 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
|
||||
filters: {
|
||||
appliesTo: "OTHER",
|
||||
trigger:
|
||||
"HAZARDOUS,OVERWEIGHT,REEFER,SHIPPING_LINE,CONSOLIDATION,LASHING,CANCELLATION,DEMURRAGE,PIL_EXTRA_FEE",
|
||||
"HAZARDOUS,OVERWEIGHT,REEFER,SHIPPING_LINE,CONSOLIDATION,LASHING,CANCELLATION,PIL_EXTRA_FEE",
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -717,6 +724,37 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
|
||||
(String(v.appliesTo ?? "") === "OTHER" &&
|
||||
["CUSTOMS_CLEARANCE", "WITH_RETURN"].includes(String(v.trigger ?? ""))),
|
||||
},
|
||||
// ── Cargo kind — customs clearance and lashing are priced separately
|
||||
// for containers (one rate per container type) and bulk ────────────────
|
||||
{
|
||||
name: "cargoKind",
|
||||
label: "Cargo kind",
|
||||
type: "select",
|
||||
required: true,
|
||||
options: INTERCITY_KINDS,
|
||||
placeholder: "Is this fee for containers or bulk?",
|
||||
description:
|
||||
"Container fees bill per box or wagon (one rate per container type); bulk fees bill per ton or wagon.",
|
||||
showIf: (v) =>
|
||||
v.appliesTo === "OTHER" &&
|
||||
["CUSTOMS_CLEARANCE", "LASHING"].includes(String(v.trigger ?? "")),
|
||||
// Not a stored column: a container fee carries its containerTypeId, a
|
||||
// bulk fee carries none.
|
||||
getInitialValue: (record) =>
|
||||
record.containerTypeId ? "CONTAINER" : "BULK",
|
||||
},
|
||||
// ── Container type — a container fee names the type it covers ─────────
|
||||
{
|
||||
name: "containerTypeId",
|
||||
label: "Container type",
|
||||
type: "select",
|
||||
required: true,
|
||||
placeholder: "Which container type this fee covers",
|
||||
showIf: (v) =>
|
||||
v.appliesTo === "OTHER" &&
|
||||
["CUSTOMS_CLEARANCE", "LASHING"].includes(String(v.trigger ?? "")) &&
|
||||
v.cargoKind === "CONTAINER",
|
||||
},
|
||||
// ── Cargo kind — Intercity only (import/export get it from appliesTo) ─
|
||||
{
|
||||
name: "intercityKind",
|
||||
|
||||
Reference in New Issue
Block a user