From 490605bb65b25a16d064b957c705e15dda0520d0 Mon Sep 17 00:00:00 2001 From: Daniel Ashton Date: Sun, 22 Mar 2026 22:16:07 +0000 Subject: [PATCH 1/8] AMCR-65 - Update Compliance and Producer payment requests to pass fileId FileId is passed if it is a resubmission so that the correct fee can be returned --- .../RegistrationSubmissions/CompliancePaymentRequest.cs | 2 ++ .../RegistrationSubmissions/ProducerPaymentRequest.cs | 2 ++ .../CompliancePaymentDetailsViewComponentTests.cs | 6 ++++-- .../ProducerPaymentDetailsViewComponentTests.cs | 2 +- .../CompliancePaymentDetailsViewComponent.cs | 1 + .../ProducerPaymentDetailsViewComponent.cs | 1 + 6 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/CompliancePaymentRequest.cs b/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/CompliancePaymentRequest.cs index 349464eb2..3753e9c04 100644 --- a/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/CompliancePaymentRequest.cs +++ b/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/CompliancePaymentRequest.cs @@ -4,6 +4,8 @@ public class CompliancePaymentRequest { public required string ApplicationReferenceNumber { get; set; } + public string? FileId { get; set; } + public required string Regulator { get; set; } public DateTime SubmissionDate { get; set; } diff --git a/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/ProducerPaymentRequest.cs b/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/ProducerPaymentRequest.cs index b3c219c14..5c6ccad17 100644 --- a/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/ProducerPaymentRequest.cs +++ b/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/ProducerPaymentRequest.cs @@ -4,6 +4,8 @@ public class ProducerPaymentRequest { public required string ApplicationReferenceNumber { get; set; } + public string? FileId { get; set; } + public int NoOfSubsidiariesOnlineMarketplace { get; set; } public int NumberOfSubsidiaries { get; set; } diff --git a/src/EPR.RegulatorService.Frontend.UnitTests/Web/ViewComponents/CompliancePaymentDetailsViewComponentTests.cs b/src/EPR.RegulatorService.Frontend.UnitTests/Web/ViewComponents/CompliancePaymentDetailsViewComponentTests.cs index c72d539d6..16c38f490 100644 --- a/src/EPR.RegulatorService.Frontend.UnitTests/Web/ViewComponents/CompliancePaymentDetailsViewComponentTests.cs +++ b/src/EPR.RegulatorService.Frontend.UnitTests/Web/ViewComponents/CompliancePaymentDetailsViewComponentTests.cs @@ -75,7 +75,7 @@ public async Task InvokeAsync_Returns_CorrectView_With_Model(bool isResubmission _paymentFacadeServiceMock.Setup(x => x.GetCompliancePaymentDetailsAsync(It.IsAny())) .ReturnsAsync(new CompliancePaymentResponse // all values in pence { - ApplicationProcessingFee = 100.00M, + ApplicationProcessingFee = 100.00M, TotalChargeableItems = 1000.00M, PreviousPaymentsReceived = 500.00M, TotalOutstanding = 500.00M, @@ -102,7 +102,9 @@ public async Task InvokeAsync_Returns_CorrectView_With_Model(bool isResubmission model.LateProducerCount.Should().Be(0); model.OnlineMarketPlaceCount.Should().Be(0); model.SubsidiariesCompanyCount.Should().Be(0); - _paymentFacadeServiceMock.Verify(r => r.GetCompliancePaymentDetailsAsync(It.IsAny()), Times.AtMostOnce); + _paymentFacadeServiceMock.Verify( + r => r.GetCompliancePaymentDetailsAsync(It.Is(c => + c.FileId == _registrationSumissionDetailsViewModel.ResubmissionFileId)), Times.AtMostOnce); } [TestMethod] diff --git a/src/EPR.RegulatorService.Frontend.UnitTests/Web/ViewComponents/ProducerPaymentDetailsViewComponentTests.cs b/src/EPR.RegulatorService.Frontend.UnitTests/Web/ViewComponents/ProducerPaymentDetailsViewComponentTests.cs index d0af1e3cc..912cc8c43 100644 --- a/src/EPR.RegulatorService.Frontend.UnitTests/Web/ViewComponents/ProducerPaymentDetailsViewComponentTests.cs +++ b/src/EPR.RegulatorService.Frontend.UnitTests/Web/ViewComponents/ProducerPaymentDetailsViewComponentTests.cs @@ -52,7 +52,7 @@ public async Task InvokeAsync_Returns_CorrectView_With_DefaultModel_When_Service var model = result.ViewData.Model as ProducerPaymentResponse; model.Should().BeNull(); _paymentFacadeServiceMock.Verify(r => r.GetProducerPaymentDetailsAsync( - It.IsAny()), Times.AtMostOnce); + It.Is(c=>c.FileId == _registrationSumissionDetailsViewModel.ResubmissionFileId)), Times.AtMostOnce); } [TestMethod] diff --git a/src/EPR.RegulatorService.Frontend.Web/ViewComponents/RegistrationSubmissions/CompliancePaymentDetailsViewComponent.cs b/src/EPR.RegulatorService.Frontend.Web/ViewComponents/RegistrationSubmissions/CompliancePaymentDetailsViewComponent.cs index 48f32d449..ee36def2d 100644 --- a/src/EPR.RegulatorService.Frontend.Web/ViewComponents/RegistrationSubmissions/CompliancePaymentDetailsViewComponent.cs +++ b/src/EPR.RegulatorService.Frontend.Web/ViewComponents/RegistrationSubmissions/CompliancePaymentDetailsViewComponent.cs @@ -31,6 +31,7 @@ public async Task InvokeAsync(RegistrationSubmissionDet new CompliancePaymentRequest { ApplicationReferenceNumber = viewModel.ReferenceNumber, + FileId = viewModel.IsResubmission ? viewModel.ResubmissionFileId : null, Regulator = viewModel.NationCode, ComplianceSchemeMembers = viewModel.CSOMembershipDetails.Select(x => (ComplianceSchemeMemberRequest)x), SubmissionDate = TimeZoneInfo.ConvertTimeToUtc(viewModel.IsResubmission diff --git a/src/EPR.RegulatorService.Frontend.Web/ViewComponents/RegistrationSubmissions/ProducerPaymentDetailsViewComponent.cs b/src/EPR.RegulatorService.Frontend.Web/ViewComponents/RegistrationSubmissions/ProducerPaymentDetailsViewComponent.cs index b00bda6c6..724c3490b 100644 --- a/src/EPR.RegulatorService.Frontend.Web/ViewComponents/RegistrationSubmissions/ProducerPaymentDetailsViewComponent.cs +++ b/src/EPR.RegulatorService.Frontend.Web/ViewComponents/RegistrationSubmissions/ProducerPaymentDetailsViewComponent.cs @@ -26,6 +26,7 @@ public async Task InvokeAsync(RegistrationSubmissionDet var producerPaymentResponse = await paymentFacadeService.GetProducerPaymentDetailsAsync(new ProducerPaymentRequest { ApplicationReferenceNumber = viewModel.ReferenceNumber, + FileId = viewModel.IsResubmission ? viewModel.ResubmissionFileId : null, NoOfSubsidiariesOnlineMarketplace = viewModel.ProducerDetails.NoOfSubsidiariesOnlineMarketPlace, NumberOfSubsidiaries = viewModel.ProducerDetails.NoOfSubsidiaries, IsLateFeeApplicable = viewModel.ProducerDetails.IsLateFeeApplicable, From b190be28a7e606cabdd79a34a8ae6a65d09b7fe5 Mon Sep 17 00:00:00 2001 From: Daniel Ashton Date: Sun, 22 Mar 2026 22:18:12 +0000 Subject: [PATCH 2/8] AMCR-65 - Pass file id when doing offline payment --- .../Models/RegistrationSubmissions/OfflinePaymentRequest.cs | 2 ++ .../Web/Controllers/RegistrationSubmissionsControllerTests.cs | 2 +- .../Web/Controllers/RegistrationSubmissionsTestBase.cs | 1 + .../RegistrationSubmissionsController.cs | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/OfflinePaymentRequest.cs b/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/OfflinePaymentRequest.cs index 0077736ef..e7bb9e029 100644 --- a/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/OfflinePaymentRequest.cs +++ b/src/EPR.RegulatorService.Frontend.Core/Models/RegistrationSubmissions/OfflinePaymentRequest.cs @@ -12,6 +12,8 @@ public class OfflinePaymentRequest public required string Description { get; set; } + public Guid? FileId { get; set; } + public DateTime? PaymentDate { get; set; } public string? Comments { get; set; } diff --git a/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/RegistrationSubmissionsControllerTests.cs b/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/RegistrationSubmissionsControllerTests.cs index 15bcbe657..1205820f3 100644 --- a/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/RegistrationSubmissionsControllerTests.cs +++ b/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/RegistrationSubmissionsControllerTests.cs @@ -2375,7 +2375,7 @@ public async Task ConfirmOfflinePaymentSubmission_RedirectsTo_ServiceNotAvailabl // Veryify the redirect URL result.RouteName.Should().Be("ServiceNotAvailable"); result.RouteValues.First().Value.Should().Be($"{PagePath.RegistrationSubmissionDetails}/{submissionId}"); - _paymentFacadeServiceMock.Verify(r => r.SubmitOfflinePaymentAsync(It.IsAny()), Times.AtMostOnce); + _paymentFacadeServiceMock.Verify(r => r.SubmitOfflinePaymentAsync(It.Is(c=>c.FileId.ToString() == submissionDetails.ResubmissionFileId)), Times.AtMostOnce); } [TestMethod] diff --git a/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/RegistrationSubmissionsTestBase.cs b/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/RegistrationSubmissionsTestBase.cs index 7e9c08ddc..c1cf1a877 100644 --- a/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/RegistrationSubmissionsTestBase.cs +++ b/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/RegistrationSubmissionsTestBase.cs @@ -166,6 +166,7 @@ protected static RegistrationSubmissionDetailsViewModel GenerateTestSubmissionDe Files = [ ], + ResubmissionFileId = Guid.NewGuid().ToString() }, ProducerComments = "producer comment", RegulatorComments = "regulator comment", diff --git a/src/EPR.RegulatorService.Frontend.Web/Controllers/RegistrationSubmissions/RegistrationSubmissionsController.cs b/src/EPR.RegulatorService.Frontend.Web/Controllers/RegistrationSubmissions/RegistrationSubmissionsController.cs index aee48427a..12de8f54f 100644 --- a/src/EPR.RegulatorService.Frontend.Web/Controllers/RegistrationSubmissions/RegistrationSubmissionsController.cs +++ b/src/EPR.RegulatorService.Frontend.Web/Controllers/RegistrationSubmissions/RegistrationSubmissionsController.cs @@ -912,7 +912,8 @@ private async Task ProcessOfflinePaymentAsync(RegistrationSubmiss Description = "Registration fee", Reference = existingModel.ReferenceNumber, Regulator = regulator, - UserId = (Guid)_currentSession.UserData.Id + UserId = (Guid)_currentSession.UserData.Id, + FileId = existingModel.IsResubmission && Guid.TryParse(existingModel.ResubmissionFileId, out var fileId) ? fileId : null }); if (response == EndpointResponseStatus.Fail) From 53ad8eba5b01eecb315fbcf3d3b1120818e92b59 Mon Sep 17 00:00:00 2001 From: Daniel Ashton Date: Sun, 22 Mar 2026 22:18:31 +0000 Subject: [PATCH 3/8] AMCR-65 - Update integration tests --- .../ProducerPaymentResponseBuilder.cs | 58 +++++++++++ .../RegistrationSubmissionDetailsBuilder.cs | 49 +++++++--- .../RegistrationSubmissionDetailsTests.cs | 95 +++++++++++++++++++ .../PaymentApi/PaymentApi.cs | 32 +++++++ .../c1d2e3f4-a5b6-7890-cdef-012345678901.json | 17 ++++ .../d2e3f4a5-b6c7-8901-defa-123456789012.json | 13 +++ 6 files changed, 251 insertions(+), 13 deletions(-) create mode 100644 src/IntegrationTests/Builders/ProducerPaymentResponseBuilder.cs create mode 100644 src/MockPaymentFacade/Responses/PaymentApi/ComplianceSchemeRegistrationFeeResubmission/c1d2e3f4-a5b6-7890-cdef-012345678901.json create mode 100644 src/MockPaymentFacade/Responses/PaymentApi/ProducerRegistrationFeeResubmission/d2e3f4a5-b6c7-8901-defa-123456789012.json diff --git a/src/IntegrationTests/Builders/ProducerPaymentResponseBuilder.cs b/src/IntegrationTests/Builders/ProducerPaymentResponseBuilder.cs new file mode 100644 index 000000000..a89e86bd1 --- /dev/null +++ b/src/IntegrationTests/Builders/ProducerPaymentResponseBuilder.cs @@ -0,0 +1,58 @@ +namespace IntegrationTests.Builders; + +public class ProducerPaymentResponseBuilder +{ + private int _producerRegistrationFee = 165800; + private readonly int _producerOnlineMarketPlaceFee = 0; + private readonly int _producerLateRegistrationFee = 0; + private int _totalFee = 165800; + private int _previousPayment = 0; + private int _outstandingPayment = 165800; + private readonly int _subsidiariesFee = 0; + + private ProducerPaymentResponseBuilder() + { + } + + public static ProducerPaymentResponseBuilder Default() => new(); + + public ProducerPaymentResponseBuilder WithProducerRegistrationFee(int feeInPence) + { + _producerRegistrationFee = feeInPence; + return this; + } + + public ProducerPaymentResponseBuilder WithTotalFee(int feeInPence) + { + _totalFee = feeInPence; + return this; + } + + public ProducerPaymentResponseBuilder WithPreviousPayment(int amountInPence) + { + _previousPayment = amountInPence; + return this; + } + + public ProducerPaymentResponseBuilder WithOutstandingPayment(int amountInPence) + { + _outstandingPayment = amountInPence; + return this; + } + + public object Build() => new + { + producerRegistrationFee = _producerRegistrationFee, + producerOnlineMarketPlaceFee = _producerOnlineMarketPlaceFee, + producerLateRegistrationFee = _producerLateRegistrationFee, + totalFee = _totalFee, + previousPayment = _previousPayment, + outstandingPayment = _outstandingPayment, + subsidiariesFee = _subsidiariesFee, + subsidiariesFeeBreakdown = new + { + totalSubsidiariesOMPFees = 0, + countOfOMPSubsidiaries = 0 + } + }; +} diff --git a/src/IntegrationTests/Builders/RegistrationSubmissionDetailsBuilder.cs b/src/IntegrationTests/Builders/RegistrationSubmissionDetailsBuilder.cs index 0f2f5bede..9af482307 100644 --- a/src/IntegrationTests/Builders/RegistrationSubmissionDetailsBuilder.cs +++ b/src/IntegrationTests/Builders/RegistrationSubmissionDetailsBuilder.cs @@ -8,6 +8,12 @@ public class RegistrationSubmissionDetailsBuilder private int _relevantYear = 2025; private string _submissionDate = "2025-01-10T09:30:00Z"; private string _submissionStatus = "Pending"; + private bool _isResubmission = false; + private string? _resubmissionFileId = null; + private string? _resubmissionDate = null; + private bool _isComplianceScheme = true; + private string _registrationJourneyType = "CsoLargeProducer"; + private string? _organisationSize = null; private RegistrationSubmissionDetailsBuilder(Guid submissionId) { @@ -46,6 +52,22 @@ public RegistrationSubmissionDetailsBuilder WithSubmissionStatus(string submissi return this; } + public RegistrationSubmissionDetailsBuilder WithIsResubmission(bool isResubmission, string? resubmissionFileId = null, string? resubmissionDate = null) + { + _isResubmission = isResubmission; + _resubmissionFileId = resubmissionFileId; + _resubmissionDate = resubmissionDate ?? "2025-06-15T10:00:00Z"; + return this; + } + + public RegistrationSubmissionDetailsBuilder AsProducer(string organisationSize = "Large") + { + _isComplianceScheme = false; + _registrationJourneyType = organisationSize == "Small" ? "DirectSmallProducer" : "DirectLargeProducer"; + _organisationSize = organisationSize; + return this; + } + public object Build() => new { submissionId = SubmissionId, @@ -53,16 +75,16 @@ public RegistrationSubmissionDetailsBuilder WithSubmissionStatus(string submissi organisationReference = "100001", organisationName = _organisationName, organisationType = _organisationType, - registrationJourneyType = "CsoLargeProducer", + registrationJourneyType = _registrationJourneyType, nationId = 1, nationCode = "GB-ENG", relevantYear = _relevantYear, submissionDate = _submissionDate, submissionStatus = _submissionStatus, - resubmissionStatus = "Pending", - resubmissionDate = (string?)null, + resubmissionStatus = _isResubmission ? "Pending" : (string?)null, + resubmissionDate = _resubmissionDate, statusPendingDate = (string?)null, - isResubmission = false, + isResubmission = _isResubmission, registrationDate = (string?)null, regulatorComments = "", producerComments = "Test comment", @@ -86,6 +108,7 @@ public RegistrationSubmissionDetailsBuilder WithSubmissionStatus(string submissi resubmissionDecisionDate = (string?)null, statusPendingDate = (string?)null, timeAndDateOfSubmission = _submissionDate, + timeAndDateOfResubmission = _resubmissionDate, submittedOnTime = false, submittedByUserId = "A1B2C3D4-E5F6-7890-ABCD-EF1234567890", accountRole = "Approved Person", @@ -106,11 +129,11 @@ public RegistrationSubmissionDetailsBuilder WithSubmissionStatus(string submissi submissionPeriod = $"January to December {_relevantYear}", accountRoleId = 1, submittedBy = "Test User", - resubmissionStatus = (string?)null, + resubmissionStatus = _isResubmission ? "Pending" : (string?)null, registrationDate = (string?)null, - resubmissionDate = (string?)null, - isResubmission = false, - resubmissionFileId = (string?)null + resubmissionDate = _resubmissionDate, + isResubmission = _isResubmission, + resubmissionFileId = _resubmissionFileId }, regulatorDecisionDate = (string?)null, regulatorResubmissionDecisionDate = (string?)null, @@ -120,10 +143,10 @@ public RegistrationSubmissionDetailsBuilder WithSubmissionStatus(string submissi numberOfSubsidiaries = 0, numberOfOnlineSubsidiaries = 0, isLateSubmission = true, - organisationSize = (string?)null, - isComplianceScheme = true, + organisationSize = _organisationSize, + isComplianceScheme = _isComplianceScheme, submissionPeriod = $"January to December {_relevantYear}", - csoMembershipDetails = new[] + csoMembershipDetails = _isComplianceScheme ? new[] { new { @@ -137,7 +160,7 @@ public RegistrationSubmissionDetailsBuilder WithSubmissionStatus(string submissi submittedDate = _submissionDate, submissionPeriodDescription = $"January to December {_relevantYear}" } - }, - resubmissionFileId = (string?)null + } : Array.Empty(), + resubmissionFileId = _resubmissionFileId }; } diff --git a/src/IntegrationTests/Features/RegistrationSubmissionDetailsTests.cs b/src/IntegrationTests/Features/RegistrationSubmissionDetailsTests.cs index e0b3705b4..0c7587b4f 100644 --- a/src/IntegrationTests/Features/RegistrationSubmissionDetailsTests.cs +++ b/src/IntegrationTests/Features/RegistrationSubmissionDetailsTests.cs @@ -82,6 +82,81 @@ public async Task ShowsPaymentDetailsFromPaymentFacade() } } + [Fact] + public async Task ShowsComplianceSchemePaymentDetailsWithPreviousPaymentOnResubmission() + { + // Arrange + var submissionId = Guid.Parse("2c3d4e5f-6a7b-8c9d-0e1f-2a3b4c5d6e7f"); + var resubmissionFileId = "c1d2e3f4-a5b6-7890-cdef-012345678901"; + + SetupFacadeMockRegistrationSubmissionDetails( + RegistrationSubmissionDetailsBuilder.Default(submissionId) + .WithOrganisationName("CSO Resubmitting Ltd") + .WithOrganisationType("compliance") + .WithIsResubmission(true, resubmissionFileId)); + + SetupPaymentFacadeMockComplianceSchemeRegistrationFeeByFileId( + resubmissionFileId, + CompliancePaymentResponseBuilder.Default() + .WithComplianceSchemeRegistrationFee(262000) + .WithTotalFee(285400) + .WithPreviousPayment(100000) + .WithOutstandingPayment(185400)); + + // Act + var detailsPage = await GetAsPageModel( + requestUri: $"/regulators/registration-submission-details/{submissionId}"); + + // Assert + using (new AssertionScope()) + { + detailsPage.PaymentDetails.Should().NotBeNull(); + detailsPage.PaymentDetails!.HasPaymentSection.Should().BeTrue(); + detailsPage.ApplicationFee.Should().Be(2620.00m); + detailsPage.SubTotal.Should().Be(2854.00m); + detailsPage.PreviousPaymentReceived.Should().Be(1000.00m); + detailsPage.TotalOutstanding.Should().Be(1854.00m); + } + } + + [Fact] + public async Task ShowsProducerPaymentDetailsWithPreviousPaymentOnResubmission() + { + // Arrange + var submissionId = Guid.Parse("3d4e5f6a-7b8c-9d0e-1f2a-3b4c5d6e7f8a"); + var resubmissionFileId = "d2e3f4a5-b6c7-8901-defa-123456789012"; + + SetupFacadeMockRegistrationSubmissionDetails( + RegistrationSubmissionDetailsBuilder.Default(submissionId) + .WithOrganisationName("Large Producer Ltd") + .WithOrganisationType("large") + .WithIsResubmission(true, resubmissionFileId) + .AsProducer("Large")); + + SetupPaymentFacadeMockProducerRegistrationFeeByFileId( + resubmissionFileId, + ProducerPaymentResponseBuilder.Default() + .WithProducerRegistrationFee(165800) + .WithTotalFee(165800) + .WithPreviousPayment(82900) + .WithOutstandingPayment(82900)); + + // Act + var detailsPage = await GetAsPageModel( + requestUri: $"/regulators/registration-submission-details/{submissionId}"); + + // Assert + using (new AssertionScope()) + { + detailsPage.PaymentDetails.Should().NotBeNull(); + detailsPage.PaymentDetails!.HasPaymentSection.Should().BeTrue(); + detailsPage.ApplicationFee.Should().Be(1658.00m); + detailsPage.SubTotal.Should().Be(1658.00m); + detailsPage.PreviousPaymentReceived.Should().Be(829.00m); + detailsPage.TotalOutstanding.Should().Be(829.00m); + } + } + private void SetupPaymentFacadeMockComplianceSchemeRegistrationFee(CompliancePaymentResponseBuilder builder) => FacadeServer.Given(Request.Create() .UsingPost() @@ -91,6 +166,26 @@ private void SetupPaymentFacadeMockComplianceSchemeRegistrationFee(CompliancePay .WithHeader("Content-Type", "application/json") .WithBody(JsonSerializer.Serialize(builder.Build()))); + private void SetupPaymentFacadeMockComplianceSchemeRegistrationFeeByFileId(string fileId, CompliancePaymentResponseBuilder builder) => + FacadeServer.Given(Request.Create() + .UsingPost() + .WithPath("/compliance-scheme/registration-fee") + .WithBody(new WireMock.Matchers.JsonPartialMatcher(JsonSerializer.Serialize(new { FileId = fileId }), ignoreCase: true))) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(builder.Build()))); + + private void SetupPaymentFacadeMockProducerRegistrationFeeByFileId(string fileId, ProducerPaymentResponseBuilder builder) => + FacadeServer.Given(Request.Create() + .UsingPost() + .WithPath("/producer/registration-fee") + .WithBody(new WireMock.Matchers.JsonPartialMatcher(JsonSerializer.Serialize(new { FileId = fileId }), ignoreCase: true))) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(builder.Build()))); + private void SetupFacadeMockRegistrationSubmissionDetails(RegistrationSubmissionDetailsBuilder builder) => FacadeServer.Given(Request.Create() .UsingGet() diff --git a/src/MockPaymentFacade/PaymentApi/PaymentApi.cs b/src/MockPaymentFacade/PaymentApi/PaymentApi.cs index 9764f3be6..09d910837 100644 --- a/src/MockPaymentFacade/PaymentApi/PaymentApi.cs +++ b/src/MockPaymentFacade/PaymentApi/PaymentApi.cs @@ -21,6 +21,38 @@ public static WireMockServer WithPaymentApi(this WireMockServer server) .WithHeader("Content-Type", "text/plain") .WithBody("MockPaymentFacade up.\nReady to process API requests.")); + // Compliance scheme registration fee resubmission - matched by FileId in request body (registered first for priority) + foreach (var filePath in Directory.GetFiles("Responses/PaymentApi/ComplianceSchemeRegistrationFeeResubmission", "*.json")) + { + server.Given(Request.Create() + .UsingPost() + .WithPath("/compliance-scheme/registration-fee") + .WithBody(new JsonPartialMatcher(JsonSerializer.Serialize(new + { + FileId = Path.GetFileNameWithoutExtension(filePath), + }), ignoreCase: true))) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithHeader("Content-Type", "application/json") + .WithBodyFromFile(filePath)); + } + + // Producer registration fee resubmission - matched by FileId in request body (registered first for priority) + foreach (var filePath in Directory.GetFiles("Responses/PaymentApi/ProducerRegistrationFeeResubmission", "*.json")) + { + server.Given(Request.Create() + .UsingPost() + .WithPath("/producer/registration-fee") + .WithBody(new JsonPartialMatcher(JsonSerializer.Serialize(new + { + FileId = Path.GetFileNameWithoutExtension(filePath), + }), ignoreCase: true))) + .RespondWith(Response.Create() + .WithStatusCode(200) + .WithHeader("Content-Type", "application/json") + .WithBodyFromFile(filePath)); + } + // Compliance scheme registration fee endpoints - matched by applicationReferenceNumber in request body foreach (var filePath in Directory.GetFiles("Responses/PaymentApi/ComplianceSchemeRegistrationFee", "*.json")) { diff --git a/src/MockPaymentFacade/Responses/PaymentApi/ComplianceSchemeRegistrationFeeResubmission/c1d2e3f4-a5b6-7890-cdef-012345678901.json b/src/MockPaymentFacade/Responses/PaymentApi/ComplianceSchemeRegistrationFeeResubmission/c1d2e3f4-a5b6-7890-cdef-012345678901.json new file mode 100644 index 000000000..0bcbb9381 --- /dev/null +++ b/src/MockPaymentFacade/Responses/PaymentApi/ComplianceSchemeRegistrationFeeResubmission/c1d2e3f4-a5b6-7890-cdef-012345678901.json @@ -0,0 +1,17 @@ +{ + "complianceSchemeRegistrationFee": 262000, + "totalFee": 285400, + "previousPayment": 100000, + "outstandingPayment": 185400, + "complianceSchemeMembersWithFees": [ + { + "memberId": "100001", + "memberType": "large", + "memberRegistrationFee": 165800, + "memberOnlineMarketPlaceFee": 0, + "memberLateRegistrationFee": 0, + "subsidiariesFee": 0, + "totalMemberFee": 165800 + } + ] +} diff --git a/src/MockPaymentFacade/Responses/PaymentApi/ProducerRegistrationFeeResubmission/d2e3f4a5-b6c7-8901-defa-123456789012.json b/src/MockPaymentFacade/Responses/PaymentApi/ProducerRegistrationFeeResubmission/d2e3f4a5-b6c7-8901-defa-123456789012.json new file mode 100644 index 000000000..cbb84deed --- /dev/null +++ b/src/MockPaymentFacade/Responses/PaymentApi/ProducerRegistrationFeeResubmission/d2e3f4a5-b6c7-8901-defa-123456789012.json @@ -0,0 +1,13 @@ +{ + "producerRegistrationFee": 165800, + "producerOnlineMarketPlaceFee": 0, + "producerLateRegistrationFee": 0, + "totalFee": 165800, + "previousPayment": 82900, + "outstandingPayment": 82900, + "subsidiariesFee": 0, + "subsidiariesFeeBreakdown": { + "totalSubsidiariesOMPFees": 0, + "countOfOMPSubsidiaries": 0 + } +} From 19efb3996c5450ba5754b88a7583df35fc790add Mon Sep 17 00:00:00 2001 From: Daniel Ashton Date: Mon, 23 Mar 2026 13:48:39 +0000 Subject: [PATCH 4/8] AMCR-65 - Update to integration tests --- .../RegistrationSubmissionDetailsTests.cs | 35 ++++++++ .../PaymentApi/PaymentApi.cs | 21 +++-- .../a1b2c3d4-e5f6-7890-abcd-ef1234567891.json | 88 +++++++++++++++++++ .../c3d4e5f6-a7b8-9012-cdef-123456789013.json | 78 ++++++++++++++++ ...organisation-registration-submissions.json | 52 ++++++++++- 5 files changed, 262 insertions(+), 12 deletions(-) create mode 100644 src/MockRegulatorFacade/Responses/FacadeApi/RegistrationSubmissionDetails/a1b2c3d4-e5f6-7890-abcd-ef1234567891.json create mode 100644 src/MockRegulatorFacade/Responses/FacadeApi/RegistrationSubmissionDetails/c3d4e5f6-a7b8-9012-cdef-123456789013.json diff --git a/src/IntegrationTests/Features/RegistrationSubmissionDetailsTests.cs b/src/IntegrationTests/Features/RegistrationSubmissionDetailsTests.cs index 0c7587b4f..c5865a350 100644 --- a/src/IntegrationTests/Features/RegistrationSubmissionDetailsTests.cs +++ b/src/IntegrationTests/Features/RegistrationSubmissionDetailsTests.cs @@ -119,6 +119,41 @@ public async Task ShowsComplianceSchemePaymentDetailsWithPreviousPaymentOnResubm } } + [Fact] + public async Task ShowsTotalOutstandingForProducerResubmissionAtKnownSubmissionId() + { + // Arrange + var submissionId = Guid.Parse("c3d4e5f6-a7b8-9012-cdef-123456789013"); + var resubmissionFileId = "d2e3f4a5-b6c7-8901-defa-123456789012"; + + SetupFacadeMockRegistrationSubmissionDetails( + RegistrationSubmissionDetailsBuilder.Default(submissionId) + .WithOrganisationName("Resubmission Direct Producer Ltd") + .WithOrganisationType("large") + .WithIsResubmission(true, resubmissionFileId) + .AsProducer("Large")); + + SetupPaymentFacadeMockProducerRegistrationFeeByFileId( + resubmissionFileId, + ProducerPaymentResponseBuilder.Default() + .WithProducerRegistrationFee(165800) + .WithTotalFee(165800) + .WithPreviousPayment(82900) + .WithOutstandingPayment(82900)); + + // Act + var detailsPage = await GetAsPageModel( + requestUri: $"/regulators/registration-submission-details/{submissionId}"); + + // Assert + using (new AssertionScope()) + { + detailsPage.PaymentDetails.Should().NotBeNull(); + detailsPage.PaymentDetails!.HasPaymentSection.Should().BeTrue(); + detailsPage.TotalOutstanding.Should().Be(829.00m); + } + } + [Fact] public async Task ShowsProducerPaymentDetailsWithPreviousPaymentOnResubmission() { diff --git a/src/MockPaymentFacade/PaymentApi/PaymentApi.cs b/src/MockPaymentFacade/PaymentApi/PaymentApi.cs index 09d910837..688c031f8 100644 --- a/src/MockPaymentFacade/PaymentApi/PaymentApi.cs +++ b/src/MockPaymentFacade/PaymentApi/PaymentApi.cs @@ -21,15 +21,15 @@ public static WireMockServer WithPaymentApi(this WireMockServer server) .WithHeader("Content-Type", "text/plain") .WithBody("MockPaymentFacade up.\nReady to process API requests.")); - // Compliance scheme registration fee resubmission - matched by FileId in request body (registered first for priority) + // Registration fee resubmission - matched by FileId in request body foreach (var filePath in Directory.GetFiles("Responses/PaymentApi/ComplianceSchemeRegistrationFeeResubmission", "*.json")) { server.Given(Request.Create() .UsingPost() - .WithPath("/compliance-scheme/registration-fee") + .WithPath("/api/v1/compliance-scheme/registration-fee") .WithBody(new JsonPartialMatcher(JsonSerializer.Serialize(new { - FileId = Path.GetFileNameWithoutExtension(filePath), + fileId = Path.GetFileNameWithoutExtension(filePath), }), ignoreCase: true))) .RespondWith(Response.Create() .WithStatusCode(200) @@ -37,15 +37,14 @@ public static WireMockServer WithPaymentApi(this WireMockServer server) .WithBodyFromFile(filePath)); } - // Producer registration fee resubmission - matched by FileId in request body (registered first for priority) foreach (var filePath in Directory.GetFiles("Responses/PaymentApi/ProducerRegistrationFeeResubmission", "*.json")) { server.Given(Request.Create() .UsingPost() - .WithPath("/producer/registration-fee") + .WithPath("/api/v1/producer/registration-fee") .WithBody(new JsonPartialMatcher(JsonSerializer.Serialize(new { - FileId = Path.GetFileNameWithoutExtension(filePath), + fileId = Path.GetFileNameWithoutExtension(filePath), }), ignoreCase: true))) .RespondWith(Response.Create() .WithStatusCode(200) @@ -58,7 +57,7 @@ public static WireMockServer WithPaymentApi(this WireMockServer server) { server.Given(Request.Create() .UsingPost() - .WithPath("/compliance-scheme/registration-fee") + .WithPath("/api/v1/compliance-scheme/registration-fee") .WithBody(new JsonPartialMatcher(JsonSerializer.Serialize(new { applicationReferenceNumber = Path.GetFileNameWithoutExtension(filePath), @@ -72,7 +71,7 @@ public static WireMockServer WithPaymentApi(this WireMockServer server) // Producer registration fee endpoint server.Given(Request.Create() .UsingPost() - .WithPath("/producer/registration-fee")) + .WithPath("/api/v1/producer/registration-fee")) .RespondWith(Response.Create() .WithStatusCode(200) .WithHeader("Content-Type", "application/json") @@ -81,14 +80,14 @@ public static WireMockServer WithPaymentApi(this WireMockServer server) // Offline payments endpoint server.Given(Request.Create() .UsingPost() - .WithPath("/offline-payments")) + .WithPath("/api/v1/offline-payments")) .RespondWith(Response.Create() .WithStatusCode(200)); // Producer resubmission fee endpoint server.Given(Request.Create() .UsingPost() - .WithPath("/producer/resubmission-fee")) + .WithPath("/api/v1/producer/resubmission-fee")) .RespondWith(Response.Create() .WithStatusCode(200) .WithHeader("Content-Type", "application/json") @@ -97,7 +96,7 @@ public static WireMockServer WithPaymentApi(this WireMockServer server) // Compliance scheme resubmission fees endpoint server.Given(Request.Create() .UsingPost() - .WithPath("/compliance-scheme/resubmission-fees")) + .WithPath("/api/v1/compliance-scheme/resubmission-fees")) .RespondWith(Response.Create() .WithStatusCode(200) .WithHeader("Content-Type", "application/json") diff --git a/src/MockRegulatorFacade/Responses/FacadeApi/RegistrationSubmissionDetails/a1b2c3d4-e5f6-7890-abcd-ef1234567891.json b/src/MockRegulatorFacade/Responses/FacadeApi/RegistrationSubmissionDetails/a1b2c3d4-e5f6-7890-abcd-ef1234567891.json new file mode 100644 index 000000000..1d7e7c290 --- /dev/null +++ b/src/MockRegulatorFacade/Responses/FacadeApi/RegistrationSubmissionDetails/a1b2c3d4-e5f6-7890-abcd-ef1234567891.json @@ -0,0 +1,88 @@ +{ + "submissionId": "A1B2C3D4-E5F6-7890-ABCD-EF1234567891", + "organisationId": "B2C3D4E5-F6A7-8901-BCDE-F12345678902", + "organisationReference": "100021", + "organisationName": "Resubmission Compliance Scheme Ltd", + "organisationType": "compliance", + "registrationJourneyType": "CsoLargeProducer", + "nationId": 1, + "nationCode": "GB-ENG", + "relevantYear": 2025, + "submissionDate": "2025-02-01T09:30:00Z", + "submissionStatus": "Queried", + "resubmissionStatus": "Pending", + "resubmissionDate": "2025-02-05T10:00:00Z", + "statusPendingDate": "2025-02-01T09:30:00Z", + "isResubmission": true, + "registrationDate": null, + "regulatorComments": "Please review the updated member list", + "producerComments": "Resubmitted with corrected member details", + "applicationReferenceNumber": "REG-2025-021", + "registrationReferenceNumber": null, + "companiesHouseNumber": "CS123456", + "buildingName": "Compliance House", + "subBuildingName": "", + "buildingNumber": "21", + "street": "Scheme Road", + "locality": "", + "dependentLocality": "", + "town": "Scheme Town", + "county": "Scheme County", + "country": "United Kingdom", + "postcode": "SC1 1AA", + "submissionDetails": { + "status": "Queried", + "decisionDate": null, + "resubmissionDecisionDate": null, + "statusPendingDate": "2025-02-01T09:30:00Z", + "timeAndDateOfSubmission": "2025-02-01T09:30:00Z", + "submittedOnTime": true, + "submittedByUserId": "3721d2cb-3ba3-4b2a-9eb3-00de35dc2054", + "accountRole": "Approved Person", + "telephone": "01234567021", + "email": "compliance.resub@example.com", + "declaredBy": "Not required (compliance scheme)", + "files": [{ + "type": "company", + "fileId": "a1b2c3d4-e5f6-7890-abcd-ef1234567800", + "fileName": "reg-20250201_093000.csv", + "blobName": "BLOB-021-001" + }, { + "type": "company", + "fileId": "c1d2e3f4-a5b6-7890-cdef-012345678901", + "fileName": "resub-20250205_100000.csv", + "blobName": "BLOB-021-002" + }], + "submissionPeriod": "January to December 2025", + "accountRoleId": 1, + "submittedBy": "Emma Scheme", + "resubmissionStatus": "Pending", + "registrationDate": null, + "resubmissionDate": "2025-02-05T10:00:00Z", + "isResubmission": true, + "resubmissionFileId": "c1d2e3f4-a5b6-7890-cdef-012345678901" + }, + "regulatorDecisionDate": null, + "regulatorResubmissionDecisionDate": null, + "producerCommentDate": "2025-02-05T10:00:00Z", + "regulatorUserId": "310187eb-e4db-49d1-b075-cf4200b2956d", + "isOnlineMarketPlace": false, + "numberOfSubsidiaries": 0, + "numberOfOnlineSubsidiaries": 0, + "isLateSubmission": false, + "organisationSize": null, + "isComplianceScheme": true, + "submissionPeriod": "January to December 2025", + "csoMembershipDetails": [{ + "memberId": "100001", + "memberType": "large", + "isOnlineMarketPlace": false, + "isLateFeeApplicable": false, + "numberOfSubsidiaries": 0, + "NumberOfSubsidiariesOnlineMarketPlace": 0, + "relevantYear": 2025, + "submittedDate": "2025-02-01T09:00:00", + "submissionPeriodDescription": "January to December 2025" + }], + "resubmissionFileId": "c1d2e3f4-a5b6-7890-cdef-012345678901" +} diff --git a/src/MockRegulatorFacade/Responses/FacadeApi/RegistrationSubmissionDetails/c3d4e5f6-a7b8-9012-cdef-123456789013.json b/src/MockRegulatorFacade/Responses/FacadeApi/RegistrationSubmissionDetails/c3d4e5f6-a7b8-9012-cdef-123456789013.json new file mode 100644 index 000000000..ccac681b9 --- /dev/null +++ b/src/MockRegulatorFacade/Responses/FacadeApi/RegistrationSubmissionDetails/c3d4e5f6-a7b8-9012-cdef-123456789013.json @@ -0,0 +1,78 @@ +{ + "submissionId": "C3D4E5F6-A7B8-9012-CDEF-123456789013", + "organisationId": "D4E5F6A7-B8C9-0123-DEFA-234567890124", + "organisationReference": "100022", + "organisationName": "Resubmission Direct Producer Ltd", + "organisationType": "large", + "registrationJourneyType": "DirectLargeProducer", + "nationId": 1, + "nationCode": "GB-ENG", + "relevantYear": 2025, + "submissionDate": "2025-02-02T10:30:00Z", + "submissionStatus": "Queried", + "resubmissionStatus": "Pending", + "resubmissionDate": "2025-02-06T11:00:00Z", + "statusPendingDate": "2025-02-02T10:30:00Z", + "isResubmission": true, + "registrationDate": null, + "regulatorComments": "Please provide updated packaging data", + "producerComments": "Resubmitted with corrected packaging figures", + "applicationReferenceNumber": "REG-2025-022", + "registrationReferenceNumber": null, + "companiesHouseNumber": "DP123456", + "buildingName": "Producer House", + "subBuildingName": "", + "buildingNumber": "22", + "street": "Producer Road", + "locality": "", + "dependentLocality": "", + "town": "Producer Town", + "county": "Producer County", + "country": "United Kingdom", + "postcode": "PR1 2BB", + "submissionDetails": { + "status": "Queried", + "decisionDate": null, + "resubmissionDecisionDate": null, + "statusPendingDate": "2025-02-02T10:30:00Z", + "timeAndDateOfSubmission": "2025-02-02T10:30:00Z", + "submittedOnTime": true, + "submittedByUserId": "e1b1039f-755e-42a1-a408-6061cf7f0fed", + "accountRole": "Approved Person", + "telephone": "01234567022", + "email": "producer.resub@example.com", + "declaredBy": "Frank Producer", + "files": [{ + "type": "company", + "fileId": "c3d4e5f6-a7b8-9012-cdef-123456789000", + "fileName": "reg-20250202_103000.csv", + "blobName": "BLOB-022-001" + }, { + "type": "company", + "fileId": "d2e3f4a5-b6c7-8901-defa-123456789012", + "fileName": "resub-20250206_110000.csv", + "blobName": "BLOB-022-002" + }], + "submissionPeriod": "January to December 2025", + "accountRoleId": 1, + "submittedBy": "Frank Producer", + "resubmissionStatus": "Pending", + "registrationDate": null, + "resubmissionDate": "2025-02-06T11:00:00Z", + "isResubmission": true, + "resubmissionFileId": "d2e3f4a5-b6c7-8901-defa-123456789012" + }, + "regulatorDecisionDate": null, + "regulatorResubmissionDecisionDate": null, + "producerCommentDate": "2025-02-06T11:00:00Z", + "regulatorUserId": "870c5a94-cb91-4af3-82bc-21bdf1940fdf", + "isOnlineMarketPlace": false, + "numberOfSubsidiaries": 5, + "numberOfOnlineSubsidiaries": 0, + "isLateSubmission": false, + "organisationSize": "Large", + "isComplianceScheme": false, + "submissionPeriod": "January to December 2025", + "csoMembershipDetails": [], + "resubmissionFileId": "d2e3f4a5-b6c7-8901-defa-123456789012" +} diff --git a/src/MockRegulatorFacade/Responses/FacadeApi/organisation-registration-submissions.json b/src/MockRegulatorFacade/Responses/FacadeApi/organisation-registration-submissions.json index 102597891..05f857b1d 100644 --- a/src/MockRegulatorFacade/Responses/FacadeApi/organisation-registration-submissions.json +++ b/src/MockRegulatorFacade/Responses/FacadeApi/organisation-registration-submissions.json @@ -499,9 +499,59 @@ "regulatorCommentDate": null, "producerCommentDate": null, "regulatorUserId": "7fff4b74-26a0-41b2-806e-1ca276b24c43" + }, + { + "submissionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567891", + "organisationId": "b2c3d4e5-f6a7-8901-bcde-f12345678902", + "organisationReference": "100021", + "organisationName": "Resubmission Compliance Scheme Ltd", + "organisationType": "compliance", + "registrationJourneyType": "CsoLargeProducer", + "applicationReferenceNumber": "REG-2025-021", + "registrationReferenceNumber": null, + "submissionDate": "2025-02-01T09:30:00Z", + "registrationYear": 2025, + "nationId": 1, + "submissionStatus": "Queried", + "resubmissionStatus": "Pending", + "statusPendingDate": "2025-02-01T09:30:00Z", + "isResubmission": true, + "resubmissionFileId": "c1d2e3f4-a5b6-7890-cdef-012345678901", + "resubmissionDate": "2025-02-05T10:00:00Z", + "registrationDate": null, + "regulatorDecisionDate": null, + "resubmissionDecisionDate": null, + "regulatorCommentDate": "2025-02-03T09:00:00Z", + "producerCommentDate": "2025-02-05T10:00:00Z", + "regulatorUserId": "310187eb-e4db-49d1-b075-cf4200b2956d" + }, + { + "submissionId": "c3d4e5f6-a7b8-9012-cdef-123456789013", + "organisationId": "d4e5f6a7-b8c9-0123-defa-234567890124", + "organisationReference": "100022", + "organisationName": "Resubmission Direct Producer Ltd", + "organisationType": "large", + "registrationJourneyType": "DirectLargeProducer", + "applicationReferenceNumber": "REG-2025-022", + "registrationReferenceNumber": null, + "submissionDate": "2025-02-02T10:30:00Z", + "registrationYear": 2025, + "nationId": 1, + "submissionStatus": "Queried", + "resubmissionStatus": "Pending", + "statusPendingDate": "2025-02-02T10:30:00Z", + "isResubmission": true, + "resubmissionFileId": "d2e3f4a5-b6c7-8901-defa-123456789012", + "resubmissionDate": "2025-02-06T11:00:00Z", + "registrationDate": null, + "regulatorDecisionDate": null, + "resubmissionDecisionDate": null, + "regulatorCommentDate": "2025-02-04T10:00:00Z", + "producerCommentDate": "2025-02-06T11:00:00Z", + "regulatorUserId": "870c5a94-cb91-4af3-82bc-21bdf1940fdf" } ], "currentPage": 1, "pageSize": 20, - "totalItems": 20 + "totalItems": 22 } From 716fec47311537e0d7e5168963b9227a9f3f6290 Mon Sep 17 00:00:00 2001 From: Daniel Ashton Date: Wed, 8 Apr 2026 12:52:11 +0100 Subject: [PATCH 5/8] AMCR-65 - Pass fileId in other payment scenarios --- .../Submissions/PackagingCompliancePaymentRequest.cs | 2 ++ .../Submissions/PackagingProducerPaymentRequest.cs | 2 ++ .../Models/Submissions/Submission.cs | 2 +- .../Web/Controllers/SubmissionControllerTests.cs | 10 +++++++--- .../Controllers/Submissions/SubmissionsController.cs | 3 ++- .../Submissions/SubmissionsController_part.cs | 9 ++++++--- .../PackagingCompliancePaymentDetailsViewComponent.cs | 3 ++- .../PackagingProducerPaymentDetailsViewComponent.cs | 3 ++- .../Submissions/SubmissionDetailsViewModel.cs | 1 + 9 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/PackagingCompliancePaymentRequest.cs b/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/PackagingCompliancePaymentRequest.cs index 17d255caf..08b179910 100644 --- a/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/PackagingCompliancePaymentRequest.cs +++ b/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/PackagingCompliancePaymentRequest.cs @@ -9,4 +9,6 @@ public class PackagingCompliancePaymentRequest public required string Regulator { get; set; } public DateTime ResubmissionDate { get; set; } + + public Guid? FileId { get; set; } } \ No newline at end of file diff --git a/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/PackagingProducerPaymentRequest.cs b/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/PackagingProducerPaymentRequest.cs index a2ad89bf1..8ad10b696 100644 --- a/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/PackagingProducerPaymentRequest.cs +++ b/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/PackagingProducerPaymentRequest.cs @@ -9,4 +9,6 @@ public class PackagingProducerPaymentRequest public required DateTime ResubmissionDate { get; set; } public required int MemberCount { get; set; } + + public Guid? FileId { get; set; } } \ No newline at end of file diff --git a/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/Submission.cs b/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/Submission.cs index dd387643c..12f886a15 100644 --- a/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/Submission.cs +++ b/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/Submission.cs @@ -3,7 +3,7 @@ namespace EPR.RegulatorService.Frontend.Core.Models.Submissions; public class Submission : AbstractSubmission { public bool IsResubmissionRequired { get; set; } - public Guid FileId { get; set; } + public Guid? FileId { get; set; } public string? ProducerType { get; set; } public Guid? ComplianceSchemeId { get; set; } public string? ActualSubmissionPeriod { get; set; } diff --git a/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/SubmissionControllerTests.cs b/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/SubmissionControllerTests.cs index 1e9a603ea..fbadd745f 100644 --- a/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/SubmissionControllerTests.cs +++ b/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/SubmissionControllerTests.cs @@ -949,12 +949,14 @@ public async Task ConfirmOfflinePaymentSubmission_POST_RedirectsToSubmissionDeta { // Arrange var submissionId = Guid.NewGuid(); + var fileId = Guid.NewGuid(); JourneySessionMock.RegulatorSubmissionSession.OrganisationSubmissions[_hashCode] = new Submission { SubmissionId = submissionId, UserId = Guid.NewGuid(), ReferenceNumber = "degreg", - NationCode = "GB-ENG" + NationCode = "GB-ENG", + FileId = fileId }; JourneySessionMock.UserData.Organisations = @@ -981,7 +983,7 @@ public async Task ConfirmOfflinePaymentSubmission_POST_RedirectsToSubmissionDeta _paymentFacadeServiceMock.Verify(r => r.SubmitOfflinePaymentAsync( - It.IsAny()), + It.Is(req => req.FileId == fileId)), Times.AtMostOnce); _facadeServiceMock.Verify(r => @@ -995,10 +997,12 @@ public async Task ConfirmOfflinePaymentSubmission_POST_RedirectsToServiceNotAvai { // Arrange var submissionId = Guid.NewGuid(); + var fileId = Guid.NewGuid(); JourneySessionMock.RegulatorSubmissionSession.OrganisationSubmissions[_hashCode] = new Submission { SubmissionId = submissionId, - UserId = Guid.NewGuid() + UserId = Guid.NewGuid(), + FileId = fileId }; JourneySessionMock.UserData.Organisations = diff --git a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs index aea887440..30606005e 100644 --- a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs +++ b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs @@ -311,7 +311,8 @@ public async Task ConfirmOfflinePaymentSubmission(ConfirmOfflineP model.OfflinePaymentAmount, submission.UserId.Value, submission.SubmissionId, - model.SubmissionHash.Value); + model.SubmissionHash.Value, + submission.FileId); } [HttpGet] diff --git a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController_part.cs b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController_part.cs index 4fa45305a..c744f029e 100644 --- a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController_part.cs +++ b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController_part.cs @@ -162,7 +162,8 @@ private ViewModels.Submissions.SubmissionDetailsViewModel GetSubmissionDetailsVi NationCode = submission.NationCode, ReferenceNumber = submission.ReferenceNumber, MemberCount = submission.MemberCount, - ComplianceSchemeId = submission.ComplianceSchemeId + ComplianceSchemeId = submission.ComplianceSchemeId, + FileId = submission.FileId }; return model; @@ -174,7 +175,8 @@ private async Task ProcessOfflinePaymentAsync( string offlinePaymentAmount, Guid userId, Guid submissionId, - int submissionHash) + int submissionHash, + Guid? fileId) { var response = await _paymentFacadeService.SubmitOfflinePaymentAsync(new OfflinePaymentRequest { @@ -182,7 +184,8 @@ private async Task ProcessOfflinePaymentAsync( Description = "Packaging data resubmission fee", Reference = referenceNumber, Regulator = nationCode, - UserId = userId + UserId = userId, + FileId = fileId }); if (response == EndpointResponseStatus.Fail) diff --git a/src/EPR.RegulatorService.Frontend.Web/ViewComponents/Submissions/PackagingCompliancePaymentDetailsViewComponent.cs b/src/EPR.RegulatorService.Frontend.Web/ViewComponents/Submissions/PackagingCompliancePaymentDetailsViewComponent.cs index 3e6575245..fb56ba922 100644 --- a/src/EPR.RegulatorService.Frontend.Web/ViewComponents/Submissions/PackagingCompliancePaymentDetailsViewComponent.cs +++ b/src/EPR.RegulatorService.Frontend.Web/ViewComponents/Submissions/PackagingCompliancePaymentDetailsViewComponent.cs @@ -46,7 +46,8 @@ public async Task InvokeAsync(SubmissionDetailsViewMode ReferenceNumber = viewModel.ReferenceNumber, MemberCount = viewModel.MemberCount, Regulator = viewModel.NationCode, - ResubmissionDate = TimeZoneInfo.ConvertTimeToUtc(viewModel.SubmittedDate) //payment facade in utc format + ResubmissionDate = TimeZoneInfo.ConvertTimeToUtc(viewModel.SubmittedDate), //payment facade in utc format + FileId = viewModel.FileId }); if (compliancePaymentResponse is null) diff --git a/src/EPR.RegulatorService.Frontend.Web/ViewComponents/Submissions/PackagingProducerPaymentDetailsViewComponent.cs b/src/EPR.RegulatorService.Frontend.Web/ViewComponents/Submissions/PackagingProducerPaymentDetailsViewComponent.cs index 574cea16a..cb304c4a4 100644 --- a/src/EPR.RegulatorService.Frontend.Web/ViewComponents/Submissions/PackagingProducerPaymentDetailsViewComponent.cs +++ b/src/EPR.RegulatorService.Frontend.Web/ViewComponents/Submissions/PackagingProducerPaymentDetailsViewComponent.cs @@ -50,7 +50,8 @@ await featureManager.IsEnabledAsync(FeatureFlags.IncludeSubsidiariesInFeeCalcula ReferenceNumber = viewModel.ReferenceNumber, Regulator = viewModel.NationCode, MemberCount = memberCount, - ResubmissionDate = TimeZoneInfo.ConvertTimeToUtc(viewModel.SubmittedDate) //payment facade in utc format + ResubmissionDate = TimeZoneInfo.ConvertTimeToUtc(viewModel.SubmittedDate), //payment facade in utc format + FileId = viewModel.FileId }); if (producerPaymentResponse is null) diff --git a/src/EPR.RegulatorService.Frontend.Web/ViewModels/Submissions/SubmissionDetailsViewModel.cs b/src/EPR.RegulatorService.Frontend.Web/ViewModels/Submissions/SubmissionDetailsViewModel.cs index 378aa1de4..d171dbd7a 100644 --- a/src/EPR.RegulatorService.Frontend.Web/ViewModels/Submissions/SubmissionDetailsViewModel.cs +++ b/src/EPR.RegulatorService.Frontend.Web/ViewModels/Submissions/SubmissionDetailsViewModel.cs @@ -25,4 +25,5 @@ public class SubmissionDetailsViewModel : BaseSubmissionDetailsViewModel public DateTime SubmittedDate { get; set; } public string SubmissionBlobName { get; set; } public Guid? ComplianceSchemeId { get; set; } + public Guid? FileId { get; set; } } \ No newline at end of file From c2922523aa6930087f810f9e42de1f917df956b1 Mon Sep 17 00:00:00 2001 From: Daniel Ashton Date: Wed, 8 Apr 2026 13:01:08 +0100 Subject: [PATCH 6/8] AMCR-65 - Revert nullable change on fileid --- .../Models/Submissions/Submission.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/Submission.cs b/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/Submission.cs index 12f886a15..dd387643c 100644 --- a/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/Submission.cs +++ b/src/EPR.RegulatorService.Frontend.Core/Models/Submissions/Submission.cs @@ -3,7 +3,7 @@ namespace EPR.RegulatorService.Frontend.Core.Models.Submissions; public class Submission : AbstractSubmission { public bool IsResubmissionRequired { get; set; } - public Guid? FileId { get; set; } + public Guid FileId { get; set; } public string? ProducerType { get; set; } public Guid? ComplianceSchemeId { get; set; } public string? ActualSubmissionPeriod { get; set; } From 670ccf4d1bd9c629a743e61a75626b6dabee330c Mon Sep 17 00:00:00 2001 From: Daniel Ashton Date: Wed, 8 Apr 2026 15:00:54 +0100 Subject: [PATCH 7/8] AMCR-65 - Make sure we pass nulls if no file id --- .../Controllers/SubmissionControllerTests.cs | 36 +++++++++++++++++++ .../Submissions/SubmissionsController.cs | 2 +- .../Submissions/SubmissionsController_part.cs | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/SubmissionControllerTests.cs b/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/SubmissionControllerTests.cs index fbadd745f..544a13b98 100644 --- a/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/SubmissionControllerTests.cs +++ b/src/EPR.RegulatorService.Frontend.UnitTests/Web/Controllers/SubmissionControllerTests.cs @@ -992,6 +992,42 @@ public async Task ConfirmOfflinePaymentSubmission_POST_RedirectsToSubmissionDeta Times.AtMostOnce); } + [TestMethod] + public async Task ConfirmOfflinePaymentSubmission_POST_PassesNullFileId_WhenFileIdIsEmpty() + { + // Arrange + var submissionId = Guid.NewGuid(); + JourneySessionMock.RegulatorSubmissionSession.OrganisationSubmissions[_hashCode] = new Submission + { + SubmissionId = submissionId, + UserId = Guid.NewGuid(), + ReferenceNumber = "degreg", + NationCode = "GB-ENG", + FileId = Guid.Empty + }; + + JourneySessionMock.UserData.Organisations = + [ + new() { NationId = 1 } + ]; + + var model = new ConfirmOfflinePaymentSubmissionViewModel + { + SubmissionHash = _hashCode, + IsOfflinePaymentConfirmed = true, + OfflinePaymentAmount = DefaultOfflinePaymentAmount, + }; + + // Act + await _systemUnderTest.ConfirmOfflinePaymentSubmission(model); + + // Assert + _paymentFacadeServiceMock.Verify(r => + r.SubmitOfflinePaymentAsync( + It.Is(req => req.FileId == null)), + Times.AtMostOnce); + } + [TestMethod] public async Task ConfirmOfflinePaymentSubmission_POST_RedirectsToServiceNotAvailable_WhenPaymentFacade_ReturnsNonSuccess() { diff --git a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs index 30606005e..71a67320e 100644 --- a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs +++ b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs @@ -312,7 +312,7 @@ public async Task ConfirmOfflinePaymentSubmission(ConfirmOfflineP submission.UserId.Value, submission.SubmissionId, model.SubmissionHash.Value, - submission.FileId); + submission.FileId == Guid.Empty ? null : submission.FileId); } [HttpGet] diff --git a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController_part.cs b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController_part.cs index c744f029e..edd778d88 100644 --- a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController_part.cs +++ b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController_part.cs @@ -163,7 +163,7 @@ private ViewModels.Submissions.SubmissionDetailsViewModel GetSubmissionDetailsVi ReferenceNumber = submission.ReferenceNumber, MemberCount = submission.MemberCount, ComplianceSchemeId = submission.ComplianceSchemeId, - FileId = submission.FileId + FileId = submission.FileId == Guid.Empty ? null : submission.FileId }; return model; From 2c8244d950d8d57cbf1ac8cae911797f72a4ecc0 Mon Sep 17 00:00:00 2001 From: Daniel Ashton Date: Wed, 8 Apr 2026 16:53:11 +0100 Subject: [PATCH 8/8] AMCR-65 - Sonar fix --- .../Controllers/Submissions/SubmissionsController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs index 71a67320e..03803b4e0 100644 --- a/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs +++ b/src/EPR.RegulatorService.Frontend.Web/Controllers/Submissions/SubmissionsController.cs @@ -296,6 +296,7 @@ public async Task ConfirmOfflinePaymentSubmission(ConfirmOfflineP } TempData.Remove("OfflinePaymentAmount"); + Guid? fileId = submission.FileId == Guid.Empty ? null : submission.FileId; return string.IsNullOrWhiteSpace(model.OfflinePaymentAmount) ? RedirectToAction( PagePath.Error, @@ -312,7 +313,7 @@ public async Task ConfirmOfflinePaymentSubmission(ConfirmOfflineP submission.UserId.Value, submission.SubmissionId, model.SubmissionHash.Value, - submission.FileId == Guid.Empty ? null : submission.FileId); + fileId); } [HttpGet]