From f44295c1cac7194d5c2e4af4469b8c5dbb1b3001 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Thu, 23 Apr 2026 17:26:39 +0200 Subject: [PATCH 1/3] feat: add report fields to Mros entity Extends the Mros entity with the core goAML SAR report-content fields that every MROS submission needs: - reportCode (default SAR) - reason (Sachverhalt long text) - action (Grund / Unternommen long text) - indicators (JSON-serialised array of goAML indicator codes) reason/action/indicators are nullable for backward compatibility with existing rows; the DTOs mark them optional so the UI can roll the fields out gradually. --- .../1776957972101-AddMrosReportFields.js | 35 +++++++++++++++++++ .../supporting/mros/dto/create-mros.dto.ts | 19 +++++++++- .../supporting/mros/dto/update-mros.dto.ts | 19 +++++++++- src/subdomains/supporting/mros/mros.entity.ts | 21 +++++++++++ .../supporting/mros/mros.service.ts | 13 +++++-- 5 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 migration/1776957972101-AddMrosReportFields.js diff --git a/migration/1776957972101-AddMrosReportFields.js b/migration/1776957972101-AddMrosReportFields.js new file mode 100644 index 0000000000..d19ebf17ed --- /dev/null +++ b/migration/1776957972101-AddMrosReportFields.js @@ -0,0 +1,35 @@ +/** + * @typedef {import('typeorm').MigrationInterface} MigrationInterface + * @typedef {import('typeorm').QueryRunner} QueryRunner + */ + +/** + * @class + * @implements {MigrationInterface} + */ +module.exports = class AddMrosReportFields1776957972101 { + name = 'AddMrosReportFields1776957972101'; + + /** + * @param {QueryRunner} queryRunner + */ + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "mros" ADD "reportCode" nvarchar(256) NOT NULL CONSTRAINT "DF_mros_reportCode" DEFAULT 'SAR'`, + ); + await queryRunner.query(`ALTER TABLE "mros" ADD "reason" nvarchar(MAX)`); + await queryRunner.query(`ALTER TABLE "mros" ADD "action" nvarchar(MAX)`); + await queryRunner.query(`ALTER TABLE "mros" ADD "indicators" nvarchar(MAX)`); + } + + /** + * @param {QueryRunner} queryRunner + */ + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "mros" DROP COLUMN "indicators"`); + await queryRunner.query(`ALTER TABLE "mros" DROP COLUMN "action"`); + await queryRunner.query(`ALTER TABLE "mros" DROP COLUMN "reason"`); + await queryRunner.query(`ALTER TABLE "mros" DROP CONSTRAINT "DF_mros_reportCode"`); + await queryRunner.query(`ALTER TABLE "mros" DROP COLUMN "reportCode"`); + } +}; diff --git a/src/subdomains/supporting/mros/dto/create-mros.dto.ts b/src/subdomains/supporting/mros/dto/create-mros.dto.ts index 8c20009d51..2143210e87 100644 --- a/src/subdomains/supporting/mros/dto/create-mros.dto.ts +++ b/src/subdomains/supporting/mros/dto/create-mros.dto.ts @@ -1,4 +1,4 @@ -import { IsDateString, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { IsArray, IsDateString, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator'; import { MrosStatus } from '../mros-status.enum'; export class CreateMrosDto { @@ -21,4 +21,21 @@ export class CreateMrosDto { @IsNotEmpty() @IsString() caseManager: string; + + @IsOptional() + @IsString() + reportCode?: string; + + @IsOptional() + @IsString() + reason?: string; + + @IsOptional() + @IsString() + action?: string; + + @IsOptional() + @IsArray() + @IsString({ each: true }) + indicators?: string[]; } diff --git a/src/subdomains/supporting/mros/dto/update-mros.dto.ts b/src/subdomains/supporting/mros/dto/update-mros.dto.ts index 94faa0c17f..c77863ea06 100644 --- a/src/subdomains/supporting/mros/dto/update-mros.dto.ts +++ b/src/subdomains/supporting/mros/dto/update-mros.dto.ts @@ -1,4 +1,4 @@ -import { IsDateString, IsEnum, IsString } from 'class-validator'; +import { IsArray, IsDateString, IsEnum, IsString } from 'class-validator'; import { IsOptionalButNotNull } from 'src/shared/validators/is-not-null.validator'; import { MrosStatus } from '../mros-status.enum'; @@ -18,4 +18,21 @@ export class UpdateMrosDto { @IsOptionalButNotNull() @IsString() caseManager?: string; + + @IsOptionalButNotNull() + @IsString() + reportCode?: string; + + @IsOptionalButNotNull() + @IsString() + reason?: string; + + @IsOptionalButNotNull() + @IsString() + action?: string; + + @IsOptionalButNotNull() + @IsArray() + @IsString({ each: true }) + indicators?: string[]; } diff --git a/src/subdomains/supporting/mros/mros.entity.ts b/src/subdomains/supporting/mros/mros.entity.ts index cae91bcf39..4a0f80eb08 100644 --- a/src/subdomains/supporting/mros/mros.entity.ts +++ b/src/subdomains/supporting/mros/mros.entity.ts @@ -11,6 +11,9 @@ export class Mros extends IEntity { @Column({ length: 256 }) status: MrosStatus; + @Column({ length: 256, default: 'SAR' }) + reportCode: string; + @Column({ type: 'datetime2', nullable: true }) submissionDate?: Date; @@ -19,4 +22,22 @@ export class Mros extends IEntity { @Column({ length: 256 }) caseManager: string; + + @Column({ length: 'MAX', nullable: true }) + reason?: string; + + @Column({ length: 'MAX', nullable: true }) + action?: string; + + // JSON-serialized string[] of goAML indicator codes (e.g. ["0002M","1004V"]) + @Column({ length: 'MAX', nullable: true }) + indicators?: string; + + get indicatorCodes(): string[] { + return this.indicators ? JSON.parse(this.indicators) : []; + } + + set indicatorCodes(codes: string[]) { + this.indicators = codes.length ? JSON.stringify(codes) : undefined; + } } diff --git a/src/subdomains/supporting/mros/mros.service.ts b/src/subdomains/supporting/mros/mros.service.ts index cdce8768f9..a87b2982ac 100644 --- a/src/subdomains/supporting/mros/mros.service.ts +++ b/src/subdomains/supporting/mros/mros.service.ts @@ -13,11 +13,14 @@ export class MrosService { ) {} async create(dto: CreateMrosDto): Promise { - const entity = this.repo.create(dto); + const { userDataId, indicators, ...rest } = dto; + const entity = this.repo.create(rest); - entity.userData = await this.userDataService.getUserData(dto.userDataId); + entity.userData = await this.userDataService.getUserData(userDataId); if (!entity.userData) throw new NotFoundException('UserData not found'); + if (indicators) entity.indicatorCodes = indicators; + return this.repo.save(entity); } @@ -25,7 +28,11 @@ export class MrosService { const entity = await this.repo.findOneBy({ id }); if (!entity) throw new NotFoundException('Mros not found'); - return this.repo.save({ ...entity, ...dto }); + const { indicators, ...rest } = dto; + Object.assign(entity, rest); + if (indicators !== undefined) entity.indicatorCodes = indicators; + + return this.repo.save(entity); } async getAll(): Promise { From 9d9ab152dfd3d26ece3dbc3477be38e06a829284 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Thu, 23 Apr 2026 17:34:09 +0200 Subject: [PATCH 2/3] style: simplify indicatorCodes setter to match priceStepsObject pattern --- src/subdomains/supporting/mros/mros.entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subdomains/supporting/mros/mros.entity.ts b/src/subdomains/supporting/mros/mros.entity.ts index 4a0f80eb08..7626c248cc 100644 --- a/src/subdomains/supporting/mros/mros.entity.ts +++ b/src/subdomains/supporting/mros/mros.entity.ts @@ -38,6 +38,6 @@ export class Mros extends IEntity { } set indicatorCodes(codes: string[]) { - this.indicators = codes.length ? JSON.stringify(codes) : undefined; + this.indicators = JSON.stringify(codes); } } From 2a9a1a8fede66ecaef75563edaa0570794079bb1 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Thu, 23 Apr 2026 17:43:57 +0200 Subject: [PATCH 3/3] revert: remove hand-written migration To be replaced by a TypeORM-generated migration via npm run migration AddMrosReportFields so the DEFAULT/constraint names match TypeORM's deterministic hash naming (learned from PR #3613 / constraint-name drift). --- .../1776957972101-AddMrosReportFields.js | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 migration/1776957972101-AddMrosReportFields.js diff --git a/migration/1776957972101-AddMrosReportFields.js b/migration/1776957972101-AddMrosReportFields.js deleted file mode 100644 index d19ebf17ed..0000000000 --- a/migration/1776957972101-AddMrosReportFields.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @typedef {import('typeorm').MigrationInterface} MigrationInterface - * @typedef {import('typeorm').QueryRunner} QueryRunner - */ - -/** - * @class - * @implements {MigrationInterface} - */ -module.exports = class AddMrosReportFields1776957972101 { - name = 'AddMrosReportFields1776957972101'; - - /** - * @param {QueryRunner} queryRunner - */ - async up(queryRunner) { - await queryRunner.query( - `ALTER TABLE "mros" ADD "reportCode" nvarchar(256) NOT NULL CONSTRAINT "DF_mros_reportCode" DEFAULT 'SAR'`, - ); - await queryRunner.query(`ALTER TABLE "mros" ADD "reason" nvarchar(MAX)`); - await queryRunner.query(`ALTER TABLE "mros" ADD "action" nvarchar(MAX)`); - await queryRunner.query(`ALTER TABLE "mros" ADD "indicators" nvarchar(MAX)`); - } - - /** - * @param {QueryRunner} queryRunner - */ - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "mros" DROP COLUMN "indicators"`); - await queryRunner.query(`ALTER TABLE "mros" DROP COLUMN "action"`); - await queryRunner.query(`ALTER TABLE "mros" DROP COLUMN "reason"`); - await queryRunner.query(`ALTER TABLE "mros" DROP CONSTRAINT "DF_mros_reportCode"`); - await queryRunner.query(`ALTER TABLE "mros" DROP COLUMN "reportCode"`); - } -};