Skip to content

Commit ef3d702

Browse files
committed
Merge branch 'main' into feat/DTFS2-6892/add-frontend-upsert-user-functionality
2 parents bbc537f + 3c2fc50 commit ef3d702

File tree

255 files changed

+10616
-782
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

255 files changed

+10616
-782
lines changed

.env.sample

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ UTILISATION_REPORT_CREATION_FAILURE_EMAIL_ADDRESS=
6767

6868
DEAL_CANCELLATION_SCHEDULE='* 2 * * *'
6969

70+
RECORD_CORRECTION_TRANSIENT_FORM_DATA_DELETE_SCHEDULE='0 2 * * *'
71+
7072
# STORAGE
7173
AZURE_PORTAL_STORAGE_ACCOUNT=
7274
AZURE_PORTAL_STORAGE_ACCESS_KEY=
@@ -114,9 +116,11 @@ CONTACT_US_SELF_SERVICE_PORTAL_URL=
114116
# FEATURE FLAGS
115117
CHANGE_STREAM_ENABLED=false
116118
CLAMAV_SCANNING_ENABLED=false
119+
GEF_DEAL_VERSION=0
117120
FF_TFM_PAYMENT_RECONCILIATION_ENABLED=false
118121
FF_TFM_DEAL_CANCELLATION_ENABLED=false
119122
FF_PORTAL_FACILITY_AMENDMENTS_ENABLED=false
120123
FF_TFM_SSO_ENABLED=false
121124
FF_FEE_RECORD_CORRECTION_ENABLED=false
125+
FF_SALESFORCE_CUSTOMER_CREATION_ENABLED=false
122126
GEF_DEAL_VERSION=0

.github/workflows/test.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,16 @@ env:
160160
FF_TFM_DEAL_CANCELLATION_ENABLED: ${{ vars.FF_TFM_DEAL_CANCELLATION_ENABLED }}
161161
FF_PORTAL_FACILITY_AMENDMENTS_ENABLED: ${{ vars.FF_PORTAL_FACILITY_AMENDMENTS_ENABLED }}
162162
FF_TFM_SSO_ENABLED: ${{ vars.FF_TFM_SSO_ENABLED }}
163+
FF_SALESFORCE_CUSTOMER_CREATION_ENABLED: ${{ vars.FF_SALESFORCE_CUSTOMER_CREATION_ENABLED }}
163164
GEF_DEAL_VERSION: ${{ vars.GEF_DEAL_VERSION }}
164165
ENTRA_ID_CLIENT_ID: ${{ vars.ENTRA_ID_CLIENT_ID }}
165166
ENTRA_ID_CLOUD_INSTANCE: ${{ vars.ENTRA_ID_CLOUD_INSTANCE }}
166167
ENTRA_ID_TENANT_ID: ${{ vars.ENTRA_ID_TENANT_ID }}
167168
ENTRA_ID_CLIENT_SECRET: ${{ vars.ENTRA_ID_CLIENT_SECRET }}
168169
ENTRA_ID_REDIRECT_URL: ${{ vars.ENTRA_ID_REDIRECT_URL }}
169170
DEAL_CANCELLATION_SCHEDULE: ${{ vars.DEAL_CANCELLATION_SCHEDULE }}
171+
RECORD_CORRECTION_TRANSIENT_FORM_DATA_DELETE_SCHEDULE: ${{ vars.RECORD_CORRECTION_TRANSIENT_FORM_DATA_DELETE_SCHEDULE }}
172+
UKEF_GEF_REPORTING_EMAIL_RECIPIENT: ${{ secrets.UKEF_GEF_REPORTING_EMAIL_RECIPIENT }}
170173

