/** * Value Object: SettlementAmount * Encapsulates a financial amount with validation. * Amounts are stored as strings to preserve precision (matches DB numeric type). */ export class SettlementAmount { private constructor(private readonly _value: string) {} /** * Create a SettlementAmount from a string value. * Validates that the value is a positive numeric string with up to 20 digits * and 8 decimal places. */ static create(value: string): SettlementAmount { if (!value || value.trim() === '') { throw new Error('Settlement amount cannot be empty'); } const numeric = parseFloat(value); if (isNaN(numeric)) { throw new Error(`Invalid settlement amount: "${value}" is not a valid number`); } if (numeric < 0) { throw new Error(`Settlement amount must be non-negative, got: ${value}`); } // Validate precision: up to 20 digits total, 8 decimal places const parts = value.split('.'); const integerPart = parts[0].replace(/^-/, ''); const decimalPart = parts[1] || ''; if (integerPart.length > 12) { throw new Error( `Settlement amount integer part exceeds maximum 12 digits: ${integerPart.length}`, ); } if (decimalPart.length > 8) { throw new Error( `Settlement amount decimal part exceeds maximum 8 digits: ${decimalPart.length}`, ); } return new SettlementAmount(value); } /** * Reconstruct from a persisted string value (no validation, trusted source). */ static fromPersisted(value: string): SettlementAmount { return new SettlementAmount(value); } get value(): string { return this._value; } toNumber(): number { return parseFloat(this._value); } /** * Subtract another amount and return the result. */ subtract(other: SettlementAmount): SettlementAmount { const result = this.toNumber() - other.toNumber(); return SettlementAmount.create(String(result)); } /** * Add another amount and return the result. */ add(other: SettlementAmount): SettlementAmount { const result = this.toNumber() + other.toNumber(); return SettlementAmount.create(String(result)); } equals(other: SettlementAmount): boolean { return this._value === other._value; } toString(): string { return this._value; } }