Skip to content

Commit

Permalink
Merge pull request #189 from companieshouse/feature/add-name-mismatch…
Browse files Browse the repository at this point in the history
…-reason-screen

Name mismatch screen and user input validation
  • Loading branch information
eedwards0 authored Dec 13, 2024
2 parents 0b56a9f + 95d6c2e commit b848a05
Show file tree
Hide file tree
Showing 16 changed files with 755 additions and 9 deletions.
15 changes: 15 additions & 0 deletions locales/cy/name_mismatch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name_mismatch_title": "to be translated",
"name_mismatch_born": "to be translated ",
"name_mismatch_details_par1": "to be translated",
"name_mismatch_details_par2": "to be translated",
"name_mismatch_details_par2_link": "to be translated",
"name_mismatch_details_par2_after_link":".",
"name_mismatch_legally_changed": "to be translated",
"name_mismatch_preferred_name": "to be translated",
"name_mismatch_translation_or_convention": "to be translated",
"name_mismatch_register_incorrect": "to be translated",
"name_mismatch_prefer_not_to_say": "to be translated",
"name_mismatch_error_summary": "to be translated",
"name_mismatch_error_inline": "to be translated"
}
15 changes: 15 additions & 0 deletions locales/en/name_mismatch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name_mismatch_title": "Why is the name on the public register different to the name this PSC used for identity verification?",
"name_mismatch_born": "Born ",
"name_mismatch_details_par1": "Tell us why the names do not match. We may contact you if we need more information.",
"name_mismatch_details_par2": "You should also consider whether this company needs to ",
"name_mismatch_details_par2_link": "update the PSC name on the public register (opens in a new tab)",
"name_mismatch_details_par2_after_link":".",
"name_mismatch_legally_changed": "Legally changed name (for example, marriage or divorce)",
"name_mismatch_preferred_name": "Preferred name",
"name_mismatch_translation_or_convention":"Translation or a different naming convention",
"name_mismatch_register_incorrect": "Error on the public register",
"name_mismatch_prefer_not_to_say": "Prefer not to say",
"name_mismatch_error_summary": "Tell us why the name on the public register is different to the name this PSC used for identity verification",
"name_mismatch_error_inline": "Tell us why the name on the public register is different to the name this PSC used for identity verification"
}
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"author": "Moses Wejuli <mwejuli@companieshouse.gov.uk>",
"license": "MIT",
"dependencies": {
"@companieshouse/api-sdk-node": "^2.0.209",
"@companieshouse/api-sdk-node": "^2.0.217",
"@companieshouse/ch-node-utils": "^1.3.12",
"@companieshouse/node-session-handler": "^5.1.4",
"@companieshouse/structured-logging-node": "^2.0.1",
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const Urls = {
INDIVIDUAL_PSC_LIST: "/individual/psc-list",
NEW_SUBMISSION: "/new-submission",
PERSONAL_CODE: `${urlWithTransactionIdAndSubmissionId}/individual/personal-code`,
NAME_MISMATCH: `${urlWithTransactionIdAndSubmissionId}/individual/psc-why-this-name`,
INDIVIDUAL_STATEMENT: `${urlWithTransactionIdAndSubmissionId}/individual/psc-statement`,
PSC_VERIFIED: `${urlWithTransactionIdAndSubmissionId}/psc-verified`,
STOP_SCREEN: "/stop/:stopType",
Expand All @@ -53,6 +54,7 @@ export const PrefixedUrls = {
INDIVIDUAL_PSC_LIST: servicePathPrefix + Urls.INDIVIDUAL_PSC_LIST,
NEW_SUBMISSION: servicePathPrefix + Urls.NEW_SUBMISSION,
PERSONAL_CODE: servicePathPrefix + Urls.PERSONAL_CODE,
NAME_MISMATCH: servicePathPrefix + Urls.NAME_MISMATCH,
INDIVIDUAL_STATEMENT: servicePathPrefix + Urls.INDIVIDUAL_STATEMENT,
PSC_VERIFIED: servicePathPrefix + Urls.PSC_VERIFIED,
STOP_SCREEN: servicePathPrefix + Urls.STOP_SCREEN,
Expand Down
7 changes: 7 additions & 0 deletions src/lib/utils/error-manifests/errorManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ const errorManifest = (lang: string = "en", attribute: string = "") => ({
},
incorrect: {}
},
nameMismatch: {
blank: {
summary: (localesService.i18nCh.resolveSingleKey("name_mismatch_error_summary", lang)),
inline: (localesService.i18nCh.resolveSingleKey("name_mismatch_error_inline", lang))
},
incorrect: {}
},
individualStatement: {
blank: {
summary: (localesService.i18nCh.resolveSingleKey("individual_statement_error_summary", lang) + attribute),
Expand Down
4 changes: 4 additions & 0 deletions src/lib/validation/form-validators/pscVerification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export class PscVerificationFormsValidator extends GenericValidator {
return this.validateForm(payload, "personalCode", "personalCode", pscName);
}

validateNameMismatch (payload: any, _lang: string, pscName: string): Promise<Object> {
return this.validateForm(payload, "nameMismatch", "nameMismatch", pscName);
}

validateIndividualStatement (payload: any, _lang: string, pscName: string): Promise<Object> {
return this.validateForm(payload, "pscIndividualStatement", "individualStatement", pscName);
}
Expand Down
3 changes: 2 additions & 1 deletion src/routerDispatch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Do Router dispatch here, i.e. map incoming routes to appropriate router
import { Application, Request, Response, Router } from "express";
import { Urls, servicePathPrefix } from "./constants";
import { CompanyNumberRouter, ConfirmCompanyRouter, HealthCheckRouter, IndividualPscListRouter, IndividualStatementRouter, NewSubmissionRouter, PersonalCodeRouter, PscVerifiedRouter, StartRouter, StopScreenRouter } from "./routers/utils";
import { CompanyNumberRouter, ConfirmCompanyRouter, HealthCheckRouter, IndividualPscListRouter, IndividualStatementRouter, NameMismatchRouter, NewSubmissionRouter, PersonalCodeRouter, PscVerifiedRouter, StartRouter, StopScreenRouter } from "./routers/utils";
import { authenticate } from "./middleware/authentication";
import { fetchVerification } from "./middleware/fetchVerification";
import { fetchCompany } from "./middleware/fetchCompany";
Expand All @@ -21,6 +21,7 @@ const routerDispatch = (app: Application) => {
router.use(Urls.INDIVIDUAL_PSC_LIST, authenticate, fetchCompany, IndividualPscListRouter);
router.use(Urls.NEW_SUBMISSION, authenticate, NewSubmissionRouter);
router.use(Urls.PERSONAL_CODE, authenticate, fetchVerification, PersonalCodeRouter);
router.use(Urls.NAME_MISMATCH, authenticate, fetchVerification, NameMismatchRouter);
router.use(Urls.INDIVIDUAL_STATEMENT, authenticate, fetchVerification, IndividualStatementRouter);
router.use(Urls.PSC_VERIFIED, authenticate, fetchVerification, fetchCompany, PscVerifiedRouter);
router.use(Urls.STOP_SCREEN, authenticate, StopScreenRouter);
Expand Down
115 changes: 115 additions & 0 deletions src/routers/handlers/name-mismatch/nameMismatchHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Request, Response } from "express";
import { PrefixedUrls, Urls } from "../../../constants";
import { logger } from "../../../lib/logger";
import { getLocaleInfo, getLocalesService, selectLang } from "../../../utils/localise";
import { addSearchParams } from "../../../utils/queryParams";
import { getUrlWithTransactionIdAndSubmissionId } from "../../../utils/url";
import { BaseViewData, GenericHandler, ViewModel } from "../generic";
import { formatDateBorn } from "../../utils";
import { NameMismatchReasonEnum, PscVerification, PscVerificationData } from "@companieshouse/api-sdk-node/dist/services/psc-verification-link/types";
import { patchPscVerification } from "../../../services/pscVerificationService";
import { getPscIndividual } from "../../../services/pscService";
import { PscVerificationFormsValidator } from "../../../lib/validation/form-validators/pscVerification";

interface NameMismatchViewData extends BaseViewData {
pscName: string,
monthYearBorn: string,
nameMismatch: string,
nextPageUrl: string,
legalNameChange: string,
preferredName: string,
translationOrDifferentConvention: string,
publicRegisterError: string,
preferNotToSay: string
}

export class NameMismatchHandler extends GenericHandler<NameMismatchViewData> {

private static templatePath = "router_views/name_mismatch/name_mismatch";

public async getViewData (req: Request, res: Response): Promise<NameMismatchViewData> {

const baseViewData = await super.getViewData(req, res);
const verification: PscVerification = res.locals.submission;
const companyNumber = verification.data.companyNumber as string;
const pscIndividual = await getPscIndividual(req, companyNumber, verification.data.pscAppointmentId as string);
const nameMismatch = req.query.nameMismatch as string;
const lang = selectLang(req.query.lang);
const locales = getLocalesService();
// Note enums match the API
const legalNameChange = NameMismatchReasonEnum.LEGAL_NAME_CHANGE;
const preferredName = NameMismatchReasonEnum.PREFERRED_NAME;
const translationOrDifferentConvention = NameMismatchReasonEnum.DIFFERENT_NAMING_CONVENTION;
const publicRegisterError = NameMismatchReasonEnum.PUBLIC_REGISTER_ERROR;
const preferNotToSay = NameMismatchReasonEnum.PREFER_NOT_TO_SAY;

return {
...baseViewData,
...getLocaleInfo(locales, lang),
pscName: pscIndividual.resource?.name!,
monthYearBorn: formatDateBorn(pscIndividual.resource?.dateOfBirth, selectLang(req.query.lang)),
nameMismatch,
legalNameChange,
preferredName,
translationOrDifferentConvention,
publicRegisterError,
preferNotToSay,
currentUrl: resolveUrlTemplate(PrefixedUrls.NAME_MISMATCH),
backURL: resolveUrlTemplate(PrefixedUrls.PERSONAL_CODE),
templateName: Urls.NAME_MISMATCH,
backLinkDataEvent: "name-mismatch-back-link"
};

function resolveUrlTemplate (prefixedUrl: string): string | null {
return addSearchParams(getUrlWithTransactionIdAndSubmissionId(prefixedUrl, req.params.transactionId, req.params.submissionId), { lang });
}
}

public async executeGet (req: Request, res: Response): Promise<ViewModel<NameMismatchViewData>> {
logger.info(`${NameMismatchHandler.name} - ${this.executeGet.name} called for transaction: ${req.params?.transactionId} and submissionId: ${req.params?.submissionId}`);
const viewData = await this.getViewData(req, res);

viewData.nameMismatch = req.query.nameMismatch as string;

return {
templatePath: NameMismatchHandler.templatePath,
viewData
};
}

public async executePost (req: Request, res: Response): Promise<ViewModel<NameMismatchViewData>> {
logger.info(`${NameMismatchHandler.name} - ${this.executePost.name} called for transaction: ${req.params?.transactionId} and submissionId: ${req.params?.submissionId}`);
const viewData = await this.getViewData(req, res);

try {
const lang = selectLang(req.query.lang);
const nameMismatchReason = req.body.nameMismatch;

const queryParams = new URLSearchParams(req.url.split("?")[1]);
const verification: PscVerificationData = {
verificationDetails: {
nameMismatchReason: nameMismatchReason
}
};

queryParams.set("lang", lang);
const nextPageUrl = getUrlWithTransactionIdAndSubmissionId(PrefixedUrls.INDIVIDUAL_STATEMENT, req.params.transactionId, req.params.submissionId);
viewData.nextPageUrl = `${nextPageUrl}?${queryParams}`;
const validator = new PscVerificationFormsValidator(lang);
viewData.errors = await validator.validateNameMismatch(req.body, lang, viewData.pscName);

logger.debug(`${NameMismatchHandler.name} - ${this.executePost.name} - patching name mismatch reason for transaction: ${req.params?.transactionId} and submissionId: ${req.params?.submissionId}`);
await patchPscVerification(req, req.params.transactionId, req.params.submissionId, verification);

} catch (err: any) {
logger.error(`${req.method} error: problem handling name mismatch request: ${err.message}`);
viewData.errors = this.processHandlerException(err);
}

return {
templatePath: NameMismatchHandler.templatePath,
viewData
};
}

}
25 changes: 25 additions & 0 deletions src/routers/nameMismatchRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NextFunction, Request, Response, Router } from "express";
import { handleExceptions } from "../utils/asyncHandler";
import { NameMismatchHandler } from "./handlers/name-mismatch/nameMismatchHandler";

const nameMismatchRouter: Router = Router({ mergeParams: true });

nameMismatchRouter.get("/", handleExceptions(async (req: Request, res: Response) => {
const handler = new NameMismatchHandler();
const { templatePath, viewData } = await handler.executeGet(req, res);
res.render(templatePath, viewData);
}));

nameMismatchRouter.post("/", handleExceptions(async (req: Request, res: Response, _next: NextFunction) => {
const handler = new NameMismatchHandler();
const params = await handler.executePost(req, res);

if (!Object.keys(params.viewData.errors).length) {
res.redirect(params.viewData.nextPageUrl);
} else {
res.render(params.templatePath, params.viewData);
}

}));

export default nameMismatchRouter;
3 changes: 2 additions & 1 deletion src/routers/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import PersonalCodeRouter from "../personalCodeRouter";
import IndividualStatementRouter from "../individualStatementRouter";
import PscVerifiedRouter from "./../pscVerifiedRouter";
import StopScreenRouter from "./../stopScreenRouter";
import NameMismatchRouter from "../nameMismatchRouter";

import { logger } from "../../lib/logger";
export { StartRouter, HealthCheckRouter, CompanyNumberRouter, ConfirmCompanyRouter, IndividualPscListRouter, PersonalCodeRouter, IndividualStatementRouter, NewSubmissionRouter, PscVerifiedRouter, StopScreenRouter };
export { StartRouter, HealthCheckRouter, CompanyNumberRouter, ConfirmCompanyRouter, IndividualPscListRouter, NameMismatchRouter, PersonalCodeRouter, IndividualStatementRouter, NewSubmissionRouter, PscVerifiedRouter, StopScreenRouter };

export function formatDateBorn (dateOfBirth: any, lang: string): string {
try {
Expand Down
111 changes: 111 additions & 0 deletions src/views/router_views/name_mismatch/name_mismatch.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{% extends "layouts/default.njk" %}

{% from "govuk/components/button/macro.njk" import govukButton %}
{% from "govuk/components/radios/macro.njk" import govukRadios %}

{% set title = i18n.name_mismatch_title%}

{% set nameAndDateBirthText =
[pscName," ","(",i18n.name_mismatch_born,monthYearBorn,")"] | join
%}

{% block main_content %}

<form action="" method="post">
{% include "includes/csrf_token.njk" %}
<span class="govuk-caption-xl">
{{ nameAndDateBirthText }}
</span>

<h1 class="govuk-heading-l">
{{ i18n.name_mismatch_title }}
</h1>

<p class="govuk-body">
{{ i18n.name_mismatch_details_par1 }}
</p>

<p class="govuk-body">
{{ i18n.name_mismatch_details_par2 }}
<a href="https://www.gov.uk/file-changes-to-a-company-with-companies-house" class="govuk-link" rel="noreferrer noopener" target="_blank">{{ i18n.name_mismatch_details_par2_link }}</a>
{{- i18n.name_mismatch_details_par2_after_link }}
</p>

{% set radioButtonConfig = {
idPrefix: "nameMismatch",
name: "nameMismatch",
value: nameMismatch,
fieldset: {
legend: {
text: "",
isPageHeading: false,
classes: "govuk-fieldset__legend--l"
}
},
id:"nameMismatch",
items: [
{
value: legalNameChange,
text: i18n.name_mismatch_legally_changed,
attributes:{
"data-event-id": "legally-changed-radio-option"
}
},
{
value: preferredName,
text: i18n.name_mismatch_preferred_name,
attributes:{
"data-event-id": "preferred-name-radio-option"
}
},
{
value: translationOrDifferentConvention,
text: i18n.name_mismatch_translation_or_convention,
attributes:{
"data-event-id": "translation-or-different-naming-convention-radio-option"
}
},
{
value: publicRegisterError,
text: i18n.name_mismatch_register_incorrect,
attributes:{
"data-event-id": "register-incorrect-radio-option"
}
},
{
value: preferNotToSay,
text: i18n.name_mismatch_prefer_not_to_say,
attributes:{
"data-event-id": "prefer-not-to-say-radio-option"
}
}
]
} %}

{% if errors is not undefined and errors is not null and errors | length %}
{{ govukRadios({
idPrefix: radioButtonConfig.idPrefix,
name: radioButtonConfig.name,
value: radioButtonConfig.value,
fieldset: radioButtonConfig.fieldset,
id:radioButtonConfig.id,
items: radioButtonConfig.items,
errorMessage: {
text: i18n.name_mismatch_error_inline
}
}) }}
{% else %}
{{ govukRadios(radioButtonConfig) }}
{% endif %}

{{ govukButton({
attributes: {
id: "submit",
"data-event-id": "name-mismatch-continue-button"
},
text: i18n.global_continue_button
})
}}
</form>

{% endblock %}
Loading

0 comments on commit b848a05

Please sign in to comment.