171174
jobs:
172175
# 1. Setup test infrastructure
@@ -386,9 +389,11 @@ jobs:
386389
env:
387390
# Various feature flags
388391
FF_TFM_PAYMENT_RECONCILIATION_ENABLED: 'true'
392+
FF_FEE_RECORD_CORRECTION_ENABLED: 'true'
389393
FF_TFM_DEAL_CANCELLATION_ENABLED: 'true'
390394
FF_PORTAL_FACILITY_AMENDMENTS_ENABLED: 'true'
391395
FF_TFM_SSO_ENABLED: 'true'
396+
FF_SALESFORCE_CUSTOMER_CREATION_ENABLED: 'true'
392397
environment:
393398
name: ${{ needs.setup.outputs.environment }}
394399
runs-on: ubuntu-latest
@@ -828,7 +833,7 @@ jobs:
828833
env:
829834
SQL_DB_HOST: ${{ secrets.SQL_DB_HOST_LOCAL }}
830835
run: npm run db:migrate
831-
836+
832837
- name: Execute
833838
uses: ./.github/actions/rerun
834839
with:
@@ -846,9 +851,12 @@ jobs:
846851
needs: [setup]
847852
env:
848853
# Various feature flags
854+
FF_TFM_PAYMENT_RECONCILIATION_ENABLED: 'true'
849855
FF_FEE_RECORD_CORRECTION_ENABLED: 'true'
850856
FF_TFM_DEAL_CANCELLATION_ENABLED: 'true'
851857
FF_PORTAL_FACILITY_AMENDMENTS_ENABLED: 'true'
858+
FF_TFM_SSO_ENABLED: 'false'
859+
FF_SALESFORCE_CUSTOMER_CREATION_ENABLED: 'true'
852860
environment:
853861
name: ${{ needs.setup.outputs.environment }}
854862
runs-on: ubuntu-latest

docker-compose.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ services:
8989
- FF_TFM_DEAL_CANCELLATION_ENABLED
9090
- DEAL_CANCELLATION_SCHEDULE
9191
- FF_PORTAL_FACILITY_AMENDMENTS_ENABLED
92+
- UKEF_GEF_REPORTING_EMAIL_RECIPIENT
93+
- TFM_UI_URL=http://localhost:5003
94+
- RECORD_CORRECTION_TRANSIENT_FORM_DATA_DELETE_SCHEDULE
9295

9396
trade-finance-manager-ui:
9497
build:
@@ -177,6 +180,7 @@ services:
177180
- CHANGE_STREAM_ENABLED
178181
- DELETION_AUDIT_LOGS_TTL_SECONDS
179182
- FF_TFM_DEAL_CANCELLATION_ENABLED
183+
- FF_SALESFORCE_CUSTOMER_CREATION_ENABLED
180184
- ENTRA_ID_CLIENT_ID
181185
- ENTRA_ID_CLOUD_INSTANCE
182186
- ENTRA_ID_TENANT_ID
@@ -320,6 +324,7 @@ services:
320324
- NODE_ENV
321325
- RATE_LIMIT_THRESHOLD
322326
- ESTORE_CRON_MANAGER_SCHEDULE
327+
- FF_SALESFORCE_CUSTOMER_CREATION_ENABLED
323328
entrypoint: npx ts-node external-api/src/index.ts
324329

