From c2e95da48e1014bd8a4dfdc6e86084b4995876a3 Mon Sep 17 00:00:00 2001 From: Jawwad Baig Date: Mon, 30 Sep 2024 14:12:28 +0100 Subject: [PATCH 1/9] #dp-543 exclusions grouped single choice type added --- Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs | 3 ++- Services/CO.CDP.Forms.WebApi/Model/FormQuestion.cs | 4 +++- .../Forms/FormQuestion.cs | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs b/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs index 0785731fe..4cabaab0b 100644 --- a/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs +++ b/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs @@ -78,5 +78,6 @@ public enum FormQuestionType Date, CheckBox, Address, - MultiLine + MultiLine, + GroupedSingleChoice } \ No newline at end of file diff --git a/Services/CO.CDP.Forms.WebApi/Model/FormQuestion.cs b/Services/CO.CDP.Forms.WebApi/Model/FormQuestion.cs index edd2a6591..a7090a9d0 100644 --- a/Services/CO.CDP.Forms.WebApi/Model/FormQuestion.cs +++ b/Services/CO.CDP.Forms.WebApi/Model/FormQuestion.cs @@ -29,5 +29,7 @@ public enum FormQuestionType CheckYourAnswers, Date, CheckBox, - Address + Address, + MultiLine, + GroupedSingleChoice } \ No newline at end of file diff --git a/Services/CO.CDP.OrganisationInformation.Persistence/Forms/FormQuestion.cs b/Services/CO.CDP.OrganisationInformation.Persistence/Forms/FormQuestion.cs index f516f6656..baf01934b 100644 --- a/Services/CO.CDP.OrganisationInformation.Persistence/Forms/FormQuestion.cs +++ b/Services/CO.CDP.OrganisationInformation.Persistence/Forms/FormQuestion.cs @@ -35,7 +35,8 @@ public enum FormQuestionType Date, CheckBox, Address, - MultiLine + MultiLine, + GroupedSingleChoice } public record FormQuestionOptions From 9f61c24c91aea0461cb460694ab15194d7a5dbf0 Mon Sep 17 00:00:00 2001 From: Jawwad Baig Date: Tue, 1 Oct 2024 01:30:44 +0100 Subject: [PATCH 2/9] #dp-543 exclusions grouped choice question type migration added --- .../FormsEngineTests.cs | 81 +- .../CO.CDP.OrganisationApp/FormsEngine.cs | 3 +- .../Models/DynamicForms.cs | 1 + .../AutoMapper/WebApiToPersistenceProfile.cs | 4 + .../Model/FormQuestionGroup.cs | 15 + .../Model/FormQuestionOptions.cs | 1 + .../Forms/FormQuestion.cs | 15 + ...ionFormDataGroupedSingleChoice.Designer.cs | 1732 +++++++++++++++++ ...11_ExclusionFormDataGroupedSingleChoice.cs | 112 ++ 9 files changed, 1933 insertions(+), 31 deletions(-) create mode 100644 Services/CO.CDP.Forms.WebApi/Model/FormQuestionGroup.cs create mode 100644 Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.Designer.cs create mode 100644 Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.cs diff --git a/Frontend/CO.CDP.OrganisationApp.Tests/FormsEngineTests.cs b/Frontend/CO.CDP.OrganisationApp.Tests/FormsEngineTests.cs index 3456a5237..584ef8977 100644 --- a/Frontend/CO.CDP.OrganisationApp.Tests/FormsEngineTests.cs +++ b/Frontend/CO.CDP.OrganisationApp.Tests/FormsEngineTests.cs @@ -48,37 +48,56 @@ private static WebApiClient.SectionQuestionsResponse CreateApiSectionQuestionsRe id: sectionId, title: "SectionTitle" ), - questions: new List - { - new WebApiClient.FormQuestion( - id: questionId, - title: "Question1", - description: "Description1", - caption: "Caption1", - summaryTitle: "Question1 Title", - type: WebApiClient.FormQuestionType.Text, - isRequired: true, - nextQuestion: nextQuestionId, - nextQuestionAlternative: null, - options: new WebApiClient.FormQuestionOptions( - choiceProviderStrategy: null, - choices: new List - { - new WebApiClient.FormQuestionChoice( - id: Guid.NewGuid(), - title: "Option1", - groupName: null, - hint: new WebApiClient.FormQuestionChoiceHint( - title: null, - description: "Hint Description" - ) + questions: new List + { + new WebApiClient.FormQuestion( + id: questionId, + title: "Question1", + description: "Description1", + caption: "Caption1", + summaryTitle: "Question1 Title", + type: WebApiClient.FormQuestionType.Text, + isRequired: true, + nextQuestion: nextQuestionId, + nextQuestionAlternative: null, + options: new WebApiClient.FormQuestionOptions( + choiceProviderStrategy: null, + choices: new List + { + new WebApiClient.FormQuestionChoice( + id: Guid.NewGuid(), + title: "Option1", + groupName: null, + hint: new WebApiClient.FormQuestionChoiceHint( + title: null, + description: "Hint Description" + ) + ) + }, + groups: new List + { + new WebApiClient.FormQuestionGroup( + name: "Group 1", + hint: "Group 1 Hint", + caption: "Group 1 Caption", + choices: new List + { + new WebApiClient.FormQuestionGroupChoice( + title: "Group Choice 1", + value: "group_choice_1" + ), + new WebApiClient.FormQuestionGroupChoice( + title: "Group Choice 2", + value: "group_choice_2" + ) + } + ) + } ) - } - ) - ) - }, - answerSets: new List() - ); + ) + }, + answerSets: new List() + ); } private static SectionQuestionsResponse CreateModelSectionQuestionsResponse(Guid sectionId, Guid questionId, Guid nextQuestionId) @@ -133,6 +152,8 @@ public async Task GetFormSectionAsync_ShouldFetchAndCacheResponse_WhenCachedResp var apiResponse = CreateApiSectionQuestionsResponse(sectionId, questionId, nextQuestionId); var expectedResponse = CreateModelSectionQuestionsResponse(sectionId, questionId, nextQuestionId); + expectedResponse.Questions[0].Options.Groups = new List { "Group 1" }; + _tempDataServiceMock.Setup(t => t.Peek(sessionKey)) .Returns((SectionQuestionsResponse?)null); _formsApiClientMock.Setup(c => c.GetFormSectionQuestionsAsync(formId, sectionId, organisationId)) diff --git a/Frontend/CO.CDP.OrganisationApp/FormsEngine.cs b/Frontend/CO.CDP.OrganisationApp/FormsEngine.cs index 3b866350a..64a53271e 100644 --- a/Frontend/CO.CDP.OrganisationApp/FormsEngine.cs +++ b/Frontend/CO.CDP.OrganisationApp/FormsEngine.cs @@ -46,7 +46,8 @@ public async Task GetFormSectionAsync(Guid organisatio Options = new Models.FormQuestionOptions { Choices = q.Options.Choices.Select(c => c.Title).ToList(), - ChoiceProviderStrategy = q.Options.ChoiceProviderStrategy + ChoiceProviderStrategy = q.Options.ChoiceProviderStrategy, + Groups = q.Options.Groups.Select(c => c.Name).ToList() } }).ToList() }; diff --git a/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs b/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs index 4cabaab0b..c66234b58 100644 --- a/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs +++ b/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs @@ -38,6 +38,7 @@ public class FormQuestionOptions { public List? Choices { get; set; } public string? ChoiceProviderStrategy { get; set; } + public List? Groups { get; set; } = []; } public class FormQuestionAnswerState diff --git a/Services/CO.CDP.Forms.WebApi/AutoMapper/WebApiToPersistenceProfile.cs b/Services/CO.CDP.Forms.WebApi/AutoMapper/WebApiToPersistenceProfile.cs index 0e1fd2271..b8548fa84 100644 --- a/Services/CO.CDP.Forms.WebApi/AutoMapper/WebApiToPersistenceProfile.cs +++ b/Services/CO.CDP.Forms.WebApi/AutoMapper/WebApiToPersistenceProfile.cs @@ -23,6 +23,10 @@ public WebApiToPersistenceProfile() CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); CreateMap() diff --git a/Services/CO.CDP.Forms.WebApi/Model/FormQuestionGroup.cs b/Services/CO.CDP.Forms.WebApi/Model/FormQuestionGroup.cs new file mode 100644 index 000000000..044023e04 --- /dev/null +++ b/Services/CO.CDP.Forms.WebApi/Model/FormQuestionGroup.cs @@ -0,0 +1,15 @@ +namespace CO.CDP.Forms.WebApi.Model; + +public class FormQuestionGroup +{ + public required string Name { get; set; } + public required string Hint { get; set; } + public required string Caption { get; set; } + public ICollection? Choices { get; set; } +} + +public class FormQuestionGroupChoice +{ + public required string? Title { get; set; } + public required string? Value { get; set; } = null; +} \ No newline at end of file diff --git a/Services/CO.CDP.Forms.WebApi/Model/FormQuestionOptions.cs b/Services/CO.CDP.Forms.WebApi/Model/FormQuestionOptions.cs index 1cef4bda7..e42c29f07 100644 --- a/Services/CO.CDP.Forms.WebApi/Model/FormQuestionOptions.cs +++ b/Services/CO.CDP.Forms.WebApi/Model/FormQuestionOptions.cs @@ -4,4 +4,5 @@ public record FormQuestionOptions { public List? Choices { get; init; } = new(); public string? ChoiceProviderStrategy { get; init; } + public List? Groups { get; set; } = new(); } \ No newline at end of file diff --git a/Services/CO.CDP.OrganisationInformation.Persistence/Forms/FormQuestion.cs b/Services/CO.CDP.OrganisationInformation.Persistence/Forms/FormQuestion.cs index baf01934b..3e8e55eb9 100644 --- a/Services/CO.CDP.OrganisationInformation.Persistence/Forms/FormQuestion.cs +++ b/Services/CO.CDP.OrganisationInformation.Persistence/Forms/FormQuestion.cs @@ -43,6 +43,7 @@ public record FormQuestionOptions { public ICollection? Choices { get; set; } = null; public string? ChoiceProviderStrategy = null; + public ICollection? Groups { get; set; } = null; } public class FormQuestionChoice @@ -57,4 +58,18 @@ public class FormQuestionChoiceHint { public required string? Title { get; set; } = null; public required string Description { get; set; } +} + +public class FormQuestionGroup +{ + public required string Name { get; set; } + public required string Hint { get; set; } + public required string Caption { get; set; } + public ICollection? Choices { get; set; } +} + +public class FormQuestionGroupChoice +{ + public required string? Title { get; set; } + public required string? Value { get; set; } = null; } \ No newline at end of file diff --git a/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.Designer.cs b/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.Designer.cs new file mode 100644 index 000000000..a49bbe66f --- /dev/null +++ b/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.Designer.cs @@ -0,0 +1,1732 @@ +// +using System; +using System.Collections.Generic; +using CO.CDP.OrganisationInformation.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace CO.CDP.OrganisationInformation.Persistence.Migrations +{ + [DbContext(typeof(OrganisationInformationContext))] + [Migration("20240930215811_ExclusionFormDataGroupedSingleChoice")] + partial class ExclusionFormDataGroupedSingleChoice + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "connected_entity_type", new[] { "organisation", "individual", "trust_or_trustee" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "connected_organisation_category", new[] { "registered_company", "director_or_the_same_responsibilities", "parent_or_subsidiary_company", "a_company_your_organisation_has_taken_over", "any_other_organisation_with_significant_influence_or_control" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "connected_person_category", new[] { "person_with_significant_control", "director_or_individual_with_the_same_responsibilities", "any_other_individual_with_significant_influence_or_control" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "connected_person_type", new[] { "individual", "trust_or_trustee" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_condition", new[] { "none", "owns_shares", "has_voting_rights", "can_appoint_or_remove_directors", "has_other_significant_influence_or_control" }); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Country") + .IsRequired() + .HasColumnType("text") + .HasColumnName("country"); + + b.Property("CountryName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("country_name"); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Locality") + .IsRequired() + .HasColumnType("text") + .HasColumnName("locality"); + + b.Property("PostalCode") + .IsRequired() + .HasColumnType("text") + .HasColumnName("postal_code"); + + b.Property("Region") + .HasColumnType("text") + .HasColumnName("region"); + + b.Property("StreetAddress") + .IsRequired() + .HasColumnType("text") + .HasColumnName("street_address"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_addresses"); + + b.ToTable("addresses", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.AuthenticationKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Key") + .IsRequired() + .HasColumnType("text") + .HasColumnName("key"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("OrganisationId") + .HasColumnType("integer") + .HasColumnName("organisation_id"); + + b.Property("Revoked") + .HasColumnType("boolean") + .HasColumnName("revoked"); + + b.Property("RevokedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("revoked_on"); + + b.Property("Scopes") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("scopes"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_authentication_keys"); + + b.HasIndex("OrganisationId") + .HasDatabaseName("ix_authentication_keys_organisation_id"); + + b.HasIndex("Name", "OrganisationId") + .IsUnique() + .HasDatabaseName("ix_authentication_keys_name_organisation_id"); + + NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("Name", "OrganisationId"), false); + + b.ToTable("authentication_keys", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.ConnectedEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CompanyHouseNumber") + .HasColumnType("text") + .HasColumnName("company_house_number"); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("end_date"); + + b.Property("EntityType") + .HasColumnType("integer") + .HasColumnName("entity_type"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("HasCompnayHouseNumber") + .HasColumnType("boolean") + .HasColumnName("has_compnay_house_number"); + + b.Property("OverseasCompanyNumber") + .HasColumnType("text") + .HasColumnName("overseas_company_number"); + + b.Property("RegisterName") + .HasColumnType("text") + .HasColumnName("register_name"); + + b.Property("RegisteredDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("registered_date"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_date"); + + b.Property("SupplierOrganisationId") + .HasColumnType("integer") + .HasColumnName("supplier_organisation_id"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_connected_entities"); + + b.HasIndex("Guid") + .IsUnique() + .HasDatabaseName("ix_connected_entities_guid"); + + b.HasIndex("SupplierOrganisationId") + .HasDatabaseName("ix_connected_entities_supplier_organisation_id"); + + b.ToTable("connected_entities", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.Form", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("IsRequired") + .HasColumnType("boolean") + .HasColumnName("is_required"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Scope") + .HasColumnType("integer") + .HasColumnName("scope"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Version") + .IsRequired() + .HasColumnType("text") + .HasColumnName("version"); + + b.HasKey("Id") + .HasName("pk_forms"); + + b.ToTable("forms", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddressValue") + .HasColumnType("jsonb") + .HasColumnName("address_value"); + + b.Property("BoolValue") + .HasColumnType("boolean") + .HasColumnName("bool_value"); + + b.Property("CreatedFrom") + .HasColumnType("uuid") + .HasColumnName("created_from"); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("DateValue") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_value"); + + b.Property("EndValue") + .HasColumnType("timestamp with time zone") + .HasColumnName("end_value"); + + b.Property("FormAnswerSetId") + .HasColumnType("integer") + .HasColumnName("form_answer_set_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("NumericValue") + .HasColumnType("double precision") + .HasColumnName("numeric_value"); + + b.Property("OptionValue") + .HasColumnType("text") + .HasColumnName("option_value"); + + b.Property("QuestionId") + .HasColumnType("integer") + .HasColumnName("question_id"); + + b.Property("StartValue") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_value"); + + b.Property("TextValue") + .HasColumnType("text") + .HasColumnName("text_value"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_form_answers"); + + b.HasIndex("FormAnswerSetId") + .HasDatabaseName("ix_form_answers_form_answer_set_id"); + + b.HasIndex("QuestionId") + .HasDatabaseName("ix_form_answers_question_id"); + + b.ToTable("form_answers", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormAnswerSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedFrom") + .HasColumnType("uuid") + .HasColumnName("created_from"); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Deleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("FurtherQuestionsExempted") + .HasColumnType("boolean") + .HasColumnName("further_questions_exempted"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("SectionId") + .HasColumnType("integer") + .HasColumnName("section_id"); + + b.Property("SharedConsentId") + .HasColumnType("integer") + .HasColumnName("shared_consent_id"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_form_answer_sets"); + + b.HasIndex("SectionId") + .HasDatabaseName("ix_form_answer_sets_section_id"); + + b.HasIndex("SharedConsentId") + .HasDatabaseName("ix_form_answer_sets_shared_consent_id"); + + b.ToTable("form_answer_sets", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Caption") + .HasColumnType("text") + .HasColumnName("caption"); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("IsRequired") + .HasColumnType("boolean") + .HasColumnName("is_required"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("NextQuestionAlternativeId") + .HasColumnType("integer") + .HasColumnName("next_question_alternative_id"); + + b.Property("NextQuestionId") + .HasColumnType("integer") + .HasColumnName("next_question_id"); + + b.Property("Options") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("options"); + + b.Property("SectionId") + .HasColumnType("integer") + .HasColumnName("section_id"); + + b.Property("SummaryTitle") + .HasColumnType("text") + .HasColumnName("summary_title"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_form_questions"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_form_questions_name"); + + b.HasIndex("NextQuestionAlternativeId") + .HasDatabaseName("ix_form_questions_next_question_alternative_id"); + + b.HasIndex("NextQuestionId") + .HasDatabaseName("ix_form_questions_next_question_id"); + + b.HasIndex("SectionId") + .HasDatabaseName("ix_form_questions_section_id"); + + b.ToTable("form_questions", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AllowsMultipleAnswerSets") + .HasColumnType("boolean") + .HasColumnName("allows_multiple_answer_sets"); + + b.Property("CheckFurtherQuestionsExempted") + .HasColumnType("boolean") + .HasColumnName("check_further_questions_exempted"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("configuration"); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("DisplayOrder") + .HasColumnType("integer") + .HasColumnName("display_order"); + + b.Property("FormId") + .HasColumnType("integer") + .HasColumnName("form_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("type"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_form_sections"); + + b.HasIndex("FormId") + .HasDatabaseName("ix_form_sections_form_id"); + + b.ToTable("form_sections", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.SharedConsent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedFrom") + .HasColumnType("uuid") + .HasColumnName("created_from"); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("FormId") + .HasColumnType("integer") + .HasColumnName("form_id"); + + b.Property("FormVersionId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("form_version_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("OrganisationId") + .HasColumnType("integer") + .HasColumnName("organisation_id"); + + b.Property("ShareCode") + .HasColumnType("text") + .HasColumnName("share_code"); + + b.Property("SubmissionState") + .HasColumnType("integer") + .HasColumnName("submission_state"); + + b.Property("SubmittedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("submitted_at"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_shared_consents"); + + b.HasIndex("FormId") + .HasDatabaseName("ix_shared_consents_form_id"); + + b.HasIndex("OrganisationId") + .HasDatabaseName("ix_shared_consents_organisation_id"); + + b.ToTable("shared_consents", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Organisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApprovedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("approved_on"); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("ReviewComment") + .HasMaxLength(10000) + .HasColumnType("character varying(10000)") + .HasColumnName("review_comment"); + + b.Property("ReviewedById") + .HasColumnType("integer") + .HasColumnName("reviewed_by_id"); + + b.Property("Roles") + .IsRequired() + .HasColumnType("integer[]") + .HasColumnName("roles"); + + b.Property("TenantId") + .HasColumnType("integer") + .HasColumnName("tenant_id"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_organisations"); + + b.HasIndex("Guid") + .IsUnique() + .HasDatabaseName("ix_organisations_guid"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_organisations_name"); + + b.HasIndex("ReviewedById") + .HasDatabaseName("ix_organisations_reviewed_by_id"); + + b.HasIndex("TenantId") + .HasDatabaseName("ix_organisations_tenant_id"); + + b.ToTable("organisations", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.OrganisationPerson", b => + { + b.Property("OrganisationId") + .HasColumnType("integer") + .HasColumnName("organisation_id"); + + b.Property("PersonId") + .HasColumnType("integer") + .HasColumnName("person_id"); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Scopes") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("scopes"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("OrganisationId", "PersonId") + .HasName("pk_organisation_person"); + + b.HasIndex("PersonId") + .HasDatabaseName("ix_organisation_person_person_id"); + + b.ToTable("organisation_person", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("first_name"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_name"); + + b.Property("Phone") + .HasColumnType("text") + .HasColumnName("phone"); + + b.Property>("Scopes") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("scopes"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("UserUrn") + .HasColumnType("text") + .HasColumnName("user_urn"); + + b.HasKey("Id") + .HasName("pk_persons"); + + b.HasIndex("Email") + .IsUnique() + .HasDatabaseName("ix_persons_email"); + + b.HasIndex("Guid") + .IsUnique() + .HasDatabaseName("ix_persons_guid"); + + b.ToTable("persons", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.PersonInvite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("first_name"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("InviteSentOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("invite_sent_on"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_name"); + + b.Property("OrganisationId") + .HasColumnType("integer") + .HasColumnName("organisation_id"); + + b.Property("PersonId") + .HasColumnType("integer") + .HasColumnName("person_id"); + + b.Property>("Scopes") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("scopes"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_person_invites"); + + b.HasIndex("Guid") + .IsUnique() + .HasDatabaseName("ix_person_invites_guid"); + + b.HasIndex("OrganisationId") + .HasDatabaseName("ix_person_invites_organisation_id"); + + b.HasIndex("PersonId") + .HasDatabaseName("ix_person_invites_person_id"); + + b.ToTable("person_invites", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("ExpiryDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiry_date"); + + b.Property("Revoked") + .HasColumnType("boolean") + .HasColumnName("revoked"); + + b.Property("TokenHash") + .IsRequired() + .HasColumnType("text") + .HasColumnName("token_hash"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_refresh_tokens"); + + b.HasIndex("TokenHash") + .IsUnique() + .HasDatabaseName("ix_refresh_tokens_token_hash"); + + b.ToTable("refresh_tokens", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("guid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id") + .HasName("pk_tenants"); + + b.HasIndex("Guid") + .IsUnique() + .HasDatabaseName("ix_tenants_guid"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_tenants_name"); + + b.ToTable("tenants", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.TenantPerson", b => + { + b.Property("PersonId") + .HasColumnType("integer") + .HasColumnName("person_id"); + + b.Property("TenantId") + .HasColumnType("integer") + .HasColumnName("tenant_id"); + + b.HasKey("PersonId", "TenantId") + .HasName("pk_tenant_person"); + + b.HasIndex("TenantId") + .HasDatabaseName("ix_tenant_person_tenant_id"); + + b.ToTable("tenant_person", (string)null); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.AuthenticationKey", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .HasConstraintName("fk_authentication_keys_organisations_organisation_id"); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.ConnectedEntity", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Organisation", "SupplierOrganisation") + .WithMany() + .HasForeignKey("SupplierOrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_connected_entities_organisations_supplier_organisation_id"); + + b.OwnsMany("CO.CDP.OrganisationInformation.Persistence.ConnectedEntity+ConnectedEntityAddress", "Addresses", b1 => + { + b1.Property("ConnectedEntityId") + .HasColumnType("integer") + .HasColumnName("connected_entity_id"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AddressId") + .HasColumnType("integer") + .HasColumnName("address_id"); + + b1.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b1.HasKey("ConnectedEntityId", "Id") + .HasName("pk_connected_entity_address"); + + b1.HasIndex("AddressId") + .HasDatabaseName("ix_connected_entity_address_address_id"); + + b1.ToTable("connected_entity_address", (string)null); + + b1.HasOne("CO.CDP.OrganisationInformation.Persistence.Address", "Address") + .WithMany() + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_connected_entity_address_address_address_id"); + + b1.WithOwner() + .HasForeignKey("ConnectedEntityId") + .HasConstraintName("fk_connected_entity_address_connected_entities_connected_entit"); + + b1.Navigation("Address"); + }); + + b.OwnsOne("CO.CDP.OrganisationInformation.Persistence.ConnectedEntity+ConnectedIndividualTrust", "IndividualOrTrust", b1 => + { + b1.Property("Id") + .HasColumnType("integer") + .HasColumnName("connected_individual_trust_id"); + + b1.Property("Category") + .HasColumnType("integer") + .HasColumnName("category"); + + b1.Property("ConnectedType") + .HasColumnType("integer") + .HasColumnName("connected_type"); + + b1.Property("ControlCondition") + .IsRequired() + .HasColumnType("integer[]") + .HasColumnName("control_condition"); + + b1.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.Property("DateOfBirth") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_of_birth"); + + b1.Property("FirstName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("first_name"); + + b1.Property("LastName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_name"); + + b1.Property("Nationality") + .HasColumnType("text") + .HasColumnName("nationality"); + + b1.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b1.Property("ResidentCountry") + .HasColumnType("text") + .HasColumnName("resident_country"); + + b1.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.HasKey("Id") + .HasName("pk_connected_individual_trust"); + + b1.ToTable("connected_individual_trust", (string)null); + + b1.WithOwner() + .HasForeignKey("Id") + .HasConstraintName("fk_connected_individual_trust_connected_entities_connected_ind"); + }); + + b.OwnsOne("CO.CDP.OrganisationInformation.Persistence.ConnectedEntity+ConnectedOrganisation", "Organisation", b1 => + { + b1.Property("Id") + .HasColumnType("integer") + .HasColumnName("connected_organisation_id"); + + b1.Property("Category") + .HasColumnType("integer") + .HasColumnName("category"); + + b1.Property("ControlCondition") + .IsRequired() + .HasColumnType("integer[]") + .HasColumnName("control_condition"); + + b1.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.Property("InsolvencyDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("insolvency_date"); + + b1.Property("LawRegistered") + .HasColumnType("text") + .HasColumnName("law_registered"); + + b1.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b1.Property("OrganisationId") + .HasColumnType("uuid") + .HasColumnName("organisation_id"); + + b1.Property("RegisteredLegalForm") + .HasColumnType("text") + .HasColumnName("registered_legal_form"); + + b1.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.HasKey("Id") + .HasName("pk_connected_organisation"); + + b1.ToTable("connected_organisation", (string)null); + + b1.WithOwner() + .HasForeignKey("Id") + .HasConstraintName("fk_connected_organisation_connected_entities_connected_organis"); + }); + + b.Navigation("Addresses"); + + b.Navigation("IndividualOrTrust"); + + b.Navigation("Organisation"); + + b.Navigation("SupplierOrganisation"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormAnswer", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Forms.FormAnswerSet", "FormAnswerSet") + .WithMany("Answers") + .HasForeignKey("FormAnswerSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_form_answers_form_answer_sets_form_answer_set_id"); + + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Forms.FormQuestion", "Question") + .WithMany() + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_form_answers_form_questions_question_id"); + + b.Navigation("FormAnswerSet"); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormAnswerSet", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Forms.FormSection", "Section") + .WithMany() + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_form_answer_sets_form_section_section_id"); + + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Forms.SharedConsent", "SharedConsent") + .WithMany("AnswerSets") + .HasForeignKey("SharedConsentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_form_answer_sets_shared_consents_shared_consent_id"); + + b.Navigation("Section"); + + b.Navigation("SharedConsent"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormQuestion", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Forms.FormQuestion", "NextQuestionAlternative") + .WithMany() + .HasForeignKey("NextQuestionAlternativeId") + .HasConstraintName("fk_form_questions_form_questions_next_question_alternative_id"); + + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Forms.FormQuestion", "NextQuestion") + .WithMany() + .HasForeignKey("NextQuestionId") + .HasConstraintName("fk_form_questions_form_questions_next_question_id"); + + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Forms.FormSection", "Section") + .WithMany("Questions") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_form_questions_form_sections_section_id"); + + b.Navigation("NextQuestion"); + + b.Navigation("NextQuestionAlternative"); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormSection", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Forms.Form", "Form") + .WithMany("Sections") + .HasForeignKey("FormId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_form_sections_forms_form_id"); + + b.Navigation("Form"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.SharedConsent", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Forms.Form", "Form") + .WithMany() + .HasForeignKey("FormId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_shared_consents_forms_form_id"); + + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_shared_consents_organisations_organisation_id"); + + b.Navigation("Form"); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Organisation", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Person", "ReviewedBy") + .WithMany() + .HasForeignKey("ReviewedById") + .HasConstraintName("fk_organisations_persons_reviewed_by_id"); + + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Tenant", "Tenant") + .WithMany("Organisations") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_organisations_tenants_tenant_id"); + + b.OwnsOne("CO.CDP.OrganisationInformation.Persistence.Organisation+BuyerInformation", "BuyerInfo", b1 => + { + b1.Property("OrganisationId") + .HasColumnType("integer") + .HasColumnName("id"); + + b1.Property("BuyerType") + .HasColumnType("text") + .HasColumnName("buyer_type"); + + b1.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.Property("DevolvedRegulations") + .IsRequired() + .HasColumnType("integer[]") + .HasColumnName("devolved_regulations"); + + b1.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.HasKey("OrganisationId") + .HasName("pk_buyer_information"); + + b1.ToTable("buyer_information", (string)null); + + b1.WithOwner() + .HasForeignKey("OrganisationId") + .HasConstraintName("fk_buyer_information_organisations_id"); + }); + + b.OwnsMany("CO.CDP.OrganisationInformation.Persistence.Organisation+ContactPoint", "ContactPoints", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.Property("Email") + .HasColumnType("text") + .HasColumnName("email"); + + b1.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b1.Property("OrganisationId") + .HasColumnType("integer") + .HasColumnName("organisation_id"); + + b1.Property("Telephone") + .HasColumnType("text") + .HasColumnName("telephone"); + + b1.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.Property("Url") + .HasColumnType("text") + .HasColumnName("url"); + + b1.HasKey("Id") + .HasName("pk_contact_points"); + + b1.HasIndex("OrganisationId") + .HasDatabaseName("ix_contact_points_organisation_id"); + + b1.ToTable("contact_points", (string)null); + + b1.WithOwner() + .HasForeignKey("OrganisationId") + .HasConstraintName("fk_contact_points_organisations_organisation_id"); + }); + + b.OwnsMany("CO.CDP.OrganisationInformation.Persistence.Organisation+Identifier", "Identifiers", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.Property("IdentifierId") + .HasColumnType("text") + .HasColumnName("identifier_id"); + + b1.Property("LegalName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("legal_name"); + + b1.Property("OrganisationId") + .HasColumnType("integer") + .HasColumnName("organisation_id"); + + b1.Property("Primary") + .HasColumnType("boolean") + .HasColumnName("primary"); + + b1.Property("Scheme") + .IsRequired() + .HasColumnType("text") + .HasColumnName("scheme"); + + b1.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.Property("Uri") + .HasColumnType("text") + .HasColumnName("uri"); + + b1.HasKey("Id") + .HasName("pk_identifiers"); + + b1.HasIndex("OrganisationId") + .HasDatabaseName("ix_identifiers_organisation_id"); + + b1.HasIndex("IdentifierId", "Scheme") + .IsUnique() + .HasDatabaseName("ix_identifiers_identifier_id_scheme"); + + b1.ToTable("identifiers", (string)null); + + b1.WithOwner() + .HasForeignKey("OrganisationId") + .HasConstraintName("fk_identifiers_organisations_organisation_id"); + }); + + b.OwnsMany("CO.CDP.OrganisationInformation.Persistence.Organisation+OrganisationAddress", "Addresses", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AddressId") + .HasColumnType("integer") + .HasColumnName("address_id"); + + b1.Property("OrganisationId") + .HasColumnType("integer") + .HasColumnName("organisation_id"); + + b1.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b1.HasKey("Id") + .HasName("pk_organisation_address"); + + b1.HasIndex("AddressId") + .HasDatabaseName("ix_organisation_address_address_id"); + + b1.HasIndex("OrganisationId") + .HasDatabaseName("ix_organisation_address_organisation_id"); + + b1.ToTable("organisation_address", (string)null); + + b1.HasOne("CO.CDP.OrganisationInformation.Persistence.Address", "Address") + .WithMany() + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_organisation_address_address_address_id"); + + b1.WithOwner() + .HasForeignKey("OrganisationId") + .HasConstraintName("fk_organisation_address_organisations_organisation_id"); + + b1.Navigation("Address"); + }); + + b.OwnsOne("CO.CDP.OrganisationInformation.Persistence.Organisation+SupplierInformation", "SupplierInfo", b1 => + { + b1.Property("OrganisationId") + .HasColumnType("integer") + .HasColumnName("id"); + + b1.Property("CompletedConnectedPerson") + .HasColumnType("boolean") + .HasColumnName("completed_connected_person"); + + b1.Property("CompletedEmailAddress") + .HasColumnType("boolean") + .HasColumnName("completed_email_address"); + + b1.Property("CompletedLegalForm") + .HasColumnType("boolean") + .HasColumnName("completed_legal_form"); + + b1.Property("CompletedOperationType") + .HasColumnType("boolean") + .HasColumnName("completed_operation_type"); + + b1.Property("CompletedPostalAddress") + .HasColumnType("boolean") + .HasColumnName("completed_postal_address"); + + b1.Property("CompletedRegAddress") + .HasColumnType("boolean") + .HasColumnName("completed_reg_address"); + + b1.Property("CompletedVat") + .HasColumnType("boolean") + .HasColumnName("completed_vat"); + + b1.Property("CompletedWebsiteAddress") + .HasColumnType("boolean") + .HasColumnName("completed_website_address"); + + b1.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.Property("OperationTypes") + .IsRequired() + .HasColumnType("integer[]") + .HasColumnName("operation_types"); + + b1.Property("SupplierType") + .HasColumnType("integer") + .HasColumnName("supplier_type"); + + b1.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b1.HasKey("OrganisationId") + .HasName("pk_supplier_information"); + + b1.ToTable("supplier_information", (string)null); + + b1.WithOwner() + .HasForeignKey("OrganisationId") + .HasConstraintName("fk_supplier_information_organisations_id"); + + b1.OwnsOne("CO.CDP.OrganisationInformation.Persistence.Organisation+LegalForm", "LegalForm", b2 => + { + b2.Property("SupplierInformationOrganisationId") + .HasColumnType("integer") + .HasColumnName("id"); + + b2.Property("CreatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b2.Property("LawRegistered") + .IsRequired() + .HasColumnType("text") + .HasColumnName("law_registered"); + + b2.Property("RegisteredLegalForm") + .IsRequired() + .HasColumnType("text") + .HasColumnName("registered_legal_form"); + + b2.Property("RegisteredUnderAct2006") + .HasColumnType("boolean") + .HasColumnName("registered_under_act2006"); + + b2.Property("RegistrationDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("registration_date"); + + b2.Property("UpdatedOn") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b2.HasKey("SupplierInformationOrganisationId") + .HasName("pk_legal_forms"); + + b2.ToTable("legal_forms", (string)null); + + b2.WithOwner() + .HasForeignKey("SupplierInformationOrganisationId") + .HasConstraintName("fk_legal_forms_supplier_information_id"); + }); + + b1.Navigation("LegalForm"); + }); + + b.Navigation("Addresses"); + + b.Navigation("BuyerInfo"); + + b.Navigation("ContactPoints"); + + b.Navigation("Identifiers"); + + b.Navigation("ReviewedBy"); + + b.Navigation("SupplierInfo"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.OrganisationPerson", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Organisation", "Organisation") + .WithMany("OrganisationPersons") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_organisation_person_organisations_organisation_id"); + + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Person", "Person") + .WithMany("PersonOrganisations") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_organisation_person_persons_person_id"); + + b.Navigation("Organisation"); + + b.Navigation("Person"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.PersonInvite", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_person_invites_organisations_organisation_id"); + + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Person", "Person") + .WithMany() + .HasForeignKey("PersonId") + .HasConstraintName("fk_person_invites_persons_person_id"); + + b.Navigation("Organisation"); + + b.Navigation("Person"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.TenantPerson", b => + { + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Person", null) + .WithMany() + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tenant_person_persons_person_id"); + + b.HasOne("CO.CDP.OrganisationInformation.Persistence.Tenant", null) + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tenant_person_tenants_tenant_id"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.Form", b => + { + b.Navigation("Sections"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormAnswerSet", b => + { + b.Navigation("Answers"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.FormSection", b => + { + b.Navigation("Questions"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Forms.SharedConsent", b => + { + b.Navigation("AnswerSets"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Organisation", b => + { + b.Navigation("OrganisationPersons"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Person", b => + { + b.Navigation("PersonOrganisations"); + }); + + modelBuilder.Entity("CO.CDP.OrganisationInformation.Persistence.Tenant", b => + { + b.Navigation("Organisations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.cs b/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.cs new file mode 100644 index 000000000..de6f9958f --- /dev/null +++ b/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.cs @@ -0,0 +1,112 @@ +using CO.CDP.OrganisationInformation.Persistence.Forms; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace CO.CDP.OrganisationInformation.Persistence.Migrations +{ + /// + public partial class ExclusionFormDataGroupedSingleChoice : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql($@" + DO $$ + DECLARE + sectionId INT; + questionId INT; + BEGIN + SELECT id INTO sectionId FROM form_sections WHERE guid = '8a75cb04-fe29-45ae-90f9-168832dbea48'; + + SELECT id INTO questionId FROM form_questions WHERE section_id = sectionId AND name = '_Exclusion06'; + + INSERT INTO form_questions (guid, section_id, next_question_id, type, is_required, title, description, options, caption, summary_title, name) + VALUES ('{Guid.NewGuid()}', + sectionId, + questionId, + {(int)FormQuestionType.GroupedSingleChoice}, + TRUE, + 'Select which exclusion applies', + '
Only select one exclusion. You can add another at the end if you need to.
', + '{{ + ""choices"": null, + ""choiceProviderStrategy"": null, + ""groups"": [ + {{ + ""name"": ""Penalties and other events"", + ""hint"": ""Defined in schedule 6 of the Procurement Act 2023 (opens in new tab)."", + ""caption"": ""Mandatory exclusions"", + ""choices"": [ + {{""title"": ""Adjustments for tax arrangements that are abusive"", ""value"": ""adjustments_for_tax_arrangements""}}, + {{""title"": ""Competition law infringements"", ""value"": ""competition_law_infringements""}}, + {{""title"": ""Defeat in respect of notifiable tax arrangements"", ""value"": ""defeat_in_respect""}}, + {{""title"": ""Failure to cooperate with an investigation"", ""value"": ""failure_to_cooperate""}}, + {{""title"": ""Finding by HMRC, in exercise of its powers in respect of VAT, of abusive practice"", ""value"": ""finding_by_HMRC""}}, + {{""title"": ""Penalties for transactions connected with VAT fraud and evasion of tax or duty"", ""value"": ""penalties_for_transactions""}}, + {{""title"": ""Penalties payable for errors in tax documentation and failure to notify, and certain VAT and excise"", ""value"": ""penalties_payable""}} + ] + }}, + {{ + ""name"": ""Convictions"", + ""hint"": ""Defined in schedule 6 of the Procurement Act 2023 (opens in new tab)."", + ""caption"": ""Mandatory exclusions"", + ""choices"": [ + {{""title"": ""Ancillary offences - aiding, abetting, encouraging or assisting crime"", ""value"": ""ancillary_offences_aiding""}}, + {{""title"": ""Cartel offences"", ""value"": ""cartel_offences""}}, + {{""title"": ""Corporate manslaughter or homicide"", ""value"": ""corporate_manslaughter""}}, + {{""title"": ""Labour market, slavery and human trafficking offences"", ""value"": ""labour_market""}}, + {{""title"": ""Organised crime"", ""value"": ""organised_crime""}}, + {{""title"": ""Tax offences"", ""value"": ""tax_offences""}}, + {{""title"": ""Terrorism and offences having a terrorist connection"", ""value"": ""terrorism_and_offences""}}, + {{""title"": ""Theft, fraud and bribery"", ""value"": ""theft_fraud""}} + ] + }}, + {{ + ""name"": ""Discretionary exclusions"", + ""hint"": ""Defined in schedule 7 of the Procurement Act 2023 (opens in new tab)."", + ""caption"": ""Discretionary exclusions"", + ""choices"": [ + {{""title"": ""Acting improperly in procurement"", ""value"": ""acting_improperly""}}, + {{""title"": ""Breach of contract and poor performance"", ""value"": ""breach_of_contract""}}, + {{""title"": ""Environmental misconduct"", ""value"": ""environmental_misconduct""}}, + {{""title"": ""Infringement of Competition Act 1998, under Chapter II prohibition"", ""value"": ""infringement_of_competition""}}, + {{""title"": ""Insolvency or bankruptcy"", ""value"": ""insolvency_bankruptcy""}}, + {{""title"": ""Labour market misconduct"", ""value"": ""labour_market_misconduct""}}, + {{""title"": ""Potential competition and competition law infringements"", ""value"": ""competition_law_infringements""}}, + {{""title"": ""Professional misconduct"", ""value"": ""professional_misconduct""}}, + {{""title"": ""Suspension or ceasing to carry on all or a substantial part of a business"", ""value"": ""substantial_part_business""}} + ] + }} + ] + }}', NULL, 'Exclusion applies', '_Exclusion08') + + RETURNING id INTO questionId; + + UPDATE form_questions SET next_question_id = questionId WHERE sectionId = id AND name = '_Exclusion07'; + END $$; + "); + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql($@" + DO $$ + DECLARE + sectionId INT; + questionId INT; + BEGIN + SELECT id INTO sectionId FROM form_sections WHERE guid = '8a75cb04-fe29-45ae-90f9-168832dbea48'; + + SELECT id INTO questionId FROM form_questions WHERE section_id = sectionId AND name = '_Exclusion06'; + + UPDATE form_questions SET next_question_id = questionId WHERE section_id = sectionId AND name = '_Exclusion07'; + + DELETE FROM form_questions WHERE section_id = sectionId AND name = '_Exclusion08'; + END $$; + "); + } + } +} From 0436a852ed00af4d17fcfeb1b7f21bd79144513e Mon Sep 17 00:00:00 2001 From: Jawwad Baig Date: Wed, 2 Oct 2024 12:06:26 +0100 Subject: [PATCH 3/9] #dp-543 grouped single choice type page and unit tests updated --- .../FormsEngineTests.cs | 34 +++++++- .../CO.CDP.OrganisationApp/FormsEngine.cs | 12 ++- .../Models/DynamicForms.cs | 16 +++- .../FormElementGroupedSingleChoiceModel.cs | 53 ++++++++++++ .../Pages/Forms/FormsQuestionPage.cshtml.cs | 5 ++ .../_FormElementGroupedSingleChoice.cshtml | 84 +++++++++++++++++++ ...11_ExclusionFormDataGroupedSingleChoice.cs | 2 +- 7 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormElementGroupedSingleChoiceModel.cs create mode 100644 Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml diff --git a/Frontend/CO.CDP.OrganisationApp.Tests/FormsEngineTests.cs b/Frontend/CO.CDP.OrganisationApp.Tests/FormsEngineTests.cs index 4eb3aca00..c3e8804c0 100644 --- a/Frontend/CO.CDP.OrganisationApp.Tests/FormsEngineTests.cs +++ b/Frontend/CO.CDP.OrganisationApp.Tests/FormsEngineTests.cs @@ -158,10 +158,25 @@ public async Task GetFormSectionAsync_ShouldFetchAndCacheResponse_WhenCachedResp var (organisationId, formId, sectionId, sessionKey) = CreateTestGuids(); var questionId = Guid.NewGuid(); var nextQuestionId = Guid.NewGuid(); + var apiResponse = CreateApiSectionQuestionsResponse(sectionId, questionId, nextQuestionId); + var expectedResponse = CreateModelSectionQuestionsResponse(sectionId, questionId, nextQuestionId); - expectedResponse.Questions[0].Options.Groups = new List { "Group 1" }; + expectedResponse.Questions[0].Options.Groups = new List + { + new FormQuestionGroup + { + Name = "Group 1", + Hint = "Group 1 Hint", + Caption = "Group 1 Caption", + Choices = new List + { + new FormQuestionGroupChoice { Title = "Group Choice 1", Value = "group_choice_1" }, + new FormQuestionGroupChoice { Title = "Group Choice 2", Value = "group_choice_2" } + } + } + }; _tempDataServiceMock.Setup(t => t.Peek(sessionKey)) .Returns((SectionQuestionsResponse?)null); @@ -277,7 +292,20 @@ public async Task GetFormSectionAsync_ShouldFetchChoicesFromCustomChoiceProvider var apiResponse = CreateApiSectionQuestionsResponse(sectionId, questionId, nextQuestionId, "ExclusionAppliesToChoiceProviderStrategy"); var expectedResponse = CreateModelSectionQuestionsResponse(sectionId, questionId, nextQuestionId, "ExclusionAppliesToChoiceProviderStrategy", ["User's current organisation", "Connected person", "Connected organisation"]); - expectedResponse.Questions[0].Options.Groups = new List { "Group 1" }; + expectedResponse.Questions[0].Options.Groups = new List + { + new FormQuestionGroup + { + Name = "Group 1", + Hint = "Group 1 Hint", + Caption = "Group 1 Caption", + Choices = new List + { + new FormQuestionGroupChoice { Title = "Group Choice 1", Value = "group_choice_1" }, + new FormQuestionGroupChoice { Title = "Group Choice 2", Value = "group_choice_2" } + } + } + }; _organisationClientMock.Setup(c => c.GetConnectedEntitiesAsync(It.IsAny())) .ReturnsAsync([ @@ -285,7 +313,7 @@ public async Task GetFormSectionAsync_ShouldFetchChoicesFromCustomChoiceProvider new ConnectedEntityLookup(new Guid(), ConnectedEntityType.Organisation, "Connected organisation", new Uri("http://whatever")) ]); _organisationClientMock.Setup(c => c.GetOrganisationAsync(organisationId)) - .ReturnsAsync(new Organisation.WebApiClient.Organisation([], [],null, null, organisationId, null, "User's current organisation", [])); + .ReturnsAsync(new Organisation.WebApiClient.Organisation([], [], null, null, organisationId, null, "User's current organisation", [])); _userInfoServiceMock.Setup(u => u.GetOrganisationId()).Returns(organisationId); _tempDataServiceMock.Setup(t => t.Peek(sessionKey)) .Returns((SectionQuestionsResponse?)null); diff --git a/Frontend/CO.CDP.OrganisationApp/FormsEngine.cs b/Frontend/CO.CDP.OrganisationApp/FormsEngine.cs index aae171f37..266dc600e 100644 --- a/Frontend/CO.CDP.OrganisationApp/FormsEngine.cs +++ b/Frontend/CO.CDP.OrganisationApp/FormsEngine.cs @@ -49,7 +49,17 @@ public async Task GetFormSectionAsync(Guid organisatio { Choices = await ExecuteChoiceProviderStrategy(q.Options), ChoiceProviderStrategy = q.Options.ChoiceProviderStrategy, - Groups = q.Options.Groups.Select(c => c.Name).ToList() + Groups = q.Options.Groups?.Select(g => new Models.FormQuestionGroup + { + Name = g.Name, + Hint = g.Hint, + Caption = g.Caption, + Choices = g.Choices?.Select(c => new Models.FormQuestionGroupChoice + { + Title = c.Title, + Value = c.Value + }).ToList() + }).ToList() } }))).ToList() }; diff --git a/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs b/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs index c66234b58..d50877867 100644 --- a/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs +++ b/Frontend/CO.CDP.OrganisationApp/Models/DynamicForms.cs @@ -38,7 +38,7 @@ public class FormQuestionOptions { public List? Choices { get; set; } public string? ChoiceProviderStrategy { get; set; } - public List? Groups { get; set; } = []; + public List? Groups { get; set; } } public class FormQuestionAnswerState @@ -67,6 +67,20 @@ public class FormAnswer public Address? AddressValue { get; init; } } +public class FormQuestionGroup +{ + public string? Name { get; set; } + public string? Hint { get; set; } + public string? Caption { get; set; } + public List? Choices { get; set; } +} + +public class FormQuestionGroupChoice +{ + public string? Title { get; set; } + public string? Value { get; set; } = null; +} + public enum FormQuestionType { NoInput, diff --git a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormElementGroupedSingleChoiceModel.cs b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormElementGroupedSingleChoiceModel.cs new file mode 100644 index 000000000..a5ee40e6d --- /dev/null +++ b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormElementGroupedSingleChoiceModel.cs @@ -0,0 +1,53 @@ +using CO.CDP.OrganisationApp.Models; +using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.DataAnnotations; + +namespace CO.CDP.OrganisationApp.Pages.Forms; + +public class FormElementGroupedSingleChoiceModel : FormElementModel, IValidatableObject +{ + [BindProperty] + public string? SelectedOption { get; set; } + + public override FormAnswer? GetAnswer() + { + if (SelectedOption != null && Options?.Groups != null) + { + var isValidOption = Options.Groups.Any(group => group.Choices != null && group.Choices.Any(choice => choice.Value == SelectedOption)); + + if (isValidOption) + { + return new FormAnswer { OptionValue = SelectedOption }; + } + } + + return null; + } + + public override void SetAnswer(FormAnswer? answer) + { + if (answer?.OptionValue != null && Options?.Groups != null) + { + var isValidOption = Options.Groups.Any(group => group.Choices != null && group.Choices.Any(choice => choice.Value == answer.OptionValue)); + + if (isValidOption) + { + SelectedOption = answer.OptionValue; + } + } + } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (string.IsNullOrWhiteSpace(SelectedOption)) + { + yield return new ValidationResult("Select an option", new[] { nameof(SelectedOption) }); + yield break; + } + + if (Options?.Groups == null || !Options.Groups.Any(group => group.Choices != null && group.Choices.Any(choice => choice.Value == SelectedOption))) + { + yield return new ValidationResult("Invalid option selected", new[] { nameof(SelectedOption) }); + } + } +} \ No newline at end of file diff --git a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormsQuestionPage.cshtml.cs b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormsQuestionPage.cshtml.cs index 49112d6c2..66e5ee688 100644 --- a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormsQuestionPage.cshtml.cs +++ b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormsQuestionPage.cshtml.cs @@ -46,6 +46,8 @@ public class FormsQuestionPageModel( [BindProperty] public FormElementSingleChoiceModel? SingleChoiceModel { get; set; } [BindProperty] + public FormElementGroupedSingleChoiceModel? GroupedSingleChoiceModel { get; set; } + [BindProperty] public FormElementCheckBoxInputModel? CheckBoxModel { get; set; } [BindProperty(SupportsGet = true)] @@ -161,6 +163,7 @@ public async Task> GetAnswers() FormQuestionType.Date => answer.Answer?.DateValue.HasValue == true ? answer.Answer.DateValue.Value.ToString("dd/MM/yyyy") : "", FormQuestionType.Address => answer.Answer?.AddressValue != null ? answer.Answer.AddressValue.ToHtmlString() : "", FormQuestionType.SingleChoice => answer.Answer?.OptionValue ?? "", + FormQuestionType.GroupedSingleChoice => answer.Answer?.OptionValue ?? "", _ => "" }; @@ -231,6 +234,7 @@ public bool PreviousQuestionHasNonUKAddressAnswer() { FormQuestionType.Address, "_FormElementAddress" }, { FormQuestionType.MultiLine, "_FormElementMultiLineInput" }, { FormQuestionType.SingleChoice, "_FormElementSingleChoice" }, + { FormQuestionType.GroupedSingleChoice, "_FormElementGroupedSingleChoice" }, }; if (formQuestionPartials.TryGetValue(question.Type, out var partialView)) @@ -257,6 +261,7 @@ public bool PreviousQuestionHasNonUKAddressAnswer() FormQuestionType.Address => AddressModel ?? new FormElementAddressModel(), FormQuestionType.SingleChoice => SingleChoiceModel ?? new FormElementSingleChoiceModel(), FormQuestionType.MultiLine => MultiLineInputModel ?? new FormElementMultiLineInputModel(), + FormQuestionType.GroupedSingleChoice => GroupedSingleChoiceModel ?? new FormElementGroupedSingleChoiceModel(), _ => throw new NotImplementedException($"Forms question: {question.Type} is not supported"), }; diff --git a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml new file mode 100644 index 000000000..4f3cb8947 --- /dev/null +++ b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml @@ -0,0 +1,84 @@ +@model FormElementGroupedSingleChoiceModel + +@{ + var hasError = ((TagBuilder)Html.ValidationMessageFor(m => m.SelectedOption)).HasInnerHtml; + string ariaDescribedby = ""; + + if (!string.IsNullOrWhiteSpace(Model.Description)) + { + ariaDescribedby += "SelectedOption-description"; + } + + if (hasError) + { + ariaDescribedby += " SelectedOption-error"; + } +} + +
+
+ @if (!string.IsNullOrWhiteSpace(Model.Heading)) + { + +

+ @Model.Heading + +

+ @if (!string.IsNullOrWhiteSpace(Model.Caption)) + { + @Model.Caption + } +
+ } + + @if (!string.IsNullOrWhiteSpace(Model.Description)) + { +
+ @Html.Raw(Model.Description) +
+ } + + @if (hasError) + { +

+ Error: + @Html.ValidationMessageFor(m => m.SelectedOption) +

+ } + +
+ @if (Model.Options?.Groups != null) + { + foreach (var group in Model.Options.Groups) + { +
+ + @group.Caption +

@group.Name

+
+ + @if (!string.IsNullOrWhiteSpace(@group.Hint)) + { +

@Html.Raw(@group.Hint)

+ } + +
+ @if (group.Choices != null) + { + foreach (var choice in group.Choices) + { + var id = $"SelectedOption_{choice.Value}"; + +
+ + +
+ } + } +
+
+ } + } +
+
+
\ No newline at end of file diff --git a/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.cs b/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.cs index de6f9958f..32a243e75 100644 --- a/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.cs +++ b/Services/CO.CDP.OrganisationInformation.Persistence/Migrations/20240930215811_ExclusionFormDataGroupedSingleChoice.cs @@ -19,7 +19,7 @@ protected override void Up(MigrationBuilder migrationBuilder) BEGIN SELECT id INTO sectionId FROM form_sections WHERE guid = '8a75cb04-fe29-45ae-90f9-168832dbea48'; - SELECT id INTO questionId FROM form_questions WHERE section_id = sectionId AND name = '_Exclusion06'; + SELECT id INTO questionId FROM form_questions WHERE section_id = sectionId AND name = '_Exclusion07'; INSERT INTO form_questions (guid, section_id, next_question_id, type, is_required, title, description, options, caption, summary_title, name) VALUES ('{Guid.NewGuid()}', From 83502a7498711b27f4f5de598d476fd6f2ed601d Mon Sep 17 00:00:00 2001 From: Jawwad Baig Date: Wed, 2 Oct 2024 14:01:19 +0100 Subject: [PATCH 4/9] dp-543 updated the ui elements --- .../_FormElementGroupedSingleChoice.cshtml | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml index 4f3cb8947..0c3e7f490 100644 --- a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml +++ b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml @@ -22,12 +22,11 @@

@Model.Heading - + @if (!string.IsNullOrWhiteSpace(Model.Caption)) + { + @Model.Caption + }

- @if (!string.IsNullOrWhiteSpace(Model.Caption)) - { - @Model.Caption - }
} @@ -46,39 +45,41 @@

} -
@if (Model.Options?.Groups != null) { + foreach (var group in Model.Options.Groups) { -
- - @group.Caption -

@group.Name

-
+

+ @group.Caption + @group.Name - @if (!string.IsNullOrWhiteSpace(@group.Hint)) - { -

@Html.Raw(@group.Hint)

- } +

-
- @if (group.Choices != null) - { - foreach (var choice in group.Choices) - { - var id = $"SelectedOption_{choice.Value}"; + @if (!string.IsNullOrWhiteSpace(@group.Hint)) + { +
+

@Html.Raw(@group.Hint)

+
+ } + +
+ @if (group.Choices != null) + { + foreach (var choice in group.Choices) + { + var id = $"SelectedOption_{choice.Value}"; -
- - -
- } - } -
-
+
+ + +
+ } + } +
+ } } - + \ No newline at end of file From c34201839f01b94a285566fbbe5f5a1d4e97c75d Mon Sep 17 00:00:00 2001 From: Jawwad Baig Date: Wed, 2 Oct 2024 14:20:46 +0100 Subject: [PATCH 5/9] #dp-543 updated unit tests with changes --- ...ormElementGroupedSingleChoiceModelTests.cs | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 Frontend/CO.CDP.OrganisationApp.Tests/Pages/Forms/FormElementGroupedSingleChoiceModelTests.cs diff --git a/Frontend/CO.CDP.OrganisationApp.Tests/Pages/Forms/FormElementGroupedSingleChoiceModelTests.cs b/Frontend/CO.CDP.OrganisationApp.Tests/Pages/Forms/FormElementGroupedSingleChoiceModelTests.cs new file mode 100644 index 000000000..72002251f --- /dev/null +++ b/Frontend/CO.CDP.OrganisationApp.Tests/Pages/Forms/FormElementGroupedSingleChoiceModelTests.cs @@ -0,0 +1,139 @@ +using CO.CDP.OrganisationApp.Models; +using CO.CDP.OrganisationApp.Pages.Forms; +using FluentAssertions; +using System.ComponentModel.DataAnnotations; + +namespace CO.CDP.OrganisationApp.Tests.Pages.Forms; +public class FormElementGroupedSingleChoiceModelTests +{ + private readonly FormElementGroupedSingleChoiceModel _model; + + public FormElementGroupedSingleChoiceModelTests() + { + _model = new FormElementGroupedSingleChoiceModel + { + Options = new FormQuestionOptions + { + Groups = new List + { + new FormQuestionGroup + { + Name = "Group 1", + Hint = "Group 1 Hint", + Caption = "Group 1 Caption", + Choices = new List + { + new FormQuestionGroupChoice { Title = "Group Choice 1", Value = "group_choice_1" }, + new FormQuestionGroupChoice { Title = "Group Choice 2", Value = "group_choice_2" } + } + } + } + } + }; + } + + [Fact] + public void GetAnswer_ShouldReturnNull_WhenNoOptionIsSelected() + { + + _model.SelectedOption = null; + + + var answer = _model.GetAnswer(); + + + answer.Should().BeNull(); + } + + [Fact] + public void GetAnswer_ShouldReturnFormAnswer_WhenValidOptionIsSelected() + { + + _model.SelectedOption = "group_choice_1"; + + + var answer = _model.GetAnswer(); + + + answer.Should().NotBeNull(); + answer!.OptionValue.Should().Be("group_choice_1"); + } + + [Fact] + public void GetAnswer_ShouldReturnNull_WhenInvalidOptionIsSelected() + { + + _model.SelectedOption = "invalid_choice"; + + + var answer = _model.GetAnswer(); + + + answer.Should().BeNull(); + } + + [Fact] + public void SetAnswer_ShouldSetSelectedOption_WhenValidOptionIsProvided() + { + + var answer = new FormAnswer { OptionValue = "group_choice_2" }; + + + _model.SetAnswer(answer); + + + _model.SelectedOption.Should().Be("group_choice_2"); + } + + [Fact] + public void SetAnswer_ShouldNotSetSelectedOption_WhenInvalidOptionIsProvided() + { + + var answer = new FormAnswer { OptionValue = "invalid_choice" }; + + + _model.SetAnswer(answer); + + + _model.SelectedOption.Should().BeNull(); + } + + [Fact] + public void Validate_ShouldReturnValidationError_WhenNoOptionIsSelected() + { + + _model.SelectedOption = null; + + + var validationResults = _model.Validate(new ValidationContext(_model)); + + + validationResults.Should().ContainSingle(v => v.ErrorMessage == "Select an option"); + } + + [Fact] + public void Validate_ShouldReturnValidationError_WhenInvalidOptionIsSelected() + { + + _model.SelectedOption = "invalid_choice"; + + + var validationResults = _model.Validate(new ValidationContext(_model)); + + + validationResults.Should().ContainSingle(v => v.ErrorMessage == "Invalid option selected"); + } + + [Fact] + public void Validate_ShouldReturnNoError_WhenValidOptionIsSelected() + { + + _model.SelectedOption = "group_choice_1"; + + + var validationResults = _model.Validate(new ValidationContext(_model)); + + + validationResults.Should().BeEmpty(); + } +} From e005bfd57d86c79714993a23b6ba80b6c10d5df8 Mon Sep 17 00:00:00 2001 From: Jawwad Baig Date: Wed, 2 Oct 2024 14:59:05 +0100 Subject: [PATCH 6/9] #dp-543 remove unnecessary lines in unit tests --- ...ormElementGroupedSingleChoiceModelTests.cs | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/Frontend/CO.CDP.OrganisationApp.Tests/Pages/Forms/FormElementGroupedSingleChoiceModelTests.cs b/Frontend/CO.CDP.OrganisationApp.Tests/Pages/Forms/FormElementGroupedSingleChoiceModelTests.cs index 72002251f..1b6276e77 100644 --- a/Frontend/CO.CDP.OrganisationApp.Tests/Pages/Forms/FormElementGroupedSingleChoiceModelTests.cs +++ b/Frontend/CO.CDP.OrganisationApp.Tests/Pages/Forms/FormElementGroupedSingleChoiceModelTests.cs @@ -35,26 +35,17 @@ public FormElementGroupedSingleChoiceModelTests() [Fact] public void GetAnswer_ShouldReturnNull_WhenNoOptionIsSelected() { - _model.SelectedOption = null; - - var answer = _model.GetAnswer(); - - answer.Should().BeNull(); } [Fact] public void GetAnswer_ShouldReturnFormAnswer_WhenValidOptionIsSelected() { - _model.SelectedOption = "group_choice_1"; - - var answer = _model.GetAnswer(); - answer.Should().NotBeNull(); answer!.OptionValue.Should().Be("group_choice_1"); } @@ -62,78 +53,54 @@ public void GetAnswer_ShouldReturnFormAnswer_WhenValidOptionIsSelected() [Fact] public void GetAnswer_ShouldReturnNull_WhenInvalidOptionIsSelected() { - _model.SelectedOption = "invalid_choice"; - var answer = _model.GetAnswer(); - - answer.Should().BeNull(); } [Fact] public void SetAnswer_ShouldSetSelectedOption_WhenValidOptionIsProvided() { - var answer = new FormAnswer { OptionValue = "group_choice_2" }; - _model.SetAnswer(answer); - - _model.SelectedOption.Should().Be("group_choice_2"); } [Fact] public void SetAnswer_ShouldNotSetSelectedOption_WhenInvalidOptionIsProvided() { - var answer = new FormAnswer { OptionValue = "invalid_choice" }; - _model.SetAnswer(answer); - - _model.SelectedOption.Should().BeNull(); } [Fact] public void Validate_ShouldReturnValidationError_WhenNoOptionIsSelected() { - _model.SelectedOption = null; - var validationResults = _model.Validate(new ValidationContext(_model)); - - validationResults.Should().ContainSingle(v => v.ErrorMessage == "Select an option"); } [Fact] public void Validate_ShouldReturnValidationError_WhenInvalidOptionIsSelected() { - _model.SelectedOption = "invalid_choice"; - var validationResults = _model.Validate(new ValidationContext(_model)); - - validationResults.Should().ContainSingle(v => v.ErrorMessage == "Invalid option selected"); } [Fact] public void Validate_ShouldReturnNoError_WhenValidOptionIsSelected() { - _model.SelectedOption = "group_choice_1"; - var validationResults = _model.Validate(new ValidationContext(_model)); - - validationResults.Should().BeEmpty(); } } From a2283d67f2db886505b8bb710a3307786a13c344 Mon Sep 17 00:00:00 2001 From: Jawwad Baig Date: Wed, 2 Oct 2024 15:04:15 +0100 Subject: [PATCH 7/9] #dp-543 removed spaces --- .../Pages/Forms/FormsQuestionPage.cshtml.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormsQuestionPage.cshtml.cs b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormsQuestionPage.cshtml.cs index c5da1fb0e..d30d8874f 100644 --- a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormsQuestionPage.cshtml.cs +++ b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormsQuestionPage.cshtml.cs @@ -53,11 +53,8 @@ public class FormsQuestionPageModel( public FormElementMultiLineInputModel? MultiLineInputModel { get; set; } [BindProperty] public FormElementUrlInputModel? UrlInputModel { get; set; } - [BindProperty(SupportsGet = true)] public string? UkOrNonUk { get; set; } - - public FormQuestionType? CurrentFormQuestionType { get; private set; } public string? PartialViewName { get; private set; } public IFormElementModel? PartialViewModel { get; private set; } @@ -166,7 +163,7 @@ public async Task> GetAnswers() FormQuestionType.SingleChoice => await choiceProviderStrategy.RenderOption(answer.Answer) ?? "", FormQuestionType.Date => answer.Answer?.DateValue.HasValue == true ? answer.Answer.DateValue.Value.ToString("dd/MM/yyyy") : "", FormQuestionType.CheckBox => answer.Answer?.BoolValue == true ? question?.Options?.Choices?.Values.FirstOrDefault() ?? "" : "", - FormQuestionType.Address => answer.Answer?.AddressValue != null ? answer.Answer.AddressValue.ToHtmlString() : "", + FormQuestionType.Address => answer.Answer?.AddressValue != null ? answer.Answer.AddressValue.ToHtmlString() : "", FormQuestionType.GroupedSingleChoice => answer.Answer?.OptionValue ?? "", FormQuestionType.MultiLine => answer.Answer?.TextValue ?? "", FormQuestionType.Url => answer.Answer?.TextValue ?? "", @@ -175,12 +172,12 @@ public async Task> GetAnswers() var summary = new AnswerSummary { - Title = question.SummaryTitle ?? question.Title, + Title = question?.SummaryTitle ?? question?.Title, Answer = answerString, ChangeLink = $"/organisation/{OrganisationId}/forms/{FormId}/sections/{SectionId}/questions/{answer.QuestionId}?frm-chk-answer=true" }; - if (question.Type == FormQuestionType.Address && answer.Answer?.AddressValue != null + if (question?.Type == FormQuestionType.Address && answer.Answer?.AddressValue != null && answer.Answer.AddressValue.Country != Constants.Country.UKCountryCode) { summary.ChangeLink += "&UkOrNonUk=non-uk"; @@ -239,7 +236,7 @@ public bool PreviousQuestionHasNonUKAddressAnswer() { FormQuestionType.Date, "_FormElementDateInput" }, { FormQuestionType.CheckBox, "_FormElementCheckBoxInput" }, { FormQuestionType.Address, "_FormElementAddress" }, - { FormQuestionType.MultiLine, "_FormElementMultiLineInput" }, + { FormQuestionType.MultiLine, "_FormElementMultiLineInput" }, { FormQuestionType.GroupedSingleChoice, "_FormElementGroupedSingleChoice" }, { FormQuestionType.Url, "_FormElementUrlInput" }, }; From 5cbabd7abf3ecf8dbff570e1b97669c9fff9834d Mon Sep 17 00:00:00 2001 From: JBaigGoaco <159906815+JBaigGoaco@users.noreply.github.com> Date: Thu, 3 Oct 2024 09:28:15 +0100 Subject: [PATCH 8/9] Update Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml Co-authored-by: Andy Mantell <134642+andymantell@users.noreply.github.com> --- .../Pages/Forms/_FormElementGroupedSingleChoice.cshtml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml index 0c3e7f490..c4b222222 100644 --- a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml +++ b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml @@ -66,14 +66,17 @@
@if (group.Choices != null) { + var index = 0; foreach (var choice in group.Choices) { - var id = $"SelectedOption_{choice.Value}"; + var id = index == 0 ? "SelectedOption" : $"SelectedOption_{index}";
+ + index++; } }
From 02a6dab847637228edf0b385bfb53d43d24f811b Mon Sep 17 00:00:00 2001 From: Jawwad Baig Date: Thu, 3 Oct 2024 10:10:39 +0100 Subject: [PATCH 9/9] #dp-543 element grouped single choice page updated --- .../_FormElementGroupedSingleChoice.cshtml | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml index c4b222222..a74e745d4 100644 --- a/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml +++ b/Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementGroupedSingleChoice.cshtml @@ -45,44 +45,44 @@

} - @if (Model.Options?.Groups != null) - { + @if (Model.Options?.Groups != null) + { - foreach (var group in Model.Options.Groups) - { -

- @group.Caption - @group.Name + foreach (var group in Model.Options.Groups) + { +

+ @group.Caption + @group.Name -

+ - @if (!string.IsNullOrWhiteSpace(@group.Hint)) - { -
-

@Html.Raw(@group.Hint)

-
- } + @if (!string.IsNullOrWhiteSpace(@group.Hint)) + { +
+

@Html.Raw(@group.Hint)

+
+ } -
- @if (group.Choices != null) +
+ @if (group.Choices != null) + { + var index = 0; + foreach (var choice in group.Choices) { - var index = 0; - foreach (var choice in group.Choices) - { - var id = index == 0 ? "SelectedOption" : $"SelectedOption_{index}"; + var id = index == 0 ? "SelectedOption" : $"SelectedOption_{index}"; -
- - -
+
+ + +
- index++; - } + index++; } -
- } +
+ } + } \ No newline at end of file