gcx/backend/services/clearing-service/src/domain/value-objects/settlement-amount.vo.ts

87 lines
2.3 KiB
TypeScript

/**
* 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;
}
}