Skip to content

Commit 79cabc2

Browse files
committed
[DTR-4664] Wire MRAR05 to real subcontractor data from getMonthlyReturnForEdit
1 parent f80933e commit 79cabc2

12 files changed

Lines changed: 521 additions & 115 deletions

app/controllers/amend/WhichSubcontractorsToAddController.scala

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@ package controllers.amend
1818

1919
import controllers.actions._
2020
import forms.amend.WhichSubcontractorsToAddFormProvider
21-
import javax.inject.Inject
2221
import models.Mode
2322
import models.amend.WhichSubcontractorsToAdd
2423
import navigation.Navigator
2524
import pages.amend.WhichSubcontractorsToAddPage
25+
import pages.monthlyreturns.{CisIdPage, DateConfirmPaymentsPage}
26+
import play.api.Logging
2627
import play.api.i18n.{I18nSupport, MessagesApi}
2728
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
2829
import repositories.SessionRepository
30+
import services.SubcontractorService
2931
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController
3032
import views.html.amend.WhichSubcontractorsToAddView
3133

34+
import javax.inject.Inject
3235
import scala.concurrent.{ExecutionContext, Future}
3336

3437
class WhichSubcontractorsToAddController @Inject() (
@@ -39,40 +42,75 @@ class WhichSubcontractorsToAddController @Inject() (
3942
getData: DataRetrievalAction,
4043
requireData: DataRequiredAction,
4144
formProvider: WhichSubcontractorsToAddFormProvider,
45+
subcontractorService: SubcontractorService,
4246
val controllerComponents: MessagesControllerComponents,
4347
view: WhichSubcontractorsToAddView
4448
)(implicit ec: ExecutionContext)
4549
extends FrontendBaseController
46-
with I18nSupport {
47-
48-
private val subcontractors = WhichSubcontractorsToAdd.mockSubcontractors
49-
val form = formProvider(subcontractors)
50-
51-
def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData) { implicit request =>
50+
with I18nSupport
51+
with Logging {
5252

53-
val selectedIds = request.userAnswers
54-
.get(WhichSubcontractorsToAddPage)
55-
.getOrElse(WhichSubcontractorsToAdd.mockPreSelectedIds)
56-
57-
val checkboxItems = WhichSubcontractorsToAdd.checkboxItems(subcontractors, selectedIds)
53+
def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData).async {
54+
implicit request =>
55+
val requiredAnswers = for {
56+
cisId <- request.userAnswers.get(CisIdPage)
57+
taxDate <- request.userAnswers.get(DateConfirmPaymentsPage)
58+
} yield (cisId, taxDate.getMonthValue, taxDate.getYear)
5859

59-
Ok(view(form, mode, checkboxItems))
60+
requiredAnswers
61+
.map { case (cisId, taxMonth, taxYear) =>
62+
subcontractorService
63+
.buildAmendWhichSubcontractorsPage(cisId, taxMonth, taxYear, Some(request.userAnswers))
64+
.map { model =>
65+
val form = formProvider(model.subcontractors)
66+
val selectedIds = request.userAnswers
67+
.get(WhichSubcontractorsToAddPage)
68+
.getOrElse(model.preSelectedIds)
69+
val checkboxItems = WhichSubcontractorsToAdd.checkboxItems(model.subcontractors, selectedIds)
70+
Ok(view(form, mode, checkboxItems))
71+
}
72+
.recover { case ex =>
73+
logger.error(s"[WhichSubcontractorsToAddController] Failed to load subcontractors: ${ex.getMessage}", ex)
74+
Redirect(controllers.routes.SystemErrorController.onPageLoad())
75+
}
76+
}
77+
.getOrElse(Future.successful(Redirect(controllers.routes.JourneyRecoveryController.onPageLoad())))
6078
}
6179

6280
def onSubmit(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData).async {
6381
implicit request =>
64-
form
65-
.bindFromRequest()
66-
.fold(
67-
formWithErrors =>
68-
Future.successful(
69-
BadRequest(view(formWithErrors, mode, WhichSubcontractorsToAdd.checkboxItems(subcontractors)))
70-
),
71-
value =>
72-
for {
73-
updatedAnswers <- Future.fromTry(request.userAnswers.set(WhichSubcontractorsToAddPage, value))
74-
_ <- sessionRepository.set(updatedAnswers)
75-
} yield Redirect(navigator.nextPage(WhichSubcontractorsToAddPage, mode, updatedAnswers))
76-
)
82+
val requiredAnswers = for {
83+
cisId <- request.userAnswers.get(CisIdPage)
84+
taxDate <- request.userAnswers.get(DateConfirmPaymentsPage)
85+
} yield (cisId, taxDate.getMonthValue, taxDate.getYear)
86+
87+
requiredAnswers
88+
.map { case (cisId, taxMonth, taxYear) =>
89+
subcontractorService
90+
.buildAmendWhichSubcontractorsPage(cisId, taxMonth, taxYear, Some(request.userAnswers))
91+
.flatMap { model =>
92+
val form = formProvider(model.subcontractors)
93+
form
94+
.bindFromRequest()
95+
.fold(
96+
formWithErrors =>
97+
Future.successful(
98+
BadRequest(
99+
view(formWithErrors, mode, WhichSubcontractorsToAdd.checkboxItems(model.subcontractors))
100+
)
101+
),
102+
value =>
103+
for {
104+
updatedAnswers <- Future.fromTry(request.userAnswers.set(WhichSubcontractorsToAddPage, value))
105+
_ <- sessionRepository.set(updatedAnswers)
106+
} yield Redirect(navigator.nextPage(WhichSubcontractorsToAddPage, mode, updatedAnswers))
107+
)
108+
}
109+
.recover { case ex =>
110+
logger.error(s"[WhichSubcontractorsToAddController] Submit failed: ${ex.getMessage}", ex)
111+
Redirect(controllers.routes.SystemErrorController.onPageLoad())
112+
}
113+
}
114+
.getOrElse(Future.successful(Redirect(controllers.routes.JourneyRecoveryController.onPageLoad())))
77115
}
78116
}

app/models/amend/WhichSubcontractorsToAdd.scala

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,12 @@ object Subcontractor {
2727
implicit val format: OFormat[Subcontractor] = Json.format[Subcontractor]
2828
}
2929

30-
object WhichSubcontractorsToAdd {
31-
32-
// TODO: Replace with real data from backend
33-
val mockSubcontractors: Seq[Subcontractor] = Seq(
34-
Subcontractor("1", "Alice, A"),
35-
Subcontractor("2", "Apex Construction Solutions"),
36-
Subcontractor("3", "Bob, B"),
37-
Subcontractor("4", "Bloggs, Joe"),
38-
Subcontractor("5", "Bloggs, Joseph"),
39-
Subcontractor("6", "Build Right Construction"),
40-
Subcontractor("7", "Charles, C"),
41-
Subcontractor("8", "Dave, D"),
42-
Subcontractor("9", "Draft Services Ltd"),
43-
Subcontractor("10", "Elise, E"),
44-
Subcontractor("11", "Frank, F"),
45-
Subcontractor("12", "Northern Trades Ltd"),
46-
Subcontractor("13", "Pro-Build Subcontractors"),
47-
Subcontractor("14", "Tynewear Ltd"),
48-
Subcontractor("15", "SubbyCo Ltd")
49-
)
30+
case class WhichSubcontractorsToAddPageModel(
31+
subcontractors: Seq[Subcontractor],
32+
preSelectedIds: Set[String]
33+
)
5034

51-
val mockPreSelectedIds: Set[String] = Set("2", "4", "6", "12", "15")
35+
object WhichSubcontractorsToAdd {
5236

5337
def checkboxItems(subcontractors: Seq[Subcontractor], preSelectedIds: Set[String] = Set.empty): Seq[CheckboxItem] =
5438
subcontractors.sortBy(_.name).zipWithIndex.map { case (sub, index) =>

app/services/SubcontractorService.scala

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
package services
1818

1919
import models.UserAnswers
20-
import models.monthlyreturns.SelectSubcontractorsPageModel
20+
import models.amend.{Subcontractor as AmendSubcontractor, WhichSubcontractorsToAddPageModel}
21+
import models.monthlyreturns.{SelectSubcontractorsPageModel, Subcontractor}
22+
import models.submission.SubcontractorType
2123
import pages.monthlyreturns.SelectedSubcontractorPage
22-
import services.SubcontractorService.{TAX_YEAR_START_DAY, TAX_YEAR_START_MONTH}
24+
import services.SubcontractorService.{TAX_YEAR_START_DAY, TAX_YEAR_START_MONTH, resolveSubcontractorName}
2325
import uk.gov.hmrc.http.HeaderCarrier
26+
import utils.Normalise.nonBlank
2427
import viewmodels.SelectSubcontractorsViewModel
2528

2629
import java.time.{LocalDate, LocalDateTime}
@@ -148,9 +151,68 @@ class SubcontractorService @Inject() (monthlyReturnService: MonthlyReturnService
148151
)
149152
}
150153

154+
def buildAmendWhichSubcontractorsPage(
155+
cisId: String,
156+
taxMonth: Int,
157+
taxYear: Int,
158+
userAnswers: Option[UserAnswers] = None
159+
)(implicit hc: HeaderCarrier): Future[WhichSubcontractorsToAddPageModel] =
160+
monthlyReturnService.retrieveMonthlyReturnForEditDetails(cisId, taxMonth, taxYear).map { data =>
161+
162+
val previouslyIncludedResourceRefs: Set[Long] =
163+
data.monthlyReturnItems.flatMap(_.itemResourceReference).toSet
164+
165+
val subcontractors: Seq[AmendSubcontractor] =
166+
data.subcontractors.map { sub =>
167+
AmendSubcontractor(
168+
id = sub.subcontractorId.toString,
169+
name = resolveSubcontractorName(sub)
170+
)
171+
}
172+
173+
val preSelectedIds: Set[String] =
174+
userAnswers.flatMap(_.get(pages.amend.WhichSubcontractorsToAddPage)) match {
175+
case Some(ids) => ids
176+
case None =>
177+
data.subcontractors
178+
.filter(sub => sub.subbieResourceRef.exists(previouslyIncludedResourceRefs.contains))
179+
.map(_.subcontractorId.toString)
180+
.toSet
181+
}
182+
183+
WhichSubcontractorsToAddPageModel(
184+
subcontractors = subcontractors,
185+
preSelectedIds = preSelectedIds
186+
)
187+
}
188+
151189
}
152190

153191
object SubcontractorService {
154192
private val TAX_YEAR_START_MONTH = 4
155193
private val TAX_YEAR_START_DAY = 6
194+
195+
import scala.util.Try
196+
197+
private val NoNameProvided = "No name provided"
198+
199+
def resolveSubcontractorName(sub: Subcontractor): String =
200+
sub.subcontractorType.flatMap(t => Try(SubcontractorType.fromString(t)).toOption) match {
201+
case Some(SubcontractorType.SoleTrader) =>
202+
(nonBlank(sub.firstName), nonBlank(sub.surname)) match {
203+
case (Some(first), Some(last)) => s"$first $last"
204+
case (_, Some(last)) => last
205+
case _ => nonBlank(sub.tradingName).getOrElse(NoNameProvided)
206+
}
207+
case Some(SubcontractorType.Company) =>
208+
nonBlank(sub.tradingName).getOrElse(NoNameProvided)
209+
case Some(SubcontractorType.Trust) =>
210+
nonBlank(sub.tradingName).getOrElse(NoNameProvided)
211+
case Some(SubcontractorType.Partnership) =>
212+
nonBlank(sub.partnershipTradingName)
213+
.orElse(nonBlank(sub.tradingName))
214+
.getOrElse(NoNameProvided)
215+
case _ =>
216+
NoNameProvided
217+
}
156218
}

app/viewmodels/checkAnswers/amend/WhichSubcontractorsToAddSummary.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package viewmodels.checkAnswers.amend
1818

1919
import controllers.amend.routes
2020
import models.{CheckMode, UserAnswers}
21-
import models.amend.WhichSubcontractorsToAdd
2221
import pages.amend.WhichSubcontractorsToAddPage
2322
import play.api.i18n.Messages
2423
import play.twirl.api.HtmlFormat
@@ -29,14 +28,12 @@ import viewmodels.implicits._
2928

3029
object WhichSubcontractorsToAddSummary {
3130

32-
def row(answers: UserAnswers)(implicit messages: Messages): Option[SummaryListRow] = {
33-
val subcontractorMap = WhichSubcontractorsToAdd.mockSubcontractors.map(s => s.id -> s.name).toMap
34-
31+
def row(answers: UserAnswers)(implicit messages: Messages): Option[SummaryListRow] =
3532
answers.get(WhichSubcontractorsToAddPage).map { selectedIds =>
3633
val value = ValueViewModel(
3734
HtmlContent(
3835
selectedIds
39-
.map(id => HtmlFormat.escape(subcontractorMap.getOrElse(id, id)).toString)
36+
.map(id => HtmlFormat.escape(id).toString)
4037
.mkString(",<br>")
4138
)
4239
)
@@ -51,5 +48,4 @@ object WhichSubcontractorsToAddSummary {
5148
)
5249
)
5350
}
54-
}
5551
}

test/controllers/amend/ConfirmCancelAmendmentYesNoControllerSpec.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
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+
117
package controllers.amend
218

319
import base.SpecBase

0 commit comments

Comments
 (0)