Files
edr-platform/apps/edr-passenger-api/src/modules/fare-engine/fare-engine.dto.ts
2026-06-29 16:03:00 +03:00

76 lines
3.4 KiB
TypeScript

import { IsString, IsOptional, IsEnum, IsInt, Min } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { Currency } from '@prisma/client';
// Nationality → home currency mapping (keys are uppercase for case-insensitive lookup)
export const NATIONALITY_CURRENCY_MAP: Record<string, string> = {
ETHIOPIAN: 'ETB',
DJIBOUTIAN: 'DJF',
};
export function resolveCurrencyFromNationality(nationality?: string): Currency {
if (!nationality) return 'ETB' as Currency;
return (NATIONALITY_CURRENCY_MAP[nationality.toUpperCase()] ?? 'USD') as Currency;
}
export class FareCalculateDto {
@ApiProperty({ example: 'route-uuid', description: 'Route UUID — used to look up stop distances' })
@IsString() routeId: string;
@ApiProperty({ example: 'station-uuid', description: 'Origin station UUID (must be a stop on the route)' })
@IsString() originStationId: string;
@ApiProperty({ example: 'station-uuid', description: 'Destination station UUID (must come after origin)' })
@IsString() destinationStationId: string;
@ApiProperty({ example: 'seat-class-uuid', description: 'SeatClass UUID — its basePrice is the per-km rate in ETB minor units' })
@IsString() seatClassId: string;
@ApiPropertyOptional({
example: 'Ethiopian',
description: 'Passenger nationality. Determines the billing currency: ETHIOPIAN → ETB, DJIBOUTIAN → DJF, other → USD. Case-insensitive. Defaults to ETB.',
})
@IsOptional() @IsString() nationality?: string;
@ApiPropertyOptional({
example: 2,
description: 'Number of adult passengers (age ≥ 5). Defaults to 1.',
})
@IsOptional() @Type(() => Number) @IsInt() @Min(1) adultCount?: number;
@ApiPropertyOptional({
example: 1,
description: 'Number of child passengers (age < 5). First child travels free.',
})
@IsOptional() @Type(() => Number) @IsInt() @Min(0) childCount?: number;
@ApiPropertyOptional({ example: 'WEEKEND15', description: 'Promo code for discount' })
@IsOptional() @IsString() promoCode?: string;
@ApiPropertyOptional({ example: 'schedule-uuid', description: 'Schedule UUID — used to match schedule-scoped FareRules first' })
@IsOptional() @IsString() scheduleId?: string;
}
export class FareBreakdownDto {
@ApiProperty({ example: 'ADD-DJI' }) routeCode: string;
@ApiProperty({ example: 'Addis Ababa' }) originName: string;
@ApiProperty({ example: 'Djibouti' }) destinationName: string;
@ApiProperty({ example: 'Economy Regular' }) seatClassName: string;
@ApiProperty({ example: 756 }) totalDistanceKm: number;
@ApiProperty({ example: 120 }) ratePerKmMinor: number;
@ApiProperty({ example: 90720 }) baseFarePerPassengerMinor: number;
@ApiProperty({ example: 2 }) adultCount: number;
@ApiProperty({ example: 1 }) childCount: number;
@ApiProperty({ example: 1 }) freeChildrenCount: number;
@ApiProperty({ example: 0 }) paidChildrenCount: number;
@ApiProperty({ example: 181440 }) subtotalMinor: number;
@ApiProperty({ example: 0 }) discountMinor: number;
@ApiProperty({ example: 9072 }) taxMinor: number;
@ApiProperty({ example: 190512 }) totalMinor: number;
@ApiProperty({ example: 'ETB', enum: Currency }) billingCurrency: Currency;
@ApiProperty({ example: 190512 }) totalInBillingCurrency: number;
@ApiProperty({ example: 3.25 }) exchangeRate: number;
@ApiProperty({ description: 'Step-by-step calculation trace for transparency' }) calculation: string;
}