Skip to content
Open
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
326 changes: 251 additions & 75 deletions backend/package-lock.json

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"dependencies": {
"@aws-sdk/client-s3": "^3.975.0",
"@nestjs/bull": "^11.0.4",
"@nestjs/cache-manager": "^3.1.0",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.0.0",
Expand All @@ -30,7 +31,7 @@
"@nestjs/mapped-types": "*",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/schedule": "^6.0.1",
"@nestjs/schedule": "^6.1.0",
"@nestjs/swagger": "^7.3.0",
"@nestjs/throttler": "^6.5.0",
"@nestjs/typeorm": "^10.0.2",
Expand All @@ -39,9 +40,12 @@
"@types/speakeasy": "^2.0.10",
"@types/uuid": "^10.0.0",
"axios": "^1.6.0",
"bcrypt": "^6.0.0",
"bcryptjs": "^3.0.3",
"bull": "^4.16.5",
"bwip-js": "^4.7.0",
"cache-manager": "^7.2.8",
"cache-manager-redis-store": "^3.0.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.3",
"date-fns": "^4.1.0",
Expand Down Expand Up @@ -71,15 +75,17 @@
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/bcrypt": "^6.0.0",
"@types/bcryptjs": "^3.0.0",
"@types/bull": "^3.15.9",
"@types/cache-manager": "^4.0.6",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.2",
"@types/json2csv": "^5.0.7",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^20.3.1",
"@types/node": "^20.19.30",
"@types/node-cron": "^3.0.11",
"@types/nodemailer": "^7.0.5",
"@types/nodemailer": "^7.0.9",
"@types/otplib": "^7.0.0",
"@types/papaparse": "^5.3.16",
"@types/passport-jwt": "^4.0.1",
Expand Down
16 changes: 10 additions & 6 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,20 @@ import { ReportExecution } from './reports/entities/report-execution.entity';
// import { DocumentVersion } from './documents/entities/document-version.entity';
// import { DocumentAccessPermission } from './documents/entities/document-access-permission.entity';
// import { DocumentAuditLog } from './documents/entities/document-audit-log.entity';
import { TransfersModule } from './transfers/transfers.module';

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
ScheduleModule.forRoot(),
ThrottlerModule.forRoot([{
ttl: 60000,
limit: 10,
}]),
ThrottlerModule.forRoot([
{
ttl: 60000,
limit: 10,
},
]),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
Expand Down Expand Up @@ -81,7 +84,8 @@ import { ReportExecution } from './reports/entities/report-execution.entity';
AuditLogsModule,
AssetsModule,
AnalyticsModule,
ReportsModule, // Add the Reports Module
ReportsModule,
TransfersModule, // Add the Reports Module
],
controllers: [AppController],
providers: [
Expand All @@ -92,4 +96,4 @@ import { ReportExecution } from './reports/entities/report-execution.entity';
AppService,
],
})
export class AppModule {}
export class AppModule {}
112 changes: 112 additions & 0 deletions backend/src/transfers/controllers/transfers.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// src/transfers/controllers/transfer.controller.ts
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
UseGuards,
Request,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import { ApprovalRuleService } from '../services/approval-rule.service';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { ApproveTransferDto } from '../dto/approve-transfer.dto';
import { CreateApprovalRuleDto } from '../dto/create-approval-rule.dto';
import { CreateTransferDto } from '../dto/create-transfer.dto';
import { QueryTransfersDto } from '../dto/query-transfers.dto';
import { RejectTransferDto } from '../dto/reject-transfer.dto';
import { UpdateApprovalRuleDto } from '../dto/update-approval-rule.dto';
import { TransferService } from '../services/transfers.service';

@Controller('api/v1/transfers')
@UseGuards(JwtAuthGuard)
export class TransferController {
constructor(
private readonly transferService: TransferService,
private readonly approvalRuleService: ApprovalRuleService,
) {}

@Post()
async create(@Body() createTransferDto: CreateTransferDto, @Request() req) {
return this.transferService.create(createTransferDto, req.user.id);
}

@Get()
async findAll(@Query() query: QueryTransfersDto) {
return this.transferService.findAll(query);
}

@Get('pending-approval')
async getPendingApprovals(@Request() req) {
return this.transferService.getPendingApprovals(req.user.id);
}

@Get(':id')
async findOne(@Param('id') id: string) {
return this.transferService.findOne(id);
}

@Put(':id/approve')
async approve(
@Param('id') id: string,
@Body() dto: ApproveTransferDto,
@Request() req,
) {
return this.transferService.approve(id, req.user.id, dto);
}

@Put(':id/reject')
async reject(
@Param('id') id: string,
@Body() dto: RejectTransferDto,
@Request() req,
) {
return this.transferService.reject(id, req.user.id, dto);
}

@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
async cancel(@Param('id') id: string, @Request() req) {
await this.transferService.cancel(id, req.user.id);
}

@Post(':id/execute')
async execute(@Param('id') id: string, @Request() req) {
return this.transferService.executeTransfer(id, req.user.id);
}

@Post(':id/undo')
async undo(@Param('id') id: string, @Request() req) {
return this.transferService.undoTransfer(id, req.user.id);
}

// Approval Rules endpoints
@Post('approval-rules')
async createRule(@Body() dto: CreateApprovalRuleDto) {
return this.approvalRuleService.create(dto);
}

@Get('approval-rules')
async findAllRules() {
return this.approvalRuleService.findAll();
}

@Put('approval-rules/:id')
async updateRule(
@Param('id') id: string,
@Body() dto: UpdateApprovalRuleDto,
) {
return this.approvalRuleService.update(id, dto);
}

@Delete('approval-rules/:id')
@HttpCode(HttpStatus.NO_CONTENT)
async deleteRule(@Param('id') id: string) {
await this.approvalRuleService.remove(id);
}
}
7 changes: 7 additions & 0 deletions backend/src/transfers/dto/approve-transfer.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IsOptional, IsString } from 'class-validator';

