From bbea5ebf51a542216d898850f9e0a391f547d03c Mon Sep 17 00:00:00 2001 From: Matt Waldron <12972009+me-matt@users.noreply.github.com> Date: Fri, 11 Oct 2024 09:15:31 +0100 Subject: [PATCH] feature/CB2-12313: Refactor certificate generation into commands (#193) * isolated the certificate commands * pr feedback updated generate to be async * refactored to use base abstract class and state bag * pr feedback - remove any on trailer registration * pr feedback - move var init * pr feedback - parallelise fetch * pr feedback - remove old lint rules --- package-lock.json | 11 + package.json | 2 + .../CertificatePayloadGenerator.ts | 94 ++ src/certificate/CertificatePayloadStateBag.ts | 8 + src/certificate/CertificateTypes.ts | 40 + src/certificate/ICertificatePayloadCommand.ts | 17 + .../commands/AdrCertificateCommand.ts | 66 + src/certificate/commands/DefectsCommand.ts | 148 +++ .../commands/IvaCertificateCommand.ts | 69 + .../commands/MakeAndModelCommand.ts | 68 + .../commands/MsvaCertificateCommand.ts | 61 + .../commands/OdometerHistoryCommand.ts | 51 + .../commands/PassOrFailCertificateCommand.ts | 74 ++ .../RoadworthinessCertificateCommand.ts | 56 + src/certificate/commands/SignatureCommand.ts | 54 + .../commands/TestHistoryCommand.ts | 34 + src/certificate/commands/WatermarkCommand.ts | 12 + src/config/DependencyInjection.ts | 30 +- src/config/config.yml | 2 + src/defect/DefectService.ts | 82 +- src/models/index.d.ts | 5 +- src/services/CertificateGenerationService.ts | 469 +------ tests/unit/certGen.unitTest.ts | 1152 +++++++++-------- tslint.json | 26 - 24 files changed, 1650 insertions(+), 981 deletions(-) create mode 100644 src/certificate/CertificatePayloadGenerator.ts create mode 100644 src/certificate/CertificatePayloadStateBag.ts create mode 100644 src/certificate/CertificateTypes.ts create mode 100644 src/certificate/ICertificatePayloadCommand.ts create mode 100644 src/certificate/commands/AdrCertificateCommand.ts create mode 100644 src/certificate/commands/DefectsCommand.ts create mode 100644 src/certificate/commands/IvaCertificateCommand.ts create mode 100644 src/certificate/commands/MakeAndModelCommand.ts create mode 100644 src/certificate/commands/MsvaCertificateCommand.ts create mode 100644 src/certificate/commands/OdometerHistoryCommand.ts create mode 100644 src/certificate/commands/PassOrFailCertificateCommand.ts create mode 100644 src/certificate/commands/RoadworthinessCertificateCommand.ts create mode 100644 src/certificate/commands/SignatureCommand.ts create mode 100644 src/certificate/commands/TestHistoryCommand.ts create mode 100644 src/certificate/commands/WatermarkCommand.ts delete mode 100644 tslint.json diff --git a/package-lock.json b/package-lock.json index f51172d2..b915425c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "aws-lambda": "^1.0.5", "aws-xray-sdk": "^3.6.0", "js-yaml": "^3.14.1", + "lodash.merge": "^4.6.2", "moment": "^2.24.0", "node-yaml": "^4.0.1", "reflect-metadata": "^0.1.13", @@ -37,6 +38,7 @@ "@types/jest": "^28.1.8", "@types/jest-plugin-context": "^2.9.2", "@types/lambda-tester": "^4.0.3", + "@types/lodash.merge": "^4.6.9", "@types/node": "^12.12.5", "@types/sinon": "^7.5.0", "@types/uuid": "^10.0.0", @@ -10173,6 +10175,15 @@ "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", "dev": true }, + "node_modules/@types/lodash.merge": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", + "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", diff --git a/package.json b/package.json index e96e111f..7f97e9e6 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "aws-lambda": "^1.0.5", "aws-xray-sdk": "^3.6.0", "js-yaml": "^3.14.1", + "lodash.merge": "^4.6.2", "moment": "^2.24.0", "node-yaml": "^4.0.1", "reflect-metadata": "^0.1.13", @@ -80,6 +81,7 @@ "@types/jest": "^28.1.8", "@types/jest-plugin-context": "^2.9.2", "@types/lambda-tester": "^4.0.3", + "@types/lodash.merge": "^4.6.9", "@types/node": "^12.12.5", "@types/sinon": "^7.5.0", "@types/uuid": "^10.0.0", diff --git a/src/certificate/CertificatePayloadGenerator.ts b/src/certificate/CertificatePayloadGenerator.ts new file mode 100644 index 00000000..ea894d49 --- /dev/null +++ b/src/certificate/CertificatePayloadGenerator.ts @@ -0,0 +1,94 @@ +import merge from 'lodash.merge'; +import { Service } from 'typedi'; +import { ITestResult } from '../models'; +import { ICertificatePayload } from '../models'; +import { CERTIFICATE_DATA } from '../models/Enums'; +import { CertificatePayloadStateBag } from './CertificatePayloadStateBag'; +import { ICertificatePayloadCommand } from './ICertificatePayloadCommand'; +import { AdrCertificateCommand } from './commands/AdrCertificateCommand'; +import { DefectsCommand } from './commands/DefectsCommand'; +import { IvaCertificateCommand } from './commands/IvaCertificateCommand'; +import { MakeAndModelCommand } from './commands/MakeAndModelCommand'; +import { MsvaCertificateCommand } from './commands/MsvaCertificateCommand'; +import { OdometerHistoryCommand } from './commands/OdometerHistoryCommand'; +import { PassOrFailCertificateCommand } from './commands/PassOrFailCertificateCommand'; +import { RoadworthinessCertificateCommand } from './commands/RoadworthinessCertificateCommand'; +import { SignatureCommand } from './commands/SignatureCommand'; +import { TestHistoryCommand } from './commands/TestHistoryCommand'; +import { WatermarkCommand } from './commands/WatermarkCommand'; + +@Service() +export class CertificatePayloadGenerator implements ICertificatePayloadCommand { + private commands: ICertificatePayloadCommand[] = [ + this.passOrFailGenerator, + this.rwtGenerator, + this.adrGenerator, + this.ivaGenerator, + this.msvaGenerator, + this.signatureCommand, + this.watermarkCommand, + this.testHistoryCommand, + this.defectsCommand, + this.makeAndModelCommand, + this.odometerHistoryCommand, + ]; + + /** + * Creates a new instance of the certificate payload generator. Generates a payload + * that can be used for generating a certificate. + */ + constructor( + private passOrFailGenerator: PassOrFailCertificateCommand, + private rwtGenerator: RoadworthinessCertificateCommand, + private adrGenerator: AdrCertificateCommand, + private ivaGenerator: IvaCertificateCommand, + private msvaGenerator: MsvaCertificateCommand, + private signatureCommand: SignatureCommand, + private watermarkCommand: WatermarkCommand, + private testHistoryCommand: TestHistoryCommand, + private defectsCommand: DefectsCommand, + private makeAndModelCommand: MakeAndModelCommand, + private odometerHistoryCommand: OdometerHistoryCommand + ) {} + + /** + * Generates certificate data for a given test result and certificate type + * @param testResult - the source test result for certificate generation + * @param type - the certificate type + * @param isWelsh - the boolean value whether the atf where test was conducted resides in Wales + */ + public async generateCertificateData( + testResult: ITestResult, + type: CERTIFICATE_DATA, + isWelsh = false + ): Promise { + this.initialise({ + type, + isWelsh, + testResult, + }); + return this.generate(); + } + + /** + * Initialises the certificate generation process. + * @param type The type of certificate to generate + * @param isWelsh True if a Welsh certificate should also be generated. + */ + public initialise(state: CertificatePayloadStateBag) { + this.commands.forEach((cmd) => cmd.initialise(state)); + } + + /** + * Generates certificate data for a given test result and certificate type + * @param testResult the source test result for certificate generation + * @returns A generated certificate payload + */ + public async generate(): Promise { + // Map over all the commands and get their certificate data. + const results = await Promise.all(this.commands.map((cmd) => cmd.generate())); + + // Flatten all the certificate data into our result payload. + return Promise.resolve(merge({} as ICertificatePayload, ...results)); + } +} diff --git a/src/certificate/CertificatePayloadStateBag.ts b/src/certificate/CertificatePayloadStateBag.ts new file mode 100644 index 00000000..5fa3e667 --- /dev/null +++ b/src/certificate/CertificatePayloadStateBag.ts @@ -0,0 +1,8 @@ +import { ITestResult } from '../models'; +import { CERTIFICATE_DATA } from '../models/Enums'; + +export type CertificatePayloadStateBag = { + type: CERTIFICATE_DATA; + isWelsh: boolean; + testResult: ITestResult; +}; diff --git a/src/certificate/CertificateTypes.ts b/src/certificate/CertificateTypes.ts new file mode 100644 index 00000000..76f4ba56 --- /dev/null +++ b/src/certificate/CertificateTypes.ts @@ -0,0 +1,40 @@ +import { Service } from 'typedi'; +import { IMOTConfig } from '../models'; +import { Configuration } from '../utils/Configuration'; + +@Service() +export class CertificateTypes { + private readonly config: Configuration = Configuration.getInstance(); + + public getCertificateType(type: string): string { + const config: IMOTConfig = this.config.getMOTConfig(); + + const certTypes = { + psv_pass: config.documentNames.vtp20, + psv_pass_bilingual: config.documentNames.vtp20_bilingual, + psv_fail: config.documentNames.vtp30, + psv_fail_bilingual: config.documentNames.vtp30_bilingual, + psv_prs: config.documentNames.psv_prs, + psv_prs_bilingual: config.documentNames.psv_prs_bilingual, + hgv_pass: config.documentNames.vtg5, + hgv_pass_bilingual: config.documentNames.vtg5_bilingual, + hgv_fail: config.documentNames.vtg30, + hgv_fail_bilingual: config.documentNames.vtg30_bilingual, + hgv_prs: config.documentNames.hgv_prs, + hgv_prs_bilingual: config.documentNames.hgv_prs_bilingual, + trl_pass: config.documentNames.vtg5a, + trl_pass_bilingual: config.documentNames.vtg5a_bilingual, + trl_fail: config.documentNames.vtg30, + trl_fail_bilingual: config.documentNames.vtg30_bilingual, + trl_prs: config.documentNames.trl_prs, + trl_prs_bilingual: config.documentNames.trl_prs_bilingual, + rwt: config.documentNames.rwt, + adr_pass: config.documentNames.adr_pass, + iva_fail: config.documentNames.iva_fail, + msva_fail: config.documentNames.msva_fail, + }; + + const keyTyped = type as keyof typeof certTypes; + return certTypes[keyTyped]; + } +} diff --git a/src/certificate/ICertificatePayloadCommand.ts b/src/certificate/ICertificatePayloadCommand.ts new file mode 100644 index 00000000..45c239e5 --- /dev/null +++ b/src/certificate/ICertificatePayloadCommand.ts @@ -0,0 +1,17 @@ +import { ICertificatePayload } from '../models'; +import { CertificatePayloadStateBag } from './CertificatePayloadStateBag'; + +export interface ICertificatePayloadCommand { + initialise(state: CertificatePayloadStateBag): void; + generate(): Promise; +} + +export abstract class BasePayloadCommand implements ICertificatePayloadCommand { + protected state: CertificatePayloadStateBag = {} as CertificatePayloadStateBag; + + initialise(state: CertificatePayloadStateBag) { + this.state = state; + } + + abstract generate(): Promise; +} diff --git a/src/certificate/commands/AdrCertificateCommand.ts b/src/certificate/commands/AdrCertificateCommand.ts new file mode 100644 index 00000000..1fdc5063 --- /dev/null +++ b/src/certificate/commands/AdrCertificateCommand.ts @@ -0,0 +1,66 @@ +import { Service } from 'typedi'; +import { ICertificatePayload } from '../../models'; +import { CERTIFICATE_DATA } from '../../models/Enums'; +import { TechRecordService } from '../../tech-record/TechRecordService'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class AdrCertificateCommand extends BasePayloadCommand { + constructor(private techRecordService: TechRecordService) { + super(); + } + + private certificateIsAnAdr = (): boolean => this.state.type === CERTIFICATE_DATA.ADR_DATA; + + public async generate(): Promise { + if (!this.certificateIsAnAdr()) { + return {} as ICertificatePayload; + } + + const { testResult } = this.state; + + const [adrDetails, makeAndModel] = await Promise.all([ + this.techRecordService.getAdrDetails(testResult), + this.techRecordService.getVehicleMakeAndModel(testResult), + ]); + + const docGenPayloadAdr = { + ChasisNumber: testResult.vin, + RegistrationNumber: testResult.vrm, + ApplicantDetails: { + name: adrDetails?.techRecord_applicantDetails_name, + address1: adrDetails?.techRecord_applicantDetails_address1, + address2: adrDetails?.techRecord_applicantDetails_address2, + address3: adrDetails?.techRecord_applicantDetails_address1, + postTown: adrDetails?.techRecord_applicantDetails_postTown, + postCode: adrDetails?.techRecord_applicantDetails_postCode, + telephoneNumber: adrDetails?.techRecord_applicantDetails_telephoneNumber, + emailAddress: adrDetails?.techRecord_applicantDetails_emailAddress, + }, + VehicleType: adrDetails?.techRecord_adrDetails_vehicleDetails_type, + PermittedDangerousGoods: adrDetails?.techRecord_adrDetails_permittedDangerousGoods, + BrakeEndurance: adrDetails?.techRecord_adrDetails_brakeEndurance, + Weight: adrDetails?.techRecord_adrDetails_weight, + TankManufacturer: adrDetails?.techRecord_adrDetails_tank_tankDetails_tankStatement_statement + ? adrDetails.techRecord_adrDetails_tank_tankDetails_tankManufacturer + : undefined, + Tc2InitApprovalNo: adrDetails?.techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateApprovalNo, + TankManufactureSerialNo: adrDetails?.techRecord_adrDetails_tank_tankDetails_tankManufacturerSerialNo, + YearOfManufacture: adrDetails?.techRecord_adrDetails_tank_tankDetails_yearOfManufacture, + TankCode: adrDetails?.techRecord_adrDetails_tank_tankDetails_tankCode, + SpecialProvisions: adrDetails?.techRecord_adrDetails_tank_tankDetails_specialProvisions, + TankStatement: adrDetails?.techRecord_adrDetails_tank_tankDetails_tankStatement_statement, + ExpiryDate: testResult.testTypes.testExpiryDate, + AtfNameAtfPNumber: testResult.testStationName + ' ' + testResult.testStationPNumber, + Notes: testResult.testTypes.additionalNotesRecorded, + TestTypeDate: testResult.testTypes.testTypeStartTimestamp, + }; + + return { + ADR_DATA: { + ...docGenPayloadAdr, + ...makeAndModel, + }, + } as ICertificatePayload; + } +} diff --git a/src/certificate/commands/DefectsCommand.ts b/src/certificate/commands/DefectsCommand.ts new file mode 100644 index 00000000..891fe537 --- /dev/null +++ b/src/certificate/commands/DefectsCommand.ts @@ -0,0 +1,148 @@ +import { Service } from 'typedi'; +import { DefectRepository } from '../../defect/DefectRepository'; +import { DefectService } from '../../defect/DefectService'; +import { ITestResult } from '../../models'; +import { ICertificatePayload } from '../../models'; +import { ITestType } from '../../models'; +import { CERTIFICATE_DATA, TEST_RESULTS } from '../../models/Enums'; +import { IFlatDefect } from '../../models/IFlatDefect'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class DefectsCommand extends BasePayloadCommand { + constructor( + private defectService: DefectService, + private defectRepository: DefectRepository + ) { + super(); + } + + private certificateIsAnPassOrFail = (): boolean => + this.state.type === CERTIFICATE_DATA.PASS_DATA || this.state.type === CERTIFICATE_DATA.FAIL_DATA; + + public async generate(): Promise { + if (!this.certificateIsAnPassOrFail()) { + return {} as ICertificatePayload; + } + + const { + testResult, + testResult: { testTypes }, + } = this.state; + + const result = {} as ICertificatePayload; + + if (testTypes.testResult !== TEST_RESULTS.FAIL) { + result.DATA = { + ...(await this.getPayloadData(testResult, CERTIFICATE_DATA.PASS_DATA)), + }; + } + + if (testTypes.testResult !== TEST_RESULTS.PASS) { + result.FAIL_DATA = { + ...(await this.getPayloadData(testResult, CERTIFICATE_DATA.FAIL_DATA)), + }; + } + + return result; + } + + private async getPayloadData(testResult: ITestResult, type: CERTIFICATE_DATA): Promise { + const { isWelsh } = this.state; + + let flattenedDefects: IFlatDefect[] = []; + + if (isWelsh) { + const defectListFromApi = await this.defectRepository.getDefectTranslations(); + flattenedDefects = this.defectService.flattenDefectsFromApi(defectListFromApi); + } + + const defects = await this.generateDefects( + testResult.testTypes, + type, + testResult.vehicleType, + flattenedDefects, + isWelsh + ); + return defects; + } + + /** + * Generates an object containing defects for a given test type and certificate type + * @param testTypes - the source test type for defect generation + * @param type - the certificate type + * @param vehicleType - the vehicle type from the test result + * @param flattenedDefects - the list of flattened defects after being retrieved from the defect service + * @param isWelsh - determines whether the atf in which the test result was conducted resides in Wales + */ + private generateDefects( + testTypes: ITestType, + type: CERTIFICATE_DATA, + vehicleType: string, + flattenedDefects: IFlatDefect[], + isWelsh = false + ) { + const defects = { + DangerousDefects: [], + MajorDefects: [], + PRSDefects: [], + MinorDefects: [], + AdvisoryDefects: [], + DangerousDefectsWelsh: [], + MajorDefectsWelsh: [], + PRSDefectsWelsh: [], + MinorDefectsWelsh: [], + AdvisoryDefectsWelsh: [], + }; + + testTypes.defects.forEach((defect) => { + switch (defect.deficiencyCategory.toLowerCase()) { + case 'dangerous': + this.defectService.generateDangerousDefects( + testTypes.testResult, + defect, + type, + defects, + vehicleType, + isWelsh, + flattenedDefects + ); + break; + case 'major': + this.defectService.generateMajorDefects( + testTypes.testResult, + defect, + type, + defects, + vehicleType, + isWelsh, + flattenedDefects + ); + break; + case 'minor': + this.defectService.generateMinorDefects( + defects, + defect, + vehicleType, + testTypes.testResult, + isWelsh, + flattenedDefects + ); + break; + case 'advisory': + this.defectService.generateAdvisoryDefects(defects, defect, vehicleType, testTypes.testResult, isWelsh); + break; + default: + break; + } + }); + + Object.entries(defects).forEach(([k, v]: [string, any]) => { + if (v.length === 0) { + Object.assign(defects, { [k]: undefined }); + } + }); + + return defects; + } +} diff --git a/src/certificate/commands/IvaCertificateCommand.ts b/src/certificate/commands/IvaCertificateCommand.ts new file mode 100644 index 00000000..0d34a833 --- /dev/null +++ b/src/certificate/commands/IvaCertificateCommand.ts @@ -0,0 +1,69 @@ +import moment from 'moment'; +import { Service } from 'typedi'; +import { DefectService } from '../../defect/DefectService'; +import { IRequiredStandard } from '../../models'; +import { ICertificatePayload } from '../../models'; +import { CERTIFICATE_DATA, IVA_30 } from '../../models/Enums'; +import { TestResultService } from '../../test-result/TestResultService'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class IvaCertificateCommand extends BasePayloadCommand { + constructor( + private defectService: DefectService, + private testResultService: TestResultService + ) { + super(); + } + + private certificateIsAnIva = (): boolean => this.state.type === CERTIFICATE_DATA.IVA_DATA; + + public async generate(): Promise { + if (!this.certificateIsAnIva()) { + return {} as ICertificatePayload; + } + + const { testResult } = this.state; + + const ivaFailDetailsForDocGen = { + vin: testResult.vin, + serialNumber: testResult.vehicleType === 'trl' ? testResult.trailerId : testResult.vrm, + vehicleTrailerNrNo: testResult.vehicleType === 'trl' ? testResult.trailerId : testResult.vrm, + testCategoryClass: testResult.euVehicleCategory, + testCategoryBasicNormal: this.testResultService.isBasicIvaTest(testResult.testTypes.testTypeId) + ? IVA_30.BASIC + : IVA_30.NORMAL, + make: testResult.make, + model: testResult.model, + bodyType: testResult.bodyType?.description, + date: moment(testResult.testTypes.testTypeStartTimestamp).format('DD/MM/YYYY'), + testerName: testResult.testerName, + reapplicationDate: testResult.testTypes?.reapplicationDate + ? moment(testResult.testTypes?.reapplicationDate).format('DD/MM/YYYY') + : '', + station: testResult.testStationName, + additionalDefects: this.defectService.formatVehicleApprovalAdditionalDefects(testResult.testTypes.customDefects), + requiredStandards: this.sortRequiredStandards(testResult.testTypes.requiredStandards), + }; + + return { + IVA_DATA: ivaFailDetailsForDocGen, + } as ICertificatePayload; + } + + /** + * Sorts required standards if present by refCalculation and then returns it + * @param requiredStandards - the requiredStandards array to sort + * @returns - the sorted requiredStandards array + */ + private sortRequiredStandards = ( + requiredStandards: IRequiredStandard[] | undefined + ): IRequiredStandard[] | undefined => { + if (!requiredStandards) { + return; + } + + const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' }); + return requiredStandards.sort((a, b) => collator.compare(a.refCalculation, b.refCalculation)); + }; +} diff --git a/src/certificate/commands/MakeAndModelCommand.ts b/src/certificate/commands/MakeAndModelCommand.ts new file mode 100644 index 00000000..ddc12f30 --- /dev/null +++ b/src/certificate/commands/MakeAndModelCommand.ts @@ -0,0 +1,68 @@ +import { Service } from 'typedi'; +import { ICertificatePayload, IMakeAndModel } from '../../models'; +import { CERTIFICATE_DATA, TEST_RESULTS } from '../../models/Enums'; +import { TechRecordService } from '../../tech-record/TechRecordService'; +import { TestResultService } from '../../test-result/TestResultService'; +import { IGetTrailerRegistrationResult } from '../../trailer/IGetTrailerRegistrationResult'; +import { TrailerRepository } from '../../trailer/TrailerRepository'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class MakeAndModelCommand extends BasePayloadCommand { + constructor( + private techRecordService: TechRecordService, + private trailerRepository: TrailerRepository, + private testResultService: TestResultService + ) { + super(); + } + + private certificateIsAnPassOrFail = (): boolean => + this.state.type === CERTIFICATE_DATA.PASS_DATA || this.state.type === CERTIFICATE_DATA.FAIL_DATA; + + public async generate(): Promise { + const result = {} as ICertificatePayload; + + if (!this.certificateIsAnPassOrFail()) { + return result; + } + + const { + testResult, + testResult: { testTypes }, + } = this.state; + + const makeAndModel = (await this.techRecordService.getVehicleMakeAndModel(testResult)) as Required; + const trnRegistration = await this.trailerRegistration(makeAndModel); + + if (testTypes.testResult !== TEST_RESULTS.FAIL) { + result.DATA = { + ...makeAndModel, + ...trnRegistration, + }; + } + + if (testTypes.testResult !== TEST_RESULTS.PASS) { + result.FAIL_DATA = { + ...makeAndModel, + ...trnRegistration, + }; + } + + return result; + } + + private async trailerRegistration(makeAndModel: IMakeAndModel): Promise { + const { + testResult, + testResult: { vehicleType }, + } = this.state; + + const isValidForTrn = this.testResultService.isValidForTrn(vehicleType, makeAndModel); + if (isValidForTrn) { + return await this.trailerRepository.getTrailerRegistrationObject(testResult.vin, makeAndModel.Make); + } + + return undefined; + } +} diff --git a/src/certificate/commands/MsvaCertificateCommand.ts b/src/certificate/commands/MsvaCertificateCommand.ts new file mode 100644 index 00000000..e17d85ee --- /dev/null +++ b/src/certificate/commands/MsvaCertificateCommand.ts @@ -0,0 +1,61 @@ +import moment from 'moment'; +import { Service } from 'typedi'; +import { DefectService } from '../../defect/DefectService'; +import { ICertificatePayload } from '../../models'; +import { IRequiredStandard } from '../../models'; +import { CERTIFICATE_DATA } from '../../models/Enums'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class MsvaCertificateCommand extends BasePayloadCommand { + constructor(private defectService: DefectService) { + super(); + } + + private certificateIsAnMsva = (): boolean => this.state.type === CERTIFICATE_DATA.MSVA_DATA; + + public async generate(): Promise { + if (!this.certificateIsAnMsva()) { + return {} as ICertificatePayload; + } + + const { testResult } = this.state; + + const msvaFailDetailsForDocGen = { + vin: testResult.vin, + serialNumber: testResult.vrm, + vehicleZNumber: testResult.vrm, + make: testResult.make, + model: testResult.model, + type: testResult.vehicleType, + testerName: testResult.testerName, + date: moment(testResult.testTypes.testTypeStartTimestamp).format('DD/MM/YYYY'), + reapplicationDate: testResult.testTypes?.reapplicationDate + ? moment(testResult.testTypes?.reapplicationDate).format('DD/MM/YYYY') + : '', + station: testResult.testStationName, + additionalDefects: this.defectService.formatVehicleApprovalAdditionalDefects(testResult.testTypes.customDefects), + requiredStandards: this.sortRequiredStandards(testResult.testTypes.requiredStandards), + }; + + return { + MSVA_DATA: msvaFailDetailsForDocGen, + } as ICertificatePayload; + } + + /** + * Sorts required standards if present by refCalculation and then returns it + * @param requiredStandards - the requiredStandards array to sort + * @returns - the sorted requiredStandards array + */ + private sortRequiredStandards = ( + requiredStandards: IRequiredStandard[] | undefined + ): IRequiredStandard[] | undefined => { + if (!requiredStandards) { + return; + } + + const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' }); + return requiredStandards.sort((a, b) => collator.compare(a.refCalculation, b.refCalculation)); + }; +} diff --git a/src/certificate/commands/OdometerHistoryCommand.ts b/src/certificate/commands/OdometerHistoryCommand.ts new file mode 100644 index 00000000..9ae69ed5 --- /dev/null +++ b/src/certificate/commands/OdometerHistoryCommand.ts @@ -0,0 +1,51 @@ +import { Service } from 'typedi'; +import { ICertificatePayload } from '../../models'; +import { ITestResult } from '../../models'; +import { CERTIFICATE_DATA, TEST_RESULTS, VEHICLE_TYPES } from '../../models/Enums'; +import { TestResultRepository } from '../../test-result/TestResultRepository'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class OdometerHistoryCommand extends BasePayloadCommand { + constructor(private testResultRepository: TestResultRepository) { + super(); + } + + private certificateIsAnPassOrFail = (): boolean => + this.state.type === CERTIFICATE_DATA.PASS_DATA || this.state.type === CERTIFICATE_DATA.FAIL_DATA; + + private vehicleIsTrailer = (testResult: ITestResult): boolean => testResult.vehicleType === VEHICLE_TYPES.TRL; + + public async generate(): Promise { + const result = {} as ICertificatePayload; + + if (!this.certificateIsAnPassOrFail()) { + return result; + } + + const { + testResult, + testResult: { testTypes, systemNumber }, + } = this.state; + + if (this.vehicleIsTrailer(testResult)) { + return result; + } + + const odometerHistory = await this.testResultRepository.getOdometerHistory(systemNumber); + + if (testTypes.testResult !== TEST_RESULTS.FAIL) { + result.DATA = { + ...odometerHistory, + }; + } + + if (testTypes.testResult !== TEST_RESULTS.PASS) { + result.FAIL_DATA = { + ...odometerHistory, + }; + } + + return result; + } +} diff --git a/src/certificate/commands/PassOrFailCertificateCommand.ts b/src/certificate/commands/PassOrFailCertificateCommand.ts new file mode 100644 index 00000000..a6e5c579 --- /dev/null +++ b/src/certificate/commands/PassOrFailCertificateCommand.ts @@ -0,0 +1,74 @@ +import moment from 'moment'; +import { Service } from 'typedi'; +import { ITestResult } from '../../models'; +import { ICertificatePayload } from '../../models'; +import { CERTIFICATE_DATA, TEST_RESULTS, VEHICLE_TYPES } from '../../models/Enums'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class PassOrFailCertificateCommand extends BasePayloadCommand { + private certificateIsAnPassOrFail = (): boolean => + this.state.type === CERTIFICATE_DATA.PASS_DATA || this.state.type === CERTIFICATE_DATA.FAIL_DATA; + + public async generate(): Promise { + const result = {} as ICertificatePayload; + + if (!this.certificateIsAnPassOrFail()) { + return result; + } + + const { + testResult, + testResult: { testTypes }, + } = this.state; + + const payload = await this.getPayloadData(testResult); + + if (testTypes.testResult !== TEST_RESULTS.FAIL) { + result.DATA = { + ...payload, + }; + } + + if (testTypes.testResult !== TEST_RESULTS.PASS) { + result.FAIL_DATA = { + ...payload, + }; + } + + return result; + } + + private async getPayloadData(testResult: ITestResult): Promise { + const testType = testResult.testTypes; + + return { + TestNumber: testType.testNumber, + TestStationPNumber: testResult.testStationPNumber, + TestStationName: testResult.testStationName, + CurrentOdometer: { + value: testResult.odometerReading, + unit: testResult.odometerReadingUnits, + }, + IssuersName: testResult.testerName, + DateOfTheTest: moment(testResult.testEndTimestamp).format('DD.MM.YYYY'), + CountryOfRegistrationCode: testResult.countryOfRegistration, + VehicleEuClassification: testResult.euVehicleCategory.toUpperCase(), + RawVIN: testResult.vin, + RawVRM: (testResult.vehicleType as VEHICLE_TYPES) === VEHICLE_TYPES.TRL ? testResult.trailerId : testResult.vrm, + ExpiryDate: testType.testExpiryDate ? moment(testType.testExpiryDate).format('DD.MM.YYYY') : undefined, + EarliestDateOfTheNextTest: + ((testResult.vehicleType as VEHICLE_TYPES) === VEHICLE_TYPES.HGV || + (testResult.vehicleType as VEHICLE_TYPES) === VEHICLE_TYPES.TRL) && + ((testResult.testTypes.testResult as TEST_RESULTS) === TEST_RESULTS.PASS || + (testResult.testTypes.testResult as TEST_RESULTS) === TEST_RESULTS.PRS) + ? moment(testType.testAnniversaryDate).subtract(1, 'months').startOf('month').format('DD.MM.YYYY') + : moment(testType.testAnniversaryDate).format('DD.MM.YYYY'), + SeatBeltTested: testType.seatbeltInstallationCheckDate ? 'Yes' : 'No', + SeatBeltPreviousCheckDate: testType.lastSeatbeltInstallationCheckDate + ? moment(testType.lastSeatbeltInstallationCheckDate).format('DD.MM.YYYY') + : '\u00A0', + SeatBeltNumber: testType.numberOfSeatbeltsFitted, + }; + } +} diff --git a/src/certificate/commands/RoadworthinessCertificateCommand.ts b/src/certificate/commands/RoadworthinessCertificateCommand.ts new file mode 100644 index 00000000..d4ab6139 --- /dev/null +++ b/src/certificate/commands/RoadworthinessCertificateCommand.ts @@ -0,0 +1,56 @@ +import moment from 'moment'; +import { Service } from 'typedi'; +import { DefectService } from '../../defect/DefectService'; +import { IRoadworthinessCertificateData } from '../../models'; +import { ICertificatePayload } from '../../models'; +import { CERTIFICATE_DATA, TEST_RESULTS, VEHICLE_TYPES } from '../../models/Enums'; +import { TechRecordService } from '../../tech-record/TechRecordService'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class RoadworthinessCertificateCommand extends BasePayloadCommand { + constructor( + private defectService: DefectService, + private techRecordService: TechRecordService + ) { + super(); + } + + private certificateIsAnRwt = (): boolean => this.state.type === CERTIFICATE_DATA.RWT_DATA; + + public async generate(): Promise { + if (!this.certificateIsAnRwt()) { + return {} as ICertificatePayload; + } + + const { testResult } = this.state; + + const weightDetails = await this.techRecordService.getWeightDetails(testResult); + + let defectRWTList: string[] | undefined; + if ((testResult.testTypes.testResult as TEST_RESULTS) === TEST_RESULTS.FAIL) { + defectRWTList = testResult.testTypes.defects.map((defect) => this.defectService.formatDefect(defect)); + } + + const testType = testResult.testTypes; + + const resultPass: IRoadworthinessCertificateData = { + Dgvw: weightDetails.dgvw, + Weight2: weightDetails.weight2, + VehicleNumber: + (testResult.vehicleType as VEHICLE_TYPES) === VEHICLE_TYPES.TRL ? testResult.trailerId : testResult.vrm, + Vin: testResult.vin, + IssuersName: testResult.testerName, + DateOfInspection: moment(testType.testTypeStartTimestamp).format('DD.MM.YYYY'), + TestStationPNumber: testResult.testStationPNumber, + DocumentNumber: testType.certificateNumber, + Date: moment(testType.testTypeStartTimestamp).format('DD.MM.YYYY'), + Defects: defectRWTList, + IsTrailer: (testResult.vehicleType as VEHICLE_TYPES) === VEHICLE_TYPES.TRL, + }; + + return { + RWT_DATA: resultPass, + } as ICertificatePayload; + } +} diff --git a/src/certificate/commands/SignatureCommand.ts b/src/certificate/commands/SignatureCommand.ts new file mode 100644 index 00000000..577954d8 --- /dev/null +++ b/src/certificate/commands/SignatureCommand.ts @@ -0,0 +1,54 @@ +import { Readable } from 'stream'; +import { GetObjectOutput } from '@aws-sdk/client-s3'; +import { Service } from 'typedi'; +import { ICertificatePayload } from '../../models'; +import { S3BucketService } from '../../services/S3BucketService'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class SignatureCommand extends BasePayloadCommand { + constructor(private s3Client: S3BucketService) { + super(); + } + + public async generate(): Promise { + const { testResult } = this.state; + + const signature = await this.getSignature(testResult.createdById ?? testResult.testerStaffId); + + return { + Signature: { + ImageType: 'png', + ImageData: signature, + }, + } as ICertificatePayload; + } + + /** + * Retrieves a signature from the cvs-signature S3 bucket + * @param staffId - staff ID of the signature you want to retrieve + * @returns the signature as a base64 encoded string + */ + private async getSignature(staffId: string): Promise { + try { + const result: GetObjectOutput = await this.s3Client.download( + `cvs-signature-${process.env.BUCKET}`, + `${staffId}.base64` + ); + + if (result.Body instanceof Readable) { + const chunks: Uint8Array[] = []; + for await (const chunk of result.Body) { + chunks.push(chunk); + } + const buffer = Buffer.concat(chunks); + return buffer.toString('utf-8'); + } else { + throw new Error(`Unexpected body type: ${typeof result.Body}`); + } + } catch (error) { + console.error(`Unable to fetch signature for staff id ${staffId}. ${(error as Error).message}`); + } + return null; + } +} diff --git a/src/certificate/commands/TestHistoryCommand.ts b/src/certificate/commands/TestHistoryCommand.ts new file mode 100644 index 00000000..78d54fa1 --- /dev/null +++ b/src/certificate/commands/TestHistoryCommand.ts @@ -0,0 +1,34 @@ +import moment from 'moment'; +import { Service } from 'typedi'; +import { ICertificatePayload } from '../../models'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class TestHistoryCommand extends BasePayloadCommand { + public async generate(): Promise { + const result = {} as ICertificatePayload; + + const { + testResult: { testTypes, testHistory, createdByName, createdAt }, + } = this.state as any; + + if (testHistory) { + // eslint-disable-next-line + for (const history of testHistory) { + // eslint-disable-next-line + for (const testType of history.testTypes) { + if (testType.testCode === testTypes.testCode) { + result.Reissue = { + Reason: 'Replacement', + Issuer: createdByName, + Date: moment(createdAt).format('DD.MM.YYYY'), + }; + break; + } + } + } + } + + return result; + } +} diff --git a/src/certificate/commands/WatermarkCommand.ts b/src/certificate/commands/WatermarkCommand.ts new file mode 100644 index 00000000..b6392c13 --- /dev/null +++ b/src/certificate/commands/WatermarkCommand.ts @@ -0,0 +1,12 @@ +import { Service } from 'typedi'; +import { ICertificatePayload } from '../../models'; +import { BasePayloadCommand } from '../ICertificatePayloadCommand'; + +@Service() +export class WatermarkCommand extends BasePayloadCommand { + public async generate(): Promise { + return { + Watermark: process.env.BRANCH === 'prod' ? '' : 'NOT VALID', + } as ICertificatePayload; + } +} diff --git a/src/config/DependencyInjection.ts b/src/config/DependencyInjection.ts index 0a77583a..0f0577bb 100644 --- a/src/config/DependencyInjection.ts +++ b/src/config/DependencyInjection.ts @@ -1,5 +1,6 @@ import { LambdaClient } from '@aws-sdk/client-lambda'; import { S3Client } from '@aws-sdk/client-s3'; +import { StandardRetryStrategy } from '@smithy/util-retry'; import * as AWSXRay from 'aws-xray-sdk'; import { Container, Service } from 'typedi'; import { IInvokeConfig, IS3Config } from '../models'; @@ -8,11 +9,32 @@ import { Configuration } from '../utils/Configuration'; @Service() export class DependencyInjection { public static register() { - const invokeConfig: IInvokeConfig = Configuration.getInstance().getInvokeConfig(); - const s3Config: IS3Config = Configuration.getInstance().getS3Config(); + const configInstance = Configuration.getInstance(); + const config = configInstance.getConfig(); + const invokeConfig: IInvokeConfig = configInstance.getInvokeConfig(); + const s3Config: IS3Config = configInstance.getS3Config(); - Container.set(LambdaClient, AWSXRay.captureAWSv3Client(new LambdaClient(invokeConfig.params))); - Container.set(S3Client, AWSXRay.captureAWSv3Client(new S3Client(s3Config))); + const retryStrategy = new StandardRetryStrategy(config.network.retryAttempts); + + Container.set( + LambdaClient, + AWSXRay.captureAWSv3Client( + new LambdaClient({ + ...invokeConfig.params, + retryStrategy, + }) + ) + ); + + Container.set( + S3Client, + AWSXRay.captureAWSv3Client( + new S3Client({ + ...s3Config, + retryStrategy, + }) + ) + ); const isOffline = (process.env.IS_OFFLINE ?? false) as boolean; if (isOffline) { diff --git a/src/config/config.yml b/src/config/config.yml index ed71c0f7..73b30b10 100644 --- a/src/config/config.yml +++ b/src/config/config.yml @@ -82,3 +82,5 @@ mot: documentDir: CVS welsh: secret_key: +network: + retryAttempts: 3 diff --git a/src/defect/DefectService.ts b/src/defect/DefectService.ts index 16a87206..44d7ef6c 100644 --- a/src/defect/DefectService.ts +++ b/src/defect/DefectService.ts @@ -1,7 +1,6 @@ -import moment from 'moment'; import { Service } from 'typedi'; import { ICustomDefect } from '../models'; -import { IVA_30, LOCATION_ENGLISH, LOCATION_WELSH } from '../models/Enums'; +import { CERTIFICATE_DATA, IVA_30, LOCATION_ENGLISH, LOCATION_WELSH, TEST_RESULTS } from '../models/Enums'; import { IDefectChild } from '../models/IDefectChild'; import { IDefectParent } from '../models/IDefectParent'; import { IFlatDefect } from '../models/IFlatDefect'; @@ -220,4 +219,83 @@ export class DefectService { return filteredWelshDefectsOnVehicleType[0]; } } + + public generateDangerousDefects( + testResult: TEST_RESULTS, + defect: any, + type: CERTIFICATE_DATA, + defects: any, + vehicleType: string, + isWelsh: boolean, + flattenedDefects: IFlatDefect[] + ) { + if ((testResult === TEST_RESULTS.PRS || defect.prs) && type === CERTIFICATE_DATA.FAIL_DATA) { + defects.PRSDefects.push(this.formatDefect(defect)); + + if (this.testResultService.isWelshCertificateAvailable(vehicleType, testResult) && isWelsh) { + defects.PRSDefectsWelsh.push(this.formatDefectWelsh(defect, vehicleType, flattenedDefects)); + } + } else if (testResult === TEST_RESULTS.FAIL) { + defects.DangerousDefects.push(this.formatDefect(defect)); + + // If the test was conducted in Wales and is valid vehicle type, format and add the welsh defects to the list + if (this.testResultService.isWelshCertificateAvailable(vehicleType, testResult) && isWelsh) { + defects.DangerousDefectsWelsh.push(this.formatDefectWelsh(defect, vehicleType, flattenedDefects)); + } + } + } + + public generateMajorDefects( + testResult: TEST_RESULTS, + defect: any, + type: CERTIFICATE_DATA, + defects: any, + vehicleType: string, + isWelsh: boolean, + flattenedDefects: IFlatDefect[] + ) { + if ((testResult === TEST_RESULTS.PRS || defect.prs) && type === CERTIFICATE_DATA.FAIL_DATA) { + defects.PRSDefects.push(this.formatDefect(defect)); + + if (this.testResultService.isWelshCertificateAvailable(vehicleType, testResult) && isWelsh) { + defects.PRSDefectsWelsh.push(this.formatDefectWelsh(defect, vehicleType, flattenedDefects)); + } + } else if (testResult === TEST_RESULTS.FAIL) { + defects.MajorDefects.push(this.formatDefect(defect)); + + // If the test was conducted in Wales and is valid vehicle type, format and add the welsh defects to the list + if (this.testResultService.isWelshCertificateAvailable(vehicleType, testResult) && isWelsh) { + defects.MajorDefectsWelsh.push(this.formatDefectWelsh(defect, vehicleType, flattenedDefects)); + } + } + } + + public generateMinorDefects( + defects: any, + defect: any, + vehicleType: string, + testResult: TEST_RESULTS, + isWelsh: boolean, + flattenedDefects: IFlatDefect[] + ) { + defects.MinorDefects.push(this.formatDefect(defect)); + + if (this.testResultService.isWelshCertificateAvailable(vehicleType, testResult) && isWelsh) { + defects.MinorDefectsWelsh.push(this.formatDefectWelsh(defect, vehicleType, flattenedDefects)); + } + } + + public generateAdvisoryDefects( + defects: any, + defect: any, + vehicleType: string, + testResult: TEST_RESULTS, + isWelsh: boolean + ) { + defects.AdvisoryDefects.push(this.formatDefect(defect)); + + if (this.testResultService.isWelshCertificateAvailable(vehicleType, testResult) && isWelsh) { + defects.AdvisoryDefectsWelsh.push(this.formatDefect(defect)); + } + } } diff --git a/src/models/index.d.ts b/src/models/index.d.ts index 78478bcf..cfdde7c7 100644 --- a/src/models/index.d.ts +++ b/src/models/index.d.ts @@ -1,3 +1,5 @@ +import { TEST_RESULTS } from './Enums'; + interface IInvokeConfig { params: { apiVersion: string; endpoint?: string }; functions: { @@ -176,6 +178,7 @@ interface ITestResult { model?: string; bodyType?: IBodyTypeModel; testTypes: ITestType; + createdById?: string; systemNumber: string; } @@ -198,7 +201,7 @@ interface ITestType { numberOfSeatbeltsFitted: number; lastSeatbeltInstallationCheckDate: string; seatbeltInstallationCheckDate: boolean; - testResult: string; + testResult: TEST_RESULTS; prohibitionIssued: boolean; reasonForAbandoning: string; additionalNotesRecorded: string; diff --git a/src/services/CertificateGenerationService.ts b/src/services/CertificateGenerationService.ts index ed52d337..b7264c56 100644 --- a/src/services/CertificateGenerationService.ts +++ b/src/services/CertificateGenerationService.ts @@ -1,34 +1,16 @@ -import { Readable } from 'stream'; import { InvocationRequest, InvocationResponse, ServiceException } from '@aws-sdk/client-lambda'; -import { GetObjectOutput } from '@aws-sdk/client-s3'; import { getProfile } from '@dvsa/cvs-feature-flags/profiles/vtx'; import { toUint8Array } from '@smithy/util-utf8'; import moment from 'moment'; import { Service } from 'typedi'; -import { DefectRepository } from '../defect/DefectRepository'; -import { DefectService } from '../defect/DefectService'; -import { - ICertificatePayload, - IFeatureFlags, - IGeneratedCertificateResponse, - IInvokeConfig, - IMOTConfig, - IRequiredStandard, - IRoadworthinessCertificateData, - ITestResult, -} from '../models'; -import { CERTIFICATE_DATA, IVA_30, TEST_RESULTS, VEHICLE_TYPES } from '../models/Enums'; -import { IDefectParent } from '../models/IDefectParent'; -import { IFlatDefect } from '../models/IFlatDefect'; -import { TechRecordType } from '../models/Types'; -import { TechRecordService } from '../tech-record/TechRecordService'; -import { TestResultRepository } from '../test-result/TestResultRepository'; +import { CertificatePayloadGenerator } from '../certificate/CertificatePayloadGenerator'; +import { CertificateTypes } from '../certificate/CertificateTypes'; +import { IFeatureFlags, IGeneratedCertificateResponse, IInvokeConfig, IMOTConfig, ITestResult } from '../models'; +import { CERTIFICATE_DATA, TEST_RESULTS, VEHICLE_TYPES } from '../models/Enums'; import { TestResultService } from '../test-result/TestResultService'; import { TestStationRepository } from '../test-station/TestStationRepository'; -import { TrailerRepository } from '../trailer/TrailerRepository'; import { Configuration } from '../utils/Configuration'; import { LambdaService } from './LambdaService'; -import { S3BucketService } from './S3BucketService'; /** * Service class for Certificate Generation @@ -38,15 +20,11 @@ class CertificateGenerationService { private readonly config: Configuration = Configuration.getInstance(); constructor( - private s3Client: S3BucketService, private lambdaClient: LambdaService, - private trailerRepository: TrailerRepository, + private certificatePayloadGenerator: CertificatePayloadGenerator, private testStationRepository: TestStationRepository, - private testResultRepository: TestResultRepository, - private defectRepository: DefectRepository, private testResultService: TestResultService, - private techRecordService: TechRecordService, - private defectService: DefectService + private certificateTypes: CertificateTypes ) {} /** @@ -62,31 +40,6 @@ class CertificateGenerationService { const payload: string = JSON.stringify(await this.generatePayload(testResult, shouldTranslateTestResult)); - const certificateTypes: any = { - psv_pass: config.documentNames.vtp20, - psv_pass_bilingual: config.documentNames.vtp20_bilingual, - psv_fail: config.documentNames.vtp30, - psv_fail_bilingual: config.documentNames.vtp30_bilingual, - psv_prs: config.documentNames.psv_prs, - psv_prs_bilingual: config.documentNames.psv_prs_bilingual, - hgv_pass: config.documentNames.vtg5, - hgv_pass_bilingual: config.documentNames.vtg5_bilingual, - hgv_fail: config.documentNames.vtg30, - hgv_fail_bilingual: config.documentNames.vtg30_bilingual, - hgv_prs: config.documentNames.hgv_prs, - hgv_prs_bilingual: config.documentNames.hgv_prs_bilingual, - trl_pass: config.documentNames.vtg5a, - trl_pass_bilingual: config.documentNames.vtg5a_bilingual, - trl_fail: config.documentNames.vtg30, - trl_fail_bilingual: config.documentNames.vtg30_bilingual, - trl_prs: config.documentNames.trl_prs, - trl_prs_bilingual: config.documentNames.trl_prs_bilingual, - rwt: config.documentNames.rwt, - adr_pass: config.documentNames.adr_pass, - iva_fail: config.documentNames.iva_fail, - msva_fail: config.documentNames.msva_fail, - }; - let vehicleTestRes: string; if (this.testResultService.isRoadworthinessTestType(testType.testTypeId)) { // CVSB-7677 is road-worthiness test @@ -106,6 +59,7 @@ class CertificateGenerationService { vehicleTestRes = testResult.vehicleType + '_' + testType.testResult; } + const certificateType = this.certificateTypes.getCertificateType(vehicleTestRes); console.log(`vehicleTestRes: ${vehicleTestRes}`); const invokeParams: InvocationRequest = { @@ -116,7 +70,7 @@ class CertificateGenerationService { JSON.stringify({ httpMethod: 'POST', pathParameters: { - documentName: certificateTypes[vehicleTestRes], + documentName: certificateType, documentDirectory: config.documentDir, }, json: true, @@ -136,7 +90,7 @@ class CertificateGenerationService { testTypeName: testResult.testTypes.testTypeName, testTypeResult: testResult.testTypes.testResult, dateOfIssue: moment(testResult.testTypes.testTypeStartTimestamp).format('D MMMM YYYY'), - certificateType: certificateTypes[vehicleTestRes].split('.')[0], + certificateType: certificateType.split('.')[0], fileFormat: 'pdf', fileName: `${testResult.testTypes.testNumber}_${testResult.vin}.pdf`, fileSize: responseBuffer.byteLength.toString(), @@ -234,405 +188,62 @@ class CertificateGenerationService { return isWelshCountry; } - /** - * Retrieves a signature from the cvs-signature S3 bucket - * @param staffId - staff ID of the signature you want to retrieve - * @returns the signature as a base64 encoded string - */ - public async getSignature(staffId: string): Promise { - try { - const result: GetObjectOutput = await this.s3Client.download( - `cvs-signature-${process.env.BUCKET}`, - `${staffId}.base64` - ); - - if (result.Body instanceof Readable) { - const chunks: Uint8Array[] = []; - for await (const chunk of result.Body) { - chunks.push(chunk); - } - const buffer = Buffer.concat(chunks); - return buffer.toString('utf-8'); - } else { - throw new Error(`Unexpected body type: ${typeof result.Body}`); - } - } catch (error) { - console.error(`Unable to fetch signature for staff id ${staffId}. ${(error as Error).message}`); - } - return null; - } - - /** - * Generates the payload for the MOT certificate generation service - * @param testResult - source test result for certificate generation - * @param isWelsh - the boolean value whether the atf where test was conducted resides in Wales - */ - public async generatePayload(testResult: any, isWelsh = false) { - let name = testResult.testerName; - - const nameArrayList: string[] = name.split(','); - - if (nameArrayList.length === 2) { - name = name.split(', ').reverse().join(' '); - testResult.testerName = name; - } - - const signature: string | null = await this.getSignature(testResult.createdById ?? testResult.testerStaffId); - - let makeAndModel: any = null; - if (!this.testResultService.isRoadworthinessTestType(testResult.testTypes.testTypeId)) { - makeAndModel = await this.techRecordService.getVehicleMakeAndModel(testResult); - } - - let payload: ICertificatePayload = { - Watermark: process.env.BRANCH === 'prod' ? '' : 'NOT VALID', - DATA: undefined, - FAIL_DATA: undefined, - RWT_DATA: undefined, - ADR_DATA: undefined, - IVA_DATA: undefined, - MSVA_DATA: undefined, - Signature: { - ImageType: 'png', - ImageData: signature, - }, - }; - - const { testTypes, vehicleType, systemNumber, testHistory } = testResult; - - if (testHistory) { - for (const history of testHistory) { - for (const testType of history.testTypes) { - if (testType.testCode === testTypes.testCode) { - payload.Reissue = { - Reason: 'Replacement', - Issuer: testResult.createdByName, - Date: moment(testResult.createdAt).format('DD.MM.YYYY'), - }; - break; - } - } - } + private getTestType(testResult: ITestResult): CERTIFICATE_DATA { + if (this.testResultService.isHgvTrlRoadworthinessCertificate(testResult)) { + return CERTIFICATE_DATA.RWT_DATA; } - if (this.testResultService.isHgvTrlRoadworthinessCertificate(testResult)) { - // CVSB-7677 for roadworthiness test for hgv or trl. - const rwtData = await this.generateCertificateData(testResult, CERTIFICATE_DATA.RWT_DATA); - payload.RWT_DATA = { ...rwtData }; - } else if ( + if ( testResult.testTypes.testResult === TEST_RESULTS.PASS && this.testResultService.isTestTypeAdr(testResult.testTypes) ) { - const adrData = await this.generateCertificateData(testResult, CERTIFICATE_DATA.ADR_DATA); - payload.ADR_DATA = { ...adrData, ...makeAndModel }; - } else if ( + return CERTIFICATE_DATA.ADR_DATA; + } + + if ( testResult.testTypes.testResult === TEST_RESULTS.FAIL && this.testResultService.isIvaTest(testResult.testTypes.testTypeId) ) { - const ivaData = await this.generateCertificateData(testResult, CERTIFICATE_DATA.IVA_DATA); - payload.IVA_DATA = { ...ivaData }; - } else if ( + return CERTIFICATE_DATA.IVA_DATA; + } + + if ( testResult.testTypes.testResult === TEST_RESULTS.FAIL && this.testResultService.isMsvaTest(testResult.testTypes.testTypeId) ) { - const msvaData = await this.generateCertificateData(testResult, CERTIFICATE_DATA.MSVA_DATA); - payload.MSVA_DATA = { ...msvaData }; - } else { - const odometerHistory = - vehicleType === VEHICLE_TYPES.TRL - ? undefined - : await this.testResultRepository.getOdometerHistory(systemNumber); - const TrnObj = this.testResultService.isValidForTrn(vehicleType, makeAndModel) - ? await this.trailerRepository.getTrailerRegistrationObject(testResult.vin, makeAndModel.Make) - : undefined; - if (testTypes.testResult !== TEST_RESULTS.FAIL) { - const passData = await this.generateCertificateData(testResult, CERTIFICATE_DATA.PASS_DATA, isWelsh); - payload.DATA = { - ...passData, - ...makeAndModel, - ...odometerHistory, - ...TrnObj, - }; - } - if (testTypes.testResult !== TEST_RESULTS.PASS) { - const failData = await this.generateCertificateData(testResult, CERTIFICATE_DATA.FAIL_DATA, isWelsh); - payload.FAIL_DATA = { - ...failData, - ...makeAndModel, - ...odometerHistory, - ...TrnObj, - }; - } + return CERTIFICATE_DATA.MSVA_DATA; } - // Purge undefined values - payload = JSON.parse(JSON.stringify(payload)); - return payload; + if (testResult.testTypes.testResult !== TEST_RESULTS.PASS) { + return CERTIFICATE_DATA.FAIL_DATA; + } + + return CERTIFICATE_DATA.PASS_DATA; } /** - * Generates certificate data for a given test result and certificate type - * @param testResult - the source test result for certificate generation - * @param type - the certificate type + * Generates the payload for the MOT certificate generation service + * @param testResult - source test result for certificate generation * @param isWelsh - the boolean value whether the atf where test was conducted resides in Wales */ - public async generateCertificateData(testResult: ITestResult, type: string, isWelsh = false) { - let defectListFromApi: IDefectParent[] = []; - let flattenedDefects: IFlatDefect[] = []; - if (isWelsh) { - defectListFromApi = await this.defectRepository.getDefectTranslations(); - flattenedDefects = this.defectService.flattenDefectsFromApi(defectListFromApi); - } - const testType: any = testResult.testTypes; - switch (type) { - case CERTIFICATE_DATA.PASS_DATA: - case CERTIFICATE_DATA.FAIL_DATA: - const defects: any = await this.generateDefects( - testResult.testTypes, - type, - testResult.vehicleType, - flattenedDefects, - isWelsh - ); + public async generatePayload(testResult: any, isWelsh = false) { + let name = testResult.testerName; - return { - TestNumber: testType.testNumber, - TestStationPNumber: testResult.testStationPNumber, - TestStationName: testResult.testStationName, - CurrentOdometer: { - value: testResult.odometerReading, - unit: testResult.odometerReadingUnits, - }, - IssuersName: testResult.testerName, - DateOfTheTest: moment(testResult.testEndTimestamp).format('DD.MM.YYYY'), - CountryOfRegistrationCode: testResult.countryOfRegistration, - VehicleEuClassification: testResult.euVehicleCategory.toUpperCase(), - RawVIN: testResult.vin, - RawVRM: testResult.vehicleType === VEHICLE_TYPES.TRL ? testResult.trailerId : testResult.vrm, - ExpiryDate: testType.testExpiryDate ? moment(testType.testExpiryDate).format('DD.MM.YYYY') : undefined, - EarliestDateOfTheNextTest: - (testResult.vehicleType === VEHICLE_TYPES.HGV || testResult.vehicleType === VEHICLE_TYPES.TRL) && - (testResult.testTypes.testResult === TEST_RESULTS.PASS || - testResult.testTypes.testResult === TEST_RESULTS.PRS) - ? moment(testType.testAnniversaryDate).subtract(1, 'months').startOf('month').format('DD.MM.YYYY') - : moment(testType.testAnniversaryDate).format('DD.MM.YYYY'), - SeatBeltTested: testType.seatbeltInstallationCheckDate ? 'Yes' : 'No', - SeatBeltPreviousCheckDate: testType.lastSeatbeltInstallationCheckDate - ? moment(testType.lastSeatbeltInstallationCheckDate).format('DD.MM.YYYY') - : '\u00A0', - SeatBeltNumber: testType.numberOfSeatbeltsFitted, - ...defects, - }; - case CERTIFICATE_DATA.RWT_DATA: - const weightDetails = await this.techRecordService.getWeightDetails(testResult); - let defectRWTList: any; - if (testResult.testTypes.testResult === TEST_RESULTS.FAIL) { - defectRWTList = []; - testResult.testTypes.defects.forEach((defect: any) => { - defectRWTList.push(this.defectService.formatDefect(defect)); - }); - } else { - defectRWTList = undefined; - } + const nameArrayList: string[] = name.split(','); - const resultPass: IRoadworthinessCertificateData = { - Dgvw: weightDetails.dgvw, - Weight2: weightDetails.weight2, - VehicleNumber: testResult.vehicleType === VEHICLE_TYPES.TRL ? testResult.trailerId : testResult.vrm, - Vin: testResult.vin, - IssuersName: testResult.testerName, - DateOfInspection: moment(testType.testTypeStartTimestamp).format('DD.MM.YYYY'), - TestStationPNumber: testResult.testStationPNumber, - DocumentNumber: testType.certificateNumber, - Date: moment(testType.testTypeStartTimestamp).format('DD.MM.YYYY'), - Defects: defectRWTList, - IsTrailer: testResult.vehicleType === VEHICLE_TYPES.TRL, - }; - return resultPass; - case CERTIFICATE_DATA.ADR_DATA: - const adrDetails: TechRecordType = await this.techRecordService.getAdrDetails(testResult); - const docGenPayloadAdr = { - ChasisNumber: testResult.vin, - RegistrationNumber: testResult.vrm, - ApplicantDetails: { - name: adrDetails?.techRecord_applicantDetails_name, - address1: adrDetails?.techRecord_applicantDetails_address1, - address2: adrDetails?.techRecord_applicantDetails_address2, - address3: adrDetails?.techRecord_applicantDetails_address1, - postTown: adrDetails?.techRecord_applicantDetails_postTown, - postCode: adrDetails?.techRecord_applicantDetails_postCode, - telephoneNumber: adrDetails?.techRecord_applicantDetails_telephoneNumber, - emailAddress: adrDetails?.techRecord_applicantDetails_emailAddress, - }, - VehicleType: adrDetails?.techRecord_adrDetails_vehicleDetails_type, - PermittedDangerousGoods: adrDetails?.techRecord_adrDetails_permittedDangerousGoods, - BrakeEndurance: adrDetails?.techRecord_adrDetails_brakeEndurance, - Weight: adrDetails?.techRecord_adrDetails_weight, - TankManufacturer: adrDetails?.techRecord_adrDetails_tank_tankDetails_tankStatement_statement - ? adrDetails.techRecord_adrDetails_tank_tankDetails_tankManufacturer - : undefined, - Tc2InitApprovalNo: adrDetails?.techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateApprovalNo, - TankManufactureSerialNo: adrDetails?.techRecord_adrDetails_tank_tankDetails_tankManufacturerSerialNo, - YearOfManufacture: adrDetails?.techRecord_adrDetails_tank_tankDetails_yearOfManufacture, - TankCode: adrDetails?.techRecord_adrDetails_tank_tankDetails_tankCode, - SpecialProvisions: adrDetails?.techRecord_adrDetails_tank_tankDetails_specialProvisions, - TankStatement: adrDetails?.techRecord_adrDetails_tank_tankDetails_tankStatement_statement, - ExpiryDate: testResult.testTypes.testExpiryDate, - AtfNameAtfPNumber: testResult.testStationName + ' ' + testResult.testStationPNumber, - Notes: testResult.testTypes.additionalNotesRecorded, - TestTypeDate: testResult.testTypes.testTypeStartTimestamp, - }; - console.log('CHECK HERE DOCGENPAYLOAD -> ', docGenPayloadAdr); - return docGenPayloadAdr; - case CERTIFICATE_DATA.IVA_DATA: - const ivaFailDetailsForDocGen = { - vin: testResult.vin, - serialNumber: testResult.vehicleType === 'trl' ? testResult.trailerId : testResult.vrm, - vehicleTrailerNrNo: testResult.vehicleType === 'trl' ? testResult.trailerId : testResult.vrm, - testCategoryClass: testResult.euVehicleCategory, - testCategoryBasicNormal: this.testResultService.isBasicIvaTest(testResult.testTypes.testTypeId) - ? IVA_30.BASIC - : IVA_30.NORMAL, - make: testResult.make, - model: testResult.model, - bodyType: testResult.bodyType?.description, - date: moment(testResult.testTypes.testTypeStartTimestamp).format('DD/MM/YYYY'), - testerName: testResult.testerName, - reapplicationDate: testResult.testTypes?.reapplicationDate - ? moment(testResult.testTypes?.reapplicationDate).format('DD/MM/YYYY') - : '', - station: testResult.testStationName, - additionalDefects: this.defectService.formatVehicleApprovalAdditionalDefects( - testResult.testTypes.customDefects - ), - requiredStandards: this.sortRequiredStandards(testResult.testTypes.requiredStandards), - }; - return ivaFailDetailsForDocGen; - case CERTIFICATE_DATA.MSVA_DATA: - const msvaFailDetailsForDocGen = { - vin: testResult.vin, - serialNumber: testResult.vrm, - vehicleZNumber: testResult.vrm, - make: testResult.make, - model: testResult.model, - type: testResult.vehicleType, - testerName: testResult.testerName, - date: moment(testResult.testTypes.testTypeStartTimestamp).format('DD/MM/YYYY'), - reapplicationDate: testResult.testTypes?.reapplicationDate - ? moment(testResult.testTypes?.reapplicationDate).format('DD/MM/YYYY') - : '', - station: testResult.testStationName, - additionalDefects: this.defectService.formatVehicleApprovalAdditionalDefects( - testResult.testTypes.customDefects - ), - requiredStandards: this.sortRequiredStandards(testResult.testTypes.requiredStandards), - }; - return msvaFailDetailsForDocGen; + if (nameArrayList.length === 2) { + name = name.split(', ').reverse().join(' '); + testResult.testerName = name; } - } - /** - * Generates an object containing defects for a given test type and certificate type - * @param testTypes - the source test type for defect generation - * @param type - the certificate type - * @param vehicleType - the vehicle type from the test result - * @param flattenedDefects - the list of flattened defects after being retrieved from the defect service - * @param isWelsh - determines whether the atf in which the test result was conducted resides in Wales - */ - private generateDefects( - testTypes: any, - type: string, - vehicleType: string, - flattenedDefects: IFlatDefect[], - isWelsh = false - ) { - const rawDefects: any = testTypes.defects; - const defects: any = { - DangerousDefects: [], - MajorDefects: [], - PRSDefects: [], - MinorDefects: [], - AdvisoryDefects: [], - DangerousDefectsWelsh: [], - MajorDefectsWelsh: [], - PRSDefectsWelsh: [], - MinorDefectsWelsh: [], - AdvisoryDefectsWelsh: [], - }; + const testType = this.getTestType(testResult); + let payload = await this.certificatePayloadGenerator.generateCertificateData(testResult, testType, isWelsh); - rawDefects.forEach((defect: any) => { - switch (defect.deficiencyCategory.toLowerCase()) { - case 'dangerous': - if ((testTypes.testResult === TEST_RESULTS.PRS || defect.prs) && type === CERTIFICATE_DATA.FAIL_DATA) { - defects.PRSDefects.push(this.defectService.formatDefect(defect)); - if (this.testResultService.isWelshCertificateAvailable(vehicleType, testTypes.testResult) && isWelsh) { - defects.PRSDefectsWelsh.push(this.defectService.formatDefectWelsh(defect, vehicleType, flattenedDefects)); - } - } else if (testTypes.testResult === 'fail') { - defects.DangerousDefects.push(this.defectService.formatDefect(defect)); - // If the test was conducted in Wales and is valid vehicle type, format and add the welsh defects to the list - if (this.testResultService.isWelshCertificateAvailable(vehicleType, testTypes.testResult) && isWelsh) { - defects.DangerousDefectsWelsh.push( - this.defectService.formatDefectWelsh(defect, vehicleType, flattenedDefects) - ); - } - } - break; - case 'major': - if ((testTypes.testResult === TEST_RESULTS.PRS || defect.prs) && type === CERTIFICATE_DATA.FAIL_DATA) { - defects.PRSDefects.push(this.defectService.formatDefect(defect)); - if (this.testResultService.isWelshCertificateAvailable(vehicleType, testTypes.testResult) && isWelsh) { - defects.PRSDefectsWelsh.push(this.defectService.formatDefectWelsh(defect, vehicleType, flattenedDefects)); - } - } else if (testTypes.testResult === 'fail') { - defects.MajorDefects.push(this.defectService.formatDefect(defect)); - // If the test was conducted in Wales and is valid vehicle type, format and add the welsh defects to the list - if (this.testResultService.isWelshCertificateAvailable(vehicleType, testTypes.testResult) && isWelsh) { - defects.MajorDefectsWelsh.push( - this.defectService.formatDefectWelsh(defect, vehicleType, flattenedDefects) - ); - } - } - break; - case 'minor': - defects.MinorDefects.push(this.defectService.formatDefect(defect)); - if (this.testResultService.isWelshCertificateAvailable(vehicleType, testTypes.testResult) && isWelsh) { - defects.MinorDefectsWelsh.push(this.defectService.formatDefectWelsh(defect, vehicleType, flattenedDefects)); - } - break; - case 'advisory': - defects.AdvisoryDefects.push(this.defectService.formatDefect(defect)); - if (this.testResultService.isWelshCertificateAvailable(vehicleType, testTypes.testResult) && isWelsh) { - defects.AdvisoryDefectsWelsh.push(this.defectService.formatDefect(defect)); - } - break; - } - }); + // Purge undefined values + payload = JSON.parse(JSON.stringify(payload)); - Object.entries(defects).forEach(([k, v]: [string, any]) => { - if (v.length === 0) { - Object.assign(defects, { [k]: undefined }); - } - }); - console.log(JSON.stringify(defects)); - return defects; + return payload; } - - /** - * Sorts required standards if present by refCalculation and then returns it - * @param requiredStandards - the requiredStandards array to sort - * @returns - the sorted requiredStandards array - */ - private sortRequiredStandards = ( - requiredStandards: IRequiredStandard[] | undefined - ): IRequiredStandard[] | undefined => { - if (!requiredStandards) { - return; - } - - const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' }); - return requiredStandards.sort((a, b) => collator.compare(a.refCalculation, b.refCalculation)); - }; } export { CertificateGenerationService, IGeneratedCertificateResponse }; diff --git a/tests/unit/certGen.unitTest.ts b/tests/unit/certGen.unitTest.ts index b4e54cce..fa285768 100644 --- a/tests/unit/certGen.unitTest.ts +++ b/tests/unit/certGen.unitTest.ts @@ -37,6 +37,12 @@ import { TechRecordRepository } from "../../src/tech-record/TechRecordRepository import { TestResultRepository } from "../../src/test-result/TestResultRepository"; import { DefectRepository } from "../../src/defect/DefectRepository"; import { DefectService } from "../../src/defect/DefectService"; +import { MsvaCertificateCommand } from "../../src/certificate/commands/MsvaCertificateCommand"; +import { CERTIFICATE_DATA } from "../../src/models/Enums"; +import { IvaCertificateCommand } from "../../src/certificate/commands/IvaCertificateCommand"; +import { PassOrFailCertificateCommand } from "../../src/certificate/commands/PassOrFailCertificateCommand"; +import { DefectsCommand } from "../../src/certificate/commands/DefectsCommand"; +import { CertificatePayloadStateBag } from "../../src/certificate/CertificatePayloadStateBag"; const sandbox = sinon.createSandbox(); @@ -4543,42 +4549,58 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with PRSDefects list in Fail data", async () => { const expectedResult: any = { - TestNumber: "W01A00310", - TestStationPNumber: "09-4129632", - TestStationName: "Abshire-Kub", - CurrentOdometer: { - value: 12312, - unit: "kilometres", - }, - IssuersName: "CVS Dev1", - DateOfTheTest: "26.02.2019", - CountryOfRegistrationCode: "gb", - VehicleEuClassification: "M1", - RawVIN: "XMGDE02FS0H012345", - RawVRM: "BQ91YHQ", - EarliestDateOfTheNextTest: "26.12.2019", - ExpiryDate: "25.02.2020", - SeatBeltTested: "Yes", - SeatBeltPreviousCheckDate: "26.02.2019", - SeatBeltNumber: 2, - DangerousDefects: [ - "54.1.a.ii Power steering: not working correctly and obviously affects steering control. Axles: 7. Inner Offside. Asdasd", - ], - MajorDefects: undefined, - MinorDefects: [ - "54.1.d.i Power steering: reservoir is below minimum level. Axles: 7. Outer Nearside.", - ], - AdvisoryDefects: [ - "5.1 Compression Ignition Engines Statutory Smoke Meter Test: null Dasdasdccc", - ], - PRSDefects: ["1.1.a A registration plate: missing. Front."], + FAIL_DATA: { + TestNumber: "W01A00310", + TestStationPNumber: "09-4129632", + TestStationName: "Abshire-Kub", + CurrentOdometer: { + value: 12312, + unit: "kilometres", + }, + IssuersName: "CVS Dev1", + DateOfTheTest: "26.02.2019", + CountryOfRegistrationCode: "gb", + VehicleEuClassification: "M1", + RawVIN: "XMGDE02FS0H012345", + RawVRM: "BQ91YHQ", + EarliestDateOfTheNextTest: "26.12.2019", + ExpiryDate: "25.02.2020", + SeatBeltTested: "Yes", + SeatBeltPreviousCheckDate: "26.02.2019", + SeatBeltNumber: 2, + DangerousDefects: [ + "54.1.a.ii Power steering: not working correctly and obviously affects steering control. Axles: 7. Inner Offside. Asdasd", + ], + MajorDefects: undefined, + MinorDefects: [ + "54.1.d.i Power steering: reservoir is below minimum level. Axles: 7. Outer Nearside.", + ], + AdvisoryDefects: [ + "5.1 Compression Ignition Engines Statutory Smoke Meter Test: null Dasdasdccc", + ], + PRSDefects: ["1.1.a A registration plate: missing. Front."], + } }; - return await certificateGenerationService - .generateCertificateData(testResult, "FAIL_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.FAIL_DATA, + testResult + } as CertificatePayloadStateBag; + + const passOrFailCommand = Container.get(PassOrFailCertificateCommand); + passOrFailCommand.initialise(state); + + const defectsCommand = Container.get(DefectsCommand); + defectsCommand.initialise(state); + + const payload = { + FAIL_DATA: { + ...(await passOrFailCommand.generate()).FAIL_DATA, + ...(await defectsCommand.generate()).FAIL_DATA + } + }; + + expect(payload).toEqual(expectedResult); }); } ); @@ -4591,42 +4613,58 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with PRSDefects list in Fail Data", async () => { const expectedResult: any = { - TestNumber: "W01A00310", - TestStationPNumber: "09-4129632", - TestStationName: "Abshire-Kub", - CurrentOdometer: { - value: 12312, - unit: "kilometres", - }, - IssuersName: "CVS Dev1", - DateOfTheTest: "26.02.2019", - CountryOfRegistrationCode: "gb", - VehicleEuClassification: "M1", - RawVIN: "XMGDE02FS0H012345", - RawVRM: "BQ91YHQ", - EarliestDateOfTheNextTest: "26.12.2019", - ExpiryDate: "25.02.2020", - SeatBeltTested: "Yes", - SeatBeltPreviousCheckDate: "26.02.2019", - SeatBeltNumber: 2, - DangerousDefects: undefined, - MajorDefects: ["1.1.a A registration plate: missing. Front."], - MinorDefects: [ - "54.1.d.i Power steering: reservoir is below minimum level. Axles: 7. Outer Nearside.", - ], - AdvisoryDefects: [ - "5.1 Compression Ignition Engines Statutory Smoke Meter Test: null Dasdasdccc", - ], - PRSDefects: [ - "54.1.a.ii Power steering: not working correctly and obviously affects steering control. Axles: 7. Inner Offside. Asdasd", - ], + FAIL_DATA: { + TestNumber: "W01A00310", + TestStationPNumber: "09-4129632", + TestStationName: "Abshire-Kub", + CurrentOdometer: { + value: 12312, + unit: "kilometres", + }, + IssuersName: "CVS Dev1", + DateOfTheTest: "26.02.2019", + CountryOfRegistrationCode: "gb", + VehicleEuClassification: "M1", + RawVIN: "XMGDE02FS0H012345", + RawVRM: "BQ91YHQ", + EarliestDateOfTheNextTest: "26.12.2019", + ExpiryDate: "25.02.2020", + SeatBeltTested: "Yes", + SeatBeltPreviousCheckDate: "26.02.2019", + SeatBeltNumber: 2, + DangerousDefects: undefined, + MajorDefects: ["1.1.a A registration plate: missing. Front."], + MinorDefects: [ + "54.1.d.i Power steering: reservoir is below minimum level. Axles: 7. Outer Nearside.", + ], + AdvisoryDefects: [ + "5.1 Compression Ignition Engines Statutory Smoke Meter Test: null Dasdasdccc", + ], + PRSDefects: [ + "54.1.a.ii Power steering: not working correctly and obviously affects steering control. Axles: 7. Inner Offside. Asdasd", + ], + } }; - return await certificateGenerationService - .generateCertificateData(testResult2, "FAIL_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.FAIL_DATA, + testResult: testResult2 + } as CertificatePayloadStateBag; + + const passOrFailCommand = Container.get(PassOrFailCertificateCommand); + passOrFailCommand.initialise(state); + + const defectsCommand = Container.get(DefectsCommand); + defectsCommand.initialise(state); + + const payload = { + FAIL_DATA: { + ...(await passOrFailCommand.generate()).FAIL_DATA, + ...(await defectsCommand.generate()).FAIL_DATA + } + }; + + expect(payload).toEqual(expectedResult); }); } ); @@ -4639,42 +4677,58 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with 0 PRSDefects list in Fail Data", async () => { const expectedResult: any = { - TestNumber: "W01A00310", - TestStationPNumber: "09-4129632", - TestStationName: "Abshire-Kub", - CurrentOdometer: { - value: 12312, - unit: "kilometres", - }, - IssuersName: "CVS Dev1", - DateOfTheTest: "26.02.2019", - CountryOfRegistrationCode: "gb", - VehicleEuClassification: "M1", - RawVIN: "XMGDE02FS0H012345", - RawVRM: "BQ91YHQ", - EarliestDateOfTheNextTest: "26.12.2019", - ExpiryDate: "25.02.2020", - SeatBeltTested: "Yes", - SeatBeltPreviousCheckDate: "26.02.2019", - SeatBeltNumber: 2, - DangerousDefects: [ - "54.1.a.ii Power steering: not working correctly and obviously affects steering control. Axles: 7. Inner Offside. Asdasd", - ], - MajorDefects: ["1.1.a A registration plate: missing. Front."], - MinorDefects: [ - "54.1.d.i Power steering: reservoir is below minimum level. Axles: 7. Outer Nearside.", - ], - AdvisoryDefects: [ - "5.1 Compression Ignition Engines Statutory Smoke Meter Test: null Dasdasdccc", - ], - PRSDefects: undefined, + FAIL_DATA: { + TestNumber: "W01A00310", + TestStationPNumber: "09-4129632", + TestStationName: "Abshire-Kub", + CurrentOdometer: { + value: 12312, + unit: "kilometres", + }, + IssuersName: "CVS Dev1", + DateOfTheTest: "26.02.2019", + CountryOfRegistrationCode: "gb", + VehicleEuClassification: "M1", + RawVIN: "XMGDE02FS0H012345", + RawVRM: "BQ91YHQ", + EarliestDateOfTheNextTest: "26.12.2019", + ExpiryDate: "25.02.2020", + SeatBeltTested: "Yes", + SeatBeltPreviousCheckDate: "26.02.2019", + SeatBeltNumber: 2, + DangerousDefects: [ + "54.1.a.ii Power steering: not working correctly and obviously affects steering control. Axles: 7. Inner Offside. Asdasd", + ], + MajorDefects: ["1.1.a A registration plate: missing. Front."], + MinorDefects: [ + "54.1.d.i Power steering: reservoir is below minimum level. Axles: 7. Outer Nearside.", + ], + AdvisoryDefects: [ + "5.1 Compression Ignition Engines Statutory Smoke Meter Test: null Dasdasdccc", + ], + PRSDefects: undefined, + } }; - return await certificateGenerationService - .generateCertificateData(testResult3, "FAIL_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.FAIL_DATA, + testResult: testResult3 + } as CertificatePayloadStateBag; + + const passOrFailCommand = Container.get(PassOrFailCertificateCommand); + passOrFailCommand.initialise(state); + + const defectsCommand = Container.get(DefectsCommand); + defectsCommand.initialise(state); + + const payload = { + FAIL_DATA: { + ...(await passOrFailCommand.generate()).FAIL_DATA, + ...(await defectsCommand.generate()).FAIL_DATA + } + }; + + expect(payload).toEqual(expectedResult); }); } ); @@ -4692,106 +4746,118 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with requiredStandards in IVA_DATA", async () => { const expectedResult: any = { - additionalDefects: [ - { - defectName: "N/A", - defectNotes: "" - } - ], - bodyType: "some bodyType", - date: "28/11/2023", - requiredStandards: [ - { - additionalInfo: true, - additionalNotes: "The exhaust was held on with blue tac", - inspectionTypes: [ - "normal", - "basic" - ], - prs: false, - refCalculation: "1.1", - requiredStandard: "The exhaust must be securely mounted", - rsNumber: 1, - sectionDescription: "Noise", - sectionNumber: "01" - } - ], - make: "some make", - model: "some model", - reapplicationDate: "", - serialNumber: "C456789", - station: "Abshire-Kub", - testCategoryBasicNormal: "Basic", - testCategoryClass: "m1", - testerName: "CVS Dev1", - vehicleTrailerNrNo: "C456789", - vin: "T12876765", + IVA_DATA: { + additionalDefects: [ + { + defectName: "N/A", + defectNotes: "" + } + ], + bodyType: "some bodyType", + date: "28/11/2023", + requiredStandards: [ + { + additionalInfo: true, + additionalNotes: "The exhaust was held on with blue tac", + inspectionTypes: [ + "normal", + "basic" + ], + prs: false, + refCalculation: "1.1", + requiredStandard: "The exhaust must be securely mounted", + rsNumber: 1, + sectionDescription: "Noise", + sectionNumber: "01" + } + ], + make: "some make", + model: "some model", + reapplicationDate: "", + serialNumber: "C456789", + station: "Abshire-Kub", + testCategoryBasicNormal: "Basic", + testCategoryClass: "m1", + testerName: "CVS Dev1", + vehicleTrailerNrNo: "C456789", + vin: "T12876765", + } }; - return await certificateGenerationService - .generateCertificateData(testResult1, "IVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.IVA_DATA, + testResult: testResult1 + } as CertificatePayloadStateBag; + + const ivaCommand = Container.get(IvaCertificateCommand); + ivaCommand.initialise(state); + const payload = await ivaCommand.generate(); + expect(payload).toEqual(expectedResult); }); it("should return Certificate Data with sorted requiredStandards in IVA_DATA", async () => { const expectedResult: any = { - additionalDefects: [ - { - defectName: "N/A", - defectNotes: "" - } - ], - bodyType: null, - date: "04/03/2024", - requiredStandards: [ - { - sectionNumber: "01", - sectionDescription: "Noise", - rsNumber: 1, - requiredStandard: "The exhaust must be securely mounted", - refCalculation: "1.1", - additionalInfo: true, - inspectionTypes: [ - "normal", - "basic" - ], - prs: false, - additionalNotes: "The exhaust was held on with blue tac" - }, - { - sectionNumber: "6a", - sectionDescription: "Lighting", - rsNumber: 2, - requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", - refCalculation: "6.2a", - additionalInfo: true, - inspectionTypes: [ - "normal", - "basic" - ], - prs: false, - additionalNotes: "The bulbs were slightly worn" - }, - ], - make: null, - model: null, - reapplicationDate: "", - serialNumber: "ZX345CV", - station: "Abshire-Kub", - testCategoryBasicNormal: "Basic", - testCategoryClass: "l1e-a", - testerName: "CVS Dev1", - vehicleTrailerNrNo: "ZX345CV", - vin: "P0123010956789", + IVA_DATA: { + additionalDefects: [ + { + defectName: "N/A", + defectNotes: "" + } + ], + bodyType: null, + date: "04/03/2024", + requiredStandards: [ + { + sectionNumber: "01", + sectionDescription: "Noise", + rsNumber: 1, + requiredStandard: "The exhaust must be securely mounted", + refCalculation: "1.1", + additionalInfo: true, + inspectionTypes: [ + "normal", + "basic" + ], + prs: false, + additionalNotes: "The exhaust was held on with blue tac" + }, + { + sectionNumber: "6a", + sectionDescription: "Lighting", + rsNumber: 2, + requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", + refCalculation: "6.2a", + additionalInfo: true, + inspectionTypes: [ + "normal", + "basic" + ], + prs: false, + additionalNotes: "The bulbs were slightly worn" + }, + ], + make: null, + model: null, + reapplicationDate: "", + serialNumber: "ZX345CV", + station: "Abshire-Kub", + testCategoryBasicNormal: "Basic", + testCategoryClass: "l1e-a", + testerName: "CVS Dev1", + vehicleTrailerNrNo: "ZX345CV", + vin: "P0123010956789", + } }; - return await certificateGenerationService - .generateCertificateData(testResult21, "IVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.IVA_DATA, + testResult: testResult21 + } as CertificatePayloadStateBag; + + const ivaCommand = Container.get(IvaCertificateCommand); + ivaCommand.initialise(state); + const payload = await ivaCommand.generate(); + expect(payload).toEqual(expectedResult); }); } ); @@ -4802,60 +4868,66 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with requiredStandards in IVA_DATA", async () => { const expectedResult: any = { - additionalDefects: [ - { - defectName: "N/A", - defectNotes: "", - } - ], - bodyType: "some bodyType", - date: "28/11/2023", - requiredStandards: [ - { - additionalInfo: true, - additionalNotes: "The exhaust was held on with blue tac", - inspectionTypes: [ - "normal", - "basic" - ], - prs: false, - refCalculation: "1.1", - requiredStandard: "The exhaust must be securely mounted", - rsNumber: 1, - sectionDescription: "Noise", - sectionNumber: "01" - }, - { - additionalInfo: false, - additionalNotes: null, - inspectionTypes: [ - "basic" - ], - prs: false, - refCalculation: "1.5", - requiredStandard: "The stationary noise must have a measured sound level not exceeding 99dbA. (see Notes 2 & 3).", - rsNumber: 5, - sectionDescription: "Noise", - sectionNumber: "01" - }, - ], - make: "some make", - model: "some model", - reapplicationDate: "", - serialNumber: "C456789", - station: "Abshire-Kub", - testCategoryBasicNormal: "Basic", - testCategoryClass: "m1", - testerName: "CVS Dev1", - vehicleTrailerNrNo: "C456789", - vin: "T12876765" + IVA_DATA: { + additionalDefects: [ + { + defectName: "N/A", + defectNotes: "", + } + ], + bodyType: "some bodyType", + date: "28/11/2023", + requiredStandards: [ + { + additionalInfo: true, + additionalNotes: "The exhaust was held on with blue tac", + inspectionTypes: [ + "normal", + "basic" + ], + prs: false, + refCalculation: "1.1", + requiredStandard: "The exhaust must be securely mounted", + rsNumber: 1, + sectionDescription: "Noise", + sectionNumber: "01" + }, + { + additionalInfo: false, + additionalNotes: null, + inspectionTypes: [ + "basic" + ], + prs: false, + refCalculation: "1.5", + requiredStandard: "The stationary noise must have a measured sound level not exceeding 99dbA. (see Notes 2 & 3).", + rsNumber: 5, + sectionDescription: "Noise", + sectionNumber: "01" + }, + ], + make: "some make", + model: "some model", + reapplicationDate: "", + serialNumber: "C456789", + station: "Abshire-Kub", + testCategoryBasicNormal: "Basic", + testCategoryClass: "m1", + testerName: "CVS Dev1", + vehicleTrailerNrNo: "C456789", + vin: "T12876765" + } }; - return await certificateGenerationService - .generateCertificateData(testResult2, "IVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.IVA_DATA, + testResult: testResult2 + } as CertificatePayloadStateBag; + + const ivaCommand = Container.get(IvaCertificateCommand); + ivaCommand.initialise(state); + const payload = await ivaCommand.generate(); + expect(payload).toEqual(expectedResult); }); } ); @@ -4866,45 +4938,51 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with requiredStandards and additionalDefects in IVA_DATA", async () => { const expectedResult: any = { - additionalDefects: [ - "Some custom defect one", - "Some other custom defect two" - ], - bodyType: "some bodyType", - date: "28/11/2023", - requiredStandards: [ - { - additionalInfo: true, - additionalNotes: "The exhaust was held on with blue tac", - inspectionTypes: [ - "normal", - "basic" - ], - prs: false, - refCalculation: "1.1", - requiredStandard: "The exhaust must be securely mounted", - rsNumber: 1, - sectionDescription: "Noise", - sectionNumber: "01" - } - ], - make: "some make", - model: "some model", - reapplicationDate: "", - serialNumber: "C456789", - station: "Abshire-Kub", - testCategoryBasicNormal: "Basic", - testCategoryClass: "m1", - testerName: "CVS Dev1", - vehicleTrailerNrNo: "C456789", - vin: "T12876765" + IVA_DATA: { + additionalDefects: [ + "Some custom defect one", + "Some other custom defect two" + ], + bodyType: "some bodyType", + date: "28/11/2023", + requiredStandards: [ + { + additionalInfo: true, + additionalNotes: "The exhaust was held on with blue tac", + inspectionTypes: [ + "normal", + "basic" + ], + prs: false, + refCalculation: "1.1", + requiredStandard: "The exhaust must be securely mounted", + rsNumber: 1, + sectionDescription: "Noise", + sectionNumber: "01" + } + ], + make: "some make", + model: "some model", + reapplicationDate: "", + serialNumber: "C456789", + station: "Abshire-Kub", + testCategoryBasicNormal: "Basic", + testCategoryClass: "m1", + testerName: "CVS Dev1", + vehicleTrailerNrNo: "C456789", + vin: "T12876765" + } }; - return await certificateGenerationService - .generateCertificateData(testResult3, "IVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.IVA_DATA, + testResult: testResult3 + } as CertificatePayloadStateBag; + + const ivaCommand = Container.get(IvaCertificateCommand); + ivaCommand.initialise(state); + const payload = await ivaCommand.generate(); + expect(payload).toEqual(expectedResult); }); } ); @@ -4915,46 +4993,52 @@ describe("cert-gen", () => { () => { it("return Certificate Data with requiredStandards and additionalDefects in IVA_DATA", async () => { const expectedResult: any = { - additionalDefects: [ - { - defectName: "N/A", - defectNotes: "", - } - ], - bodyType: "some bodyType", - date: "28/11/2023", - requiredStandards: [ - { - additionalInfo: false, - additionalNotes: null, - inspectionTypes: [ - "basic" - ], - prs: false, - refCalculation: "1.5", - requiredStandard: "The stationary noise must have a measured sound level not exceeding 99dbA. (see Notes 2 & 3).", - rsNumber: 5, - sectionDescription: "Noise", - sectionNumber: "01" - } - ], - make: "some make", - model: "some model", - reapplicationDate: "", - serialNumber: "C456789", - station: "Abshire-Kub", - testCategoryBasicNormal: "Basic", - testCategoryClass: "m1", - testerName: "CVS Dev1", - vehicleTrailerNrNo: "C456789", - vin: "T12876765" + IVA_DATA: { + additionalDefects: [ + { + defectName: "N/A", + defectNotes: "", + } + ], + bodyType: "some bodyType", + date: "28/11/2023", + requiredStandards: [ + { + additionalInfo: false, + additionalNotes: null, + inspectionTypes: [ + "basic" + ], + prs: false, + refCalculation: "1.5", + requiredStandard: "The stationary noise must have a measured sound level not exceeding 99dbA. (see Notes 2 & 3).", + rsNumber: 5, + sectionDescription: "Noise", + sectionNumber: "01" + } + ], + make: "some make", + model: "some model", + reapplicationDate: "", + serialNumber: "C456789", + station: "Abshire-Kub", + testCategoryBasicNormal: "Basic", + testCategoryClass: "m1", + testerName: "CVS Dev1", + vehicleTrailerNrNo: "C456789", + vin: "T12876765" + } }; - return await certificateGenerationService - .generateCertificateData(testResult4, "IVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.IVA_DATA, + testResult: testResult4 + } as CertificatePayloadStateBag; + + const ivaCommand = Container.get(IvaCertificateCommand); + ivaCommand.initialise(state); + const payload = await ivaCommand.generate(); + expect(payload).toEqual(expectedResult); }); } ); @@ -4965,47 +5049,53 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with requiredStandards in IVA_DATA", async () => { const expectedResult: any = { - additionalDefects: [ - { - defectName: "N/A", - defectNotes: "", - } - ], - bodyType: "some bodyType", - date: "28/11/2023", - requiredStandards: [ - { - additionalInfo: true, - additionalNotes: "The exhaust was held on with blue tac", - inspectionTypes: [ - "normal", - "basic" - ], - prs: false, - refCalculation: "1.1", - requiredStandard: "The exhaust must be securely mounted", - rsNumber: 1, - sectionDescription: "Noise", - sectionNumber: "01" - } - ], - make: "some make", - model: "some model", - reapplicationDate: "", - serialNumber: "C456789", - station: "Abshire-Kub", - testCategoryBasicNormal: "Normal", - testCategoryClass: "m1", - testerName: "CVS Dev1", - vehicleTrailerNrNo: "C456789", - vin: "T12876765" + IVA_DATA: { + additionalDefects: [ + { + defectName: "N/A", + defectNotes: "", + } + ], + bodyType: "some bodyType", + date: "28/11/2023", + requiredStandards: [ + { + additionalInfo: true, + additionalNotes: "The exhaust was held on with blue tac", + inspectionTypes: [ + "normal", + "basic" + ], + prs: false, + refCalculation: "1.1", + requiredStandard: "The exhaust must be securely mounted", + rsNumber: 1, + sectionDescription: "Noise", + sectionNumber: "01" + } + ], + make: "some make", + model: "some model", + reapplicationDate: "", + serialNumber: "C456789", + station: "Abshire-Kub", + testCategoryBasicNormal: "Normal", + testCategoryClass: "m1", + testerName: "CVS Dev1", + vehicleTrailerNrNo: "C456789", + vin: "T12876765" + } }; - return await certificateGenerationService - .generateCertificateData(testResult5, "IVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.IVA_DATA, + testResult: testResult5 + } as CertificatePayloadStateBag; + + const ivaCommand = Container.get(IvaCertificateCommand); + ivaCommand.initialise(state); + const payload = await ivaCommand.generate(); + expect(payload).toEqual(expectedResult); }); } ); @@ -5016,42 +5106,48 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with requiredStandards in MSVA_DATA", async () => { const expectedResult: any = { - vin: "P0123010956789", - serialNumber: "ZX345CV", - vehicleZNumber: "ZX345CV", - make: null, - model: null, - type: "motorcycle", - testerName: "CVS Dev1", - date: "04/03/2024", - station: "Abshire-Kub", - reapplicationDate: "", - additionalDefects: [ - { - defectName: "N/A", - defectNotes: "", - } - ], - requiredStandards: [ - { - additionalInfo: true, - additionalNotes: "The bulbs were slightly worn", - inspectionTypes: [], - prs: false, - refCalculation: "6.2a", - requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", - rsNumber: 2, - sectionDescription: "Lighting", - sectionNumber: "06" - } - ], + MSVA_DATA: { + vin: "P0123010956789", + serialNumber: "ZX345CV", + vehicleZNumber: "ZX345CV", + make: null, + model: null, + type: "motorcycle", + testerName: "CVS Dev1", + date: "04/03/2024", + station: "Abshire-Kub", + reapplicationDate: "", + additionalDefects: [ + { + defectName: "N/A", + defectNotes: "", + } + ], + requiredStandards: [ + { + additionalInfo: true, + additionalNotes: "The bulbs were slightly worn", + inspectionTypes: [], + prs: false, + refCalculation: "6.2a", + requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", + rsNumber: 2, + sectionDescription: "Lighting", + sectionNumber: "06" + } + ], + } }; - return await certificateGenerationService - .generateCertificateData(testResult6, "MSVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.MSVA_DATA, + testResult: testResult6 + } as CertificatePayloadStateBag; + + const msvaCommand = Container.get(MsvaCertificateCommand); + msvaCommand.initialise(state); + const payload = await msvaCommand.generate(); + expect(payload).toEqual(expectedResult); }); } ); @@ -5062,104 +5158,116 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with requiredStandards in MSVA_DATA", async () => { const expectedResult: any = { - vin: "P0123010956789", - serialNumber: "ZX345CV", - vehicleZNumber: "ZX345CV", - make: null, - model: null, - type: "motorcycle", - testerName: "CVS Dev1", - date: "04/03/2024", - reapplicationDate: "", - station: "Abshire-Kub", - additionalDefects: [ - { - defectName: "N/A", - defectNotes: "", - } - ], - requiredStandards: [ - { - additionalInfo: true, - additionalNotes: "The bulbs were slightly worn", - inspectionTypes: [], - prs: false, - refCalculation: "6.2a", - requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", - rsNumber: 2, - sectionDescription: "Lighting", - sectionNumber: "06" - }, - { - additionalInfo: true, - additionalNotes: "Switch was missing", - inspectionTypes: [], - prs: false, - refCalculation: "6.3a", - requiredStandard: "Any light switch; missing", - rsNumber: 3, - sectionDescription: "Lighting", - sectionNumber: "06" - }, - ], + MSVA_DATA: { + vin: "P0123010956789", + serialNumber: "ZX345CV", + vehicleZNumber: "ZX345CV", + make: null, + model: null, + type: "motorcycle", + testerName: "CVS Dev1", + date: "04/03/2024", + reapplicationDate: "", + station: "Abshire-Kub", + additionalDefects: [ + { + defectName: "N/A", + defectNotes: "", + } + ], + requiredStandards: [ + { + additionalInfo: true, + additionalNotes: "The bulbs were slightly worn", + inspectionTypes: [], + prs: false, + refCalculation: "6.2a", + requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", + rsNumber: 2, + sectionDescription: "Lighting", + sectionNumber: "06" + }, + { + additionalInfo: true, + additionalNotes: "Switch was missing", + inspectionTypes: [], + prs: false, + refCalculation: "6.3a", + requiredStandard: "Any light switch; missing", + rsNumber: 3, + sectionDescription: "Lighting", + sectionNumber: "06" + }, + ], + } }; - return await certificateGenerationService - .generateCertificateData(testResult7, "MSVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.MSVA_DATA, + testResult: testResult7 + } as CertificatePayloadStateBag; + + const msvaCommand = Container.get(MsvaCertificateCommand); + msvaCommand.initialise(state); + const payload = await msvaCommand.generate(); + expect(payload).toEqual(expectedResult); }); it("should return Certificate Data with sorted requiredStandards in MSVA_DATA", async () => { const expectedResult: any = { - vin: "P0123010956789", - serialNumber: "ZX345CV", - vehicleZNumber: "ZX345CV", - make: null, - model: null, - type: "motorcycle", - testerName: "CVS Dev1", - date: "04/03/2024", - reapplicationDate: "", - station: "Abshire-Kub", - additionalDefects: [ - { - defectName: "N/A", - defectNotes: "", - } - ], - requiredStandards: [ - { - additionalInfo: true, - additionalNotes: "The bulbs were slightly worn", - inspectionTypes: [], - prs: false, - refCalculation: "6.2a", - requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", - rsNumber: 2, - sectionDescription: "Lighting", - sectionNumber: "06" - }, - { - additionalInfo: true, - additionalNotes: "Switch was missing", - inspectionTypes: [], - prs: false, - refCalculation: "6.3a", - requiredStandard: "Any light switch; missing", - rsNumber: 3, - sectionDescription: "Lighting", - sectionNumber: "06" - }, - ], + MSVA_DATA: { + vin: "P0123010956789", + serialNumber: "ZX345CV", + vehicleZNumber: "ZX345CV", + make: null, + model: null, + type: "motorcycle", + testerName: "CVS Dev1", + date: "04/03/2024", + reapplicationDate: "", + station: "Abshire-Kub", + additionalDefects: [ + { + defectName: "N/A", + defectNotes: "", + } + ], + requiredStandards: [ + { + additionalInfo: true, + additionalNotes: "The bulbs were slightly worn", + inspectionTypes: [], + prs: false, + refCalculation: "6.2a", + requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", + rsNumber: 2, + sectionDescription: "Lighting", + sectionNumber: "06" + }, + { + additionalInfo: true, + additionalNotes: "Switch was missing", + inspectionTypes: [], + prs: false, + refCalculation: "6.3a", + requiredStandard: "Any light switch; missing", + rsNumber: 3, + sectionDescription: "Lighting", + sectionNumber: "06" + }, + ], + } }; - return await certificateGenerationService - .generateCertificateData(testResult7, "MSVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.MSVA_DATA, + testResult: testResult7 + } as CertificatePayloadStateBag; + + const msvaCommand = Container.get(MsvaCertificateCommand); + msvaCommand.initialise(state); + const payload = await msvaCommand.generate(); + expect(payload).toEqual(expectedResult); }); } ); @@ -5170,42 +5278,48 @@ describe("cert-gen", () => { () => { it("should return Certificate Data with requiredStandards and additionalDefects in IVA_DATA", async () => { const expectedResult: any = { - vin: "P0123010956789", - serialNumber: "ZX345CV", - vehicleZNumber: "ZX345CV", - make: null, - model: null, - type: "motorcycle", - testerName: "CVS Dev1", - date: "04/03/2024", - reapplicationDate: "", - station: "Abshire-Kub", - additionalDefects: [ - { - defectName: "Rust", - defectNotes: "slight rust around the wheel arch", - } - ], - requiredStandards: [ - { - additionalInfo: true, - additionalNotes: "The bulbs were slightly worn", - inspectionTypes: [], - prs: false, - refCalculation: "6.2a", - requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", - rsNumber: 2, - sectionDescription: "Lighting", - sectionNumber: "6" - } - ], + MSVA_DATA: { + vin: "P0123010956789", + serialNumber: "ZX345CV", + vehicleZNumber: "ZX345CV", + make: null, + model: null, + type: "motorcycle", + testerName: "CVS Dev1", + date: "04/03/2024", + reapplicationDate: "", + station: "Abshire-Kub", + additionalDefects: [ + { + defectName: "Rust", + defectNotes: "slight rust around the wheel arch", + } + ], + requiredStandards: [ + { + additionalInfo: true, + additionalNotes: "The bulbs were slightly worn", + inspectionTypes: [], + prs: false, + refCalculation: "6.2a", + requiredStandard: "An obligatory (or optional) lamp or reflector; incorrect number fitted", + rsNumber: 2, + sectionDescription: "Lighting", + sectionNumber: "6" + } + ], + } }; - return await certificateGenerationService - .generateCertificateData(testResult8, "MSVA_DATA") - .then((payload: any) => { - expect(payload).toEqual(expectedResult); - }); + const state = { + type: CERTIFICATE_DATA.MSVA_DATA, + testResult: testResult8 + } as CertificatePayloadStateBag; + + const msvaCommand = Container.get(MsvaCertificateCommand); + msvaCommand.initialise(state); + const payload = await msvaCommand.generate(); + expect(payload).toEqual(expectedResult); }); } ); @@ -7201,7 +7315,7 @@ describe("cert-gen", () => { "inspectionTypes": [ "normal", "basic" - ], + ], "prs": false, "refCalculation": "1.1", "requiredStandard": "The exhaust must be securely mounted", diff --git a/tslint.json b/tslint.json deleted file mode 100644 index c76a0f2c..00000000 --- a/tslint.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": ["tslint:recommended", "tslint-no-unused-expression-chai"], - "rules": { - "max-line-length": { - "options": [240] - }, - "new-parens": false, - "no-arg": true, - "no-bitwise": false, - "no-conditional-assignment": false, - "no-consecutive-blank-lines": false, - "no-console": [false], - "object-literal-sort-keys": false, - "trailing-comma": [false], - "member-ordering": false, - "ordered-imports": false, - "jsdoc-format": true, - "completed-docs": [ - true, - { - "methods": true, - "classes": true - } - ] - } -}