325330
gef-ui:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { HttpStatusCode } from 'axios';
2+
import {
3+
Bank,
4+
FEE_RECORD_STATUS,
5+
FeeRecordCorrectionEntityMockBuilder,
6+
FeeRecordEntityMockBuilder,
7+
RECONCILIATION_IN_PROGRESS,
8+
RECORD_CORRECTION_REASON,
9+
UtilisationReportEntityMockBuilder,
10+
} from '@ukef/dtfs2-common';
11+
import { testApi } from '../../../test-api';
12+
import { SqlDbHelper } from '../../../sql-db-helper';
13+
import { aBank } from '../../../../test-helpers';
14+
import { mongoDbClient } from '../../../../src/drivers/db-client';
15+
import { wipe } from '../../../wipeDB';
16+
import { replaceUrlParameterPlaceholders } from '../../../../test-helpers/replace-url-parameter-placeholders';
17+
18+
console.error = jest.fn();
19+
20+
const BASE_URL = '/v1/bank/:bankId/utilisation-reports/completed-corrections';
21+
22+
describe(`GET ${BASE_URL}`, () => {
23+
const bankId = '123';
24+
const bank: Bank = {
25+
...aBank(),
26+
id: bankId,
27+
};
28+
29+
const report = UtilisationReportEntityMockBuilder.forStatus(RECONCILIATION_IN_PROGRESS).withBankId(bankId).build();
30+
const feeRecord = FeeRecordEntityMockBuilder.forReport(report).withExporter('An exporter').withStatus(FEE_RECORD_STATUS.TO_DO_AMENDED).build();
31+
report.feeRecords = [feeRecord];
32+
33+
beforeAll(async () => {
34+
await SqlDbHelper.initialize();
35+
await SqlDbHelper.deleteAllEntries('UtilisationReport');
36+
await wipe(['banks']);
37+
38+
const banksCollection = await mongoDbClient.getCollection('banks');
39+
await banksCollection.insertOne(bank);
40+
41+
await SqlDbHelper.saveNewEntries('UtilisationReport', [report]);
42+
});
43+
44+
afterEach(async () => {
45+
await SqlDbHelper.deleteAllEntries('FeeRecordCorrection');
46+
});
47+
48+
afterAll(async () => {
49+
await SqlDbHelper.deleteAllEntries('UtilisationReport');
50+
await wipe(['banks']);
51+
});
52+
53+
it(`should return '${HttpStatusCode.Ok}' and the completed fee record corrections response`, async () => {
54+
// Arrange
55+
const dateCorrectionReceived = new Date('2024-01-01');
56+
const oldFacilityId = feeRecord.facilityId;
57+
const newFacilityId = '12345678';
58+
59+
const feeRecordCorrection = FeeRecordCorrectionEntityMockBuilder.forFeeRecordAndIsCompleted(feeRecord, true)
60+
.withId(1)
61+
.withDateReceived(dateCorrectionReceived)
62+
.withReasons([RECORD_CORRECTION_REASON.FACILITY_ID_INCORRECT, RECORD_CORRECTION_REASON.OTHER])
63+
.withPreviousValues({
64+
facilityId: oldFacilityId,
65+
})
66+
.withCorrectedValues({
67+
facilityId: newFacilityId,
68+
})
69+
.withBankCommentary('Some bank commentary')
70+
.build();
71+
72+
await SqlDbHelper.saveNewEntry('FeeRecordCorrection', feeRecordCorrection);
73+
74+
// Act
75+
const response = await testApi.get(replaceUrlParameterPlaceholders(BASE_URL, { bankId }));
76+
77+
// Assert
78+
expect(response.status).toEqual(HttpStatusCode.Ok);
79+
80+
const expectedMappedCompletedCorrections = [
81+
{
82+
id: feeRecordCorrection.id,
83+
dateSent: dateCorrectionReceived.toISOString(),
84+
exporter: feeRecord.exporter,
85+
formattedReasons: 'Facility ID is incorrect, Other',
86+
formattedPreviousValues: `${oldFacilityId}, -`,
87+
formattedCorrectedValues: `${newFacilityId}, -`,
88+
bankCommentary: feeRecordCorrection.bankCommentary,
89+
},
90+
];
91+
92+
expect(response.body).toEqual(expectedMappedCompletedCorrections);
93+
});
94+
95+
it(`should return '${HttpStatusCode.Ok}' and an empty array when no completed fee record corrections with the supplied bank id can be found`, async () => {
96+
// Arrange
97+
const feeRecordCorrection = FeeRecordCorrectionEntityMockBuilder.forFeeRecordAndIsCompleted(feeRecord, true).build();
98+
99+
await SqlDbHelper.saveNewEntry('FeeRecordCorrection', feeRecordCorrection);
100+
101+
// Act
102+
const response = await testApi.get(replaceUrlParameterPlaceholders(BASE_URL, { bankId: `${bankId}123` }));
103+
104+
// Assert
105+
expect(response.status).toEqual(HttpStatusCode.Ok);
106+
107+
expect(response.body).toEqual([]);
108+
});
109+
});

dtfs-central-api/api-tests/v1/utilisation-reports/fee-record-corrections/put-fee-record-correction-transient-form-data.api-test.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { CustomErrorResponse } from '../../../helpers/custom-error-response';
2424
console.error = jest.fn();
2525

2626
interface PutFeeRecordCorrectionTransientFormDataResponse extends Response {
27-
body: RecordCorrectionFormValueValidationErrors | string | undefined;
27+
body: { validationErrors?: RecordCorrectionFormValueValidationErrors } | string;
2828
}
2929

3030
const BASE_URL = '/v1/bank/:bankId/fee-record-corrections/:correctionId/transient-form-data';
@@ -166,30 +166,40 @@ describe(`PUT ${BASE_URL}`, () => {
166166
.to(replaceUrlParameterPlaceholders(BASE_URL, { bankId, correctionId }))) as PutFeeRecordCorrectionTransientFormDataResponse;
167167

168168
// Assert
169-
const expectedBody: RecordCorrectionFormValueValidationErrors = {
169+
const validationErrors: RecordCorrectionFormValueValidationErrors = {
170170
facilityIdErrorMessage: 'You must enter a facility ID between 8 and 10 digits using the numbers 0-9 only',
171171
reportedCurrencyErrorMessage: 'You must select a currency',
172172
reportedFeeErrorMessage: 'You must enter the reported fee in a valid format',
173173
utilisationErrorMessage: 'You must enter the utilisation in a valid format',
174174
additionalCommentsErrorMessage: 'You must enter a comment',
175175
};
176176

177+
const expectedBody = {
178+
validationErrors,
179+
};
180+
177181
expect(status).toEqual(HttpStatusCode.Ok);
178182

179183
expect(body).toEqual(expectedBody);
180184
});
181185
});
182186

