Skip to content

Commit d30f4f2

Browse files
committed
DDCE-7698 Update declare the employee to exclude
1 parent df5361c commit d30f4f2

File tree

10 files changed

+507
-92
lines changed

10 files changed

+507
-92
lines changed

app/controllers/ExclusionListController.scala

Lines changed: 98 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ package controllers
1919
import config.PbikAppConfig
2020
import connectors.PbikConnector
2121
import controllers.actions.{AuthAction, NoSessionCheckAction}
22-
import models._
22+
import models.*
2323
import models.auth.AuthenticatedRequest
2424
import models.form.MandatoryRadioButton
2525
import models.v1.IabdType.IabdType
26-
import models.v1.exclusion._
26+
import models.v1.exclusion.*
2727
import models.v1.trace.{TracePeopleByNinoRequest, TracePeopleByPersonalDetailsRequest, TracePersonResponse}
2828
import play.api.Logging
2929
import play.api.data.Form
3030
import play.api.i18n.{I18nSupport, Messages, MessagesApi}
31-
import play.api.mvc._
31+
import play.api.mvc.*
3232
import services.{BikListService, ExclusionService, SessionService}
3333
import uk.gov.hmrc.domain.EmpRef
3434
import uk.gov.hmrc.http.HeaderCarrier
@@ -37,10 +37,11 @@ import uk.gov.hmrc.play.bootstrap.controller.WithUnsafeDefaultFormBinding
3737
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController
3838
import uk.gov.hmrc.play.http.HeaderCarrierConverter
3939
import utils.Exceptions.InvalidBikTypeException
40-
import utils._
40+
import utils.*
4141
import views.html.ErrorPage
42-
import views.html.exclusion._
42+
import views.html.exclusion.*
4343

44+
import java.lang.ProcessBuilder
4445
import javax.inject.{Inject, Singleton}
4546
import scala.concurrent.{ExecutionContext, Future}
4647

