Files
gridpilot.gg/core/racing/domain/value-objects/Money.ts
2025-12-17 01:23:09 +01:00

102 lines
2.7 KiB
TypeScript

/**
* Value Object: Money
* Represents a monetary amount with currency and platform fee calculation
*/
import { RacingDomainValidationError } from '../errors/RacingDomainError';
import type { IValueObject } from '@core/shared/domain';
export type Currency = 'USD' | 'EUR' | 'GBP';
export interface MoneyProps {
amount: number;
currency: Currency;
}
export class Money implements IValueObject<MoneyProps> {
private static readonly PLATFORM_FEE_PERCENTAGE = 0.10;
readonly amount: number;
readonly currency: Currency;
private constructor(amount: number, currency: Currency) {
this.amount = amount;
this.currency = currency;
}
static create(amount: number, currency: Currency = 'USD'): Money {
if (amount < 0) {
throw new RacingDomainValidationError('Money amount cannot be negative');
}
if (!Number.isFinite(amount)) {
throw new RacingDomainValidationError('Money amount must be a finite number');
}
return new Money(amount, currency);
}
/**
* Calculate platform fee (10%)
*/
calculatePlatformFee(): Money {
const feeAmount = this.amount * Money.PLATFORM_FEE_PERCENTAGE;
return new Money(feeAmount, this.currency);
}
/**
* Calculate net amount after platform fee
*/
calculateNetAmount(): Money {
const platformFee = this.calculatePlatformFee();
return new Money(this.amount - platformFee.amount, this.currency);
}
/**
* Add two money amounts
*/
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new RacingDomainValidationError('Cannot add money with different currencies');
}
return new Money(this.amount + other.amount, this.currency);
}
/**
* Subtract two money amounts
*/
subtract(other: Money): Money {
if (this.currency !== other.currency) {
throw new RacingDomainValidationError('Cannot subtract money with different currencies');
}
const result = this.amount - other.amount;
if (result < 0) {
throw new RacingDomainValidationError('Subtraction would result in negative amount');
}
return new Money(result, this.currency);
}
/**
* Check if this money is greater than another
*/
isGreaterThan(other: Money): boolean {
if (this.currency !== other.currency) {
throw new RacingDomainValidationError('Cannot compare money with different currencies');
}
return this.amount > other.amount;
}
get props(): MoneyProps {
return {
amount: this.amount,
currency: this.currency,
};
}
/**
* Check if this money equals another
*/
equals(other: IValueObject<MoneyProps>): boolean {
const a = this.props;
const b = other.props;
return a.amount === b.amount && a.currency === b.currency;
}
}