b6a2c97f9f
Add comprehensive asset management system with depreciation tracking similar to Xero. Features: - Create and manage fixed assets with purchase details - Calculate depreciation using straight-line and declining balance methods - Track asset book value and accumulated depreciation - Dispose/sell assets with gain/loss calculations - Automatic depreciation schedule generation - Integration with chart of accounts for depreciation entries Database: - Add assets table with purchase and depreciation fields - Add asset_depreciation_entries table for schedule tracking - Add accumulated depreciation account to seed data Server-side: - Assets module with CRUD operations - Depreciation calculation service - Asset disposal service with gain/loss tracking - REST API endpoints with permission guards Frontend: - Assets list page with data table - Asset form with purchase and depreciation sections - React Query hooks for API integration - Sidebar navigation under Accounting Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
90 lines
2.5 KiB
TypeScript
90 lines
2.5 KiB
TypeScript
import { Injectable, Scope } from '@nestjs/common';
|
|
import { Knex } from 'knex';
|
|
import { Inject } from '@nestjs/common';
|
|
import { TenantRepository } from '@/modules/Tenancy/TenancyDB/TenantRepository';
|
|
import { TENANCY_DB_CONNECTION } from '@/modules/Tenancy/TenancyDB/TenancyDB.constants';
|
|
import { AssetDepreciationEntry } from '../models/AssetDepreciationEntry.model';
|
|
|
|
@Injectable({ scope: Scope.REQUEST })
|
|
export class AssetDepreciationEntryRepository extends TenantRepository {
|
|
constructor(
|
|
@Inject(TENANCY_DB_CONNECTION)
|
|
private readonly tenantDBKnex: () => Knex,
|
|
) {
|
|
super();
|
|
}
|
|
|
|
get model(): typeof AssetDepreciationEntry {
|
|
return AssetDepreciationEntry.bindKnex(this.tenantDBKnex());
|
|
}
|
|
|
|
/**
|
|
* Find entries by asset ID.
|
|
*/
|
|
async findByAssetId(assetId: number): Promise<AssetDepreciationEntry[]> {
|
|
return this.model
|
|
.query()
|
|
.where('assetId', assetId)
|
|
.orderBy(['periodYear', 'periodMonth']);
|
|
}
|
|
|
|
/**
|
|
* Find unposted entries by period.
|
|
*/
|
|
async findUnpostedByPeriod(year: number, month: number): Promise<AssetDepreciationEntry[]> {
|
|
return this.model
|
|
.query()
|
|
.where('periodYear', year)
|
|
.where('periodMonth', month)
|
|
.where('isPosted', false);
|
|
}
|
|
|
|
/**
|
|
* Find entries by asset and period.
|
|
*/
|
|
async findByPeriod(assetId: number, year: number, month: number): Promise<AssetDepreciationEntry[]> {
|
|
return this.model
|
|
.query()
|
|
.where('assetId', assetId)
|
|
.where('periodYear', year)
|
|
.where('periodMonth', month);
|
|
}
|
|
|
|
/**
|
|
* Delete unposted entries for an asset.
|
|
*/
|
|
async deleteUnpostedEntries(assetId: number): Promise<void> {
|
|
await this.model
|
|
.query()
|
|
.where('assetId', assetId)
|
|
.where('isPosted', false)
|
|
.delete();
|
|
}
|
|
|
|
/**
|
|
* Create a depreciation entry.
|
|
*/
|
|
async create(data: Partial<AssetDepreciationEntry>): Promise<AssetDepreciationEntry> {
|
|
return this.model.query().insert(data);
|
|
}
|
|
|
|
/**
|
|
* Update a depreciation entry.
|
|
*/
|
|
async update(id: number, data: Partial<AssetDepreciationEntry>): Promise<AssetDepreciationEntry> {
|
|
return this.model.query().patchAndFetchById(id, data);
|
|
}
|
|
|
|
/**
|
|
* Get the last posted entry for an asset.
|
|
*/
|
|
async getLastPostedEntry(assetId: number): Promise<AssetDepreciationEntry | undefined> {
|
|
return this.model
|
|
.query()
|
|
.where('assetId', assetId)
|
|
.where('isPosted', true)
|
|
.orderBy(['periodYear', 'periodMonth'], 'desc')
|
|
.first();
|
|
}
|
|
}
|