Files
gridpilot.gg/core/racing/domain/entities/league-wallet/LeagueWallet.ts
2025-12-29 18:34:12 +01:00

163 lines
4.4 KiB
TypeScript

/**
* 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<LeagueWalletId> {
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(),
});
}
static rehydrate(props: {
id: string;
leagueId: string;
balance: Money;
transactionIds: string[];
createdAt: Date;
}): LeagueWallet {
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,
});
}
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());
}
}