110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
/**
|
|
* Value Object: Money
|
|
* Represents a monetary amount with currency and platform fee calculation
|
|
*/
|
|
|
|
import type { ValueObject } from '@core/shared/domain/ValueObject';
|
|
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
|
|
|
export type Currency = 'USD' | 'EUR' | 'GBP';
|
|
|
|
export const isCurrency = (value: string): value is Currency =>
|
|
value === 'USD' || value === 'EUR' || value === 'GBP';
|
|
|
|
export interface MoneyProps {
|
|
amount: number;
|
|
currency: Currency;
|
|
}
|
|
|
|
export class Money implements ValueObject<MoneyProps> {
|
|
static readonly DEFAULT_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 a fee amount for a given percentage.
|
|
* Defaults to the current platform fee percentage.
|
|
*/
|
|
calculatePlatformFee(platformFeePercentage: number = Money.DEFAULT_PLATFORM_FEE_PERCENTAGE): Money {
|
|
if (!Number.isFinite(platformFeePercentage) || platformFeePercentage < 0) {
|
|
throw new RacingDomainValidationError('Platform fee percentage must be a non-negative finite number');
|
|
}
|
|
const feeAmount = this.amount * platformFeePercentage;
|
|
return new Money(feeAmount, this.currency);
|
|
}
|
|
|
|
/**
|
|
* Calculate net amount after subtracting a fee.
|
|
* Defaults to subtracting the current platform fee percentage.
|
|
*/
|
|
calculateNetAmount(platformFeePercentage: number = Money.DEFAULT_PLATFORM_FEE_PERCENTAGE): Money {
|
|
const platformFee = this.calculatePlatformFee(platformFeePercentage);
|
|
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: ValueObject<MoneyProps>): boolean {
|
|
const a = this.props;
|
|
const b = other.props;
|
|
return a.amount === b.amount && a.currency === b.currency;
|
|
}
|
|
} |