From 156be62407534ebe18f0fdb55c8c5a5a2affb628 Mon Sep 17 00:00:00 2001 From: zainZzKk <51957827+Zainzzkk@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:04:37 +0000 Subject: [PATCH] feat(FN-3819): save bankTeamName, bankTeamEmails in fee record correction table (#4187) --- ...correction-requested.event-handler.test.ts | 9 +++ .../correction-requested.event-handler.ts | 6 +- .../fee-record.state-machine.test.ts | 2 + .../helpers.test.ts | 71 +++---------------- .../helpers.ts | 40 +++++------ .../index.test.ts | 16 ++++- .../index.ts | 8 ++- ...-bank-payment-officer-team-details.test.ts | 54 ++++++++++++++ .../get-bank-payment-officer-team-details.ts | 18 +++++ libs/common/src/helpers/date.test.ts | 4 +- ...dCorrectionBankTeamNameAndEmailsColumns.ts | 25 +++++++ .../fee-record-correction.entity.test.ts | 15 +++- .../fee-record-correction.entity.ts | 18 +++++ .../fee-record-correction.types.ts | 2 + ...e-record-correction.entity.mock-builder.ts | 2 + trade-finance-manager-ui/server/api.js | 2 +- 16 files changed, 202 insertions(+), 90 deletions(-) create mode 100644 dtfs-central-api/src/v1/controllers/utilisation-report-service/helpers/get-bank-payment-officer-team-details.test.ts create mode 100644 dtfs-central-api/src/v1/controllers/utilisation-report-service/helpers/get-bank-payment-officer-team-details.ts create mode 100644 libs/common/src/sql-db-connection/migrations/1738320805938-addFeeRecordCorrectionBankTeamNameAndEmailsColumns.ts diff --git a/dtfs-central-api/src/services/state-machines/fee-record/event-handlers/correction-requested/correction-requested.event-handler.test.ts b/dtfs-central-api/src/services/state-machines/fee-record/event-handlers/correction-requested/correction-requested.event-handler.test.ts index 7582f9f80e..977bcd4f85 100644 --- a/dtfs-central-api/src/services/state-machines/fee-record/event-handlers/correction-requested/correction-requested.event-handler.test.ts +++ b/dtfs-central-api/src/services/state-machines/fee-record/event-handlers/correction-requested/correction-requested.event-handler.test.ts @@ -23,6 +23,9 @@ describe('handleFeeRecordCorrectionRequestedEvent', () => { lastName: 'User', }; + const bankTeamName = 'Payment Officer Team'; + const bankTeamEmails = ['test@ukexportfinance.gov.uk']; + const aCorrectionRequestedEventPayload = (): FeeRecordCorrectionRequestedEvent['payload'] => ({ transactionEntityManager: mockEntityManager, reasons: [RECORD_CORRECTION_REASON.REPORTED_CURRENCY_INCORRECT], @@ -31,6 +34,8 @@ describe('handleFeeRecordCorrectionRequestedEvent', () => { ...requestedByUser, }, requestSource: aDbRequestSource(), + bankTeamName, + bankTeamEmails, }); afterEach(() => { @@ -104,6 +109,8 @@ describe('handleFeeRecordCorrectionRequestedEvent', () => { additionalInfo, requestedByUser, requestSource, + bankTeamName, + bankTeamEmails, }); // Assert @@ -113,6 +120,8 @@ describe('handleFeeRecordCorrectionRequestedEvent', () => { reasons, additionalInfo, requestSource, + bankTeamName, + bankTeamEmails, }); expect(mockSave).toHaveBeenCalledWith(FeeRecordCorrectionEntity, newCorrection); }); diff --git a/dtfs-central-api/src/services/state-machines/fee-record/event-handlers/correction-requested/correction-requested.event-handler.ts b/dtfs-central-api/src/services/state-machines/fee-record/event-handlers/correction-requested/correction-requested.event-handler.ts index 61f10cf6a4..b1f78cbeb1 100644 --- a/dtfs-central-api/src/services/state-machines/fee-record/event-handlers/correction-requested/correction-requested.event-handler.ts +++ b/dtfs-central-api/src/services/state-machines/fee-record/event-handlers/correction-requested/correction-requested.event-handler.ts @@ -8,6 +8,8 @@ type CorrectionRequestedEventPayload = { requestedByUser: RequestedByUser; reasons: RecordCorrectionReason[]; additionalInfo: string; + bankTeamName: string; + bankTeamEmails: string[]; }; export type FeeRecordCorrectionRequestedEvent = BaseFeeRecordEvent<'CORRECTION_REQUESTED', CorrectionRequestedEventPayload>; @@ -25,7 +27,7 @@ export type FeeRecordCorrectionRequestedEvent = BaseFeeRecordEvent<'CORRECTION_R */ export const handleFeeRecordCorrectionRequestedEvent = async ( feeRecord: FeeRecordEntity, - { transactionEntityManager, requestSource, requestedByUser, reasons, additionalInfo }: CorrectionRequestedEventPayload, + { transactionEntityManager, requestSource, requestedByUser, reasons, additionalInfo, bankTeamName, bankTeamEmails }: CorrectionRequestedEventPayload, ): Promise => { const correction = FeeRecordCorrectionEntity.createRequestedCorrection({ feeRecord, @@ -33,6 +35,8 @@ export const handleFeeRecordCorrectionRequestedEvent = async ( reasons, additionalInfo, requestSource, + bankTeamName, + bankTeamEmails, }); await transactionEntityManager.save(FeeRecordCorrectionEntity, correction); diff --git a/dtfs-central-api/src/services/state-machines/fee-record/fee-record.state-machine.test.ts b/dtfs-central-api/src/services/state-machines/fee-record/fee-record.state-machine.test.ts index c06c4d46b2..056171d4ac 100644 --- a/dtfs-central-api/src/services/state-machines/fee-record/fee-record.state-machine.test.ts +++ b/dtfs-central-api/src/services/state-machines/fee-record/fee-record.state-machine.test.ts @@ -93,6 +93,8 @@ describe('FeeRecordStateMachine', () => { reasons: [RECORD_CORRECTION_REASON.FACILITY_ID_INCORRECT], additionalInfo: 'some additional information', requestSource: { platform: REQUEST_PLATFORM_TYPE.TFM, userId: 'abc123' }, + bankTeamName: 'Payment Officer Team', + bankTeamEmails: ['test@ukexportfinance.gov.uk'], }, }); diff --git a/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/helpers.test.ts b/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/helpers.test.ts index 03555d16f5..b2e468696a 100644 --- a/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/helpers.test.ts +++ b/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/helpers.test.ts @@ -1,8 +1,6 @@ import { getFormattedReportPeriodWithLongMonth, mapReasonToDisplayValue, RECORD_CORRECTION_REASON, RecordCorrectionReason } from '@ukef/dtfs2-common'; import { formatReasonsAsBulletedListForEmail, generateFeeRecordCorrectionRequestEmailParameters, sendFeeRecordCorrectionRequestEmails } from './helpers'; -import { aBank, aReportPeriod } from '../../../../../../test-helpers'; -import { getBankById } from '../../../../../repositories/banks-repo'; -import { NotFoundError } from '../../../../../errors'; +import { aReportPeriod } from '../../../../../../test-helpers'; import externalApi from '../../../../../external-api/api'; import EMAIL_TEMPLATE_IDS from '../../../../../constants/email-template-ids'; @@ -19,14 +17,7 @@ describe('post-fee-record-correction.controller helpers', () => { const firstPaymentOfficerEmail = 'officer-1@example.com'; const secondPaymentOfficerEmail = 'officer-2@example.com'; const teamName = 'Payment Officer Team'; - - const bank = { - ...aBank(), - paymentOfficerTeam: { - teamName, - emails: [firstPaymentOfficerEmail, secondPaymentOfficerEmail], - }, - }; + const teamEmails = [firstPaymentOfficerEmail, secondPaymentOfficerEmail]; describe('formatReasonsAsBulletedListForEmail', () => { it('should format reasons as a bulleted list when there is a single reasons', () => { @@ -61,15 +52,11 @@ describe('post-fee-record-correction.controller helpers', () => { const reasons: RecordCorrectionReason[] = [RECORD_CORRECTION_REASON.UTILISATION_INCORRECT, RECORD_CORRECTION_REASON.REPORTED_CURRENCY_INCORRECT]; const reportPeriod = aReportPeriod(); const exporter = 'Test Exporter'; - const bankId = '123'; const requestedByUserEmail = 'user@example.com'; - it('should generate email parameters', async () => { - // Arrange - jest.mocked(getBankById).mockResolvedValue(bank); - + it('should generate email parameters', () => { // Act - const result = await generateFeeRecordCorrectionRequestEmailParameters(reasons, reportPeriod, exporter, bankId, requestedByUserEmail); + const result = generateFeeRecordCorrectionRequestEmailParameters(reasons, reportPeriod, exporter, requestedByUserEmail, teamName, teamEmails); // Assert expect(result).toEqual({ @@ -82,17 +69,6 @@ describe('post-fee-record-correction.controller helpers', () => { }, }); }); - - it('should throw a NotFoundError if the bank is not found', async () => { - // Arrange - jest.mocked(getBankById).mockResolvedValue(null); - - // Act & Assert - await expect(generateFeeRecordCorrectionRequestEmailParameters(reasons, reportPeriod, exporter, bankId, requestedByUserEmail)).rejects.toThrow( - new NotFoundError(`Bank not found: ${bankId}`), - ); - expect(console.error).toHaveBeenCalledWith('Bank not found: %s', bankId); - }); }); describe('sendFeeRecordCorrectionRequestEmails', () => { @@ -101,16 +77,13 @@ describe('post-fee-record-correction.controller helpers', () => { const reasons: RecordCorrectionReason[] = [RECORD_CORRECTION_REASON.UTILISATION_INCORRECT, RECORD_CORRECTION_REASON.REPORTED_CURRENCY_INCORRECT]; const reportPeriod = aReportPeriod(); const exporter = 'Potato exporter'; - const bankId = '567'; const requestedByUserEmail = 'tfm-user@email.com'; - jest.mocked(getBankById).mockResolvedValue(bank); - // Act - await sendFeeRecordCorrectionRequestEmails(reasons, reportPeriod, exporter, bankId, requestedByUserEmail); + await sendFeeRecordCorrectionRequestEmails(reasons, reportPeriod, exporter, requestedByUserEmail, teamName, teamEmails); // Assert - const { variables } = await generateFeeRecordCorrectionRequestEmailParameters(reasons, reportPeriod, exporter, bankId, requestedByUserEmail); + const { variables } = generateFeeRecordCorrectionRequestEmailParameters(reasons, reportPeriod, exporter, requestedByUserEmail, teamName, teamEmails); expect(externalApi.sendEmail).toHaveBeenCalledTimes(3); expect(externalApi.sendEmail).toHaveBeenCalledWith(EMAIL_TEMPLATE_IDS.FEE_RECORD_CORRECTION_REQUEST, firstPaymentOfficerEmail, variables); expect(externalApi.sendEmail).toHaveBeenCalledWith(EMAIL_TEMPLATE_IDS.FEE_RECORD_CORRECTION_REQUEST, secondPaymentOfficerEmail, variables); @@ -122,49 +95,23 @@ describe('post-fee-record-correction.controller helpers', () => { const reasons: RecordCorrectionReason[] = [RECORD_CORRECTION_REASON.UTILISATION_INCORRECT, RECORD_CORRECTION_REASON.REPORTED_CURRENCY_INCORRECT]; const reportPeriod = aReportPeriod(); const exporter = 'Potato exporter'; - const bankId = '567'; const requestedByUserEmail = 'tfm-user@email.com'; - jest.mocked(getBankById).mockResolvedValue(bank); - // Act - const response = await sendFeeRecordCorrectionRequestEmails(reasons, reportPeriod, exporter, bankId, requestedByUserEmail); + const response = await sendFeeRecordCorrectionRequestEmails(reasons, reportPeriod, exporter, requestedByUserEmail, teamName, teamEmails); // Assert - const { emails } = await generateFeeRecordCorrectionRequestEmailParameters(reasons, reportPeriod, exporter, bankId, requestedByUserEmail); + const { emails } = generateFeeRecordCorrectionRequestEmailParameters(reasons, reportPeriod, exporter, requestedByUserEmail, teamName, teamEmails); expect(response).toEqual({ emails }); }); - it('should throw NotFoundError error if the bank cannot be found', async () => { - // Arrange - const bankId = '123'; - jest.mocked(getBankById).mockResolvedValue(null); - - const expectedError = new NotFoundError(`Bank not found: ${bankId}`); - - // Act + Assert - await expect(sendFeeRecordCorrectionRequestEmails([], aReportPeriod(), 'test exporter', bankId, 'test@test.com')).rejects.toThrow(expectedError); - expect(console.error).toHaveBeenCalledTimes(1); - expect(console.error).toHaveBeenCalledWith('Bank not found: %s', bankId); - }); - it('should log and rethrow error if sending an email fails', async () => { - // Arrange - const bankId = '123'; - jest.mocked(getBankById).mockResolvedValue({ - ...aBank(), - paymentOfficerTeam: { - teamName, - emails: ['test1@test.com'], - }, - }); - const error = new Error('Failed to send second email'); jest.mocked(externalApi.sendEmail).mockResolvedValueOnce().mockRejectedValueOnce(error); // Act + Assert - await expect(sendFeeRecordCorrectionRequestEmails([], aReportPeriod(), 'test exporter', bankId, 'test2@test.com')).rejects.toThrow(error); + await expect(sendFeeRecordCorrectionRequestEmails([], aReportPeriod(), 'test exporter', 'test2@test.com', teamName, teamEmails)).rejects.toThrow(error); expect(console.error).toHaveBeenCalledWith('Error sending fee record correction request email: %o', error); }); }); diff --git a/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/helpers.ts b/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/helpers.ts index f21b807c10..adefc9abe3 100644 --- a/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/helpers.ts +++ b/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/helpers.ts @@ -2,8 +2,6 @@ import { getFormattedReportPeriodWithLongMonth, mapReasonToDisplayValue, RecordC import externalApi from '../../../../../external-api/api'; import EMAIL_TEMPLATE_IDS from '../../../../../constants/email-template-ids'; import { FeeRecordCorrectionRequestEmails, FeeRecordCorrectionRequestEmailAddresses } from '../../../../../types/utilisation-reports'; -import { getBankById } from '../../../../../repositories/banks-repo'; -import { NotFoundError } from '../../../../../errors'; /** * Formats the reasons for record correction into a bulleted list. @@ -27,28 +25,20 @@ export const formatReasonsAsBulletedListForEmail = (reasons: RecordCorrectionRea * @param report * @returns emails and variables for the email */ -export const generateFeeRecordCorrectionRequestEmailParameters = async ( +export const generateFeeRecordCorrectionRequestEmailParameters = ( reasons: RecordCorrectionReason[], reportPeriod: ReportPeriod, exporter: string, - bankId: string, requestedByUserEmail: string, -): Promise => { - const bank = await getBankById(bankId); - - if (!bank) { - console.error('Bank not found: %s', bankId); - throw new NotFoundError(`Bank not found: ${bankId}`); - } - - const { teamName, emails } = bank.paymentOfficerTeam; - + paymentOfficerTeamName: string, + paymentOfficerTeamEmails: string[], +): FeeRecordCorrectionRequestEmails => { const reportPeriodString = getFormattedReportPeriodWithLongMonth(reportPeriod); return { - emails: [...emails, requestedByUserEmail], + emails: [...paymentOfficerTeamEmails, requestedByUserEmail], variables: { - recipient: teamName, + recipient: paymentOfficerTeamName, reportPeriod: reportPeriodString, exporterName: exporter, reasonsList: formatReasonsAsBulletedListForEmail(reasons), @@ -63,9 +53,9 @@ export const generateFeeRecordCorrectionRequestEmailParameters = async ( * @param reasons - The reasons for the record correction request * @param reportPeriod - The report period of the fee's report * @param exporter - The exporter of the fee record - * @param bankId - The id of the bank - * @param requestedByUserEmail - The email of the TFM user who is - * requesting the correction + * @param requestedByUserEmail - The email of the TFM user who is requesting the correction + * @param paymentOfficerTeamName - The name of the bank payment officer team + * @param paymentOfficerTeamEmails - The email addresses of the bank payment officer team * @returns A promise that resolves to an object containing the email addresses * that were notified. */ @@ -73,10 +63,18 @@ export const sendFeeRecordCorrectionRequestEmails = async ( reasons: RecordCorrectionReason[], reportPeriod: ReportPeriod, exporter: string, - bankId: string, requestedByUserEmail: string, + paymentOfficerTeamName: string, + paymentOfficerTeamEmails: string[], ): Promise => { - const { emails, variables } = await generateFeeRecordCorrectionRequestEmailParameters(reasons, reportPeriod, exporter, bankId, requestedByUserEmail); + const { emails, variables } = generateFeeRecordCorrectionRequestEmailParameters( + reasons, + reportPeriod, + exporter, + requestedByUserEmail, + paymentOfficerTeamName, + paymentOfficerTeamEmails, + ); try { await Promise.all(emails.map((email) => externalApi.sendEmail(EMAIL_TEMPLATE_IDS.FEE_RECORD_CORRECTION_REQUEST, email, variables))); diff --git a/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/index.test.ts b/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/index.test.ts index 923b084149..c01bc9be39 100644 --- a/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/index.test.ts +++ b/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/index.test.ts @@ -19,12 +19,14 @@ import { FEE_RECORD_EVENT_TYPE } from '../../../../../services/state-machines/fe import { FeeRecordRepo } from '../../../../../repositories/fee-record-repo'; import { sendFeeRecordCorrectionRequestEmails } from './helpers'; import { FeeRecordCorrectionRequestEmailAddresses } from '../../../../../types/utilisation-reports'; +import { getBankPaymentOfficerTeamDetails } from '../../helpers/get-bank-payment-officer-team-details'; jest.mock('../../../../../helpers'); jest.mock('../../../../../services/state-machines/fee-record/fee-record.state-machine'); jest.mock('../../../../../repositories/fee-record-correction-request-transient-form-data-repo'); jest.mock('../../../../../repositories/fee-record-repo'); jest.mock('./helpers'); +jest.mock('../../helpers/get-bank-payment-officer-team-details'); console.error = jest.fn(); @@ -40,6 +42,14 @@ describe('post-fee-record-correction.controller', () => { const mockHandleEvent = jest.fn(); const mockForFeeRecordStateMachineConstructor = jest.fn(); + const mockTeamName = 'Mock team name'; + const mockEmails = ['test1@ukexportfinance.gov.uk', 'test2@ukexportfinance.gov.uk']; + + const getBankPaymentOfficerTeamDetailsResponse = { + teamName: mockTeamName, + emails: mockEmails, + }; + beforeEach(() => { jest.mocked(executeWithSqlTransaction).mockImplementation(async (functionToExecute) => { return await functionToExecute(mockEntityManager); @@ -54,6 +64,7 @@ describe('post-fee-record-correction.controller', () => { deleteByUserIdAndFeeRecordId: mockDeleteTransientFormData, }); jest.spyOn(FeeRecordRepo, 'withTransaction').mockReturnValue({ findOneByIdAndReportIdWithReport: mockFindFeeRecordWithReport }); + jest.mocked(getBankPaymentOfficerTeamDetails).mockResolvedValue(getBankPaymentOfficerTeamDetailsResponse); }); afterEach(() => { @@ -149,8 +160,9 @@ describe('post-fee-record-correction.controller', () => { reasons, mockReport.reportPeriod, mockFeeRecord.exporter, - mockReport.bankId, user.email, + mockTeamName, + mockEmails, ); }); @@ -171,6 +183,8 @@ describe('post-fee-record-correction.controller', () => { platform: REQUEST_PLATFORM_TYPE.TFM, userId, }, + bankTeamName: mockTeamName, + bankTeamEmails: mockEmails, }; // Act diff --git a/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/index.ts b/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/index.ts index 0bc49bbed9..f698c42ad8 100644 --- a/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/index.ts +++ b/dtfs-central-api/src/v1/controllers/utilisation-report-service/fee-record-correction/post-fee-record-correction.controller/index.ts @@ -11,6 +11,7 @@ import { PostFeeRecordCorrectionPayload } from '../../../../routes/middleware/pa import { FeeRecordRepo } from '../../../../../repositories/fee-record-repo'; import { sendFeeRecordCorrectionRequestEmails } from './helpers'; import { FeeRecordCorrectionRequestEmailAddresses } from '../../../../../types/utilisation-reports'; +import { getBankPaymentOfficerTeamDetails } from '../../helpers/get-bank-payment-officer-team-details'; export type PostFeeRecordCorrectionRequest = CustomExpressRequest<{ params: { @@ -64,6 +65,8 @@ export const postFeeRecordCorrection = async (req: PostFeeRecordCorrectionReques throw new NotFoundError(`Failed to find a fee record with id ${feeRecordId} and report id ${reportId}`); } + const { teamName, emails } = await getBankPaymentOfficerTeamDetails(feeRecord.report.bankId); + const stateMachine = FeeRecordStateMachine.forFeeRecord(feeRecord); await stateMachine.handleEvent({ @@ -81,6 +84,8 @@ export const postFeeRecordCorrection = async (req: PostFeeRecordCorrectionReques platform: REQUEST_PLATFORM_TYPE.TFM, userId, }, + bankTeamName: teamName, + bankTeamEmails: emails, }, }); @@ -90,8 +95,9 @@ export const postFeeRecordCorrection = async (req: PostFeeRecordCorrectionReques reasons, feeRecord.report.reportPeriod, feeRecord.exporter, - feeRecord.report.bankId, user.email, + teamName, + emails, ); return notifiedEmails; diff --git a/dtfs-central-api/src/v1/controllers/utilisation-report-service/helpers/get-bank-payment-officer-team-details.test.ts b/dtfs-central-api/src/v1/controllers/utilisation-report-service/helpers/get-bank-payment-officer-team-details.test.ts new file mode 100644 index 0000000000..68ce8a81a4 --- /dev/null +++ b/dtfs-central-api/src/v1/controllers/utilisation-report-service/helpers/get-bank-payment-officer-team-details.test.ts @@ -0,0 +1,54 @@ +import { getBankPaymentOfficerTeamDetails } from './get-bank-payment-officer-team-details'; +import { aBank } from '../../../../../test-helpers'; +import { getBankById } from '../../../../repositories/banks-repo'; +import { NotFoundError } from '../../../../errors'; + +jest.mock('../../../../repositories/banks-repo'); + +console.error = jest.fn(); + +describe('get-bank-payment-officer-team-details', () => { + const firstPaymentOfficerEmail = 'officer-1@example.com'; + const secondPaymentOfficerEmail = 'officer-2@example.com'; + const teamName = 'Payment Officer Team'; + + const bankId = '123'; + + const bank = { + ...aBank(), + paymentOfficerTeam: { + teamName, + emails: [firstPaymentOfficerEmail, secondPaymentOfficerEmail], + }, + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('when a bank is found', () => { + it('should return the bank payment officer team details', async () => { + jest.mocked(getBankById).mockResolvedValue(bank); + + const result = await getBankPaymentOfficerTeamDetails(bankId); + + const expected = { + emails: [firstPaymentOfficerEmail, secondPaymentOfficerEmail], + teamName, + }; + + expect(result).toEqual(expected); + }); + }); + + describe('when a bank is not found', () => { + it('should throw a NotFoundError', async () => { + // Arrange + jest.mocked(getBankById).mockResolvedValue(null); + + // Act & Assert + await expect(getBankPaymentOfficerTeamDetails(bankId)).rejects.toThrow(new NotFoundError(`Bank not found: ${bankId}`)); + expect(console.error).toHaveBeenCalledWith('Bank not found: %s', bankId); + }); + }); +}); diff --git a/dtfs-central-api/src/v1/controllers/utilisation-report-service/helpers/get-bank-payment-officer-team-details.ts b/dtfs-central-api/src/v1/controllers/utilisation-report-service/helpers/get-bank-payment-officer-team-details.ts new file mode 100644 index 0000000000..266a795cc9 --- /dev/null +++ b/dtfs-central-api/src/v1/controllers/utilisation-report-service/helpers/get-bank-payment-officer-team-details.ts @@ -0,0 +1,18 @@ +import { getBankById } from '../../../../repositories/banks-repo'; +import { NotFoundError } from '../../../../errors'; + +export const getBankPaymentOfficerTeamDetails = async (bankId: string) => { + const bank = await getBankById(bankId); + + if (!bank) { + console.error('Bank not found: %s', bankId); + throw new NotFoundError(`Bank not found: ${bankId}`); + } + + const { teamName, emails } = bank.paymentOfficerTeam; + + return { + emails, + teamName, + }; +}; diff --git a/libs/common/src/helpers/date.test.ts b/libs/common/src/helpers/date.test.ts index a76fbeb334..6b67d606e7 100644 --- a/libs/common/src/helpers/date.test.ts +++ b/libs/common/src/helpers/date.test.ts @@ -69,7 +69,7 @@ describe('date helpers', () => { const result = getISO8601(); const year = now().getFullYear(); const month = (now().getMonth() + 1).toString().padStart(2, '0'); - const date = now().getDate(); + const date = now().getDate().toString().padStart(2, '0'); // Assert expect(result).toContain(`${year}-${month}-${date}`); @@ -82,7 +82,7 @@ describe('date helpers', () => { const result = getISO8601(); const year = now().getFullYear(); const month = (now().getMonth() + 1).toString().padStart(2, '0'); - const date = now().getDate(); + const date = now().getDate().toString().padStart(2, '0'); // Assert expect(result).toContain(`${year}-${month}-${date}`); diff --git a/libs/common/src/sql-db-connection/migrations/1738320805938-addFeeRecordCorrectionBankTeamNameAndEmailsColumns.ts b/libs/common/src/sql-db-connection/migrations/1738320805938-addFeeRecordCorrectionBankTeamNameAndEmailsColumns.ts new file mode 100644 index 0000000000..1ffdfdd348 --- /dev/null +++ b/libs/common/src/sql-db-connection/migrations/1738320805938-addFeeRecordCorrectionBankTeamNameAndEmailsColumns.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddFeeRecordCorrectionBankTeamNameAndEmailsColumns1738320805938 implements MigrationInterface { + name = 'AddFeeRecordCorrectionBankTeamNameAndEmailsColumns1738320805938'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "FeeRecordCorrection" + ADD "bankTeamName" nvarchar(500) NOT NULL + `); + await queryRunner.query(` + ALTER TABLE "FeeRecordCorrection" + ADD "bankTeamEmails" nvarchar(1000) NOT NULL + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "FeeRecordCorrection" DROP COLUMN "bankTeamEmails" + `); + await queryRunner.query(` + ALTER TABLE "FeeRecordCorrection" DROP COLUMN "bankTeamName" + `); + } +} diff --git a/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.entity.test.ts b/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.entity.test.ts index c269a42f3d..81e5350fe6 100644 --- a/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.entity.test.ts +++ b/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.entity.test.ts @@ -21,14 +21,27 @@ describe('FeeRecordEntity', () => { platform: REQUEST_PLATFORM_TYPE.TFM, }; + const bankTeamName = 'Payment Officer Team'; + const bankTeamEmails = ['test@ukexportfinance.gov.uk', 'test2@ukexportfinance.gov.uk']; + // Act - const correctionEntity = FeeRecordCorrectionEntity.createRequestedCorrection({ feeRecord, requestedByUser, reasons, additionalInfo, requestSource }); + const correctionEntity = FeeRecordCorrectionEntity.createRequestedCorrection({ + feeRecord, + requestedByUser, + reasons, + additionalInfo, + requestSource, + bankTeamName, + bankTeamEmails, + }); // Assert expect(correctionEntity.reasons).toEqual(reasons); expect(correctionEntity.additionalInfo).toEqual(additionalInfo); expect(correctionEntity.requestedByUser).toEqual(requestedByUser); expect(correctionEntity.isCompleted).toEqual(false); + expect(correctionEntity.bankTeamName).toEqual(bankTeamName); + expect(correctionEntity.bankTeamEmails).toEqual('test@ukexportfinance.gov.uk,test2@ukexportfinance.gov.uk'); }); }); diff --git a/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.entity.ts b/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.entity.ts index 07b3376256..409da4012e 100644 --- a/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.entity.ts +++ b/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.entity.ts @@ -61,6 +61,18 @@ export class FeeRecordCorrectionEntity extends AuditableBaseEntity { @Column({ type: 'nvarchar', length: '500', nullable: true }) bankCommentary!: string | null; + /** + * Bank team name + */ + @Column({ type: 'nvarchar', length: '500' }) + bankTeamName!: string; + + /** + * Bank team emails + */ + @Column({ type: 'nvarchar', length: '1000' }) + bankTeamEmails!: string; + /** * The previous values of the fields of the fee record that the * correction is correcting @@ -97,6 +109,8 @@ export class FeeRecordCorrectionEntity extends AuditableBaseEntity { * @param param.reasons - The reasons for the correction * @param param.additionalInfo - The user provided additional information * @param param.requestSource - The request source + * @param param.bankTeamName - The bank payment officer team name + * @param param.bankTeamEmails - The bank payment officer team email address array * @returns The fee record correction */ static createRequestedCorrection({ @@ -105,6 +119,8 @@ export class FeeRecordCorrectionEntity extends AuditableBaseEntity { reasons, additionalInfo, requestSource, + bankTeamName, + bankTeamEmails, }: CreateFeeRecordCorrectionParams): FeeRecordCorrectionEntity { const recordCorrection = new FeeRecordCorrectionEntity(); recordCorrection.feeRecord = feeRecord; @@ -113,6 +129,8 @@ export class FeeRecordCorrectionEntity extends AuditableBaseEntity { recordCorrection.additionalInfo = additionalInfo; recordCorrection.isCompleted = false; recordCorrection.updateLastUpdatedBy(requestSource); + recordCorrection.bankTeamName = bankTeamName; + recordCorrection.bankTeamEmails = bankTeamEmails.join(','); return recordCorrection; } diff --git a/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.types.ts b/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.types.ts index 8749f1c8cb..64e3433bb8 100644 --- a/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.types.ts +++ b/libs/common/src/sql-db-entities/fee-record-correction/fee-record-correction.types.ts @@ -8,6 +8,8 @@ export type CreateFeeRecordCorrectionParams = { reasons: RecordCorrectionReason[]; additionalInfo: string; requestSource: DbRequestSource; + bankTeamName: string; + bankTeamEmails: string[]; }; export type CompleteCorrectionParams = { diff --git a/libs/common/src/test-helpers/mock-data/fee-record-correction.entity.mock-builder.ts b/libs/common/src/test-helpers/mock-data/fee-record-correction.entity.mock-builder.ts index c7b9d06f1a..a46e16bbe4 100644 --- a/libs/common/src/test-helpers/mock-data/fee-record-correction.entity.mock-builder.ts +++ b/libs/common/src/test-helpers/mock-data/fee-record-correction.entity.mock-builder.ts @@ -31,6 +31,8 @@ export class FeeRecordCorrectionEntityMockBuilder { }; data.additionalInfo = 'some info'; data.reasons = [RECORD_CORRECTION_REASON.UTILISATION_INCORRECT]; + data.bankTeamName = 'some team'; + data.bankTeamEmails = 'test1@ukexportfinance.gov.uk, test2@ukexportfinance.gov.uk'; if (isCompleted) { data.isCompleted = true; diff --git a/trade-finance-manager-ui/server/api.js b/trade-finance-manager-ui/server/api.js index 72aad6d0ec..b8b93df96f 100644 --- a/trade-finance-manager-ui/server/api.js +++ b/trade-finance-manager-ui/server/api.js @@ -96,7 +96,7 @@ const getDeals = async (queryParams, token) => { }); const { deals, pagination } = response.data; - if (queryParams.page >= pagination.totalPages) { + if (queryParams.page >= pagination?.totalPages) { throw new PageOutOfBoundsError('Requested page number exceeds the maximum page number'); }