From 0eba7b9b82c43638fe9c91cb6ccd0842fa0765df Mon Sep 17 00:00:00 2001 From: Musa Khalid <112591148+Mkalbani@users.noreply.github.com> Date: Fri, 23 Jan 2026 21:28:57 +0000 Subject: [PATCH] Feat: implemented asset mgt module --- backend/src/app.module.ts | 2 +- .../asset-categories/asset-category.entity.ts | 40 +++++++++++++++++ backend/src/assets/assets.module.ts | 14 +++--- backend/src/assets/assets.service.ts | 37 +++++++++++++--- backend/src/assets/dto/asset-query.dto.ts | 4 +- backend/src/assets/dto/create-asset.dto.ts | 5 +-- backend/src/assets/entities/asset.entity.ts | 6 ++- backend/src/departments/department.entity.ts | 37 ++++++++++++++++ backend/src/locations/location.entity.ts | 43 +++++++++++++++++++ backend/src/users/entities/user.entity.ts | 41 ++++++++++++++++++ 10 files changed, 209 insertions(+), 20 deletions(-) create mode 100644 backend/src/asset-categories/asset-category.entity.ts create mode 100644 backend/src/departments/department.entity.ts create mode 100644 backend/src/locations/location.entity.ts create mode 100644 backend/src/users/entities/user.entity.ts diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index ff1bb95..47276a5 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -27,7 +27,7 @@ import { AssetCategory } from './asset-categories/asset-category.entity'; import { Department } from './departments/department.entity'; import { User } from './users/entities/user.entity'; import { FileUpload } from './file-uploads/entities/file-upload.entity'; -import { Asset } from './assets/entities/assest.entity'; +import { Asset } from './assets/entities/asset.entity'; import { Supplier } from './suppliers/entities/supplier.entity'; import { AssetCategoriesModule } from './asset-categories/asset-categories.module'; import { DepartmentsModule } from './departments/departments.module'; diff --git a/backend/src/asset-categories/asset-category.entity.ts b/backend/src/asset-categories/asset-category.entity.ts new file mode 100644 index 0000000..12a4577 --- /dev/null +++ b/backend/src/asset-categories/asset-category.entity.ts @@ -0,0 +1,40 @@ +import { + Entity, + Column, + PrimaryGeneratedColumn, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + Index, +} from 'typeorm'; + +@Entity('asset_categories') +@Index(['code'], { unique: true }) +export class AssetCategory { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ length: 100, unique: true }) + code: string; + + @Column({ length: 200 }) + name: string; + + @Column({ type: 'text', nullable: true }) + description: string; + + @Column({ type: 'decimal', precision: 5, scale: 2, default: 0 }) + depreciationRate: number; + + @Column({ default: true }) + isActive: boolean; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt: Date; + + @DeleteDateColumn({ name: 'deleted_at' }) + deletedAt: Date; +} diff --git a/backend/src/assets/assets.module.ts b/backend/src/assets/assets.module.ts index f1459f8..bbd52e6 100644 --- a/backend/src/assets/assets.module.ts +++ b/backend/src/assets/assets.module.ts @@ -3,17 +3,19 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AssetsService } from './assets.service'; import { AssetsController } from './assets.controller'; import { Asset } from './entities/asset.entity'; -// import { AssetCategory } from '../asset-categories/asset-category.entity'; -// import { Department } from '../departments/department.entity'; -// import { User } from '../users/entities/user.entity'; +import { AssetCategory } from '../asset-categories/asset-category.entity'; +import { Department } from '../departments/department.entity'; +import { Location } from '../locations/location.entity'; +import { User } from '../users/entities/user.entity'; @Module({ imports: [ TypeOrmModule.forFeature([ Asset, - // AssetCategory, - // Department, - // User, + AssetCategory, + Department, + Location, + User, ]), ], controllers: [AssetsController], diff --git a/backend/src/assets/assets.service.ts b/backend/src/assets/assets.service.ts index 799ed9e..29137e1 100644 --- a/backend/src/assets/assets.service.ts +++ b/backend/src/assets/assets.service.ts @@ -10,6 +10,7 @@ import { Repository, DataSource } from 'typeorm'; import { Asset, AssetStatus } from './entities/asset.entity'; import { AssetCategory } from '../asset-categories/asset-category.entity'; import { Department } from '../departments/department.entity'; +import { Location } from '../locations/location.entity'; import { User } from '../users/entities/user.entity'; import { CreateAssetDto, BulkCreateAssetDto } from './dto/create-asset.dto'; import { @@ -38,6 +39,8 @@ export class AssetsService { private readonly categoryRepository: Repository, @InjectRepository(Department) private readonly departmentRepository: Repository, + @InjectRepository(Location) + private readonly locationRepository: Repository, @InjectRepository(User) private readonly userRepository: Repository, private readonly dataSource: DataSource, @@ -67,6 +70,18 @@ export class AssetsService { ); } + let location = null; + if (createAssetDto.locationId) { + location = await this.locationRepository.findOne({ + where: { id: createAssetDto.locationId }, + }); + if (!location) { + throw new NotFoundException( + `Location with ID ${createAssetDto.locationId} not found`, + ); + } + } + let assignedUser = null; if (createAssetDto.assignedToId) { assignedUser = await this.userRepository.findOne({ @@ -123,7 +138,7 @@ export class AssetsService { assetId, category, department, - location: createAssetDto.location, + location, assignedTo: assignedUser, currentValue, createdBy: creatingUser, @@ -154,7 +169,7 @@ export class AssetsService { sortOrder = 'DESC', categoryId, departmentId, - location, + locationId, assignedToId, status, condition, @@ -169,6 +184,7 @@ export class AssetsService { .createQueryBuilder('asset') .leftJoinAndSelect('asset.category', 'category') .leftJoinAndSelect('asset.department', 'department') + .leftJoinAndSelect('asset.location', 'location') .leftJoinAndSelect('asset.assignedTo', 'assignedTo') .leftJoinAndSelect('asset.createdBy', 'createdBy') .leftJoinAndSelect('asset.updatedBy', 'updatedBy'); @@ -188,10 +204,8 @@ export class AssetsService { queryBuilder.andWhere('asset.departmentId = :departmentId', { departmentId }); } - if (location) { - queryBuilder.andWhere('asset.location ILIKE :location', { - location: `%${location}%`, - }); + if (locationId) { + queryBuilder.andWhere('asset.locationId = :locationId', { locationId }); } if (assignedToId) { @@ -302,6 +316,17 @@ export class AssetsService { } } + if (updateAssetDto.locationId) { + const location = await this.locationRepository.findOne({ + where: { id: updateAssetDto.locationId }, + }); + if (!location) { + throw new NotFoundException( + `Location with ID ${updateAssetDto.locationId} not found`, + ); + } + } + if (updateAssetDto.assignedToId) { const assignedUser = await this.userRepository.findOne({ where: { id: updateAssetDto.assignedToId }, diff --git a/backend/src/assets/dto/asset-query.dto.ts b/backend/src/assets/dto/asset-query.dto.ts index 5e34d3d..75653c7 100644 --- a/backend/src/assets/dto/asset-query.dto.ts +++ b/backend/src/assets/dto/asset-query.dto.ts @@ -42,8 +42,8 @@ export class AssetQueryDto { departmentId?: string; @IsOptional() - @IsString() - location?: string; + @IsUUID() + locationId?: string; @IsOptional() @IsUUID() diff --git a/backend/src/assets/dto/create-asset.dto.ts b/backend/src/assets/dto/create-asset.dto.ts index 96a57b9..af0da72 100644 --- a/backend/src/assets/dto/create-asset.dto.ts +++ b/backend/src/assets/dto/create-asset.dto.ts @@ -69,9 +69,8 @@ export class CreateAssetDto { departmentId: string; @IsOptional() - @IsString() - @MaxLength(200) - location?: string; + @IsUUID() + locationId?: string; @IsOptional() @IsUUID() diff --git a/backend/src/assets/entities/asset.entity.ts b/backend/src/assets/entities/asset.entity.ts index 7882ee4..1cfb5a0 100644 --- a/backend/src/assets/entities/asset.entity.ts +++ b/backend/src/assets/entities/asset.entity.ts @@ -11,6 +11,7 @@ import { } from 'typeorm'; import { AssetCategory } from '../../asset-categories/asset-category.entity'; import { Department } from '../../departments/department.entity'; +import { Location } from '../../locations/location.entity'; import { User } from '../../users/entities/user.entity'; export enum AssetStatus { @@ -96,8 +97,9 @@ export class Asset { @JoinColumn({ name: 'department_id' }) department: Department; - @Column({ name: 'location', length: 200, nullable: true }) - location: string; + @ManyToOne(() => Location, { eager: true, nullable: true }) + @JoinColumn({ name: 'location_id' }) + location: Location; @ManyToOne(() => User, { nullable: true, eager: true }) @JoinColumn({ name: 'assigned_to_id' }) diff --git a/backend/src/departments/department.entity.ts b/backend/src/departments/department.entity.ts new file mode 100644 index 0000000..45eff97 --- /dev/null +++ b/backend/src/departments/department.entity.ts @@ -0,0 +1,37 @@ +import { + Entity, + Column, + PrimaryGeneratedColumn, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + Index, +} from 'typeorm'; + +@Entity('departments') +@Index(['code'], { unique: true }) +export class Department { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ length: 100, unique: true }) + code: string; + + @Column({ length: 200 }) + name: string; + + @Column({ type: 'text', nullable: true }) + description: string; + + @Column({ default: true }) + isActive: boolean; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt: Date; + + @DeleteDateColumn({ name: 'deleted_at' }) + deletedAt: Date; +} diff --git a/backend/src/locations/location.entity.ts b/backend/src/locations/location.entity.ts new file mode 100644 index 0000000..9f2a78c --- /dev/null +++ b/backend/src/locations/location.entity.ts @@ -0,0 +1,43 @@ +import { + Entity, + Column, + PrimaryGeneratedColumn, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + Index, +} from 'typeorm'; + +@Entity('locations') +@Index(['code'], { unique: true }) +export class Location { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ length: 100, unique: true }) + code: string; + + @Column({ length: 200 }) + name: string; + + @Column({ type: 'text', nullable: true }) + description: string; + + @Column({ length: 100, nullable: true }) + building: string; + + @Column({ length: 100, nullable: true }) + floor: string; + + @Column({ default: true }) + isActive: boolean; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt: Date; + + @DeleteDateColumn({ name: 'deleted_at' }) + deletedAt: Date; +} diff --git a/backend/src/users/entities/user.entity.ts b/backend/src/users/entities/user.entity.ts new file mode 100644 index 0000000..2b8c001 --- /dev/null +++ b/backend/src/users/entities/user.entity.ts @@ -0,0 +1,41 @@ +import { + Entity, + Column, + PrimaryGeneratedColumn, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, +} from 'typeorm'; + +@Entity('users') +export class User { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ length: 100, unique: true }) + username: string; + + @Column({ length: 255, unique: true }) + email: string; + + @Column({ length: 100 }) + firstName: string; + + @Column({ length: 100 }) + lastName: string; + + @Column({ select: false, nullable: true }) + password: string; + + @Column({ default: true }) + isActive: boolean; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt: Date; + + @DeleteDateColumn({ name: 'deleted_at' }) + deletedAt: Date; +}