@@ -65,6 +66,8 @@ class ExclusionListController @Inject() (
6566
ninoExclusionSearchFormView: NinoExclusionSearchForm,
6667
noNinoExclusionSearchFormView: NoNinoExclusionSearchForm,
6768
searchResultsView: SearchResults,
69+
searchResultsMPBIKView: SearchResultsMPBIK,
70+
declareEmployeeMPBIKView: DeclareEmployeeMPBIK,
6871
whatNextExclusionView: WhatNextExclusion,
6972
whatNextExclusionMpbikView: WhatNextExclusionMpbik,
7073
removalConfirmationView: RemovalConfirmation,
@@ -434,7 +437,8 @@ class ExclusionListController @Inject() (
434437
(authenticate andThen noSessionCheck).async { implicit request =>
435438
if (exclusionsAllowed) {
436439
val futureResult = formType match {
437-
case ControllersReferenceDataCodes.FORM_TYPE_NINO => searchResultsByNino(isCurrentTaxYear, iabdType, formType)
440+
case ControllersReferenceDataCodes.FORM_TYPE_NINO =>
441+
searchResultsByNino(isCurrentTaxYear, iabdType, formType)
438442
case ControllersReferenceDataCodes.FORM_TYPE_NONINO =>
439443
searchResultsByPersonalDetails(isCurrentTaxYear, iabdType, formType)
440444
}
@@ -479,10 +483,11 @@ class ExclusionListController @Inject() (
479483
iabdType: IabdType,
480484
currentExclusions: List[TracePersonResponse]
481485
)(implicit request: AuthenticatedRequest[?]): Result = {
486+
val uniqueMatch: Int = 1
482487
val uniqueListOfMatches: List[TracePersonResponse] =
483488
exclusionService.searchResultsRemoveAlreadyExcluded(currentExclusions, listOfMatches)
484489
uniqueListOfMatches.size match {
485-
case 0 =>
490+
case 0 =>
486491
logger.warn("[ExclusionListController][searchResultsHandleValidResult] List of un-excluded matches is empty")
487492
val message = Messages("ExclusionSearch.Fail.Exists.P")
488493
if (listOfMatches.nonEmpty) {
@@ -512,17 +517,37 @@ class ExclusionListController @Inject() (
512517
)
513518
)
514519
}
515-
case _ =>
516-
Ok(
517-
searchResultsView(
518-
controllersReferenceData.yearRange,
519-
isCurrentTaxYear,
520-
iabdType,
521-
uniqueListOfMatches,
522-
formMappings.individualSelectionForm,
523-
formType
520+
case matches =>
521+
if (mpbikToggle) {
522+
if (matches == uniqueMatch) { // testing with n < 1
523+
Redirect(
524+
routes.ExclusionListController
525+
.declareEmployeeToExclude(isCurrentTaxYear, iabdType)
526+
)
527+
} else {
528+
Ok(
529+
searchResultsMPBIKView(
530+
controllersReferenceData.yearRange,
531+
isCurrentTaxYear,
532+
iabdType,
533+
uniqueListOfMatches,
534+
formMappings.individualSelectionForm,
535+
formType
536+
)
537+
)
538+
}
539+
} else {
540+
Ok(
541+
searchResultsView(
542+
controllersReferenceData.yearRange,
543+
isCurrentTaxYear,
544+
iabdType,
545+
uniqueListOfMatches,
546+
formMappings.individualSelectionForm,
547+
formType
548+
)
524549
)
525-
)
550+
}
526551
}
527552
}
528553

@@ -534,29 +559,53 @@ class ExclusionListController @Inject() (
534559
.bindFromRequest()
535560
.fold(
536561
formWithErrors =>
537-
Future.successful(
538-
BadRequest(
539-
searchResultsView(
540-
controllersReferenceData.yearRange,
541-
year,
542-
iabdType,
543-
session.get.listOfMatches.get.pbikExclusionList,
544-
formWithErrors,
545-
formType
562+
if (mpbikToggle) {
563+
Future.successful(
564+
BadRequest(
565+
searchResultsMPBIKView(
566+
controllersReferenceData.yearRange,
567+
year,
568+
iabdType,
569+
session.get.listOfMatches.get.pbikExclusionList,
570+
formWithErrors,
571+
formType
572+
)
546573
)
547574
)
548-
),
575+
} else {
576+
Future.successful(
577+
BadRequest(
578+
searchResultsView(
579+
controllersReferenceData.yearRange,
580+
year,
581+
iabdType,
582+
session.get.listOfMatches.get.pbikExclusionList,
583+
formWithErrors,
584+
formType
585+
)
586+
)
587+
)
588+
},
549589
values => {
550590
val individualsDetails: Option[TracePersonResponse] =
551591
session.get.listOfMatches.get.pbikExclusionList
552592
.find(person => person.nationalInsuranceNumber == values.nino)
553-
validateRequest(year, iabdType).flatMap { _ =>
554-
commitExclusion(
555-
year,
556-
iabdType,
557-
session.get.listOfMatches.get.updatedEmployerOptimisticLock,
558-
individualsDetails
593+
if (mpbikToggle && individualsDetails.isDefined) {
594+
Future.successful(
595+
Redirect(
596+
routes.ExclusionListController
597+
.declareEmployeeToExclude(year, iabdType)
598+
)
559599
)
600+
} else {
601+
validateRequest(year, iabdType).flatMap { _ =>
602+
commitExclusion(
603+
year,
604+
iabdType,
605+
session.get.listOfMatches.get.updatedEmployerOptimisticLock,
606+
individualsDetails
607+
)
608+
}
560609
}
561610
}
562611
)
@@ -576,6 +625,22 @@ class ExclusionListController @Inject() (
576625
}
577626
}
578627

628+
def declareEmployeeToExclude(year: String, iabdType: IabdType): Action[AnyContent] =
629+
(authenticate andThen noSessionCheck).async { implicit request =>
630+
val resultFuture = for {
631+
_ <- validateRequest(year, iabdType)
632+
session <- sessionService.fetchPbikSession()
633+
} yield Ok(
634+
declareEmployeeMPBIKView(
635+
controllersReferenceData.yearRange,
636+
year,
637+
iabdType,
638+
session.get.listOfMatches.get.pbikExclusionList.head
639+
)
640+
)
641+
controllersReferenceData.responseErrorHandler(resultFuture)
642+
}
643+
579644
def commitExclusion(
580645
year: String,
581646
iabdType: IabdType,
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
@*
2+
* Copyright 2026 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*@
16+
17+
@import utils.TaxDateUtils
18+
@import views.templatemodels.PageTitle
19+
20+
@this(
21+
govukLayoutWrapper: GovukLayoutWrapper,
22+
govukWarningText: GovukWarningText,
23+
formWithCSRF: FormWithCSRF,
24+
taxDateUtils: TaxDateUtils
25+
)
26+
27+
@(
28+
taxYearRange: TaxYearRange,
29+
year: String,
30+
iabdType: IabdType,
31+
employee: TracePersonResponse
32+
)(implicit request: AuthenticatedRequest[?], messages: Messages)
33+
34+
@expenseOrBenefit = @{messages(s"BenefitInKind.label.${iabdType.id}")}
35+
@govukLayoutWrapper(PageTitle(messages("ExclusionSearchMPBIK.title.single"))) {
36+
37+
<h1 id="title" class="govuk-heading-xl">
38+
<span class="govuk-caption-l">@messages("ExclusionSearchMPBIK.title.span.taxYear.cy0", s"${taxYearRange.cy}", s"${taxYearRange.cyplus1}")</span>
39+
@messages("ExclusionSearchMPBIK.title.single")
40+
</h1>
41+
42+
<h2 class="govuk-heading-m">@expenseOrBenefit</h2>
43+
44+
<p class="govuk-body">@messages("ExclusionImportantMPBIK.Reminder", expenseOrBenefit, taxDateUtils.getDisplayTodayDate())</p>
45+
46+
<div class="govuk-inset-text">
47+
@messages("ExclusionSearchMPBIK.inset.text")
48+
</div>
49+
50+
<dl class="govuk-summary-list">
51+
<div class="govuk-summary-list__row">
52+
<dt class="govuk-summary-list__key">@messages("ExclusionSearchMPBIK.field.employee.name")</dt>
53+
<dd class="govuk-summary-list__value">@employee.fullName</dd>
54+
</div>
55+
<div class="govuk-summary-list__row">
56+
<dt class="govuk-summary-list__key">@messages("ExclusionSearchMPBIK.field.employee.ni")</dt>
57+
<dd class="govuk-summary-list__value">@employee.nationalInsuranceNumber</dd>
58+
</div>
59+
<div class="govuk-summary-list__row">
60+
<dt class="govuk-summary-list__key">@messages("ExclusionSearchMPBIK.field.employee.payrollId")</dt>
61+
<dd class="govuk-summary-list__value">@employee.getWorksPayrollNumber</dd>
62+
</div>
63+
</dl>
64+
65+
@govukWarningText(WarningText(
66+
iconFallbackText = Some(messages("site.warning")),
67+
content = Text(messages("ExclusionSearchMPBIK.warning"))
68+
))
69+
70+
<p id="i-confirm-that" class="govuk-body">
71+
@messages("ExclusionSearchMPBIK.confirmation." + request.userType + ".p")
72+
</p>
73+
<ul class="govuk-list govuk-list--bullet">
74+
<li>@messages("ExclusionSearchMPBIK.confirmation." + request.userType + ".bullet1")</li>
75+
<li>@messages("ExclusionSearchMPBIK.confirmation." + request.userType + ".bullet2")</li>
76+
</ul>
77+
78+
<p id="i-understand-that" class="govuk-body">
79+
@messages("ExclusionSearchMPBIK.understanding." + request.userType + ".p")
80+
</p>
81+
<ul class="govuk-list govuk-list--bullet">
82+
<li>@messages("ExclusionSearchMPBIK.understanding." + request.userType + ".bullet1")</li>
83+
</ul>
84+
85+
<div class="govuk-form-group">
86+
<a href="@routes.ExclusionListController.updateExclusions(year, iabdType)" class="govuk-button" role="button" id="button-confirm">@messages("Service.confirmAndContinue")</a>
87+
</div>
88+
89+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
@*
2+
* Copyright 2026 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*@
16+
17+
@import utils.TaxDateUtils
18+
@import views.templatemodels.PageTitle
19+
20+
@this(
21+
govukLayoutWrapper: GovukLayoutWrapper,
22+
govukWarningText: GovukWarningText,
23+
formWithCSRF: FormWithCSRF,
24+
taxDateUtils: TaxDateUtils
25+
)
26+
27+
@(
28+
taxYearRange: TaxYearRange,
29+
year: String,
30+
iabdType: IabdType,
31+
listOfMatches: List[TracePersonResponse],
32+
listOfMatchesForm: Form[ExclusionNino],
33+
formType: String
34+
)(implicit request: Request[?], messages: Messages)
35+
36+
@govukLayoutWrapper(PageTitle(messages("ExclusionSearchMPBIK.title.multiple"), listOfMatchesForm.hasErrors)) {
37+
38+
@if(listOfMatchesForm("individualNino").hasErrors) {
39+
<div class="govuk-error-summary" role="group" aria-labelledby="error-summary-heading-1" tabindex="-1">
40+
41+
<h2 class="govuk-error-summary__title" id="error-summary-heading-1">
42+
@messages("Service.errorSummary.heading")
43+
</h2>
44+
<div class="govuk-error-summary__body">
45+
<ul class="govuk-list govuk-error-summary__list">
46+
@if(listOfMatchesForm.error("individualNino").get.message.equals("error.required")){
47+
<li><a href="#employee-radio-0">@messages("error.exclusion.multi.selection")</a></li>
48+
}
49+
</ul>
50+
</div>
51+
</div>
52+
}
53+
54+
<h1 id="title" class="govuk-heading-xl">
55+
<span class="govuk-caption-l">@messages("ExclusionSearchMPBIK.form.tax.year", s"${taxYearRange.cy}", s"${taxYearRange.cyplus1}")</span>
56+
@messages("ExclusionSearchMPBIK.title.multiple")
57+
</h1>
58+
59+
@formWithCSRF(action = routes.ExclusionListController.updateMultipleExclusions(year, iabdType, formType)) {
60+
<div class="govuk-form-group @if(listOfMatchesForm("individualNino").hasErrors){govuk-form-group--error}">
61+
<fieldset class="govuk-fieldset" @if(listOfMatchesForm("individualNino").hasErrors){aria-describedby="selection-error"}>
62+
@if(listOfMatchesForm("individualNino").hasErrors) {
63+
<span id="selection-error" class="govuk-error-message">
64+
<span class="govuk-visually-hidden">@messages("Service.error"):</span>
65+
@messages("error.exclusion.multi.selection")
66+
</span>
67+
}
68+
<div class="govuk-radios">
69+
@for(index <- 0 until listOfMatches.length) {
70+
<div class="govuk-radios__item">
71+
<input class="govuk-radios__input" id="employee-radio-@index" type="radio" aria-describedby="employee-hint-@index" name="individualNino" value='@listOfMatches(index).nationalInsuranceNumber'>
72+
<label class="govuk-label govuk-radios__label govuk-label--s" for="employee-radio-@index">
73+
@listOfMatches(index).fullName
74+
</label>
75+
<span id="employee-hint-@index" class="govuk-hint govuk-radios__hint">
76+
@messages("Service.field.nino"): @listOfMatches(index).nationalInsuranceNumber <br>
77+
@messages("Service.field.worksnumber"): @listOfMatches(index).getWorksPayrollNumber
78+
</span>
79+
</div>
80+
}
81+
</div>
82+
</fieldset>
83+
</div>
84+
85+
<input type="submit" class="govuk-button" role="button" id="button-continue" value="@messages("Service.continueMPBIK")">
86+
}
87+
}

conf/app.routes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ POST /:year/:iabdType/:formType/search-for-employee controllers.Exc
6363
GET /:year/:iabdType/:formType/exclude-employee-results controllers.ExclusionListController.showResults(year: String, iabdType: models.v1.IabdType.IabdType, formType: String)
6464
POST /:year/:iabdType/:formType/exclude-employee-results controllers.ExclusionListController.updateMultipleExclusions(year: String, iabdType: models.v1.IabdType.IabdType, formType: String)
6565
GET /:year/:iabdType/exclude-employee-results-single controllers.ExclusionListController.updateExclusions(year: String, iabdType: models.v1.IabdType.IabdType)
66+
GET /:year/:iabdType/declare-employee-exclusion controllers.ExclusionListController.declareEmployeeToExclude(year: String, iabdType: models.v1.IabdType.IabdType)
6667

6768
GET /:year/:iabdType/exclusion-complete controllers.ExclusionListController.showExclusionConfirmation(year: String, iabdType: models.v1.IabdType.IabdType)
6869

0 commit comments

Comments
 (0)