import type {Asset} from '@prisma/client'; import {AssetRepository} from '../repositories/AssetRepository'; import {NotFoundError, ValidationError} from '../utils/errors'; type AssetType = 'cash' | 'investment' | 'property' | 'vehicle' | 'other'; const VALID_ASSET_TYPES: AssetType[] = ['cash', 'investment', 'property', 'vehicle', 'other']; interface CreateAssetDTO { name: string; type: AssetType; value: number; } interface UpdateAssetDTO { name?: string; type?: AssetType; value?: number; } /** * Asset Service * Implements Single Responsibility: Handles asset business logic * Implements Open/Closed: Extensible for new asset-related features */ export class AssetService { constructor(private assetRepository: AssetRepository) {} async getAll(userId: string): Promise { return this.assetRepository.findAllByUser(userId); } async getById(id: string, userId: string): Promise { const asset = await this.assetRepository.findByIdAndUser(id, userId); if (!asset) { throw new NotFoundError('Asset not found'); } return asset; } async create(userId: string, data: CreateAssetDTO): Promise { this.validateAssetData(data); return this.assetRepository.create({ name: data.name, type: data.type, value: data.value, user: {connect: {id: userId}} }); } async update(id: string, userId: string, data: UpdateAssetDTO): Promise { const asset = await this.assetRepository.findByIdAndUser(id, userId); if (!asset) { throw new NotFoundError('Asset not found'); } if (data.value !== undefined || data.name !== undefined || data.type !== undefined) { this.validateAssetData({ name: data.name || asset.name, type: (data.type || asset.type) as AssetType, value: data.value !== undefined ? data.value : asset.value }); } return this.assetRepository.update(id, data); } async delete(id: string, userId: string): Promise { const asset = await this.assetRepository.findByIdAndUser(id, userId); if (!asset) { throw new NotFoundError('Asset not found'); } await this.assetRepository.delete(id); } async getTotalValue(userId: string): Promise { return this.assetRepository.getTotalValue(userId); } async getByType(userId: string): Promise> { const assets = await this.assetRepository.findAllByUser(userId); return assets.reduce( (acc, asset) => { const type = asset.type; if (!acc[type]) { acc[type] = []; } acc[type].push(asset); return acc; }, {} as Record ); } private validateAssetData(data: CreateAssetDTO): void { if (!data.name || data.name.trim().length === 0) { throw new ValidationError('Asset name is required'); } if (data.value < 0) { throw new ValidationError('Asset value cannot be negative'); } if (!VALID_ASSET_TYPES.includes(data.type)) { throw new ValidationError('Invalid asset type'); } } }