Skip to content

Commit 965a620

Browse files
Merge branch 'main' into feat/SettingsModule
2 parents bf07fda + 57b04e7 commit 965a620

File tree

78 files changed

+8179
-185
lines changed

Some content is hidden

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

78 files changed

+8179
-185
lines changed

.github/workflows/CI.yaml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
defaults:
13+
run:
14+
working-directory: contracts # 👈 all cargo commands run in contracts/
15+
16+
jobs:
17+
format:
18+
name: Format Check
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
- name: Install Rust toolchain
23+
uses: dtolnay/rust-toolchain@stable
24+
with:
25+
components: rustfmt
26+
- name: Check formatting
27+
run: cargo fmt --all -- --check
28+
29+
clippy:
30+
name: Clippy Lint
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
- name: Install Rust toolchain
35+
uses: dtolnay/rust-toolchain@stable
36+
with:
37+
components: clippy
38+
- name: Cache cargo registry
39+
uses: actions/cache@v4
40+
with:
41+
path: |
42+
~/.cargo/registry
43+
~/.cargo/git
44+
contracts/target
45+
key: ${{ runner.os }}-cargo-${{ hashFiles('contracts/Cargo.lock') }}
46+
restore-keys: |
47+
${{ runner.os }}-cargo-
48+
- name: Run clippy
49+
run: cargo clippy --all-targets --all-features -- -D warnings
50+
51+
test:
52+
name: Test Suite
53+
runs-on: ubuntu-latest
54+
steps:
55+
- uses: actions/checkout@v4
56+
- name: Install Rust toolchain
57+
uses: dtolnay/rust-toolchain@stable
58+
- name: Cache cargo registry
59+
uses: actions/cache@v4
60+
with:
61+
path: |
62+
~/.cargo/registry
63+
~/.cargo/git
64+
contracts/target
65+
key: ${{ runner.os }}-cargo-test-${{ hashFiles('contracts/Cargo.lock') }}
66+
restore-keys: |
67+
${{ runner.os }}-cargo-test-
68+
${{ runner.os }}-cargo-
69+
- name: Run tests
70+
run: cargo test --all --verbose
71+
72+
build:
73+
name: Build Check
74+
runs-on: ubuntu-latest
75+
steps:
76+
- uses: actions/checkout@v4
77+
- name: Install Rust toolchain
78+
uses: dtolnay/rust-toolchain@stable
79+
- name: Cache cargo registry
80+
uses: actions/cache@v4
81+
with:
82+
path: |
83+
~/.cargo/registry
84+
~/.cargo/git
85+
contracts/target
86+
key: ${{ runner.os }}-cargo-build-${{ hashFiles('contracts/Cargo.lock') }}
87+
restore-keys: |
88+
${{ runner.os }}-cargo-build-
89+
${{ runner.os }}-cargo-
90+
- name: Build all crates
91+
run: cargo build --all --verbose
92+
93+
wasm-check:
94+
name: WASM Compatibility Check
95+
runs-on: ubuntu-latest
96+
steps:
97+
- uses: actions/checkout@v4
98+
- name: Install Rust toolchain
99+
uses: dtolnay/rust-toolchain@stable
100+
with:
101+
targets: wasm32-unknown-unknown
102+
- name: Cache cargo registry
103+
uses: actions/cache@v4
104+
with:
105+
path: |
106+
~/.cargo/registry
107+
~/.cargo/git
108+
contracts/target
109+
key: ${{ runner.os }}-cargo-wasm-${{ hashFiles('contracts/Cargo.lock') }}
110+
restore-keys: |
111+
${{ runner.os }}-cargo-wasm-
112+
${{ runner.os }}-cargo-
113+
- name: Build for WASM target
114+
run: cargo build --all --target wasm32-unknown-unknown --verbose
115+
116+
ci-success:
117+
name: CI Success
118+
runs-on: ubuntu-latest
119+
needs: [format, clippy, test, build, wasm-check]
120+
if: always()
121+
defaults:
122+
run:
123+
working-directory: .
124+
steps:
125+
- name: Check all jobs
126+
run: |
127+
if [[ "${{ needs.format.result }}" != "success" || \
128+
"${{ needs.clippy.result }}" != "success" || \
129+
"${{ needs.test.result }}" != "success" || \
130+
"${{ needs.build.result }}" != "success" || \
131+
"${{ needs.wasm-check.result }}" != "success" ]]; then
132+
echo "One or more CI jobs failed"
133+
exit 1
134+
fi
135+
echo "All CI jobs passed successfully"

.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: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ import { SettingsModule } from './settings/settings.module';
88
import { AssetCategory } from './asset-categories/asset-category.entity';
99
import { DepartmentsModule } from './departments/departments.module';
1010
import { Department } from './departments/department.entity';
11+
import { UsersModule } from './users/users.module';
12+
import { User } from './users/entities/user.entity';
1113

1214
@Module({
1315
imports: [
1416
ConfigModule.forRoot({
1517
isGlobal: true,
1618
}),
19+
1720
TypeOrmModule.forRootAsync({
1821
imports: [ConfigModule],
1922
useFactory: (configService: ConfigService) => ({
@@ -23,14 +26,15 @@ import { Department } from './departments/department.entity';
2326
username: configService.get('DB_USERNAME', 'postgres'),
2427
password: configService.get('DB_PASSWORD', 'password'),
2528
database: configService.get('DB_DATABASE', 'manage_assets'),
26-
entities: [AssetCategory, Department],
29+
entities: [AssetCategory, Department, User],
2730
synchronize: configService.get('NODE_ENV') !== 'production', // Only for development
2831
}),
2932
inject: [ConfigService],
3033
}),
31-
AssetCategoriesModule,
32-
DepartmentsModule,
33-
SettingsModule,
34+
AssetCategoriesModule,
35+
DepartmentsModule,
36+
AssetTransfersModule,
37+
UsersModule,
3438
],
3539
controllers: [AppController],
3640
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)