diff --git a/app/connectors/ConstructionIndustrySchemeConnector.scala b/app/connectors/ConstructionIndustrySchemeConnector.scala index dfa16429..e963f659 100644 --- a/app/connectors/ConstructionIndustrySchemeConnector.scala +++ b/app/connectors/ConstructionIndustrySchemeConnector.scala @@ -89,15 +89,13 @@ class ConstructionIndustrySchemeConnector @Inject() (config: ServicesConfig, htt .withBody(Json.toJson(payload)) .execute[NilMonthlyReturnResponse] - def updateNilMonthlyReturn( - payload: NilMonthlyReturnRequest - )(implicit hc: HeaderCarrier): Future[Unit] = + def updateMonthlyReturn(payload: UpdateMonthlyReturnRequest)(implicit hc: HeaderCarrier): Future[Unit] = http - .post(url"$cisBaseUrl/monthly-returns/nil/update") + .post(url"$cisBaseUrl/monthly-returns/update") .withBody(Json.toJson(payload)) .execute[HttpResponse] .flatMap { response => - if (response.status / 100 == 2) { + if (response.status == 204) { Future.unit } else { Future.failed(UpstreamErrorResponse(response.body, response.status, response.status)) diff --git a/app/controllers/monthlyreturns/CheckYourAnswersController.scala b/app/controllers/monthlyreturns/CheckYourAnswersController.scala index 991502fc..d488fc1d 100644 --- a/app/controllers/monthlyreturns/CheckYourAnswersController.scala +++ b/app/controllers/monthlyreturns/CheckYourAnswersController.scala @@ -17,7 +17,8 @@ package controllers.monthlyreturns import controllers.actions.* -import pages.monthlyreturns.NilReturnStatusPage +import models.monthlyreturns.UpdateMonthlyReturnRequest +import pages.monthlyreturns.ReturnTypePage import play.api.i18n.{I18nSupport, MessagesApi} import play.api.mvc.{Action, AnyContent, MessagesControllerComponents} import services.MonthlyReturnService @@ -68,32 +69,38 @@ class CheckYourAnswersController @Inject() ( } def onSubmit(): Action[AnyContent] = (identify andThen getData andThen requireData).async { implicit request => - request.userAnswers.get(NilReturnStatusPage) match { + request.userAnswers.get(ReturnTypePage) match { case None => logger.warn( - "[CheckYourAnswersController] C6 submit without FormP record (missing NilReturnStatusPage); redirecting to journey recovery" + "[CheckYourAnswersController] C6 submit without FormP record (missing ReturnTypePage); redirecting to journey recovery" ) Future.successful(Redirect(controllers.routes.JourneyRecoveryController.onPageLoad())) - case Some(_) => + case Some(returnType) => implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequestAndSession(request, request.session) - monthlyReturnService - .updateNilMonthlyReturn(request.userAnswers) - .map { _ => - logger.info( - "[CheckYourAnswersController] Updated FormP monthly nil return confirmation/nil flags; redirecting to submission" - ) - Redirect(controllers.monthlyreturns.routes.SubmissionSendingController.onPageLoad()) - } - .recover { case ex => - logger.error( - s"[CheckYourAnswersController] Failed to update FormP monthly nil return: ${ex.getMessage}", - ex - ) - Redirect(controllers.routes.SystemErrorController.onPageLoad()) - } + val updateRequest = UpdateMonthlyReturnRequest.fromUserAnswers(request.userAnswers) + + updateRequest match { + case Left(error) => + logger.error(s"[CheckYourAnswersController] Failed to build update request: $error") + Future.successful(InternalServerError) + + case Right(req) => + monthlyReturnService + .updateMonthlyReturn(req) + .map { _ => + logger.info( + s"[CheckYourAnswersController] Successfully updated monthly return ($returnType), redirecting to submission" + ) + Redirect(controllers.monthlyreturns.routes.SubmissionSendingController.onPageLoad()) + } + .recover { case t => + logger.error("[CheckYourAnswersController] Failed to update monthly return ($returnType)", t) + Redirect(controllers.routes.SystemErrorController.onPageLoad()) + } + } } } } diff --git a/app/models/monthlyreturns/UpdateMonthlyReturnRequest.scala b/app/models/monthlyreturns/UpdateMonthlyReturnRequest.scala new file mode 100644 index 00000000..58ffb07b --- /dev/null +++ b/app/models/monthlyreturns/UpdateMonthlyReturnRequest.scala @@ -0,0 +1,113 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package models.monthlyreturns + +import models.ReturnType.{MonthlyNilReturn, MonthlyStandardReturn} +import models.{ReturnType, UserAnswers} +import pages.monthlyreturns.* +import play.api.libs.json.{Json, OFormat} + +import java.time.LocalDate + +case class UpdateMonthlyReturnRequest( + instanceId: String, + taxYear: Int, + taxMonth: Int, + amendment: String, + decEmpStatusConsidered: Option[String] = None, + decAllSubsVerified: Option[String] = None, + decNoMoreSubPayments: Option[String] = None, + decNilReturnNoPayments: Option[String] = None, + decInformationCorrect: Option[String] = None, + nilReturnIndicator: String, + status: String, + version: Option[Long] = None +) + +object UpdateMonthlyReturnRequest { + + implicit val format: OFormat[UpdateMonthlyReturnRequest] = Json.format[UpdateMonthlyReturnRequest] + + private def toYN(value: Boolean): String = if (value) "Y" else "N" + + private def dateFor(returnType: ReturnType, ua: UserAnswers): Either[String, LocalDate] = + returnType match { + case MonthlyNilReturn => ua.get(DateConfirmNilPaymentsPage).toRight("Missing date for nil return") + case MonthlyStandardReturn => ua.get(DateConfirmPaymentsPage).toRight("Missing date for standard return") + } + + private def inactivityY(returnType: ReturnType, ua: UserAnswers): Option[String] = + returnType match { + case MonthlyStandardReturn => + ua.get(SubmitInactivityRequestPage) match { + case Some(true) => Some("Y") + case Some(false) => None + case None => None + } + + case MonthlyNilReturn => + ua.get(InactivityRequestPage).collect { case InactivityRequest.Option1 => "Y" } + } + + private def nilIndicator(returnType: ReturnType): String = + returnType match { + case MonthlyNilReturn => "Y" + case MonthlyStandardReturn => "N" + } + + def fromUserAnswers(ua: UserAnswers): Either[String, UpdateMonthlyReturnRequest] = + for { + returnType <- ua.get(ReturnTypePage).toRight("Missing return type") + instanceId <- ua.get(CisIdPage).toRight("Missing instanceId") + date <- dateFor(returnType, ua) + + decInformationCorrect = returnType match { + case MonthlyNilReturn => + ua.get(DeclarationPage).flatMap { declaration => + if (declaration.nonEmpty) Some("Y") else None + } + + case MonthlyStandardReturn => + ua.get(PaymentDetailsConfirmationPage).map(toYN) + } + } yield { + val base = UpdateMonthlyReturnRequest( + instanceId = instanceId, + taxYear = date.getYear, + taxMonth = date.getMonthValue, + amendment = "N", + decInformationCorrect = decInformationCorrect, + nilReturnIndicator = nilIndicator(returnType), + status = "STARTED", + version = None + ) + + returnType match { + case MonthlyStandardReturn => + base.copy( + decEmpStatusConsidered = ua.get(EmploymentStatusDeclarationPage).map(toYN), + decAllSubsVerified = ua.get(VerifiedStatusDeclarationPage).map(toYN), + decNoMoreSubPayments = inactivityY(returnType, ua) + ) + + case MonthlyNilReturn => + base.copy( + decNilReturnNoPayments = inactivityY(returnType, ua) + ) + } + } +} diff --git a/app/services/MonthlyReturnService.scala b/app/services/MonthlyReturnService.scala index fe90b74d..49ab5622 100644 --- a/app/services/MonthlyReturnService.scala +++ b/app/services/MonthlyReturnService.scala @@ -114,27 +114,8 @@ class MonthlyReturnService @Inject() ( } yield saved } - def updateNilMonthlyReturn(userAnswers: UserAnswers)(implicit hc: HeaderCarrier): Future[Unit] = { - logger.info("[MonthlyReturnService] Updating FormP monthly nil return confirmation/nil flags at C6") - - for { - cisId <- getCisId(userAnswers) - year <- getTaxYear(userAnswers) - month <- getTaxMonth(userAnswers) - infoCorrect <- getInfoCorrectOrDefault(userAnswers) - nilNoPayments <- getNilNoPaymentsOrDefault(userAnswers) - _ <- { - val payload = NilMonthlyReturnRequest( - instanceId = cisId, - taxYear = year, - taxMonth = month, - decInformationCorrect = infoCorrect, - decNilReturnNoPayments = nilNoPayments - ) - cisConnector.updateNilMonthlyReturn(payload) - } - } yield () - } + def updateMonthlyReturn(request: UpdateMonthlyReturnRequest)(implicit hc: HeaderCarrier): Future[Unit] = + cisConnector.updateMonthlyReturn(request) def createMonthlyReturn(request: MonthlyReturnRequest)(implicit hc: HeaderCarrier): Future[Unit] = cisConnector.createMonthlyReturn(request) diff --git a/it/test/connectors/ConstructionIndustrySchemeConnectorSpec.scala b/it/test/connectors/ConstructionIndustrySchemeConnectorSpec.scala index f146dc60..75d8523f 100644 --- a/it/test/connectors/ConstructionIndustrySchemeConnectorSpec.scala +++ b/it/test/connectors/ConstructionIndustrySchemeConnectorSpec.scala @@ -19,7 +19,7 @@ package connectors import com.github.tomakehurst.wiremock.client.WireMock.* import itutil.ApplicationWithWiremock import models.requests.SendSuccessEmailRequest -import models.monthlyreturns.{DeleteMonthlyReturnItemRequest, MonthlyReturnRequest} +import models.monthlyreturns.* import models.submission.{ChrisSubmissionRequest, CreateSubmissionRequest, UpdateSubmissionRequest} import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures} import org.scalatest.matchers.must.Matchers @@ -493,6 +493,54 @@ class ConstructionIndustrySchemeConnectorSpec extends AnyWordSpec } } + "updateMonthlyReturn(payload)" should { + + "POST to /cis/monthly-returns/update and return Unit on 204" in { + val req = UpdateMonthlyReturnRequest( + instanceId = cisId, + taxYear = 2025, + taxMonth = 1, + amendment = "N", + decNilReturnNoPayments = Some("Y"), + decInformationCorrect = Some("Y"), + nilReturnIndicator = "Y", + status = "STARTED" + ) + + stubFor( + post(urlPathEqualTo("/cis/monthly-returns/update")) + .withHeader("Content-Type", equalTo("application/json")) + .withRequestBody(equalToJson(Json.toJson(req).toString(), true, true)) + .willReturn(aResponse().withStatus(NO_CONTENT)) + ) + + connector.updateMonthlyReturn(req).futureValue mustBe ((): Unit) + } + + "fail the future with UpstreamErrorResponse on non-204 (e.g. 500)" in { + val req = UpdateMonthlyReturnRequest( + instanceId = cisId, + taxYear = 2025, + taxMonth = 1, + amendment = "N", + decNilReturnNoPayments = Some("Y"), + decInformationCorrect = Some("Y"), + nilReturnIndicator = "Y", + status = "STARTED" + ) + + stubFor( + post(urlPathEqualTo("/cis/monthly-returns/update")) + .willReturn(aResponse().withStatus(INTERNAL_SERVER_ERROR).withBody("boom")) + ) + + val err = connector.updateMonthlyReturn(req).failed.futureValue + err mustBe a[UpstreamErrorResponse] + err.asInstanceOf[UpstreamErrorResponse].statusCode mustBe INTERNAL_SERVER_ERROR + err.getMessage must include("boom") + } + } + "createSubmission" should { "POST to /cis/submissions/create and return submissionId on 201" in { diff --git a/test/controllers/monthlyreturns/CheckYourAnswersControllerSpec.scala b/test/controllers/monthlyreturns/CheckYourAnswersControllerSpec.scala index 47459477..f3b6bad2 100644 --- a/test/controllers/monthlyreturns/CheckYourAnswersControllerSpec.scala +++ b/test/controllers/monthlyreturns/CheckYourAnswersControllerSpec.scala @@ -28,7 +28,7 @@ import viewmodels.govuk.SummaryListFluency import viewmodels.checkAnswers.monthlyreturns.{PaymentsToSubcontractorsSummary, ReturnTypeSummary} import views.html.monthlyreturns.CheckYourAnswersView import org.scalatestplus.mockito.MockitoSugar -import pages.monthlyreturns.{CisIdPage, DateConfirmNilPaymentsPage, NilReturnStatusPage, ReturnTypePage} +import pages.monthlyreturns.{CisIdPage, DateConfirmNilPaymentsPage, ReturnTypePage} import java.time.LocalDate import scala.concurrent.Future @@ -99,20 +99,20 @@ class CheckYourAnswersControllerSpec extends SpecBase with SummaryListFluency wi } } - "must call updateNilMonthlyReturn and redirect to submission sending on POST when FormP record already exists (NilReturnStatusPage set)" in { + "must call updateMonthlyReturn and redirect to submission sending on POST when ReturnTypePage is present" in { val userAnswers = emptyUserAnswers .set(CisIdPage, "test-cis-id") .success .value - .set(DateConfirmNilPaymentsPage, LocalDate.of(2024, 3, 1)) + .set(ReturnTypePage, ReturnType.MonthlyNilReturn) .success .value - .set(NilReturnStatusPage, "STARTED") + .set(DateConfirmNilPaymentsPage, LocalDate.of(2024, 3, 1)) .success .value val mockService = mock[MonthlyReturnService] - when(mockService.updateNilMonthlyReturn(any())(any())) + when(mockService.updateMonthlyReturn(any())(any())) .thenReturn(Future.successful(())) val application = applicationBuilder(userAnswers = Some(userAnswers)) @@ -131,7 +131,7 @@ class CheckYourAnswersControllerSpec extends SpecBase with SummaryListFluency wi } } - "must redirect to journey recovery on POST when NilReturnStatusPage is missing" in { + "must redirect to journey recovery on POST when ReturnTypePage is missing" in { val application = applicationBuilder(userAnswers = Some(emptyUserAnswers)).build() @@ -144,5 +144,55 @@ class CheckYourAnswersControllerSpec extends SpecBase with SummaryListFluency wi redirectLocation(result).value mustEqual controllers.routes.JourneyRecoveryController.onPageLoad().url } } + + "must return InternalServerError on POST when update request cannot be built" in { + val userAnswers = emptyUserAnswers + .set(ReturnTypePage, ReturnType.MonthlyNilReturn) + .success + .value + + val mockService = mock[MonthlyReturnService] + + val application = applicationBuilder(userAnswers = Some(userAnswers)) + .overrides(bind[MonthlyReturnService].toInstance(mockService)) + .build() + + running(application) { + val request = FakeRequest(POST, controllers.monthlyreturns.routes.CheckYourAnswersController.onSubmit().url) + val result = route(application, request).value + + status(result) mustEqual INTERNAL_SERVER_ERROR + } + } + + "must redirect to system error on POST when updateMonthlyReturn fails" in { + val userAnswers = emptyUserAnswers + .set(CisIdPage, "test-cis-id") + .success + .value + .set(ReturnTypePage, ReturnType.MonthlyNilReturn) + .success + .value + .set(DateConfirmNilPaymentsPage, LocalDate.of(2024, 3, 1)) + .success + .value + + val mockService = mock[MonthlyReturnService] + when(mockService.updateMonthlyReturn(any())(any())) + .thenReturn(Future.failed(new RuntimeException("boom"))) + + val application = applicationBuilder(userAnswers = Some(userAnswers)) + .overrides(bind[MonthlyReturnService].toInstance(mockService)) + .build() + + running(application) { + val request = FakeRequest(POST, controllers.monthlyreturns.routes.CheckYourAnswersController.onSubmit().url) + + val result = route(application, request).value + + status(result) mustEqual SEE_OTHER + redirectLocation(result).value mustEqual controllers.routes.SystemErrorController.onPageLoad().url + } + } } } diff --git a/test/models/monthlyreturns/UpdateMonthlyReturnRequestSpec.scala b/test/models/monthlyreturns/UpdateMonthlyReturnRequestSpec.scala new file mode 100644 index 00000000..d05b123c --- /dev/null +++ b/test/models/monthlyreturns/UpdateMonthlyReturnRequestSpec.scala @@ -0,0 +1,195 @@ +/* + * Copyright 2026 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package models.monthlyreturns + +import models.ReturnType.{MonthlyNilReturn, MonthlyStandardReturn} +import models.UserAnswers +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec +import org.scalatest.TryValues +import pages.monthlyreturns.* +import play.api.libs.json.{JsSuccess, Json} + +import java.time.LocalDate + +class UpdateMonthlyReturnRequestSpec extends AnyWordSpec with Matchers with TryValues { + + "UpdateMonthlyReturnRequest.format" should { + + "serialize and deserialize (round-trip) model" in { + val model = UpdateMonthlyReturnRequest( + instanceId = "instance-123", + taxYear = 2024, + taxMonth = 1, + amendment = "N", + nilReturnIndicator = "N", + status = "STARTED" + ) + + val json = Json.toJson(model) + json.validate[UpdateMonthlyReturnRequest] shouldBe JsSuccess(model) + } + } + + "UpdateMonthlyReturnRequest.fromUserAnswers" should { + + "build a request for MonthlyNilReturn (minimal required answers)" in { + val ua = UserAnswers("test-user") + .set(ReturnTypePage, MonthlyNilReturn) + .success + .value + .set(CisIdPage, "CIS-123") + .success + .value + .set(DateConfirmNilPaymentsPage, LocalDate.of(2024, 3, 1)) + .success + .value + .set(InactivityRequestPage, InactivityRequest.Option1) + .success + .value + + val result = UpdateMonthlyReturnRequest.fromUserAnswers(ua) + + result shouldBe Right( + UpdateMonthlyReturnRequest( + instanceId = "CIS-123", + taxYear = 2024, + taxMonth = 3, + amendment = "N", + decNilReturnNoPayments = Some("Y"), + decInformationCorrect = None, + nilReturnIndicator = "Y", + status = "STARTED", + version = None + ) + ) + } + + "build a request for MonthlyStandardReturn with standard declarations" in { + val ua = UserAnswers("test-user") + .set(ReturnTypePage, MonthlyStandardReturn) + .success + .value + .set(CisIdPage, "CIS-456") + .success + .value + .set(DateConfirmPaymentsPage, LocalDate.of(2024, 4, 1)) + .success + .value + .set(PaymentDetailsConfirmationPage, true) + .success + .value + .set(EmploymentStatusDeclarationPage, true) + .success + .value + .set(VerifiedStatusDeclarationPage, false) + .success + .value + .set(SubmitInactivityRequestPage, true) + .success + .value + + val result = UpdateMonthlyReturnRequest.fromUserAnswers(ua) + + result shouldBe Right( + UpdateMonthlyReturnRequest( + instanceId = "CIS-456", + taxYear = 2024, + taxMonth = 4, + amendment = "N", + decEmpStatusConsidered = Some("Y"), + decAllSubsVerified = Some("N"), + decNoMoreSubPayments = Some("Y"), + decInformationCorrect = Some("Y"), + nilReturnIndicator = "N", + status = "STARTED", + version = None + ) + ) + } + + "build a request for MonthlyStandardReturn without optional inactivity when SubmitInactivityRequestPage is false" in { + val ua = UserAnswers("test-user") + .set(ReturnTypePage, MonthlyStandardReturn) + .success + .value + .set(CisIdPage, "CIS-789") + .success + .value + .set(DateConfirmPaymentsPage, LocalDate.of(2024, 5, 1)) + .success + .value + .set(PaymentDetailsConfirmationPage, false) + .success + .value + .set(EmploymentStatusDeclarationPage, false) + .success + .value + .set(VerifiedStatusDeclarationPage, true) + .success + .value + .set(SubmitInactivityRequestPage, false) + .success + .value + + val result = UpdateMonthlyReturnRequest.fromUserAnswers(ua) + + result shouldBe Right( + UpdateMonthlyReturnRequest( + instanceId = "CIS-789", + taxYear = 2024, + taxMonth = 5, + amendment = "N", + decEmpStatusConsidered = Some("N"), + decAllSubsVerified = Some("Y"), + decNoMoreSubPayments = None, + decInformationCorrect = Some("N"), + nilReturnIndicator = "N", + status = "STARTED", + version = None + ) + ) + } + + "not include inactivity when SubmitInactivityRequestPage is missing" in { + val ua = UserAnswers("test-user") + .set(ReturnTypePage, MonthlyStandardReturn).success.value + .set(CisIdPage, "CIS-999").success.value + .set(DateConfirmPaymentsPage, LocalDate.of(2024, 6, 1)).success.value + .set(PaymentDetailsConfirmationPage, true).success.value + .set(EmploymentStatusDeclarationPage, true).success.value + .set(VerifiedStatusDeclarationPage, true).success.value + + val result = UpdateMonthlyReturnRequest.fromUserAnswers(ua).toOption.get + + result.decNoMoreSubPayments shouldBe None + } + + "set decInformationCorrect when DeclarationPage has a value" in { + val ua = UserAnswers("test-user") + .set(ReturnTypePage, MonthlyNilReturn).success.value + .set(CisIdPage, "CIS-321").success.value + .set(DateConfirmNilPaymentsPage, LocalDate.of(2024, 7, 1)).success.value + .set(InactivityRequestPage, InactivityRequest.Option1).success.value + .set(DeclarationPage, Set(Declaration.Confirmed)).success.value + + val result = UpdateMonthlyReturnRequest.fromUserAnswers(ua).toOption.get + + result.decInformationCorrect shouldBe Some("Y") + } + } +} diff --git a/test/services/MonthlyReturnServiceSpec.scala b/test/services/MonthlyReturnServiceSpec.scala index 07a31647..a31bbadb 100644 --- a/test/services/MonthlyReturnServiceSpec.scala +++ b/test/services/MonthlyReturnServiceSpec.scala @@ -638,6 +638,57 @@ class MonthlyReturnServiceSpec extends AnyWordSpec with ScalaFutures with Matche } } + "updateMonthlyReturn" should { + + "delegate to connector and complete successfully" in { + val (service, connector, sessionRepo) = newService() + + val req = UpdateMonthlyReturnRequest( + instanceId = "CIS-123", + taxYear = 2024, + taxMonth = 10, + amendment = "N", + decNilReturnNoPayments = Some("Y"), + decInformationCorrect = Some("Y"), + nilReturnIndicator = "Y", + status = "STARTED" + ) + + when(connector.updateMonthlyReturn(eqTo(req))(any[HeaderCarrier])) + .thenReturn(Future.successful(())) + + service.updateMonthlyReturn(req).futureValue mustBe () + + verify(connector).updateMonthlyReturn(eqTo(req))(any[HeaderCarrier]) + verifyNoInteractions(sessionRepo) + } + + "propagate failures from the connector" in { + val (service, connector, _) = newService() + + val req = UpdateMonthlyReturnRequest( + instanceId = "CIS-123", + taxYear = 2024, + taxMonth = 10, + amendment = "N", + decNilReturnNoPayments = Some("Y"), + decInformationCorrect = Some("Y"), + nilReturnIndicator = "Y", + status = "STARTED" + ) + + when(connector.updateMonthlyReturn(eqTo(req))(any[HeaderCarrier])) + .thenReturn(Future.failed(new RuntimeException("boom"))) + + val ex = intercept[RuntimeException] { + service.updateMonthlyReturn(req).futureValue + } + ex.getMessage must include("boom") + + verify(connector).updateMonthlyReturn(eqTo(req))(any[HeaderCarrier]) + } + } + "hasClient" should { "delegate to connector and return the boolean" in {