mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 20:40:55 +00:00
fix(rates): render stored rate currency, default last mile to birr
The rate matrix currency cell hardcoded USD, so a last-mile rate priced in ETB still displayed as dollars. formatCell now takes the row and reads its currency code, falling back to USD. Last-mile currency select gets defaultValue ETB (new generic FormFieldDef.defaultValue for create-time pre-selection) and lists ETB (Birr) first; USD stays selectable. Backend already persisted and validated the chosen currency.
This commit is contained in:
@@ -460,8 +460,21 @@ export class LastMileService {
|
||||
@OnEvent("last_mile.invoice.paid")
|
||||
async onBookingInvoicePaid(payload: InvoiceEventPayload): Promise<void> {
|
||||
try {
|
||||
// Invoice paid → the delivery is complete. Route through update() so it
|
||||
// also frees the trucks + records history (same as "Mark Delivered").
|
||||
if (payload.type === 'LAST_MILE_ADVANCE') {
|
||||
// Advance paid → the leg becomes dispatchable, not delivered.
|
||||
await this.update(payload.sourceId, {
|
||||
status: 'READY_TO_TRANSIT',
|
||||
advancedPayment: payload.totalAmount,
|
||||
} as unknown as UpdateLastMileDto);
|
||||
this.logger.log(
|
||||
`Last-mile ${payload.sourceId} READY_TO_TRANSIT on advance invoice ${payload.invoiceId} payment`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (payload.type !== 'DELIVERY_FEE') return;
|
||||
// Delivery-fee invoice paid → the delivery is complete. Route through
|
||||
// update() so it also frees the trucks + records history (same as
|
||||
// "Mark Delivered").
|
||||
await this.update(payload.sourceId, { status: 'DELIVERED', paid: true } as unknown as UpdateLastMileDto);
|
||||
this.logger.log(`Last-mile ${payload.sourceId} marked DELIVERED on invoice ${payload.invoiceId} payment`);
|
||||
} catch (err) {
|
||||
|
||||
@@ -43,6 +43,12 @@ export class YardsRepository implements IYardsRepository {
|
||||
findPaged(query: ListYardsQueryDto): Promise<PaginatedResponse<Yard>> {
|
||||
const qb = this.repo
|
||||
.createQueryBuilder('yard')
|
||||
// createQueryBuilder does NOT auto-apply the soft-delete filter that
|
||||
// repo.find()/findOne() get for free — without this, a renamed/replaced
|
||||
// yard (e.g. an old "DMP" superseded by a new one) still shows up
|
||||
// alongside the live one in every picker built off this endpoint, and a
|
||||
// route picked against the dead yard id never matches any LIVE rate.
|
||||
.where('yard.deleted_at IS NULL')
|
||||
.orderBy(`yard.${query.sortBy ?? 'displayOrder'}`, query.sortOrder ?? 'ASC')
|
||||
.addOrderBy('yard.label', 'ASC');
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ const RuleEngineCardGrid = ({
|
||||
{col.header}:
|
||||
</Text>
|
||||
<div style={{ textAlign: "right", flex: 1 }}>
|
||||
{formatCell(displayValue, col.format)}
|
||||
{formatCell(displayValue, col.format, record)}
|
||||
</div>
|
||||
</Group>
|
||||
);
|
||||
|
||||
@@ -127,6 +127,8 @@ const buildInitialValues = (
|
||||
} else {
|
||||
values[field.name] = raw;
|
||||
}
|
||||
} else if (field.defaultValue !== undefined) {
|
||||
values[field.name] = field.defaultValue;
|
||||
} else if (field.type === "boolean") {
|
||||
values[field.name] = false;
|
||||
} else if (field.type === "number") {
|
||||
|
||||
@@ -17,7 +17,13 @@ const extractLabel = (value: unknown): string | null => {
|
||||
);
|
||||
};
|
||||
|
||||
export const formatCell = (value: unknown, format?: ColumnFormat): ReactNode => {
|
||||
export const formatCell = (
|
||||
value: unknown,
|
||||
format?: ColumnFormat,
|
||||
// The row the cell came from — currency amounts read their code off it so a
|
||||
// last-mile rate priced in birr does not render as USD.
|
||||
row?: Record<string, unknown>,
|
||||
): ReactNode => {
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return <Text size="sm" c="dimmed">—</Text>;
|
||||
}
|
||||
@@ -109,9 +115,10 @@ export const formatCell = (value: unknown, format?: ColumnFormat): ReactNode =>
|
||||
|
||||
if (format === "currency") {
|
||||
const num = Number(value);
|
||||
const code = typeof row?.currency === "string" ? row.currency : "USD";
|
||||
return (
|
||||
<Text size="sm" fw={500}>
|
||||
{Number.isNaN(num) ? String(value) : `USD ${num.toLocaleString()}`}
|
||||
{Number.isNaN(num) ? String(value) : `${code} ${num.toLocaleString()}`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -501,7 +501,7 @@ const RuleEngineResourcePage = () => {
|
||||
header: col.header,
|
||||
meta: { headerClassName, cellClassName },
|
||||
cell: ({ row }) => {
|
||||
const cell = formatCell(row.original[col.accessorKey], col.format);
|
||||
const cell = formatCell(row.original[col.accessorKey], col.format, row.original);
|
||||
// On the rate column, show the proposed value under the live one — the
|
||||
// live value stays the headline because it is what still gets charged.
|
||||
if (!isRates || col.accessorKey !== "rateValue") return cell;
|
||||
|
||||
@@ -70,6 +70,8 @@ export interface FormFieldDef {
|
||||
* relation list (`wagonTypeIds` read from `record.wagonTypes`).
|
||||
*/
|
||||
getInitialValue?: (record: Record<string, unknown>) => unknown;
|
||||
/** Pre-selected value on create (no record yet) — e.g. last-mile currency = ETB. */
|
||||
defaultValue?: string;
|
||||
/**
|
||||
* Fully derived field: its value is computed from the live form values on
|
||||
* every render and the input is locked. Used for the priority-rule min
|
||||
@@ -297,8 +299,8 @@ export const rateUnitOptions = (
|
||||
};
|
||||
|
||||
const CURRENCIES = [
|
||||
{ label: "ETB (Birr)", value: "ETB" },
|
||||
{ label: "USD", value: "USD" },
|
||||
{ label: "ETB", value: "ETB" },
|
||||
];
|
||||
|
||||
const PRIORITY_CONFIG_TYPES = [
|
||||
@@ -1020,6 +1022,8 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
|
||||
required: true,
|
||||
options: CURRENCIES,
|
||||
showWhen: { field: "appliesTo", equals: ["LAST_MILE"] },
|
||||
// Birr is the norm for domestic trucking; USD stays selectable.
|
||||
defaultValue: "ETB",
|
||||
getInitialValue: (record) => String(record.currency ?? "ETB"),
|
||||
},
|
||||
// ── Distance tiers (create only — the page swaps this for the single
|
||||
|
||||
Reference in New Issue
Block a user