diff --git a/migration/1776937038432-AddMros.js b/migration/1776937038432-AddMros.js new file mode 100644 index 0000000000..50d735c418 --- /dev/null +++ b/migration/1776937038432-AddMros.js @@ -0,0 +1,28 @@ +/** + * @typedef {import('typeorm').MigrationInterface} MigrationInterface + * @typedef {import('typeorm').QueryRunner} QueryRunner + */ + +/** + * @class + * @implements {MigrationInterface} + */ +module.exports = class AddMros1776937038432 { + name = 'AddMros1776937038432' + + /** + * @param {QueryRunner} queryRunner + */ + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "mros" ("id" int NOT NULL IDENTITY(1,1), "updated" datetime2 NOT NULL CONSTRAINT "DF_f6ade72c09ca260e3ce42ba0781" DEFAULT getdate(), "created" datetime2 NOT NULL CONSTRAINT "DF_d7ed4994a2c27be9ea6c21b1c21" DEFAULT getdate(), "status" nvarchar(256) NOT NULL, "submissionDate" datetime2, "authorityReference" nvarchar(256), "caseManager" nvarchar(256) NOT NULL, "userDataId" int NOT NULL, CONSTRAINT "PK_48a5606a1194ef6f78c24999754" PRIMARY KEY ("id"))`); + await queryRunner.query(`ALTER TABLE "mros" ADD CONSTRAINT "FK_021227644566f36c31912257a39" FOREIGN KEY ("userDataId") REFERENCES "user_data"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + + /** + * @param {QueryRunner} queryRunner + */ + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "mros" DROP CONSTRAINT "FK_021227644566f36c31912257a39"`); + await queryRunner.query(`DROP TABLE "mros"`); + } +} diff --git a/src/subdomains/supporting/mros/dto/create-mros.dto.ts b/src/subdomains/supporting/mros/dto/create-mros.dto.ts new file mode 100644 index 0000000000..8c20009d51 --- /dev/null +++ b/src/subdomains/supporting/mros/dto/create-mros.dto.ts @@ -0,0 +1,24 @@ +import { IsDateString, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { MrosStatus } from '../mros-status.enum'; + +export class CreateMrosDto { + @IsNotEmpty() + @IsInt() + userDataId: number; + + @IsNotEmpty() + @IsEnum(MrosStatus) + status: MrosStatus; + + @IsOptional() + @IsDateString() + submissionDate?: Date; + + @IsOptional() + @IsString() + authorityReference?: string; + + @IsNotEmpty() + @IsString() + caseManager: string; +} diff --git a/src/subdomains/supporting/mros/dto/update-mros.dto.ts b/src/subdomains/supporting/mros/dto/update-mros.dto.ts new file mode 100644 index 0000000000..94faa0c17f --- /dev/null +++ b/src/subdomains/supporting/mros/dto/update-mros.dto.ts @@ -0,0 +1,21 @@ +import { IsDateString, IsEnum, IsString } from 'class-validator'; +import { IsOptionalButNotNull } from 'src/shared/validators/is-not-null.validator'; +import { MrosStatus } from '../mros-status.enum'; + +export class UpdateMrosDto { + @IsOptionalButNotNull() + @IsEnum(MrosStatus) + status?: MrosStatus; + + @IsOptionalButNotNull() + @IsDateString() + submissionDate?: Date; + + @IsOptionalButNotNull() + @IsString() + authorityReference?: string; + + @IsOptionalButNotNull() + @IsString() + caseManager?: string; +} diff --git a/src/subdomains/supporting/mros/mros-status.enum.ts b/src/subdomains/supporting/mros/mros-status.enum.ts new file mode 100644 index 0000000000..fa5dc906f9 --- /dev/null +++ b/src/subdomains/supporting/mros/mros-status.enum.ts @@ -0,0 +1,6 @@ +export enum MrosStatus { + DRAFT = 'Draft', + SUBMITTED = 'Submitted', + CONFIRMED = 'Confirmed', + CLOSED = 'Closed', +} diff --git a/src/subdomains/supporting/mros/mros.controller.ts b/src/subdomains/supporting/mros/mros.controller.ts new file mode 100644 index 0000000000..78991b976c --- /dev/null +++ b/src/subdomains/supporting/mros/mros.controller.ts @@ -0,0 +1,48 @@ +import { Body, Controller, Get, Param, Post, Put, UseGuards } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; +import { ApiBearerAuth, ApiExcludeEndpoint, ApiTags } from '@nestjs/swagger'; +import { RoleGuard } from 'src/shared/auth/role.guard'; +import { UserActiveGuard } from 'src/shared/auth/user-active.guard'; +import { UserRole } from 'src/shared/auth/user-role.enum'; +import { CreateMrosDto } from './dto/create-mros.dto'; +import { UpdateMrosDto } from './dto/update-mros.dto'; +import { Mros } from './mros.entity'; +import { MrosService } from './mros.service'; + +@ApiTags('Mros') +@Controller('mros') +export class MrosController { + constructor(private readonly mrosService: MrosService) {} + + @Post() + @ApiBearerAuth() + @ApiExcludeEndpoint() + @UseGuards(AuthGuard(), RoleGuard(UserRole.COMPLIANCE), UserActiveGuard()) + async createMros(@Body() dto: CreateMrosDto): Promise { + await this.mrosService.create(dto); + } + + @Put(':id') + @ApiBearerAuth() + @ApiExcludeEndpoint() + @UseGuards(AuthGuard(), RoleGuard(UserRole.COMPLIANCE), UserActiveGuard()) + async updateMros(@Param('id') id: string, @Body() dto: UpdateMrosDto): Promise { + await this.mrosService.update(+id, dto); + } + + @Get() + @ApiBearerAuth() + @ApiExcludeEndpoint() + @UseGuards(AuthGuard(), RoleGuard(UserRole.COMPLIANCE), UserActiveGuard()) + async getAll(): Promise { + return this.mrosService.getAll(); + } + + @Get(':id') + @ApiBearerAuth() + @ApiExcludeEndpoint() + @UseGuards(AuthGuard(), RoleGuard(UserRole.COMPLIANCE), UserActiveGuard()) + async getById(@Param('id') id: string): Promise { + return this.mrosService.getById(+id); + } +} diff --git a/src/subdomains/supporting/mros/mros.entity.ts b/src/subdomains/supporting/mros/mros.entity.ts new file mode 100644 index 0000000000..cae91bcf39 --- /dev/null +++ b/src/subdomains/supporting/mros/mros.entity.ts @@ -0,0 +1,22 @@ +import { IEntity } from 'src/shared/models/entity'; +import { UserData } from 'src/subdomains/generic/user/models/user-data/user-data.entity'; +import { Column, Entity, ManyToOne } from 'typeorm'; +import { MrosStatus } from './mros-status.enum'; + +@Entity() +export class Mros extends IEntity { + @ManyToOne(() => UserData, { nullable: false }) + userData: UserData; + + @Column({ length: 256 }) + status: MrosStatus; + + @Column({ type: 'datetime2', nullable: true }) + submissionDate?: Date; + + @Column({ length: 256, nullable: true }) + authorityReference?: string; + + @Column({ length: 256 }) + caseManager: string; +} diff --git a/src/subdomains/supporting/mros/mros.module.ts b/src/subdomains/supporting/mros/mros.module.ts new file mode 100644 index 0000000000..3593564319 --- /dev/null +++ b/src/subdomains/supporting/mros/mros.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SharedModule } from 'src/shared/shared.module'; +import { UserModule } from 'src/subdomains/generic/user/user.module'; +import { MrosController } from './mros.controller'; +import { Mros } from './mros.entity'; +import { MrosRepository } from './mros.repository'; +import { MrosService } from './mros.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([Mros]), SharedModule, UserModule], + controllers: [MrosController], + providers: [MrosRepository, MrosService], + exports: [], +}) +export class MrosModule {} diff --git a/src/subdomains/supporting/mros/mros.repository.ts b/src/subdomains/supporting/mros/mros.repository.ts new file mode 100644 index 0000000000..cfbf8592e7 --- /dev/null +++ b/src/subdomains/supporting/mros/mros.repository.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { BaseRepository } from 'src/shared/repositories/base.repository'; +import { EntityManager } from 'typeorm'; +import { Mros } from './mros.entity'; + +@Injectable() +export class MrosRepository extends BaseRepository { + constructor(manager: EntityManager) { + super(Mros, manager); + } +} diff --git a/src/subdomains/supporting/mros/mros.service.ts b/src/subdomains/supporting/mros/mros.service.ts new file mode 100644 index 0000000000..cdce8768f9 --- /dev/null +++ b/src/subdomains/supporting/mros/mros.service.ts @@ -0,0 +1,41 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { UserDataService } from 'src/subdomains/generic/user/models/user-data/user-data.service'; +import { CreateMrosDto } from './dto/create-mros.dto'; +import { UpdateMrosDto } from './dto/update-mros.dto'; +import { Mros } from './mros.entity'; +import { MrosRepository } from './mros.repository'; + +@Injectable() +export class MrosService { + constructor( + private readonly repo: MrosRepository, + private readonly userDataService: UserDataService, + ) {} + + async create(dto: CreateMrosDto): Promise { + const entity = this.repo.create(dto); + + entity.userData = await this.userDataService.getUserData(dto.userDataId); + if (!entity.userData) throw new NotFoundException('UserData not found'); + + return this.repo.save(entity); + } + + async update(id: number, dto: UpdateMrosDto): Promise { + const entity = await this.repo.findOneBy({ id }); + if (!entity) throw new NotFoundException('Mros not found'); + + return this.repo.save({ ...entity, ...dto }); + } + + async getAll(): Promise { + return this.repo.find({ relations: { userData: true } }); + } + + async getById(id: number): Promise { + const entity = await this.repo.findOne({ where: { id }, relations: { userData: true } }); + if (!entity) throw new NotFoundException('Mros not found'); + + return entity; + } +} diff --git a/src/subdomains/supporting/recall/recall.controller.ts b/src/subdomains/supporting/recall/recall.controller.ts index 0e13e23051..f049ba98b8 100644 --- a/src/subdomains/supporting/recall/recall.controller.ts +++ b/src/subdomains/supporting/recall/recall.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Param, Post, Put, UseGuards } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post, Put, UseGuards } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { ApiBearerAuth, ApiExcludeEndpoint, ApiTags } from '@nestjs/swagger'; import { RoleGuard } from 'src/shared/auth/role.guard'; @@ -6,6 +6,7 @@ import { UserActiveGuard } from 'src/shared/auth/user-active.guard'; import { UserRole } from 'src/shared/auth/user-role.enum'; import { CreateRecallDto } from './dto/create-recall.dto'; import { UpdateRecallDto } from './dto/update-recall.dto'; +import { Recall } from './recall.entity'; import { RecallService } from './recall.service'; @ApiTags('Recall') @@ -16,7 +17,7 @@ export class RecallController { @Post() @ApiBearerAuth() @ApiExcludeEndpoint() - @UseGuards(AuthGuard(), RoleGuard(UserRole.ADMIN), UserActiveGuard()) + @UseGuards(AuthGuard(), RoleGuard(UserRole.COMPLIANCE), UserActiveGuard()) async createRecall(@Body() dto: CreateRecallDto): Promise { await this.recallService.create(dto); } @@ -24,8 +25,24 @@ export class RecallController { @Put(':id') @ApiBearerAuth() @ApiExcludeEndpoint() - @UseGuards(AuthGuard(), RoleGuard(UserRole.ADMIN), UserActiveGuard()) + @UseGuards(AuthGuard(), RoleGuard(UserRole.COMPLIANCE), UserActiveGuard()) async updateRecall(@Param('id') id: string, @Body() dto: UpdateRecallDto): Promise { await this.recallService.update(+id, dto); } + + @Get() + @ApiBearerAuth() + @ApiExcludeEndpoint() + @UseGuards(AuthGuard(), RoleGuard(UserRole.COMPLIANCE), UserActiveGuard()) + async getAll(): Promise { + return this.recallService.getAll(); + } + + @Get(':id') + @ApiBearerAuth() + @ApiExcludeEndpoint() + @UseGuards(AuthGuard(), RoleGuard(UserRole.COMPLIANCE), UserActiveGuard()) + async getById(@Param('id') id: string): Promise { + return this.recallService.getById(+id); + } } diff --git a/src/subdomains/supporting/recall/recall.service.ts b/src/subdomains/supporting/recall/recall.service.ts index ec19931891..f638875d95 100644 --- a/src/subdomains/supporting/recall/recall.service.ts +++ b/src/subdomains/supporting/recall/recall.service.ts @@ -48,4 +48,18 @@ export class RecallService { return this.repo.save({ ...entity, ...dto }); } + + async getAll(): Promise { + return this.repo.find({ relations: { bankTx: true, checkoutTx: true, user: true } }); + } + + async getById(id: number): Promise { + const entity = await this.repo.findOne({ + where: { id }, + relations: { bankTx: true, checkoutTx: true, user: true }, + }); + if (!entity) throw new NotFoundException('Recall not found'); + + return entity; + } } diff --git a/src/subdomains/supporting/supporting.module.ts b/src/subdomains/supporting/supporting.module.ts index 5f2650bebb..6ead699738 100644 --- a/src/subdomains/supporting/supporting.module.ts +++ b/src/subdomains/supporting/supporting.module.ts @@ -8,6 +8,7 @@ import { DexModule } from './dex/dex.module'; import { FiatOutputModule } from './fiat-output/fiat-output.module'; import { FiatPayInModule } from './fiat-payin/fiat-payin.module'; import { LogModule } from './log/log.module'; +import { MrosModule } from './mros/mros.module'; import { NotificationModule } from './notification/notification.module'; import { PayInModule } from './payin/payin.module'; import { PayoutModule } from './payout/payout.module'; @@ -34,6 +35,7 @@ import { SupportIssueModule } from './support-issue/support-issue.module'; FiatOutputModule, SupportIssueModule, RecallModule, + MrosModule, ], controllers: [], providers: [],