183187
describe('when there are no validation errors', () => {
184-
it(`should return '${HttpStatusCode.Ok}' if the correction exists`, async () => {
188+
it(`should return '${HttpStatusCode.Ok}' if the correction exists with an empty response body`, async () => {
185189
// Arrange
186190
const requestBody = aValidRequestBody();
187191

188192
// Act
189-
const { status } = await testApi.put(requestBody).to(replaceUrlParameterPlaceholders(BASE_URL, { bankId, correctionId }));
193+
const { status, body } = (await testApi
194+
.put(requestBody)
195+
.to(replaceUrlParameterPlaceholders(BASE_URL, { bankId, correctionId }))) as PutFeeRecordCorrectionTransientFormDataResponse;
190196

191197
// Assert
198+
const expectedBody = {};
199+
192200
expect(status).toEqual(HttpStatusCode.Ok);
201+
202+
expect(body).toEqual(expectedBody);
193203
});
194204
});
195205
});

dtfs-central-api/api-tests/v1/utilisation-reports/fee-record-corrections/put-fee-record-correction.api-test.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import { HttpStatusCode } from 'axios';
22
import { Response } from 'supertest';
33
import {
44
FEE_RECORD_STATUS,
5+
FeeRecordCorrectionEntity,
56
FeeRecordCorrectionEntityMockBuilder,
67
FeeRecordCorrectionTransientFormDataEntity,
78
FeeRecordCorrectionTransientFormDataEntityMockBuilder,
9+
FeeRecordEntity,
810
FeeRecordEntityMockBuilder,
911
RECONCILIATION_IN_PROGRESS,
12+
RECORD_CORRECTION_REASON,
1013
ReportPeriod,
1114
UtilisationReportEntityMockBuilder,
1215
} from '@ukef/dtfs2-common';
@@ -33,12 +36,13 @@ const BASE_URL = '/v1/bank/:bankId/fee-record-corrections/:correctionId';
3336

