rename to core
This commit is contained in:
115
core/racing/domain/value-objects/Money.ts
Normal file
115
core/racing/domain/value-objects/Money.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Value Object: Money
|
||||
* Represents a monetary amount with currency and platform fee calculation
|
||||
*/
|
||||
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
import type { IValueObject } from '@gridpilot/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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format money for display
|
||||
*/
|
||||
format(): string {
|
||||
const formatter = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: this.currency,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
return formatter.format(this.amount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user