Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(EMS-3342): no pdf - application submission - XLSX - loss payee - financial information #2562

Merged
merged 13 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions e2e-tests/constants/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export const APPLICATION = {
POLICY_TYPE: {
SINGLE: 'Single contract policy',
MULTIPLE: 'Multiple contract policy',
ABBREVIATED: {
SINGLE: 'Single',
MULTIPLE: 'Multiple',
},
},
POLICY: {
TOTAL_VALUE_OF_CONTRACT: {
Expand Down
402 changes: 227 additions & 175 deletions src/api/.keystone/config.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/api/constants/application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export const APPLICATION = {
POLICY_TYPE: {
SINGLE: 'Single contract policy',
MULTIPLE: 'Multiple contract policy',
ABBREVIATED: {
SINGLE: 'Single',
MULTIPLE: 'Multiple',
},
},
POLICY: {
TOTAL_VALUE_OF_CONTRACT: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,12 @@ describe('custom-resolvers/submit-application', () => {
where: { id: submittedApplication.id },
});

populatedApplication = await getPopulatedApplication(context, fullSubmittedApplication);
populatedApplication = await getPopulatedApplication.get({
context,
application: fullSubmittedApplication,
decryptFinancialUk: true,
decryptFinancialInternational: true,
});
});

test('it should call generate.XLSX', async () => {
Expand Down
11 changes: 10 additions & 1 deletion src/api/custom-resolvers/mutations/submit-application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const submitApplication = async (root: any, variables: SubmitApplicationVariable
const canSubmit = isInProgress && validSubmissionDate && isFirstSubmission;

if (canSubmit) {
console.info('Submitting application - updating status, submission date and count %s', variables.applicationId);

// change the status and add submission date
const update = {
status: APPLICATION.STATUS.SUBMITTED,
Expand All @@ -54,8 +56,15 @@ const submitApplication = async (root: any, variables: SubmitApplicationVariable
data: update,
});

console.info('Submitting application - getting populated application %s', variables.applicationId);

// get a fully populated application for XLSX generation
const populatedApplication = await getPopulatedApplication(context, updatedApplication);
const populatedApplication = await getPopulatedApplication.get({
context,
application: updatedApplication,
decryptFinancialUk: true,
decryptFinancialInternational: true,
});

// generate a XLSX for UKEF underwriting team email
const xlsxPath = await generate.XLSX(populatedApplication);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import createLossPayeeFinancialDetailsInternationalVector from '../../../test-he

describe('custom-resolvers/update-loss-payee-financial-details-international', () => {
let context: Context;
let lossPayeeFinancialDetailsInternational: ApplicationLossPayeeFinancialInternational;
let lossPayeeFinancialDetailsInternationalResponse: SuccessResponse;
let vector: ApplicationLossPayeeFinancialInternationalVector;

Expand All @@ -26,7 +27,7 @@ describe('custom-resolvers/update-loss-payee-financial-details-international', (
describe('successfully updates loss payee financial international', () => {
beforeEach(async () => {
jest.resetAllMocks();
const lossPayeeFinancialDetailsInternational = (await createLossPayeeFinancialDetailsInternational({
lossPayeeFinancialDetailsInternational = (await createLossPayeeFinancialDetailsInternational({
context,
})) as ApplicationLossPayeeFinancialInternational;

Expand Down Expand Up @@ -76,14 +77,14 @@ describe('custom-resolvers/update-loss-payee-financial-details-international', (
describe('when an error occurs whilst updating loss payee financial international vector', () => {
it('should throw an error', async () => {
/**
* Create a new LossPayeeFinancialDetailsInternational,
* Without a vector relationship.
* This will then cause the vector call to fail,
* because there is no associated vector ID.
* Delete the LossPayeeFinancialInternationalVector relationship,
* to cause the vector data saving to fail.
*/
const lossPayeeFinancialDetailsInternational = (await createLossPayeeFinancialDetailsInternational({
context,
})) as ApplicationLossPayeeFinancialInternational;
await context.query.LossPayeeFinancialInternationalVector.deleteOne({
where: {
id: vector.id,
},
});

variables.id = lossPayeeFinancialDetailsInternational.id;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import createLossPayeeFinancialDetailsUkVector from '../../../test-helpers/loss-

describe('custom-resolvers/update-loss-payee-financial-details-uk', () => {
let context: Context;
let lossPayeeFinancialDetailsUk: ApplicationLossPayeeFinancialUk;
let lossPayeeFinancialDetailsUkResponse: SuccessResponse;
let vector: ApplicationLossPayeeFinancialUkVector;

Expand All @@ -26,7 +27,7 @@ describe('custom-resolvers/update-loss-payee-financial-details-uk', () => {
describe('successfully updates loss payee financial uk', () => {
beforeEach(async () => {
jest.resetAllMocks();
const lossPayeeFinancialDetailsUk = (await createLossPayeeFinancialDetailsUk({ context })) as ApplicationLossPayeeFinancialUk;
lossPayeeFinancialDetailsUk = (await createLossPayeeFinancialDetailsUk({ context })) as ApplicationLossPayeeFinancialUk;

vector = (await createLossPayeeFinancialDetailsUkVector({
context,
Expand Down Expand Up @@ -59,14 +60,14 @@ describe('custom-resolvers/update-loss-payee-financial-details-uk', () => {
describe('when an error occurs whilst updating loss payee financial uk vector', () => {
it('should throw an error', async () => {
/**
* Create a new LossPayeeFinancialDetailsUk,
* Without a vector relationship.
* This will then cause the vector call to fail,
* because there is no associated vector ID.
* Delete the LossPayeeFinancialUkVector relationship,
* to cause the vector data saving to fail.
*/
const lossPayeeFinancialDetailsUk = (await createLossPayeeFinancialDetailsUk({
context,
})) as ApplicationLossPayeeFinancialUk;
await context.query.LossPayeeFinancialUkVector.deleteOne({
where: {
id: vector.id,
},
});

variables.id = lossPayeeFinancialDetailsUk.id;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,98 +1,126 @@
import getApplicationByReferenceNumber from '.';
import { mockLossPayeeFinancialDetailsUk } from '../../../test-mocks/mock-application';
import { Context } from '../../../types';
import { Context } from '.keystone/types'; // eslint-disable-line
import getApplicationByReferenceNumberQuery from '.';
import getApplicationByReferenceNumber from '../../../helpers/get-application-by-reference-number';
import getPopulatedApplication from '../../../helpers/get-populated-application';
import getKeystoneContext from '../../../test-helpers/get-keystone-context';
import { createFullApplication } from '../../../test-helpers/create-full-application';
import decrypt from '../../../helpers/decrypt';
import { mockApplication } from '../../../test-mocks';

describe('custom-resolvers/get-application-by-reference-number', () => {
let context: Context;
let fullApplication;
let refNumber: number;

const populatedApplicationResponse = mockApplication;

jest.mock('../../../helpers/decrypt');
const mockDecryptedValue = '123456';
let decryptSpy = jest.fn();
jest.mock('../../../helpers/get-populated-application');

let getPopulatedApplicationSpy = jest.fn();

beforeAll(() => {
context = getKeystoneContext();
});

let refNumber: number;

beforeAll(async () => {
jest.resetAllMocks();

const application = await createFullApplication(context);
fullApplication = await createFullApplication(context);

const { referenceNumber } = application;
const { referenceNumber } = fullApplication;

refNumber = referenceNumber;
});

beforeEach(async () => {
jest.resetAllMocks();

decryptSpy = jest.fn(() => mockDecryptedValue);
getPopulatedApplicationSpy = jest.fn(() => populatedApplicationResponse);

decrypt.decrypt = decryptSpy;
getPopulatedApplication.get = getPopulatedApplicationSpy;
});

describe('when the decryptFinancialUk variable is not set', () => {
it('should return success=true and application without decryption', async () => {
const result = await getApplicationByReferenceNumber({}, { referenceNumber: refNumber }, context);
describe('when the decryptFinancialUk and decryptFinancialInternational variables are NOT provided', () => {
it('should call getPopulatedApplication.get', async () => {
await getApplicationByReferenceNumberQuery({}, { referenceNumber: refNumber }, context);

const { financialUk } = result.application.nominatedLossPayee;
const expectedApplication = await getApplicationByReferenceNumber(refNumber, context);

expect(result.success).toEqual(true);
expect(financialUk.sortCode).toEqual(mockLossPayeeFinancialDetailsUk.sortCode);
expect(financialUk.accountNumber).toEqual(mockLossPayeeFinancialDetailsUk.accountNumber);
expect(getPopulatedApplicationSpy).toHaveBeenCalledTimes(1);
expect(getPopulatedApplicationSpy).toHaveBeenCalledWith({
context,
application: expectedApplication,
decryptFinancialUk: undefined,
decryptFinancialInternational: undefined,
});
});

it('should NOT call decrypt', async () => {
await getApplicationByReferenceNumber({}, { referenceNumber: refNumber }, context);
it('should return success=true and populated application', async () => {
const result = await getApplicationByReferenceNumberQuery({}, { referenceNumber: refNumber }, context);

const expected = {
success: true,
application: populatedApplicationResponse,
};

expect(decryptSpy).toHaveBeenCalledTimes(0);
expect(result).toEqual(expected);
});
});

describe('when the decryptFinancialUk variable is set to "true"', () => {
it('should return success=true and application with decryption', async () => {
const result = await getApplicationByReferenceNumber({}, { referenceNumber: refNumber, decryptFinancialUk: true }, context);
describe('when the decryptFinancialUk variable is set to true', () => {
it('should call getPopulatedApplication.get', async () => {
await getApplicationByReferenceNumberQuery({}, { referenceNumber: refNumber, decryptFinancialUk: true }, context);

const { financialUk } = result.application.nominatedLossPayee;
const expectedApplication = await getApplicationByReferenceNumber(refNumber, context);

expect(result.success).toEqual(true);
expect(financialUk.sortCode).toEqual(mockDecryptedValue);
expect(financialUk.accountNumber).toEqual(mockDecryptedValue);
expect(getPopulatedApplicationSpy).toHaveBeenCalledTimes(1);
expect(getPopulatedApplicationSpy).toHaveBeenCalledWith({
context,
application: expectedApplication,
decryptFinancialUk: true,
});
});

it('should call decrypt', async () => {
await getApplicationByReferenceNumber({}, { referenceNumber: refNumber, decryptFinancialUk: true }, context);
it('should return success=true and populated application', async () => {
const result = await getApplicationByReferenceNumberQuery({}, { referenceNumber: refNumber, decryptFinancialUk: true }, context);

expect(decryptSpy).toHaveBeenCalledTimes(2);
const expected = {
success: true,
application: populatedApplicationResponse,
};

expect(result).toEqual(expected);
});
});

describe('when the decryptFinancialInternational variable is set to "true"', () => {
it('should return success=true and application with decryption', async () => {
const result = await getApplicationByReferenceNumber({}, { referenceNumber: refNumber, decryptFinancialInternational: true }, context);
describe('when the decryptFinancialInternational variable is set to true', () => {
it('should call getPopulatedApplication.get', async () => {
await getApplicationByReferenceNumberQuery({}, { referenceNumber: refNumber, decryptFinancialInternational: true }, context);

const { financialInternational } = result.application.nominatedLossPayee;
const expectedApplication = await getApplicationByReferenceNumber(refNumber, context);

expect(result.success).toEqual(true);
expect(financialInternational.iban).toEqual(mockDecryptedValue);
expect(financialInternational.bicSwiftCode).toEqual(mockDecryptedValue);
expect(getPopulatedApplicationSpy).toHaveBeenCalledTimes(1);
expect(getPopulatedApplicationSpy).toHaveBeenCalledWith({
context,
application: expectedApplication,
decryptFinancialInternational: true,
});
});

it('should call decrypt', async () => {
await getApplicationByReferenceNumber({}, { referenceNumber: refNumber, decryptFinancialUk: true }, context);
it('should return success=true and populated application', async () => {
const result = await getApplicationByReferenceNumberQuery({}, { referenceNumber: refNumber, decryptFinancialInternational: true }, context);

const expected = {
success: true,
application: populatedApplicationResponse,
};

expect(decryptSpy).toHaveBeenCalledTimes(2);
expect(result).toEqual(expected);
});
});

describe('when the application cannot be found', () => {
it('should return success=false', async () => {
const result = await getApplicationByReferenceNumber({}, { referenceNumber: 123 }, context);
const result = await getApplicationByReferenceNumberQuery({}, { referenceNumber: 123 }, context);

expect(result.success).toEqual(false);
});
Expand All @@ -101,7 +129,7 @@ describe('custom-resolvers/get-application-by-reference-number', () => {
describe('when an error occurs', () => {
it('should throw an error', async () => {
try {
await getApplicationByReferenceNumber({}, { referenceNumber: 0 }, context);
await getApplicationByReferenceNumberQuery({}, { referenceNumber: 0 }, context);
} catch (err) {
const errorString = String(err);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { Context } from '.keystone/types'; // eslint-disable-line
import { GetApplicationByReferenceNumberResponse, GetApplicationByReferenceNumberVariables } from '../../../types';
import getApplicationByReferenceNumber from '../../../helpers/get-application-by-reference-number';
import getPopulatedApplication from '../../../helpers/get-populated-application';
import decryptNominatedLossPayee from '../../../helpers/decrypt-nominated-loss-payee';
import { GetApplicationByReferenceNumberResponse, GetApplicationByReferenceNumberVariables } from '../../../types';

/**
* getApplicationByReferenceNumberQuery
* Gets an application by reference number
* Based on decrypt variables, decrypts part of application
* returns full application
* Get an application by reference number,
* call getPopulatedApplication
* @param {Object} GraphQL root variables
* @param {Object} GraphQL variables for the getApplicationByReferenceNumberVariables query
* @param {Context} KeystoneJS context API
Expand All @@ -34,23 +32,14 @@ const getApplicationByReferenceNumberQuery = async (
/**
* Populate the application,
* with all relationships.
* This function also handles decrypting financial details.
*/
const populatedApplication = await getPopulatedApplication(context, application);

/**
* if decrypt variables are set to true
* decrypts relevant nominatedLossPayee fields
* if decryptFinancialUk then decrypts financial uk
* if decryptFinancialInternational then decrypts financialInternational
* returns decrypted application
*/
if (decryptFinancialUk || decryptFinancialInternational) {
const { nominatedLossPayee } = populatedApplication;

const decryptedNominatedLossPayee = decryptNominatedLossPayee(nominatedLossPayee, decryptFinancialUk, decryptFinancialInternational);

populatedApplication.nominatedLossPayee = decryptedNominatedLossPayee;
}
const populatedApplication = await getPopulatedApplication.get({
context,
application,
decryptFinancialUk,
decryptFinancialInternational,
});

return {
success: true,
Expand Down
Loading
Loading