mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
always-on calendar, currency cards
This commit is contained in:
@@ -87,6 +87,33 @@ describe('BookingPricingService — domestic corridor', () => {
|
||||
expect(result.lineItems[0].amount).toBe(Math.round(35 * 120 * MOCK_CBE_RATE));
|
||||
});
|
||||
|
||||
it('keeps the exchange rate decimals — ETB amounts round to cents, not whole birr', async () => {
|
||||
exchangeService.getRate.mockResolvedValue(162.2132);
|
||||
const booking = {
|
||||
id: 'b-1-frac',
|
||||
freightType: 'BULK',
|
||||
tradeDirection: 'DOMESTIC',
|
||||
paymentCurrency: 'ETB',
|
||||
cargoTotalWeightVgm: 120,
|
||||
originYardId: MOJO,
|
||||
destinationYardId: DIRE,
|
||||
bookingContainers: [],
|
||||
} as unknown as Booking;
|
||||
|
||||
const result = await (
|
||||
service as unknown as {
|
||||
computeBaseRailLinesWithRates: (
|
||||
b: Booking,
|
||||
input: { containers: [] },
|
||||
) => Promise<{ lineItems: Array<{ amount: number }> }>;
|
||||
}
|
||||
).computeBaseRailLinesWithRates(booking, { containers: [] });
|
||||
|
||||
// 35 × 120 × 162.2132 = 681,295.44 — the .44 must survive (whole-birr
|
||||
// rounding here billed with the integer part of the rate, in effect).
|
||||
expect(result.lineItems[0].amount).toBe(681295.44);
|
||||
});
|
||||
|
||||
it('prices domestic bulk in USD using INTERCITY_BULK USD rate directly', async () => {
|
||||
const booking = {
|
||||
id: 'b-1-usd',
|
||||
|
||||
@@ -6,6 +6,7 @@ import { RatesService } from '../rule-engine/services/rates.service';
|
||||
import { Rate } from '../rule-engine/entities/rate.entity';
|
||||
import { isBulkQuantityUnit } from '../rule-engine/entities/rate-unit.util';
|
||||
import { ContractRateSnapshot } from '../contracts/entities/contract-rate-snapshot.entity';
|
||||
import { round2 } from '../billing/invoice-settlement.util';
|
||||
import { ExchangeService } from '@edr/api-common';
|
||||
import {
|
||||
AppliedCargoModifier,
|
||||
@@ -205,14 +206,14 @@ export class BookingPricingService {
|
||||
const unitAmount = frozen
|
||||
? Number(frozen.unitPrice)
|
||||
: isEtbBooking
|
||||
? Math.round(unitUsd * usdToEtb)
|
||||
? round2(unitUsd * usdToEtb)
|
||||
: unitUsd;
|
||||
const convertedAmount = frozen
|
||||
? isEtbBooking
|
||||
? Math.round(unitAmount * quantity)
|
||||
? round2(unitAmount * quantity)
|
||||
: unitAmount * quantity
|
||||
: isEtbBooking
|
||||
? Math.round(usdAmount * usdToEtb)
|
||||
? round2(usdAmount * usdToEtb)
|
||||
: usdAmount;
|
||||
|
||||
const item: PriceLineItemDto = {
|
||||
@@ -583,8 +584,8 @@ export class BookingPricingService {
|
||||
} else {
|
||||
const unitUsd = Number(rate!.rateValue);
|
||||
const usdAmount = this.amountForRate(rate!, container.quantity, lineWagons);
|
||||
amount = isEtbBooking ? Math.round(usdAmount * usdToEtb) : usdAmount;
|
||||
unitAmount = isEtbBooking ? Math.round(unitUsd * usdToEtb) : unitUsd;
|
||||
amount = isEtbBooking ? round2(usdAmount * usdToEtb) : usdAmount;
|
||||
unitAmount = isEtbBooking ? round2(unitUsd * usdToEtb) : unitUsd;
|
||||
}
|
||||
if (rate) usedRatesMap.set(rate.id, rate);
|
||||
lines.push({
|
||||
@@ -652,8 +653,8 @@ export class BookingPricingService {
|
||||
);
|
||||
} else {
|
||||
const usdAmount = this.amountForRate(fallback, quantity, wagonCount);
|
||||
amount = isEtbBooking ? Math.round(usdAmount * usdToEtb) : usdAmount;
|
||||
unitAmount = isEtbBooking ? Math.round(unitUsd * usdToEtb) : unitUsd;
|
||||
amount = isEtbBooking ? round2(usdAmount * usdToEtb) : usdAmount;
|
||||
unitAmount = isEtbBooking ? round2(unitUsd * usdToEtb) : unitUsd;
|
||||
}
|
||||
lines.push({
|
||||
code: rateType,
|
||||
@@ -763,12 +764,12 @@ export class BookingPricingService {
|
||||
if (frozen) {
|
||||
unitAmount = Number(frozen.unitPrice);
|
||||
amount = isEtbBooking
|
||||
? Math.round(unitAmount * quantity)
|
||||
? round2(unitAmount * quantity)
|
||||
: unitAmount * quantity;
|
||||
} else {
|
||||
const usdAmount = value * quantity;
|
||||
amount = isEtbBooking ? Math.round(usdAmount * usdToEtb) : usdAmount;
|
||||
unitAmount = isEtbBooking ? Math.round(value * usdToEtb) : value;
|
||||
amount = isEtbBooking ? round2(usdAmount * usdToEtb) : usdAmount;
|
||||
unitAmount = isEtbBooking ? round2(value * usdToEtb) : value;
|
||||
}
|
||||
// Skip legs that resolve to nothing (zero rate, or zero km / count / tons).
|
||||
if (!(amount > 0)) continue;
|
||||
@@ -971,7 +972,7 @@ export class BookingPricingService {
|
||||
if (!(usdToEtb > 0)) return null;
|
||||
const converted =
|
||||
snap.currency === 'USD' && bookingCurrency === 'ETB'
|
||||
? Math.round(unitPrice * usdToEtb)
|
||||
? round2(unitPrice * usdToEtb)
|
||||
: snap.currency === 'ETB' && bookingCurrency === 'USD'
|
||||
? unitPrice / usdToEtb
|
||||
: null;
|
||||
@@ -1027,7 +1028,7 @@ export class BookingPricingService {
|
||||
const currency = booking.paymentCurrency;
|
||||
const isEtb = currency === 'ETB';
|
||||
const usdToEtb = isEtb ? await this.exchangeService.getRate('USD', 'ETB') : 1;
|
||||
const convert = (usd: number): number => (isEtb ? Math.round(usd * usdToEtb) : usd);
|
||||
const convert = (usd: number): number => (isEtb ? round2(usd * usdToEtb) : usd);
|
||||
|
||||
const onLeg = liveRates.filter(
|
||||
(r) =>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Injectable, UnprocessableEntityException } from '@nestjs/common';
|
||||
|
||||
import { RatesService } from '../rule-engine/services/rates.service';
|
||||
import { ContainerTypesService } from '../rule-engine/services/container-types.service';
|
||||
import { round2 } from '../billing/invoice-settlement.util';
|
||||
import { ExchangeService } from '@edr/api-common';
|
||||
import { ContractsRepository } from './contracts.repository';
|
||||
import { Contract } from './entities/contract.entity';
|
||||
@@ -79,7 +80,7 @@ export class ContractPricingService {
|
||||
const currency = contract.paymentCurrency;
|
||||
const isEtb = currency === 'ETB';
|
||||
const usdToEtb = isEtb ? await this.exchangeService.getRate('USD', 'ETB') : 1;
|
||||
const convert = (usd: number): number => (isEtb ? Math.round(usd * usdToEtb) : usd);
|
||||
const convert = (usd: number): number => (isEtb ? round2(usd * usdToEtb) : usd);
|
||||
|
||||
const lineItems: ContractUnitRateLineItem[] = [];
|
||||
const baseType = this.baseRateType(contract);
|
||||
|
||||
Reference in New Issue
Block a user