Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions backend/src/assets/assets.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
Controller,
Get,
Post,
Body,
Param,
Delete,
Patch,
} from '@nestjs/common';
import { AssetsService } from './assets.service';
import { CreateAssetDto } from './dto/create-asset.dto';
import { UpdateAssetDto } from './dto/update-asset.dto';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';

@ApiTags('Assets')
@Controller('api/v1/assets')
export class AssetsController {
constructor(private readonly assetsService: AssetsService) {}

@Post()
@ApiOperation({ summary: 'Register a new asset' })
@ApiResponse({ status: 201, description: 'Asset created successfully' })
create(@Body() dto: CreateAssetDto) {
return this.assetsService.create(dto);
}

@Get()
@ApiOperation({ summary: 'Get all registered assets' })
findAll() {
return this.assetsService.findAll();
}

@Get(':id')
@ApiOperation({ summary: 'Get an asset by ID' })
findOne(@Param('id') id: string) {
return this.assetsService.findOne(+id);
}

@Patch(':id')
@ApiOperation({ summary: 'Update an asset' })
update(@Param('id') id: string, @Body() dto: UpdateAssetDto) {
return this.assetsService.update(+id, dto);
}

@Delete(':id')
@ApiOperation({ summary: 'Delete an asset' })
remove(@Param('id') id: string) {
return this.assetsService.remove(+id);
}
}
85 changes: 85 additions & 0 deletions backend/src/assets/assets.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Asset } from './entities/asset.entity';
import { CreateAssetDto } from './dto/create-asset.dto';
import { UpdateAssetDto } from './dto/update-asset.dto';
import { Supplier } from '../suppliers/entities/supplier.entity';
import { Department } from '../departments/entities/department.entity';
import { Category } from '../categories/entities/category.entity';

@Injectable()
export class AssetsService {
constructor(
@InjectRepository(Asset)
private assetsRepo: Repository<Asset>,
@InjectRepository(Supplier)
private supplierRepo: Repository<Supplier>,
@InjectRepository(Department)
private departmentRepo: Repository<Department>,
@InjectRepository(Category)
private categoryRepo: Repository<Category>,
) {}

async create(dto: CreateAssetDto): Promise<Asset> {
const supplier = await this.supplierRepo.findOneBy({ id: dto.supplierId });
if (!supplier) throw new NotFoundException('Supplier not found');

const category = await this.categoryRepo.findOneBy({ id: dto.categoryId });
if (!category) throw new NotFoundException('Category not found');

let department: Department = null;
if (dto.assignedDepartmentId) {
department = await this.departmentRepo.findOneBy({ id: dto.assignedDepartmentId });
if (!department) throw new NotFoundException('Department not found');
}

const asset = this.assetsRepo.create({
...dto,
purchaseDate: new Date(dto.purchaseDate),
warrantyEnd: dto.warrantyEnd ? new Date(dto.warrantyEnd) : null,
supplier,
category,
assignedDepartment: department,
});

return this.assetsRepo.save(asset);
}

async findAll(): Promise<Asset[]> {
return this.assetsRepo.find({
relations: ['supplier', 'category', 'assignedDepartment'],
});
}

async findOne(id: number): Promise<Asset> {
const asset = await this.assetsRepo.findOne({
where: { id },
relations: ['supplier', 'category', 'assignedDepartment'],
});
if (!asset) throw new NotFoundException(`Asset with ID ${id} not found`);
return asset;
}

async update(id: number, dto: UpdateAssetDto): Promise<Asset> {
const asset = await this.findOne(id);

if (dto.supplierId) {
asset.supplier = await this.supplierRepo.findOneBy({ id: dto.supplierId });
}
if (dto.categoryId) {
asset.category = await this.categoryRepo.findOneBy({ id: dto.categoryId });
}
if (dto.assignedDepartmentId) {
asset.assignedDepartment = await this.departmentRepo.findOneBy({ id: dto.assignedDepartmentId });
}

Object.assign(asset, dto);
return this.assetsRepo.save(asset);
}

async remove(id: number): Promise<void> {
const asset = await this.findOne(id);
await this.assetsRepo.remove(asset);
}
}
31 changes: 31 additions & 0 deletions backend/src/assets/dto/create-asset.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { IsString, IsDateString, IsOptional, IsNumber } from 'class-validator';

export class CreateAssetDto {
@IsString()
serialNumber: string;

@IsDateString()
purchaseDate: string;

@IsOptional()
@IsDateString()
warrantyEnd?: string;

@IsNumber()
supplierId: number;

@IsOptional()
@IsNumber()
assignedDepartmentId?: number;

@IsNumber()
categoryId: number;

@IsOptional()
@IsNumber()
purchaseCost?: number;

@IsOptional()
@IsString()
description?: string;
}
4 changes: 4 additions & 0 deletions backend/src/assets/dto/update-asset.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateAssetDto } from './create-asset.dto';

export class UpdateAssetDto extends PartialType(CreateAssetDto) {}
54 changes: 54 additions & 0 deletions backend/src/assets/entity/asset.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
JoinColumn,
} from 'typeorm';
import { Supplier } from '../../suppliers/entities/supplier.entity';
import { Department } from '../../departments/entities/department.entity';
import { Category } from '../../categories/entities/category.entity';

@Entity('assets')
export class Asset {
@PrimaryGeneratedColumn()
id: number;

@Column({ unique: true })
serialNumber: string;

@Column({ type: 'date' })
purchaseDate: Date;

@Column({ type: 'date', nullable: true })
warrantyEnd?: Date;

@ManyToOne(() => Supplier, { nullable: false })
@JoinColumn({ name: 'supplier_id' })
supplier: Supplier;

@ManyToOne(() => Department, { nullable: true })
@JoinColumn({ name: 'assigned_department_id' })
assignedDepartment?: Department;

@ManyToOne(() => Category, { nullable: false })
@JoinColumn({ name: 'category_id' })
category: Category;

@Column({ type: 'decimal', precision: 12, scale: 2, nullable: true })
purchaseCost?: number;

@Column({ type: 'text', nullable: true })
description?: string;

@Column({ default: true })
isActive: boolean;

@CreateDateColumn()
createdAt: Date;

@UpdateDateColumn()
updatedAt: Date;
}