3437
describe(`PUT ${BASE_URL}`, () => {
3538
const bankId = '1';
39+
const feeRecordId = 1;
3640
const correctionId = 2;
3741
const userId = new ObjectId().toString();
3842

3943
const reportPeriod = { start: { month: 3, year: 2023 }, end: { month: 4, year: 2023 } };
4044

41-
const paymentOfficerTeamEmails = ['email1@ukexportfinance.gov.uk'];
45+
const paymentOfficerTeamEmails = ['payment-officer1@ukexportfinance.gov.uk'];
4246

4347
const mockBank = {
4448
...aBank(),
@@ -53,6 +57,11 @@ describe(`PUT ${BASE_URL}`, () => {
5357
user: { _id: userId },
5458
});
5559

60+
const reasons = [RECORD_CORRECTION_REASON.FACILITY_ID_INCORRECT];
61+
62+
const incorrectFacilityId = '11111111';
63+
const correctFacilityId = '22222222';
64+
5665
beforeAll(async () => {
5766
await SqlDbHelper.initialize();
5867
await wipeDB.wipe(['banks']);
@@ -68,16 +77,24 @@ describe(`PUT ${BASE_URL}`, () => {
6877

6978
const report = UtilisationReportEntityMockBuilder.forStatus(RECONCILIATION_IN_PROGRESS).withBankId(bankId).withReportPeriod(reportPeriod).build();
7079

71-
const feeRecord = FeeRecordEntityMockBuilder.forReport(report).withStatus(FEE_RECORD_STATUS.PENDING_CORRECTION).build();
80+
const feeRecord = FeeRecordEntityMockBuilder.forReport(report)
81+
.withId(feeRecordId)
82+
.withStatus(FEE_RECORD_STATUS.PENDING_CORRECTION)
83+
.withFacilityId(incorrectFacilityId)
84+
.build();
7285

73-
const correction = FeeRecordCorrectionEntityMockBuilder.forFeeRecordAndIsCompleted(feeRecord, false).withId(correctionId).build();
86+
const correction = FeeRecordCorrectionEntityMockBuilder.forFeeRecordAndIsCompleted(feeRecord, false).withId(correctionId).withReasons(reasons).build();
7487

7588
feeRecord.corrections = [correction];
7689
report.feeRecords = [feeRecord];
7790

7891
await SqlDbHelper.saveNewEntry('UtilisationReport', report);
7992

80-
const formData = new FeeRecordCorrectionTransientFormDataEntityMockBuilder().withCorrectionId(correctionId).withUserId(userId).build();
93+
const formData = new FeeRecordCorrectionTransientFormDataEntityMockBuilder()
94+
.withCorrectionId(correctionId)
95+
.withUserId(userId)
96+
.withFormData({ facilityId: correctFacilityId })
97+
.build();
8198

8299
await SqlDbHelper.saveNewEntry('FeeRecordCorrectionTransientFormData', formData);
83100
});
@@ -149,6 +166,50 @@ describe(`PUT ${BASE_URL}`, () => {
149166
expect(status).toEqual(HttpStatusCode.Ok);
150167
});
151168

169+
it(`should update the fee record with corrected values and set status to ${FEE_RECORD_STATUS.TO_DO_AMENDED}`, async () => {
170+
// Arrange
171+
const requestBody = aValidRequestBody();
172+
173+
// Act
174+
await testApi.put(requestBody).to(replaceUrlParameterPlaceholders(BASE_URL, { bankId, correctionId }));
175+
176+
// Assert
177+
const feeRecord = await SqlDbHelper.manager.findOneBy(FeeRecordEntity, { id: feeRecordId });
178+
expect(feeRecord?.facilityId).toEqual(correctFacilityId);
179+
expect(feeRecord?.status).toEqual(FEE_RECORD_STATUS.TO_DO_AMENDED);
180+
});
181+
182+
it('should save the previous and corrected values on the correction and complete it', async () => {
183+
// Arrange
184+
const requestBody = aValidRequestBody();
185+
186+
// Act
187+
await testApi.put(requestBody).to(replaceUrlParameterPlaceholders(BASE_URL, { bankId, correctionId }));
188+
189+
// Assert
190+
const correction = await SqlDbHelper.manager.findOneBy(FeeRecordCorrectionEntity, { id: correctionId });
191+
192+
const expectedPreviousValues = {
193+
facilityId: incorrectFacilityId,
194+
feesPaidToUkefForThePeriod: null,
195+
feesPaidToUkefForThePeriodCurrency: null,
196+
facilityUtilisation: null,
197+
};
198+
199+
expect(correction?.previousValues).toEqual(expectedPreviousValues);
200+
201+
const expectedCorrectedValues = {
202+
facilityId: correctFacilityId,
203+
feesPaidToUkefForThePeriod: null,
204+
feesPaidToUkefForThePeriodCurrency: null,
205+
facilityUtilisation: null,
206+
};
207+
208+
expect(correction?.correctedValues).toEqual(expectedCorrectedValues);
209+
210+
expect(correction?.isCompleted).toEqual(true);
211+
});
212+
152213
it('should delete the transient form data', async () => {
153214
// Arrange
154215
const requestBody = aValidRequestBody();
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
const REPORT_RECONCILED = 'f6a10d2f-a1bb-4640-9bf0-0699ef442551';
22
const FEE_RECORD_CORRECTION_REQUEST = 'c32528aa-4e12-43c1-9ff0-11f327513b9e';
3+
const FEE_RECORD_CORRECTION_RECEIVED_BANK_NOTIFICATION = 'c60489c5-a2e8-4100-a63a-3f5e691bdb20';
4+
const FEE_RECORD_CORRECTION_RECEIVED_UKEF_NOTIFICATION = '2af491e6-2968-4476-8db9-74202138dfad';
35
const REPORT_INSERTION_CRON_FAILURE = 'a8a2b0db-67a8-409b-b8b7-7104b137acfd';
46

5-
export default { REPORT_RECONCILED, FEE_RECORD_CORRECTION_REQUEST, REPORT_INSERTION_CRON_FAILURE };
7+
export default {
8+
REPORT_RECONCILED,
9+
FEE_RECORD_CORRECTION_REQUEST,
10+
FEE_RECORD_CORRECTION_RECEIVED_BANK_NOTIFICATION,
11+
FEE_RECORD_CORRECTION_RECEIVED_UKEF_NOTIFICATION,
12+
REPORT_INSERTION_CRON_FAILURE,
13+
};

0 commit comments

Comments
 (0)