Skip to content

Commit e57045c

Browse files
Merge branch 'main' into feat/supplierModule
2 parents 4b36bf9 + e9be435 commit e57045c

File tree

79 files changed

+8455
-44
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+8455
-44
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
node_modules

backend/inventory-items/entities/inventory-item.entity.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export class InventoryItem {
1414
@Column({ type: 'int', default: 10 })
1515
reorderLevel: number; // This is the threshold for reordering
1616

17+
@Column({ type: 'int', nullable: true })
18+
currentDepartmentId: number;
19+
1720
@CreateDateColumn()
1821
createdAt: Date;
1922

backend/src/app.module.ts

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,60 +3,37 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
33
import { AppController } from './app.controller';
44
import { AppService } from './app.service';
55
import { AssetCategoriesModule } from './asset-categories/asset-categories.module';
6+
import { SettingsModule } from './settings/settings.module';
67
import { AssetCategory } from './asset-categories/asset-category.entity';
78
import { DepartmentsModule } from './departments/departments.module';
89
import { Department } from './departments/department.entity';
9-
import { SuppliersModule } from './suppliers/suppliers.module';
10-
import {
11-
I18nModule,
12-
QueryResolver,
13-
HeaderResolver,
14-
AcceptLanguageResolver,
15-
} from 'nestjs-i18n';
16-
import * as path from 'path';
17-
import { Supplier } from './suppliers/entities/supplier.entity';
18-
import { TypeOrmModule } from '@nestjs/typeorm';
10+
import { UsersModule } from './users/users.module';
11+
import { User } from './users/entities/user.entity';
1912

2013
@Module({
2114
imports: [
2215
ConfigModule.forRoot({
2316
isGlobal: true,
2417
}),
25-
26-
// i18n should be registered at top-level imports (not nested inside TypeOrm import)
27-
I18nModule.forRoot({
28-
fallbackLanguage: 'en',
29-
loaderOptions: {
30-
path: path.join(__dirname, '/i18n/'),
31-
watch: true,
32-
},
33-
resolvers: [
34-
{ use: QueryResolver, options: ['lang', 'l'] },
35-
{ use: HeaderResolver, options: ['x-custom-lang-header'] },
36-
AcceptLanguageResolver,
37-
],
38-
}),
39-
40-
// TypeORM async config
4118
TypeOrmModule.forRootAsync({
4219
imports: [ConfigModule],
4320
useFactory: (configService: ConfigService) => ({
4421
type: 'postgres',
45-
host: configService.get<string>('DB_HOST', 'localhost'),
46-
port: Number(configService.get<number>('DB_PORT', 5432)),
47-
username: configService.get<string>('DB_USERNAME', 'postgres'),
48-
password: configService.get<string>('DB_PASSWORD', 'password'),
49-
database: configService.get<string>('DB_DATABASE', 'manage_assets'),
50-
// include all entity classes used by your app
51-
entities: [AssetCategory, Department, Supplier],
52-
synchronize: configService.get<string>('NODE_ENV') !== 'production',
22+
host: configService.get('DB_HOST', 'localhost'),
23+
port: configService.get('DB_PORT', 5432),
24+
username: configService.get('DB_USERNAME', 'postgres'),
25+
password: configService.get('DB_PASSWORD', 'password'),
26+
database: configService.get('DB_DATABASE', 'manage_assets'),
27+
entities: [AssetCategory, Department, User],
28+
synchronize: configService.get('NODE_ENV') !== 'production', // Only for development
5329
}),
5430
inject: [ConfigService],
5531
}),
5632

5733
AssetCategoriesModule,
5834
DepartmentsModule,
59-
SuppliersModule,
35+
AssetTransfersModule,
36+
UsersModule,
6037
],
6138
controllers: [AppController],
6239
providers: [AppService],
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Asset Depreciation Module
2+
3+
This module implements asset depreciation functionality using straight-line depreciation method to help companies estimate the current value of their assets.
4+
5+
## Features
6+
7+
- **Straight-line Depreciation**: Calculates depreciation using the formula: `(Purchase Price - Salvage Value) / Useful Life Years`
8+
- **Automatic Calculation**: Current depreciated values are calculated automatically based on time elapsed
9+
- **Comprehensive API**: Full CRUD operations plus specialized endpoints for depreciation data
10+
- **Validation**: Input validation for purchase dates, salvage values, and useful life
11+
- **Filtering**: Advanced filtering options for retrieving assets by depreciation status, method, and value ranges
12+
13+
## API Endpoints
14+
15+
### Basic CRUD Operations
16+
17+
- `POST /asset-depreciation` - Create a new asset depreciation record
18+
- `GET /asset-depreciation` - Get all asset depreciation records with optional filters
19+
- `GET /asset-depreciation/:id` - Get a specific asset by ID
20+
- `PATCH /asset-depreciation/:id` - Update an asset depreciation record
21+
- `DELETE /asset-depreciation/:id` - Delete an asset depreciation record
22+
23+
### Specialized Depreciation Endpoints
24+
25+
- `GET /asset-depreciation/current-values` - Get current depreciated values for all assets
26+
- `GET /asset-depreciation/:id/current-value` - Get current depreciated value for a specific asset
27+
- `GET /asset-depreciation/summary` - Get depreciation summary statistics
28+
- `GET /asset-depreciation/fully-depreciated` - Get all fully depreciated assets
29+
- `GET /asset-depreciation/nearing-end-of-life?threshold=1` - Get assets nearing end of useful life
30+
- `GET /asset-depreciation/:id/projected-value?date=2025-12-31` - Get projected value at a future date
31+
32+
## Data Transfer Objects (DTOs)
33+
34+
### CreateAssetDepreciationDto
35+
```typescript
36+
{
37+
assetName: string; // Required, max 255 chars
38+
description?: string; // Optional description
39+
purchasePrice: number; // Required, positive number with max 2 decimal places
40+
purchaseDate: string; // Required, ISO date string (YYYY-MM-DD)
41+
usefulLifeYears: number; // Required, 1-100 years
42+
depreciationMethod?: DepreciationMethod; // Optional, defaults to STRAIGHT_LINE
43+
salvageValue?: number; // Optional, must be less than purchase price
44+
}
45+
```
46+
47+
### UpdateAssetDepreciationDto
48+
All fields are optional versions of CreateAssetDepreciationDto fields.
49+
50+
### DepreciatedValueResponseDto
51+
```typescript
52+
{
53+
id: number;
54+
assetName: string;
55+
description?: string;
56+
purchasePrice: number;
57+
purchaseDate: string;
58+
usefulLifeYears: number;
59+
depreciationMethod: DepreciationMethod;
60+
salvageValue?: number;
61+
currentDepreciatedValue: number; // Calculated field
62+
annualDepreciation: number; // Calculated field
63+
totalDepreciationToDate: number; // Calculated field
64+
remainingUsefulLife: number; // Calculated field
65+
isFullyDepreciated: boolean; // Calculated field
66+
createdAt: Date;
67+
updatedAt: Date;
68+
}
69+
```
70+
71+
## Entity Methods
72+
73+
The `AssetDepreciation` entity provides several calculation methods:
74+
75+
- `getCurrentDepreciatedValue()`: Returns current value after depreciation
76+
- `getAnnualDepreciation()`: Returns annual depreciation amount
77+
- `getTotalDepreciationToDate()`: Returns total depreciation to current date
78+
- `getRemainingUsefulLife()`: Returns remaining years of useful life
79+
- `isFullyDepreciated()`: Returns true if asset is fully depreciated
80+
81+
## Usage Examples
82+
83+
### Creating an Asset
84+
```typescript
85+
POST /asset-depreciation
86+
{
87+
"assetName": "Dell Laptop",
88+
"description": "Development laptop for engineering team",
89+
"purchasePrice": 15000,
90+
"purchaseDate": "2023-01-01",
91+
"usefulLifeYears": 5,
92+
"salvageValue": 2000
93+
}
94+
```
95+
96+
### Getting Current Depreciated Value
97+
```typescript
98+
GET /asset-depreciation/1/current-value
99+
100+
Response:
101+
{
102+
"id": 1,
103+
"assetName": "Dell Laptop",
104+
"purchasePrice": 15000,
105+
"currentDepreciatedValue": 12400,
106+
"annualDepreciation": 2600,
107+
"totalDepreciationToDate": 2600,
108+
"remainingUsefulLife": 4,
109+
"isFullyDepreciated": false,
110+
// ... other fields
111+
}
112+
```
113+
114+
### Getting Assets with Filters
115+
```typescript
116+
GET /asset-depreciation?isFullyDepreciated=false&minValue=10000&maxValue=50000
117+
```
118+
119+
### Getting Summary Statistics
120+
```typescript
121+
GET /asset-depreciation/summary
122+
123+
Response:
124+
{
125+
"totalAssets": 10,
126+
"totalPurchaseValue": 150000,
127+
"totalCurrentValue": 85000,
128+
"totalDepreciation": 65000,
129+
"fullyDepreciatedAssets": 2,
130+
"averageAge": 2.5
131+
}
132+
```
133+
134+
## Validation Rules
135+
136+
- Purchase date cannot be in the future
137+
- Salvage value must be less than purchase price
138+
- Useful life must be between 1 and 100 years
139+
- Purchase price must be positive
140+
- Asset names must be unique (database constraint)
141+
142+
## Depreciation Formula
143+
144+
The straight-line depreciation uses this formula:
145+
146+
```
147+
Annual Depreciation = (Purchase Price - Salvage Value) / Useful Life Years
148+
Current Value = Purchase Price - (Annual Depreciation × Years Elapsed)
149+
```
150+
151+
The current value will never go below the salvage value, ensuring realistic depreciation calculations.
152+
153+
## Testing
154+
155+
Run tests with:
156+
```bash
157+
npm test -- asset-depreciation.service.spec.ts
158+
```
159+
160+
The test suite covers:
161+
- CRUD operations with validation
162+
- Depreciation calculations
163+
- Edge cases (fully depreciated assets, zero salvage value)
164+
- Error handling and exceptions
165+
- Service business logic

0 commit comments

Comments
 (0)