mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-05 06:03:40 +00:00
108 lines
3.0 KiB
TypeScript
108 lines
3.0 KiB
TypeScript
/**
|
|
* Utility functions for age-based fare calculations
|
|
* Implements the "first child free" pricing policy
|
|
*/
|
|
|
|
export interface PassengerWithAge {
|
|
dateOfBirth?: string;
|
|
name?: string;
|
|
}
|
|
|
|
/**
|
|
* Calculate age from date of birth
|
|
*/
|
|
export function calculateAge(dateOfBirth: string | Date): number {
|
|
const today = new Date();
|
|
const birth = new Date(dateOfBirth);
|
|
let age = today.getFullYear() - birth.getFullYear();
|
|
const monthDiff = today.getMonth() - birth.getMonth();
|
|
|
|
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
|
|
age--;
|
|
}
|
|
|
|
return Math.max(0, age);
|
|
}
|
|
|
|
/**
|
|
* Check if passenger is a child (under 5 years old)
|
|
*/
|
|
export function isChild(passenger: PassengerWithAge): boolean {
|
|
if (!passenger.dateOfBirth) return false;
|
|
return calculateAge(passenger.dateOfBirth) < 5;
|
|
}
|
|
|
|
/**
|
|
* Check if this child gets a free fare (1 free child per adult)
|
|
*/
|
|
export function isFirstChild(passengers: PassengerWithAge[], currentIndex: number): boolean {
|
|
const currentPassenger = passengers[currentIndex];
|
|
if (!isChild(currentPassenger)) return false;
|
|
|
|
const adultCount = passengers.filter(p => !isChild(p)).length;
|
|
const childrenBefore = passengers.slice(0, currentIndex).filter(p => isChild(p));
|
|
return childrenBefore.length < adultCount;
|
|
}
|
|
|
|
/**
|
|
* Calculate fare for a single passenger with first child free logic
|
|
*/
|
|
export function calculatePassengerFare(
|
|
passengers: PassengerWithAge[],
|
|
passengerIndex: number,
|
|
baseFare: number
|
|
): number {
|
|
const passenger = passengers[passengerIndex];
|
|
|
|
if (isChild(passenger) && isFirstChild(passengers, passengerIndex)) {
|
|
return 0; // First child travels free
|
|
}
|
|
|
|
return baseFare;
|
|
}
|
|
|
|
/**
|
|
* Calculate total fare for all passengers with first child free logic
|
|
*/
|
|
export function calculateTotalFare(
|
|
passengers: PassengerWithAge[],
|
|
baseFare: number
|
|
): number {
|
|
return passengers.reduce((total, _, index) => {
|
|
return total + calculatePassengerFare(passengers, index, baseFare);
|
|
}, 0);
|
|
}
|
|
|
|
/**
|
|
* Get passenger category for display purposes
|
|
*/
|
|
export function getPassengerCategory(passenger: PassengerWithAge): 'ADULT' | 'CHILD' {
|
|
return isChild(passenger) ? 'CHILD' : 'ADULT';
|
|
}
|
|
|
|
/**
|
|
* Format fare amount for display
|
|
*/
|
|
export function formatFare(amountMinor: number, currency: string = 'ETB'): string {
|
|
return `${currency} ${(amountMinor / 100).toFixed(2)}`;
|
|
}
|
|
|
|
/**
|
|
* Get pricing summary for a list of passengers
|
|
*/
|
|
export function getPricingSummary(passengers: PassengerWithAge[], baseFare: number) {
|
|
const adults = passengers.filter(p => !isChild(p));
|
|
const children = passengers.filter(p => isChild(p));
|
|
const freeChildren = Math.min(children.length, adults.length);
|
|
const paidChildren = Math.max(0, children.length - adults.length);
|
|
|
|
return {
|
|
adultCount: adults.length,
|
|
childCount: children.length,
|
|
freeChildrenCount: freeChildren,
|
|
paidChildrenCount: paidChildren,
|
|
adultFare: adults.length * baseFare,
|
|
paidChildFare: paidChildren * baseFare,
|
|
totalFare: calculateTotalFare(passengers, baseFare)
|
|
};
|
|
} |