Skip to content

Commit fe662b5

Browse files
Merge pull request #353 from nafiuishaaq/feat/AssetAudit
Feat/asset audit
2 parents 5338611 + 9db1216 commit fe662b5

11 files changed

Lines changed: 284 additions & 12 deletions

backend/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { AuthModule } from './auth/auth.module';
77
import { UsersModule } from './users/users.module';
88
import { EmailModule } from './email/email.module';
99
import { NewsletterModule } from './newsletter/newsletter.module';
10+
import { AssetAuditsModule } from './asset-audits/asset-audits.module';
1011
import { APP_GUARD } from '@nestjs/core';
1112
import { JwtAuthGuard } from './auth/guards/jwt.guard';
1213
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
@@ -65,6 +66,7 @@ import { CountriesCurrenciesModule } from './countries-currencies/countries-curr
6566
NewsletterModule,
6667
InventoryItemsModule,
6768
CountriesCurrenciesModule,
69+
AssetAuditsModule,
6870
],
6971
controllers: [AppController],
7072
providers: [
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
2+
import { AssetAuditsService } from './asset-audits.service';
3+
import { CreateAssetAuditDto } from './dto/create-asset-audit.dto';
4+
import { UpdateAssetAuditDto } from './dto/update-asset-audit.dto';
5+
6+
@Controller('asset-audits')
7+
export class AssetAuditsController {
8+
constructor(private readonly assetAuditsService: AssetAuditsService) {}
9+
10+
@Post()
11+
create(@Body() createAssetAuditDto: CreateAssetAuditDto) {
12+
return this.assetAuditsService.create(createAssetAuditDto);
13+
}
14+
15+
@Get()
16+
findAll() {
17+
return this.assetAuditsService.findAll();
18+
}
19+
20+
@Get(':id')
21+
findOne(@Param('id') id: string) {
22+
return this.assetAuditsService.findOne(id);
23+
}
24+
25+
@Patch(':id')
26+
update(@Param('id') id: string, @Body() updateAssetAuditDto: UpdateAssetAuditDto) {
27+
return this.assetAuditsService.update(id, updateAssetAuditDto);
28+
}
29+
30+
@Delete(':id')
31+
remove(@Param('id') id: string) {
32+
return this.assetAuditsService.remove(id);
33+
}
34+
35+
@Get('report/audited-vs-missing')
36+
getAuditedVsMissingReport() {
37+
return this.assetAuditsService.getAuditedVsMissingReport();
38+
}
39+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Module } from '@nestjs/common';
2+
import { TypeOrmModule } from '@nestjs/typeorm';
3+
import { AssetAudit } from './entities/asset-audit.entity';
4+
import { AssetAuditsService } from './asset-audits.service';
5+
import { AssetAuditsController } from './asset-audits.controller';
6+
import { AssetsModule } from '../assets/assets.module';
7+
8+
@Module({
9+
imports: [TypeOrmModule.forFeature([AssetAudit]), AssetsModule],
10+
providers: [AssetAuditsService],
11+
controllers: [AssetAuditsController],
12+
})
13+
export class AssetAuditsModule {}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { Injectable, NotFoundException } from '@nestjs/common';
2+
import { InjectRepository } from '@nestjs/typeorm';
3+
import { Repository } from 'typeorm';
4+
import { AssetAudit } from './entities/asset-audit.entity';
5+
import { CreateAssetAuditDto } from './dto/create-asset-audit.dto';
6+
import { UpdateAssetAuditDto } from './dto/update-asset-audit.dto';
7+
import { AssetsService } from '../assets/assets.service';
8+
9+
@Injectable()
10+
export class AssetAuditsService {
11+
constructor(
12+
@InjectRepository(AssetAudit) private readonly assetAuditRepository: Repository<AssetAudit>,
13+
private readonly assetsService: AssetsService,
14+
) {}
15+
16+
async create(createAssetAuditDto: CreateAssetAuditDto): Promise<AssetAudit> {
17+
const { assetId } = createAssetAuditDto;
18+
const asset = await this.assetsService.findOne(assetId);
19+
if (!asset) {
20+
throw new NotFoundException(`Asset with ID ${assetId} not found.`);
21+
}
22+
const assetAudit = this.assetAuditRepository.create({ ...createAssetAuditDto, asset });
23+
return this.assetAuditRepository.save(assetAudit);
24+
}
25+
26+
async findAll(): Promise<AssetAudit[]> {
27+
return this.assetAuditRepository.find({ relations: ['asset'] });
28+
}
29+
30+
async findOne(id: string): Promise<AssetAudit> {
31+
const assetAudit = await this.assetAuditRepository.findOne({ where: { id }, relations: ['asset'] });
32+
if (!assetAudit) {
33+
throw new NotFoundException(`Asset Audit with ID ${id} not found.`);
34+
}
35+
return assetAudit;
36+
}
37+
38+
async update(id: string, updateAssetAuditDto: UpdateAssetAuditDto): Promise<AssetAudit> {
39+
const assetAudit = await this.findOne(id);
40+
const updated = Object.assign(assetAudit, updateAssetAuditDto);
41+
return this.assetAuditRepository.save(updated);
42+
}
43+
44+
async remove(id: string): Promise<void> {
45+
const result = await this.assetAuditRepository.delete(id);
46+
if (result.affected === 0) {
47+
throw new NotFoundException(`Asset Audit with ID ${id} not found.`);
48+
}
49+
}
50+
51+
async getAuditedVsMissingReport(): Promise<any> {
52+
const totalAssets = await this.assetsService.countAll();
53+
const auditedAssets = await this.assetAuditRepository.count();
54+
55+
const missingAssets = totalAssets - auditedAssets;
56+
57+
return {
58+
totalAssets,
59+
auditedAssets,
60+
missingAssets,
61+
percentageAudited: totalAssets > 0 ? (auditedAssets / totalAssets) * 100 : 0,
62+
percentageMissing: totalAssets > 0 ? (missingAssets / totalAssets) * 100 : 0,
63+
};
64+
}
65+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { IsUUID, IsDateString, IsString, IsNotEmpty, IsOptional } from 'class-validator';
2+
3+
export class CreateAssetAuditDto {
4+
@IsUUID()
5+
@IsNotEmpty()
6+
assetId: string;
7+
8+
@IsDateString()
9+
@IsOptional()
10+
auditDate?: Date;
11+
12+
@IsString()
13+
@IsNotEmpty()
14+
auditedBy: string;
15+
16+
@IsString()
17+
@IsNotEmpty()
18+
status: string;
19+
20+
@IsString()
21+
@IsOptional()
22+
remarks?: string;
23+
}
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 { CreateAssetAuditDto } from './create-asset-audit.dto';
3+
4+
export class UpdateAssetAuditDto extends PartialType(CreateAssetAuditDto) {}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
2+
import { Asset } from '../../assets/assets.entity';
3+
4+
@Entity('asset_audits')
5+
export class AssetAudit {
6+
@PrimaryGeneratedColumn('uuid')
7+
id: string;
8+
9+
@ManyToOne(() => Asset, (asset) => asset.audits)
10+
asset: Asset;
11+
12+
@Column({ type: 'uuid' })
13+
assetId: string;
14+
15+
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
16+
auditDate: Date;
17+
18+
@Column()
19+
auditedBy: string;
20+
21+
@Column()
22+
status: string; // e.g., 'compliant', 'non-compliant', 'missing'
23+
24+
@Column({ nullable: true })
25+
remarks: string;
26+
}

backend/src/assets/assets.entity.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from 'typeorm';
22
import { Supplier } from '../suppliers/suppliers.entity';
33
import { AssetDisposal } from 'src/asset-disposals/entities/asset-disposal.entity';
4+
import { AssetAudit } from '../asset-audits/entities/asset-audit.entity';
45

56
@Entity('assets')
67
export class Asset {
@@ -19,6 +20,9 @@ export class Asset {
1920
@OneToMany(() => AssetDisposal, (disposal) => disposal.asset)
2021
disposals: AssetDisposal[];
2122

23+
@OneToMany(() => AssetAudit, (assetAudit) => assetAudit.asset)
24+
audits: AssetAudit[];
25+
2226
@Column({ default: 'active' })
2327
status: 'active' | 'disposed';
2428
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { TypeOrmModule } from '@nestjs/typeorm';
2+
import { Module } from '@nestjs/common';
3+
import { Asset } from './assets.entity';
4+
import { AssetsService } from './assets.service';
5+
6+
@Module({
7+
imports: [TypeOrmModule.forFeature([Asset])],
8+
providers: [AssetsService],
9+
exports: [AssetsService], // Export AssetsService
10+
})
11+
export class AssetsModule {}
Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,44 @@
1-
import { Injectable } from "@nestjs/common";
2-
import { InjectRepository } from "@nestjs/typeorm";
3-
import { Repository } from "typeorm";
4-
import { Asset } from "./assets.entity";
1+
import { Injectable, NotFoundException } from '@nestjs/common';
2+
import { InjectRepository } from '@nestjs/typeorm';
3+
import { Repository } from 'typeorm';
4+
import { Asset } from './assets.entity';
55

66
@Injectable()
7-
export class AssetService {
8-
constructor(
9-
@InjectRepository(Asset)
10-
private assetRepo: Repository<Asset>,
11-
) {}
7+
export class AssetsService {
8+
constructor(
9+
@InjectRepository(Asset) private readonly assetRepository: Repository<Asset>,
10+
) {}
1211

13-
async findActive() {
14-
return this.assetRepo.find({ where: { status: 'active' } });
15-
}
12+
async create(asset: Partial<Asset>): Promise<Asset> {
13+
const newAsset = this.assetRepository.create(asset);
14+
return this.assetRepository.save(newAsset);
15+
}
16+
17+
async findAll(): Promise<Asset[]> {
18+
return this.assetRepository.find();
19+
}
20+
21+
async findOne(id: string): Promise<Asset> {
22+
const asset = await this.assetRepository.findOne({ where: { id } });
23+
if (!asset) {
24+
throw new NotFoundException(`Asset with ID ${id} not found`);
25+
}
26+
return asset;
27+
}
28+
29+
async update(id: string, asset: Partial<Asset>): Promise<Asset> {
30+
await this.assetRepository.update(id, asset);
31+
return this.findOne(id);
32+
}
33+
34+
async remove(id: string): Promise<void> {
35+
const result = await this.assetRepository.delete(id);
36+
if (result.affected === 0) {
37+
throw new NotFoundException(`Asset with ID ${id} not found`);
38+
}
39+
}
40+
41+
async countAll(): Promise<number> {
42+
return this.assetRepository.count();
43+
}
1644
}

0 commit comments

Comments
 (0)