/** * Domain Entity: LeagueWallet * * Represents a league's financial wallet. * Aggregate root for managing league finances and transactions. */ import { RacingDomainValidationError, RacingDomainInvariantError } from '../../errors/RacingDomainError'; import type { IEntity } from '@core/shared/domain'; import type { Money } from '../../value-objects/Money'; import { LeagueWalletId } from './LeagueWalletId'; import { LeagueId } from '../LeagueId'; import { TransactionId } from './TransactionId'; export interface LeagueWalletProps { id: LeagueWalletId; leagueId: LeagueId; balance: Money; transactionIds: TransactionId[]; createdAt: Date; } export class LeagueWallet implements IEntity { readonly id: LeagueWalletId; readonly leagueId: LeagueId; readonly balance: Money; readonly transactionIds: TransactionId[]; readonly createdAt: Date; private constructor(props: LeagueWalletProps) { this.id = props.id; this.leagueId = props.leagueId; this.balance = props.balance; this.transactionIds = props.transactionIds; this.createdAt = props.createdAt; } static create(props: { id: string; leagueId: string; balance: Money; createdAt?: Date; transactionIds?: string[]; }): LeagueWallet { this.validate(props); const id = LeagueWalletId.create(props.id); const leagueId = LeagueId.create(props.leagueId); const transactionIds = props.transactionIds?.map(tid => TransactionId.create(tid)) ?? []; return new LeagueWallet({ id, leagueId, balance: props.balance, transactionIds, createdAt: props.createdAt ?? new Date(), }); } private static validate(props: { id: string; leagueId: string; balance: Money; }): void { if (!props.id || props.id.trim().length === 0) { throw new RacingDomainValidationError('LeagueWallet ID is required'); } if (!props.leagueId || props.leagueId.trim().length === 0) { throw new RacingDomainValidationError('LeagueWallet leagueId is required'); } if (!props.balance) { throw new RacingDomainValidationError('LeagueWallet balance is required'); } } /** * Add funds to wallet (from sponsorship or membership payments) */ addFunds(netAmount: Money, transactionId: string): LeagueWallet { if (this.balance.currency !== netAmount.currency) { throw new RacingDomainInvariantError('Cannot add funds with different currency'); } const newBalance = this.balance.add(netAmount); const newTransactionId = TransactionId.create(transactionId); return new LeagueWallet({ ...this, balance: newBalance, transactionIds: [...this.transactionIds, newTransactionId], }); } /** * Withdraw funds from wallet * Domain rule: Cannot withdraw if insufficient balance */ withdrawFunds(amount: Money, transactionId: string): LeagueWallet { if (this.balance.currency !== amount.currency) { throw new RacingDomainInvariantError('Cannot withdraw funds with different currency'); } if (!this.balance.isGreaterThan(amount) && !this.balance.equals(amount)) { throw new RacingDomainInvariantError('Insufficient balance for withdrawal'); } const newBalance = this.balance.subtract(amount); const newTransactionId = TransactionId.create(transactionId); return new LeagueWallet({ ...this, balance: newBalance, transactionIds: [...this.transactionIds, newTransactionId], }); } /** * Check if wallet can withdraw a specific amount */ canWithdraw(amount: Money): boolean { if (this.balance.currency !== amount.currency) { return false; } return this.balance.isGreaterThan(amount) || this.balance.equals(amount); } /** * Get current balance */ getBalance(): Money { return this.balance; } /** * Get all transaction IDs */ getTransactionIds(): string[] { return this.transactionIds.map(tid => tid.toString()); } }