Skip to content

Commit e9be435

Browse files
Merge pull request #285 from mdauwal/feature/asset-registration-module
feat(assets): implement asset registration module with CRUD APIs
2 parents 466125a + fa4cddd commit e9be435

File tree

5 files changed

+224
-0
lines changed

5 files changed

+224
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import {
2+
Controller,
3+
Get,
4+
Post,
5+
Body,
6+
Param,
7+
Delete,
8+
Patch,
9+
} from '@nestjs/common';
10+
import { AssetsService } from './assets.service';
11+
import { CreateAssetDto } from './dto/create-asset.dto';
12+
import { UpdateAssetDto } from './dto/update-asset.dto';
13+
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
14+
15+
@ApiTags('Assets')
16+
@Controller('api/v1/assets')
17+
export class AssetsController {
18+
constructor(private readonly assetsService: AssetsService) {}
19+
20+
@Post()
21+
@ApiOperation({ summary: 'Register a new asset' })
22+
@ApiResponse({ status: 201, description: 'Asset created successfully' })
23+
create(@Body() dto: CreateAssetDto) {
24+
return this.assetsService.create(dto);
25+
}
26+
27+
@Get()
28+
@ApiOperation({ summary: 'Get all registered assets' })
29+
findAll() {
30+
return this.assetsService.findAll();
31+
}
32+
33+
@Get(':id')
34+
@ApiOperation({ summary: 'Get an asset by ID' })
35+
findOne(@Param('id') id: string) {
36+
return this.assetsService.findOne(+id);
37+
}
38+
39+
@Patch(':id')
40+
@ApiOperation({ summary: 'Update an asset' })
41+
update(@Param('id') id: string, @Body() dto: UpdateAssetDto) {
42+
return this.assetsService.update(+id, dto);
43+
}
44+
45+
@Delete(':id')
46+
@ApiOperation({ summary: 'Delete an asset' })
47+
remove(@Param('id') id: string) {
48+
return this.assetsService.remove(+id);
49+
}
50+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { Injectable, NotFoundException } from '@nestjs/common';
2+
import { InjectRepository } from '@nestjs/typeorm';
3+
import { Repository } from 'typeorm';
4+
import { Asset } from './entities/asset.entity';
5+
import { CreateAssetDto } from './dto/create-asset.dto';
6+
import { UpdateAssetDto } from './dto/update-asset.dto';
7+
import { Supplier } from '../suppliers/entities/supplier.entity';
8+
import { Department } from '../departments/entities/department.entity';
9+
import { Category } from '../categories/entities/category.entity';
10+
11+
@Injectable()
12+
export class AssetsService {
13+
constructor(
14+
@InjectRepository(Asset)
15+
private assetsRepo: Repository<Asset>,
16+
@InjectRepository(Supplier)
17+
private supplierRepo: Repository<Supplier>,
18+
@InjectRepository(Department)
19+
private departmentRepo: Repository<Department>,
20+
@InjectRepository(Category)
21+
private categoryRepo: Repository<Category>,
22+
) {}
23+
24+
async create(dto: CreateAssetDto): Promise<Asset> {
25+
const supplier = await this.supplierRepo.findOneBy({ id: dto.supplierId });
26+
if (!supplier) throw new NotFoundException('Supplier not found');
27+
28+
const category = await this.categoryRepo.findOneBy({ id: dto.categoryId });
29+
if (!category) throw new NotFoundException('Category not found');
30+
31+
let department: Department = null;
32+
if (dto.assignedDepartmentId) {
33+
department = await this.departmentRepo.findOneBy({ id: dto.assignedDepartmentId });
34+
if (!department) throw new NotFoundException('Department not found');
35+
}
36+
37+
const asset = this.assetsRepo.create({
38+
...dto,
39+
purchaseDate: new Date(dto.purchaseDate),
40+
warrantyEnd: dto.warrantyEnd ? new Date(dto.warrantyEnd) : null,
41+
supplier,
42+
category,
43+
assignedDepartment: department,
44+
});
45+
46+
return this.assetsRepo.save(asset);
47+
}
48+
49+
async findAll(): Promise<Asset[]> {
50+
return this.assetsRepo.find({
51+
relations: ['supplier', 'category', 'assignedDepartment'],
52+
});
53+
}
54+
55+
async findOne(id: number): Promise<Asset> {
56+
const asset = await this.assetsRepo.findOne({
57+
where: { id },
58+
relations: ['supplier', 'category', 'assignedDepartment'],
59+
});
60+
if (!asset) throw new NotFoundException(`Asset with ID ${id} not found`);
61+
return asset;
62+
}
63+
64+
async update(id: number, dto: UpdateAssetDto): Promise<Asset> {
65+
const asset = await this.findOne(id);
66+
67+
if (dto.supplierId) {
68+
asset.supplier = await this.supplierRepo.findOneBy({ id: dto.supplierId });
69+
}
70+
if (dto.categoryId) {
71+
asset.category = await this.categoryRepo.findOneBy({ id: dto.categoryId });
72+
}
73+
if (dto.assignedDepartmentId) {
74+
asset.assignedDepartment = await this.departmentRepo.findOneBy({ id: dto.assignedDepartmentId });
75+
}
76+
77+
Object.assign(asset, dto);
78+
return this.assetsRepo.save(asset);
79+
}
80+
81+
async remove(id: number): Promise<void> {
82+
const asset = await this.findOne(id);
83+
await this.assetsRepo.remove(asset);
84+
}
85+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { IsString, IsDateString, IsOptional, IsNumber } from 'class-validator';
2+
3+
export class CreateAssetDto {
4+
@IsString()
5+
serialNumber: string;
6+
7+
@IsDateString()
8+
purchaseDate: string;
9+
10+
@IsOptional()
11+
@IsDateString()
12+
warrantyEnd?: string;
13+
14+
@IsNumber()
15+
supplierId: number;
16+
17+
@IsOptional()
18+
@IsNumber()
19+
assignedDepartmentId?: number;
20+
21+
@IsNumber()
22+
categoryId: number;
23+
24+
@IsOptional()
25+
@IsNumber()
26+
purchaseCost?: number;
27+
28+
@IsOptional()
29+
@IsString()
30+
description?: string;
31+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { PartialType } from '@nestjs/mapped-types';
2+
import { CreateAssetDto } from './create-asset.dto';
3+
4+
export class UpdateAssetDto extends PartialType(CreateAssetDto) {}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {
2+
Entity,
3+
PrimaryGeneratedColumn,
4+
Column,
5+
CreateDateColumn,
6+
UpdateDateColumn,
7+
ManyToOne,
8+
JoinColumn,
9+
} from 'typeorm';
10+
import { Supplier } from '../../suppliers/entities/supplier.entity';
11+
import { Department } from '../../departments/entities/department.entity';
12+
import { Category } from '../../categories/entities/category.entity';
13+
14+
@Entity('assets')
15+
export class Asset {
16+
@PrimaryGeneratedColumn()
17+
id: number;
18+
19+
@Column({ unique: true })
20+
serialNumber: string;
21+
22+
@Column({ type: 'date' })
23+
purchaseDate: Date;
24+
25+
@Column({ type: 'date', nullable: true })
26+
warrantyEnd?: Date;
27+
28+
@ManyToOne(() => Supplier, { nullable: false })
29+
@JoinColumn({ name: 'supplier_id' })
30+
supplier: Supplier;
31+
32+
@ManyToOne(() => Department, { nullable: true })
33+
@JoinColumn({ name: 'assigned_department_id' })
34+
assignedDepartment?: Department;
35+
36+
@ManyToOne(() => Category, { nullable: false })
37+
@JoinColumn({ name: 'category_id' })
38+
category: Category;
39+
40+
@Column({ type: 'decimal', precision: 12, scale: 2, nullable: true })
41+
purchaseCost?: number;
42+
43+
@Column({ type: 'text', nullable: true })
44+
description?: string;
45+
46+
@Column({ default: true })
47+
isActive: boolean;
48+
49+
@CreateDateColumn()
50+
createdAt: Date;
51+
52+
@UpdateDateColumn()
53+
updatedAt: Date;
54+
}

0 commit comments

Comments
 (0)