export class ApproveTransferDto {
@IsOptional()
@IsString()
notes?: string;
}
38 changes: 38 additions & 0 deletions backend/src/transfers/dto/create-approval-rule.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
IsString,
IsNotEmpty,
IsOptional,
IsBoolean,
IsNumber,
IsObject,
IsUUID,
} from 'class-validator';
import { ApprovalConditions } from '../entities/transfer-approval-rule.entity';

export class CreateApprovalRuleDto {
@IsString()
@IsNotEmpty()
name: string;

@IsOptional()
@IsString()
description?: string;

@IsObject()
conditions: ApprovalConditions;

@IsUUID()
approverRoleId: string;

@IsOptional()
@IsUUID()
approverUserId?: string;

@IsOptional()
@IsBoolean()
isActive?: boolean;

@IsOptional()
@IsNumber()
priority?: number;
}
92 changes: 92 additions & 0 deletions backend/src/transfers/dto/create-transfer.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// src/transfers/dto/create-transfer.dto.ts
import {
IsEnum,
IsUUID,
IsArray,
IsString,
IsOptional,
MinLength,
MaxLength,
IsDateString,
IsNotEmpty,
ArrayMinSize,
ValidateIf,
} from 'class-validator';
import { TransferType } from '../entities/transfer.entity';

export class CreateTransferDto {
@IsEnum(TransferType)
transferType: TransferType;

@IsArray()
@ArrayMinSize(1, { message: 'At least one asset must be selected' })
@IsUUID('4', { each: true })
assetIds: string[];

@ValidateIf(
(o) =>
o.transferType === TransferType.USER ||
o.transferType === TransferType.COMPLETE,
)
@IsUUID()
@IsOptional()
fromUserId?: string;

@ValidateIf(
(o) =>
o.transferType === TransferType.USER ||
o.transferType === TransferType.COMPLETE,
)
@IsUUID()
@IsNotEmpty()
toUserId?: string;

@ValidateIf(
(o) =>
o.transferType === TransferType.DEPARTMENT ||
o.transferType === TransferType.COMPLETE,
)
@IsUUID()
@IsOptional()
fromDepartmentId?: string;

@ValidateIf(
(o) =>
o.transferType === TransferType.DEPARTMENT ||
o.transferType === TransferType.COMPLETE,
)
@IsUUID()
@IsNotEmpty()
toDepartmentId?: string;

@ValidateIf(
(o) =>
o.transferType === TransferType.LOCATION ||
o.transferType === TransferType.COMPLETE,
)
@IsUUID()
@IsOptional()
fromLocationId?: string;

@ValidateIf(
(o) =>
o.transferType === TransferType.LOCATION ||
o.transferType === TransferType.COMPLETE,
)
@IsUUID()
@IsNotEmpty()
toLocationId?: string;

@IsString()
@MinLength(10, { message: 'Reason must be at least 10 characters' })
@MaxLength(500, { message: 'Reason must not exceed 500 characters' })
reason: string;

@IsOptional()
@IsString()
notes?: string;

@IsOptional()
@IsDateString()
scheduledDate?: string;
}
30 changes: 30 additions & 0 deletions backend/src/transfers/dto/query-transfers.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { IsOptional, IsEnum, IsUUID, IsDateString } from 'class-validator';
import { TransferStatus, TransferType } from '../entities/transfer.entity';

export class QueryTransfersDto {
@IsOptional()
@IsEnum(TransferStatus)
status?: TransferStatus;

@IsOptional()
@IsEnum(TransferType)
transferType?: TransferType;

@IsOptional()
@IsUUID()
requestedBy?: string;

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

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

@IsOptional()
page?: number = 1;

@IsOptional()
limit?: number = 20;
}
8 changes: 8 additions & 0 deletions backend/src/transfers/dto/reject-transfer.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IsString, IsNotEmpty, MinLength } from 'class-validator';

export class RejectTransferDto {
@IsString()
@IsNotEmpty()
@MinLength(10, { message: 'Rejection reason must be at least 10 characters' })
rejectionReason: string;
}
4 changes: 4 additions & 0 deletions backend/src/transfers/dto/update-approval-rule.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateApprovalRuleDto } from './create-approval-rule.dto';

export class UpdateApprovalRuleDto extends PartialType(CreateApprovalRuleDto) {}
4 changes: 4 additions & 0 deletions backend/src/transfers/dto/update-transfer.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateTransferDto } from './create-transfer.dto';

export class UpdateTransferDto extends PartialType(CreateTransferDto) {}
Loading
Loading