Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model, Types } from 'mongoose'; import { Asset, AssetCategory, AssetConfiguration } from '../schemas'; @Injectable() export class AssetService { constructor( @InjectModel(Asset.name) private readonly assetModel: Model<Asset>, @InjectModel(AssetCategory.name) private readonly categoryModel: Model<AssetCategory>, @InjectModel(AssetConfiguration.name) private readonly configModel: Model<AssetConfiguration> ) {} async createConfig(tenantId: string, data: any): Promise<AssetConfiguration> { const config = new this.configModel({ ...data, tenantId }); return config.save(); } async getConfig(tenantId: string): Promise<AssetConfiguration> { const config = await this.configModel.findOne({ tenantId }).exec(); if (!config) { // Auto-create default config if none exists return this.createConfig(tenantId, {}); } return config; } async createCategory(tenantId: string, data: any): Promise<AssetCategory> { const duplicate = await this.categoryModel.findOne({ tenantId, categoryCode: data.categoryCode }).exec(); if (duplicate) throw new BadRequestException(`Category code ${data.categoryCode} already exists.`); const category = new this.categoryModel({ ...data, tenantId }); return category.save(); } async listCategories(tenantId: string): Promise<AssetCategory[]> { return this.categoryModel.find({ tenantId }).exec(); } async createAsset(tenantId: string, data: any): Promise<Asset> { // Unique serial number check if (data.serialNumber) { const duplicate = await this.assetModel.findOne({ tenantId, serialNumber: data.serialNumber, status: { $ne: 'disposed' } }).exec(); if (duplicate) { throw new BadRequestException(`Asset with serial number ${data.serialNumber} already exists and is active.`); } } const count = await this.assetModel.countDocuments({ tenantId }).exec(); const assetNumber = `AST-${String(count + 1).padStart(6, '0')}`; const config = await this.getConfig(tenantId); // Low value asset capitalization checks let status = 'capitalization_pending'; if (data.purchaseCostMinor < config.capitalizationThresholdMinor) { status = 'capitalized'; // auto-capitalize low-value items directly as expense } const asset = new this.assetModel({ ...data, tenantId, assetNumber, status: data.status || status, currentBookValueMinor: data.purchaseCostMinor, accumulatedDepreciationMinor: 0 }); return asset.save(); } async getAssetById(tenantId: string, id: string): Promise<Asset> { const asset = await this.assetModel.findOne({ _id: id, tenantId }).exec(); if (!asset) throw new NotFoundException(`Asset ${id} not found.`); return asset; } async listAssets(tenantId: string, filters: any = {}): Promise<any> { const query: any = { tenantId }; if (filters.status) query.status = filters.status; if (filters.categoryId) query.categoryId = filters.categoryId; if (filters.condition) query.condition = filters.condition; const limit = parseInt(filters.limit) || 20; const skip = parseInt(filters.skip) || 0; const data = await this.assetModel.find(query).sort({ createdAt: -1 }).skip(skip).limit(limit).exec(); const total = await this.assetModel.countDocuments(query).exec(); return { data, total, limit, skip }; } async updateAssetStatus(tenantId: string, id: string, status: string, additionalUpdate: any = {}): Promise<Asset> { const asset = await this.getAssetById(tenantId, id); asset.status = status; Object.assign(asset, additionalUpdate); return asset.save(); } } |