From b36842c17db2969243300a064fbc6b72ee9a5609 Mon Sep 17 00:00:00 2001 From: Andrew Horth Date: Mon, 12 Aug 2024 13:03:40 +0100 Subject: [PATCH 1/3] Rename person_employments, add end date logic --- .../Mappings/TpsCsvExtractItemMapping.cs | 2 +- .../Mappings/TpsCsvExtractLoadItemMapping.cs | 2 +- ...mentMapping.cs => TpsEmploymentMapping.cs} | 20 +- ...0807133540_RenameTpsEmployment.Designer.cs | 2252 +++++++++++++++++ .../20240807133540_RenameTpsEmployment.cs | 175 ++ .../Migrations/TrsDbContextModelSnapshot.cs | 200 +- .../Postgres/Models/TpsCsvExtractItem.cs | 2 +- .../Postgres/Models/TpsCsvExtractLoadItem.cs | 2 +- .../{PersonEmployment.cs => TpsEmployment.cs} | 13 +- ...mployments_person_search_attributes_v1.sql | 15 + ...mployments_person_search_attributes_v1.sql | 14 + ...mployments_person_search_attributes_v1.sql | 21 + ...mployments_person_search_attributes_v1.sql | 37 + ...mployments_person_search_attributes_v1.sql | 6 + ...mployments_person_search_attributes_v1.sql | 6 + ...mployments_person_search_attributes_v1.sql | 6 + .../DataStore/Postgres/TrsDbContext.cs | 2 +- .../{PersonEmployment.cs => TpsEmployment.cs} | 12 +- .../Events/PersonEmploymentUpdatedEvent.cs | 25 - ...dEvent.cs => TpsEmploymentCreatedEvent.cs} | 4 +- .../Events/TpsEmploymentUpdatedEvent.cs | 26 + ...ersonEmployment.cs => NewTpsEmployment.cs} | 8 +- .../TpsCsvExtractFileImporter.cs | 6 +- .../WorkforceData/TpsCsvExtractProcessor.cs | 335 +-- ...nEmployment.cs => UpdatedTpsEmployment.cs} | 13 +- ...Date.cs => UpdatedTpsEmploymentEndDate.cs} | 7 +- ...s => UpdatedTpsEmploymentEstablishment.cs} | 7 +- ...tionalInsuranceNumberAndPersonPostcode.cs} | 7 +- .../TeachingRecordSystem.Core.csproj | 14 + .../PersonMatchingServiceTests.cs | 6 +- .../TpsCsvExtractFileImporterTests.cs | 4 +- .../TpsCsvExtractProcessorTests.cs | 241 +- .../TestData.CreatePersonEmployment.cs | 14 +- .../TestData.CreateTpsCsvExtract.cs | 11 +- docs/workforce-data-import/useful-queries.md | 762 +++--- 35 files changed, 3571 insertions(+), 706 deletions(-) rename TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/{PersonEmploymentMapping.cs => TpsEmploymentMapping.cs} (55%) create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/20240807133540_RenameTpsEmployment.Designer.cs create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/20240807133540_RenameTpsEmployment.cs rename TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/{PersonEmployment.cs => TpsEmployment.cs} (60%) create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_delete_tps_employments_person_search_attributes_v1.sql create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_insert_tps_employments_person_search_attributes_v1.sql create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_update_tps_employments_person_search_attributes_v1.sql create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/p_refresh_tps_employments_person_search_attributes_v1.sql create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_delete_tps_employments_person_search_attributes_v1.sql create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_insert_tps_employments_person_search_attributes_v1.sql create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_update_tps_employments_person_search_attributes_v1.sql rename TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/Models/{PersonEmployment.cs => TpsEmployment.cs} (70%) delete mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/PersonEmploymentUpdatedEvent.cs rename TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/{PersonEmploymentCreatedEvent.cs => TpsEmploymentCreatedEvent.cs} (50%) create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/TpsEmploymentUpdatedEvent.cs rename TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/{NewPersonEmployment.cs => NewTpsEmployment.cs} (69%) rename TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/{UpdatedPersonEmployment.cs => UpdatedTpsEmployment.cs} (62%) rename TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/{UpdatedPersonEmploymentEndDate.cs => UpdatedTpsEmploymentEndDate.cs} (73%) rename TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/{UpdatedPersonEmploymentEstablishment.cs => UpdatedTpsEmploymentEstablishment.cs} (73%) rename TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/{UpdatedPersonEmploymentNationalInsuranceNumberAndPersonPostcode.cs => UpdatedTpsEmploymentNationalInsuranceNumberAndPersonPostcode.cs} (73%) diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsCsvExtractItemMapping.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsCsvExtractItemMapping.cs index 078056228..10a994081 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsCsvExtractItemMapping.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsCsvExtractItemMapping.cs @@ -20,7 +20,7 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.EstablishmentEmailAddress).HasMaxLength(200); builder.Property(x => x.EmploymentStartDate).IsRequired(); builder.Property(x => x.EmploymentType).IsRequired(); - builder.Property(x => x.WithdrawlIndicator).HasMaxLength(1).IsFixedLength(); + builder.Property(x => x.WithdrawalIndicator).HasMaxLength(1).IsFixedLength(); builder.Property(x => x.Gender).HasMaxLength(10).IsRequired(); builder.Property(x => x.Created).IsRequired(); builder.Property(x => x.Key).HasMaxLength(50).IsRequired(); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsCsvExtractLoadItemMapping.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsCsvExtractLoadItemMapping.cs index 401adebe1..19eec84ae 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsCsvExtractLoadItemMapping.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsCsvExtractLoadItemMapping.cs @@ -24,7 +24,7 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.EmploymentStartDate).HasMaxLength(TpsCsvExtractLoadItem.FieldMaxLength); builder.Property(x => x.EmploymentEndDate).HasMaxLength(TpsCsvExtractLoadItem.FieldMaxLength); builder.Property(x => x.FullOrPartTimeIndicator).HasMaxLength(TpsCsvExtractLoadItem.FieldMaxLength); - builder.Property(x => x.WithdrawlIndicator).HasMaxLength(TpsCsvExtractLoadItem.FieldMaxLength); + builder.Property(x => x.WithdrawalIndicator).HasMaxLength(TpsCsvExtractLoadItem.FieldMaxLength); builder.Property(x => x.ExtractDate).HasMaxLength(TpsCsvExtractLoadItem.FieldMaxLength); builder.Property(x => x.Gender).HasMaxLength(TpsCsvExtractLoadItem.FieldMaxLength); builder.Property(x => x.Created).IsRequired(); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/PersonEmploymentMapping.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsEmploymentMapping.cs similarity index 55% rename from TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/PersonEmploymentMapping.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsEmploymentMapping.cs index f36cafffb..8796d2353 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/PersonEmploymentMapping.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Mappings/TpsEmploymentMapping.cs @@ -4,23 +4,25 @@ namespace TeachingRecordSystem.Core.DataStore.Postgres.Mappings; -public class PersonEmploymentMapping : IEntityTypeConfiguration +public class TpsEmploymentMapping : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) + public void Configure(EntityTypeBuilder builder) { - builder.ToTable("person_employments"); - builder.HasKey(e => e.PersonEmploymentId); + builder.ToTable("tps_employments"); + builder.HasKey(e => e.TpsEmploymentId); builder.Property(e => e.StartDate).IsRequired(); + builder.Property(e => e.LastKnownTpsEmployedDate).IsRequired(); builder.Property(e => e.EmploymentType).IsRequired(); + builder.Property(e => e.WithdrawalConfirmed).IsRequired(); builder.Property(e => e.CreatedOn).IsRequired(); builder.Property(e => e.UpdatedOn).IsRequired(); builder.Property(e => e.Key).HasMaxLength(50).IsRequired(); builder.Property(e => e.NationalInsuranceNumber).HasMaxLength(9).IsFixedLength(); builder.Property(e => e.PersonPostcode).HasMaxLength(10); - builder.HasIndex(e => e.Key).HasDatabaseName(PersonEmployment.KeyIndexName); - builder.HasIndex(e => e.PersonId).HasDatabaseName(PersonEmployment.PersonIdIndexName); - builder.HasIndex(e => e.EstablishmentId).HasDatabaseName(PersonEmployment.EstablishmentIdIndexName); - builder.HasOne().WithMany().HasForeignKey(e => e.PersonId).HasConstraintName("fk_person_employments_person_id"); - builder.HasOne().WithMany().HasForeignKey(e => e.EstablishmentId).HasConstraintName("fk_person_employments_establishment_id"); + builder.HasIndex(e => e.Key).HasDatabaseName(TpsEmployment.KeyIndexName); + builder.HasIndex(e => e.PersonId).HasDatabaseName(TpsEmployment.PersonIdIndexName); + builder.HasIndex(e => e.EstablishmentId).HasDatabaseName(TpsEmployment.EstablishmentIdIndexName); + builder.HasOne().WithMany().HasForeignKey(e => e.PersonId).HasConstraintName("fk_tps_employments_person_id"); + builder.HasOne().WithMany().HasForeignKey(e => e.EstablishmentId).HasConstraintName("fk_tps_employments_establishment_id"); } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/20240807133540_RenameTpsEmployment.Designer.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/20240807133540_RenameTpsEmployment.Designer.cs new file mode 100644 index 000000000..f75c5d724 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/20240807133540_RenameTpsEmployment.Designer.cs @@ -0,0 +1,2252 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using TeachingRecordSystem.Core.DataStore.Postgres; + +#nullable disable + +namespace TeachingRecordSystem.Core.DataStore.Postgres.Migrations +{ + [DbContext(typeof(TrsDbContext))] + [Migration("20240807133540_RenameTpsEmployment")] + partial class RenameTpsEmployment + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("application_type"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("client_id"); + + b.Property("ClientSecret") + .HasColumnType("text") + .HasColumnName("client_secret"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("client_type"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("concurrency_token"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("consent_type"); + + b.Property("DisplayName") + .HasColumnType("text") + .HasColumnName("display_name"); + + b.Property("DisplayNames") + .HasColumnType("text") + .HasColumnName("display_names"); + + b.Property("JsonWebKeySet") + .HasColumnType("text") + .HasColumnName("json_web_key_set"); + + b.Property("Permissions") + .HasColumnType("text") + .HasColumnName("permissions"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text") + .HasColumnName("post_logout_redirect_uris"); + + b.Property("Properties") + .HasColumnType("text") + .HasColumnName("properties"); + + b.Property("RedirectUris") + .HasColumnType("text") + .HasColumnName("redirect_uris"); + + b.Property("Requirements") + .HasColumnType("text") + .HasColumnName("requirements"); + + b.Property("Settings") + .HasColumnType("text") + .HasColumnName("settings"); + + b.HasKey("Id") + .HasName("pk_oidc_applications"); + + b.HasIndex("ClientId") + .IsUnique() + .HasDatabaseName("ix_oidc_applications_client_id"); + + b.ToTable("oidc_applications", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ApplicationId") + .HasColumnType("uuid") + .HasColumnName("application_id"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("concurrency_token"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("creation_date"); + + b.Property("Properties") + .HasColumnType("text") + .HasColumnName("properties"); + + b.Property("Scopes") + .HasColumnType("text") + .HasColumnName("scopes"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("status"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)") + .HasColumnName("subject"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("pk_oidc_authorizations"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type") + .HasDatabaseName("ix_oidc_authorizations_application_id_status_subject_type"); + + b.ToTable("oidc_authorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("concurrency_token"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Descriptions") + .HasColumnType("text") + .HasColumnName("descriptions"); + + b.Property("DisplayName") + .HasColumnType("text") + .HasColumnName("display_name"); + + b.Property("DisplayNames") + .HasColumnType("text") + .HasColumnName("display_names"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("Properties") + .HasColumnType("text") + .HasColumnName("properties"); + + b.Property("Resources") + .HasColumnType("text") + .HasColumnName("resources"); + + b.HasKey("Id") + .HasName("pk_oidc_scopes"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_oidc_scopes_name"); + + b.ToTable("oidc_scopes", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ApplicationId") + .HasColumnType("uuid") + .HasColumnName("application_id"); + + b.Property("AuthorizationId") + .HasColumnType("uuid") + .HasColumnName("authorization_id"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("concurrency_token"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("creation_date"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_date"); + + b.Property("Payload") + .HasColumnType("text") + .HasColumnName("payload"); + + b.Property("Properties") + .HasColumnType("text") + .HasColumnName("properties"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("redemption_date"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("reference_id"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("status"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)") + .HasColumnName("subject"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("pk_oidc_tokens"); + + b.HasIndex("ReferenceId") + .IsUnique() + .HasDatabaseName("ix_oidc_tokens_reference_id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type") + .HasDatabaseName("ix_oidc_tokens_application_id_status_subject_type"); + + b.ToTable("oidc_tokens", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.ApiKey", b => + { + b.Property("ApiKeyId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("api_key_id"); + + b.Property("ApplicationUserId") + .HasColumnType("uuid") + .HasColumnName("application_user_id"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone") + .HasColumnName("expires"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("key"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on"); + + b.HasKey("ApiKeyId") + .HasName("pk_api_keys"); + + b.HasIndex("ApplicationUserId") + .HasDatabaseName("ix_api_keys_application_user_id"); + + b.HasIndex("Key") + .IsUnique() + .HasDatabaseName("ix_api_keys_key"); + + b.ToTable("api_keys", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.EntityChangesJournal", b => + { + b.Property("Key") + .HasColumnType("text") + .HasColumnName("key"); + + b.Property("EntityLogicalName") + .HasColumnType("text") + .HasColumnName("entity_logical_name"); + + b.Property("DataToken") + .HasColumnType("text") + .HasColumnName("data_token"); + + b.Property("LastUpdated") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_updated"); + + b.Property("LastUpdatedBy") + .HasColumnType("text") + .HasColumnName("last_updated_by"); + + b.Property("NextQueryPageNumber") + .HasColumnType("integer") + .HasColumnName("next_query_page_number"); + + b.Property("NextQueryPageSize") + .HasColumnType("integer") + .HasColumnName("next_query_page_size"); + + b.Property("NextQueryPagingCookie") + .HasColumnType("text") + .HasColumnName("next_query_paging_cookie"); + + b.HasKey("Key", "EntityLogicalName") + .HasName("pk_entity_changes_journals"); + + b.ToTable("entity_changes_journals", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.Establishment", b => + { + b.Property("EstablishmentId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("establishment_id"); + + b.Property("Address3") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("address3") + .UseCollation("case_insensitive"); + + b.Property("County") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("county") + .UseCollation("case_insensitive"); + + b.Property("EstablishmentName") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)") + .HasColumnName("establishment_name") + .UseCollation("case_insensitive"); + + b.Property("EstablishmentNumber") + .HasMaxLength(4) + .HasColumnType("character(4)") + .HasColumnName("establishment_number") + .IsFixedLength(); + + b.Property("EstablishmentSourceId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1) + .HasColumnName("establishment_source_id"); + + b.Property("EstablishmentStatusCode") + .HasColumnType("integer") + .HasColumnName("establishment_status_code"); + + b.Property("EstablishmentStatusName") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("establishment_status_name"); + + b.Property("EstablishmentTypeCode") + .HasMaxLength(3) + .HasColumnType("character varying(3)") + .HasColumnName("establishment_type_code"); + + b.Property("EstablishmentTypeGroupCode") + .HasColumnType("integer") + .HasColumnName("establishment_type_group_code"); + + b.Property("EstablishmentTypeGroupName") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("establishment_type_group_name"); + + b.Property("EstablishmentTypeName") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("establishment_type_name") + .UseCollation("case_insensitive"); + + b.Property("LaCode") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("character(3)") + .HasColumnName("la_code") + .IsFixedLength(); + + b.Property("LaName") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("la_name") + .UseCollation("case_insensitive"); + + b.Property("Locality") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("locality") + .UseCollation("case_insensitive"); + + b.Property("Postcode") + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("postcode") + .UseCollation("case_insensitive"); + + b.Property("Street") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("street") + .UseCollation("case_insensitive"); + + b.Property("Town") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("town") + .UseCollation("case_insensitive"); + + b.Property("Urn") + .HasMaxLength(6) + .HasColumnType("integer") + .HasColumnName("urn") + .IsFixedLength(); + + b.HasKey("EstablishmentId") + .HasName("pk_establishments"); + + b.HasIndex("EstablishmentSourceId") + .HasDatabaseName("ix_establishment_establishment_source_id"); + + b.HasIndex("Urn") + .HasDatabaseName("ix_establishment_urn"); + + b.HasIndex("LaCode", "EstablishmentNumber") + .HasDatabaseName("ix_establishment_la_code_establishment_number"); + + b.ToTable("establishments", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.EstablishmentSource", b => + { + b.Property("EstablishmentSourceId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("establishment_source_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("EstablishmentSourceId")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("name") + .UseCollation("case_insensitive"); + + b.HasKey("EstablishmentSourceId") + .HasName("pk_establishment_sources"); + + b.ToTable("establishment_sources", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.Event", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("event_id"); + + b.Property("Created") + .HasColumnType("timestamp with time zone") + .HasColumnName("created"); + + b.Property("EventName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("event_name"); + + b.Property("Inserted") + .HasColumnType("timestamp with time zone") + .HasColumnName("inserted"); + + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("key"); + + b.Property("Payload") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("payload"); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("Published") + .HasColumnType("boolean") + .HasColumnName("published"); + + b.HasKey("EventId") + .HasName("pk_events"); + + b.HasIndex("Key") + .IsUnique() + .HasDatabaseName("ix_events_key") + .HasFilter("key is not null"); + + b.HasIndex("Payload") + .HasDatabaseName("ix_events_payload"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Payload"), "gin"); + + b.HasIndex("PersonId", "EventName") + .HasDatabaseName("ix_events_person_id_event_name") + .HasFilter("person_id is not null"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("PersonId", "EventName"), new[] { "Payload" }); + + b.ToTable("events", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.EytsAwardedEmailsJob", b => + { + b.Property("EytsAwardedEmailsJobId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("eyts_awarded_emails_job_id"); + + b.Property("AwardedToUtc") + .HasColumnType("timestamp with time zone") + .HasColumnName("awarded_to_utc"); + + b.Property("ExecutedUtc") + .HasColumnType("timestamp with time zone") + .HasColumnName("executed_utc"); + + b.HasKey("EytsAwardedEmailsJobId") + .HasName("pk_eyts_awarded_emails_jobs"); + + b.HasIndex("ExecutedUtc") + .HasDatabaseName("ix_eyts_awarded_emails_jobs_executed_utc"); + + b.ToTable("eyts_awarded_emails_jobs", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.EytsAwardedEmailsJobItem", b => + { + b.Property("EytsAwardedEmailsJobId") + .HasColumnType("uuid") + .HasColumnName("eyts_awarded_emails_job_id"); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("EmailAddress") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("email_address"); + + b.Property("EmailSent") + .HasColumnType("boolean") + .HasColumnName("email_sent"); + + b.Property("Personalization") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("personalization"); + + b.Property("Trn") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("character(7)") + .HasColumnName("trn") + .IsFixedLength(); + + b.HasKey("EytsAwardedEmailsJobId", "PersonId") + .HasName("pk_eyts_awarded_emails_job_items"); + + b.HasIndex("Personalization") + .HasDatabaseName("ix_eyts_awarded_emails_job_items_personalization"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Personalization"), "gin"); + + b.ToTable("eyts_awarded_emails_job_items", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.InductionCompletedEmailsJob", b => + { + b.Property("InductionCompletedEmailsJobId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("induction_completed_emails_job_id"); + + b.Property("AwardedToUtc") + .HasColumnType("timestamp with time zone") + .HasColumnName("awarded_to_utc"); + + b.Property("ExecutedUtc") + .HasColumnType("timestamp with time zone") + .HasColumnName("executed_utc"); + + b.HasKey("InductionCompletedEmailsJobId") + .HasName("pk_induction_completed_emails_jobs"); + + b.HasIndex("ExecutedUtc") + .HasDatabaseName("ix_induction_completed_emails_jobs_executed_utc"); + + b.ToTable("induction_completed_emails_jobs", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.InductionCompletedEmailsJobItem", b => + { + b.Property("InductionCompletedEmailsJobId") + .HasColumnType("uuid") + .HasColumnName("induction_completed_emails_job_id"); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("EmailAddress") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("email_address"); + + b.Property("EmailSent") + .HasColumnType("boolean") + .HasColumnName("email_sent"); + + b.Property("Personalization") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("personalization"); + + b.Property("Trn") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("character(7)") + .HasColumnName("trn") + .IsFixedLength(); + + b.HasKey("InductionCompletedEmailsJobId", "PersonId") + .HasName("pk_induction_completed_emails_job_items"); + + b.HasIndex("Personalization") + .HasDatabaseName("ix_induction_completed_emails_job_items_personalization"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Personalization"), "gin"); + + b.ToTable("induction_completed_emails_job_items", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.InternationalQtsAwardedEmailsJob", b => + { + b.Property("InternationalQtsAwardedEmailsJobId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("international_qts_awarded_emails_job_id"); + + b.Property("AwardedToUtc") + .HasColumnType("timestamp with time zone") + .HasColumnName("awarded_to_utc"); + + b.Property("ExecutedUtc") + .HasColumnType("timestamp with time zone") + .HasColumnName("executed_utc"); + + b.HasKey("InternationalQtsAwardedEmailsJobId") + .HasName("pk_international_qts_awarded_emails_jobs"); + + b.HasIndex("ExecutedUtc") + .HasDatabaseName("ix_international_qts_awarded_emails_jobs_executed_utc"); + + b.ToTable("international_qts_awarded_emails_jobs", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.InternationalQtsAwardedEmailsJobItem", b => + { + b.Property("InternationalQtsAwardedEmailsJobId") + .HasColumnType("uuid") + .HasColumnName("international_qts_awarded_emails_job_id"); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("EmailAddress") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("email_address"); + + b.Property("EmailSent") + .HasColumnType("boolean") + .HasColumnName("email_sent"); + + b.Property("Personalization") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("personalization"); + + b.Property("Trn") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("character(7)") + .HasColumnName("trn") + .IsFixedLength(); + + b.HasKey("InternationalQtsAwardedEmailsJobId", "PersonId") + .HasName("pk_international_qts_awarded_emails_job_items"); + + b.HasIndex("Personalization") + .HasDatabaseName("ix_international_qts_awarded_emails_job_items_personalization"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Personalization"), "gin"); + + b.ToTable("international_qts_awarded_emails_job_items", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.JourneyState", b => + { + b.Property("InstanceId") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("instance_id"); + + b.Property("Completed") + .HasColumnType("timestamp with time zone") + .HasColumnName("completed"); + + b.Property("Created") + .HasColumnType("timestamp with time zone") + .HasColumnName("created"); + + b.Property("State") + .IsRequired() + .HasColumnType("text") + .HasColumnName("state"); + + b.Property("Updated") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("user_id"); + + b.HasKey("InstanceId") + .HasName("pk_journey_states"); + + b.ToTable("journey_states", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.MandatoryQualificationProvider", b => + { + b.Property("MandatoryQualificationProviderId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("mandatory_qualification_provider_id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.HasKey("MandatoryQualificationProviderId") + .HasName("pk_mandatory_qualification_providers"); + + b.ToTable("mandatory_qualification_providers", (string)null); + + b.HasData( + new + { + MandatoryQualificationProviderId = new Guid("e28ea41d-408d-4c89-90cc-8b9b04ac68f5"), + Name = "University of Birmingham" + }, + new + { + MandatoryQualificationProviderId = new Guid("89f9a1aa-3d68-4985-a4ce-403b6044c18c"), + Name = "University of Leeds" + }, + new + { + MandatoryQualificationProviderId = new Guid("aa5c300e-3b7c-456c-8183-3520b3d55dca"), + Name = "University of Manchester" + }, + new + { + MandatoryQualificationProviderId = new Guid("f417e73e-e2ad-40eb-85e3-55865be7f6be"), + Name = "Mary Hare School / University of Hertfordshire" + }, + new + { + MandatoryQualificationProviderId = new Guid("fbf22e04-b274-4c80-aba8-79fb6a7a32ce"), + Name = "University of Edinburgh" + }, + new + { + MandatoryQualificationProviderId = new Guid("26204149-349c-4ad6-9466-bb9b83723eae"), + Name = "Liverpool John Moores University" + }, + new + { + MandatoryQualificationProviderId = new Guid("0c30f666-647c-4ea8-8883-0fc6010b56be"), + Name = "University of Oxford/Oxford Polytechnic" + }, + new + { + MandatoryQualificationProviderId = new Guid("d0e6d54c-5e90-438a-945d-f97388c2b352"), + Name = "University of Cambridge" + }, + new + { + MandatoryQualificationProviderId = new Guid("aec32252-ef25-452e-a358-34a04e03369c"), + Name = "University of Newcastle-upon-Tyne" + }, + new + { + MandatoryQualificationProviderId = new Guid("d9ee7054-7fde-4cfd-9a5e-4b99511d1b3d"), + Name = "University of Plymouth" + }, + new + { + MandatoryQualificationProviderId = new Guid("707d58ca-1953-413b-9a46-41e9b0be885e"), + Name = "University of Hertfordshire" + }, + new + { + MandatoryQualificationProviderId = new Guid("3fc648a7-18e4-49e7-8a4b-1612616b72d5"), + Name = "University of London" + }, + new + { + MandatoryQualificationProviderId = new Guid("374dceb8-8224-45b8-b7dc-a6b0282b1065"), + Name = "Bristol Polytechnic" + }, + new + { + MandatoryQualificationProviderId = new Guid("d4fc958b-21de-47ec-9f03-36ae237a1b11"), + Name = "University College, Swansea" + }); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.NameSynonyms", b => + { + b.Property("NameSynonymsId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("name_synonyms_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("NameSynonymsId")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name") + .UseCollation("case_insensitive"); + + b.Property("Synonyms") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("synonyms") + .UseCollation("case_insensitive"); + + b.HasKey("NameSynonymsId") + .HasName("pk_name_synonyms"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_name_synonyms_name"); + + b.ToTable("name_synonyms", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.OneLoginUser", b => + { + b.Property("Subject") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("subject"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("email"); + + b.Property("FirstOneLoginSignIn") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_one_login_sign_in"); + + b.Property("FirstSignIn") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_sign_in"); + + b.Property("LastCoreIdentityVc") + .HasColumnType("jsonb") + .HasColumnName("last_core_identity_vc"); + + b.Property("LastOneLoginSignIn") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_one_login_sign_in"); + + b.Property("LastSignIn") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_sign_in"); + + b.Property("MatchRoute") + .HasColumnType("integer") + .HasColumnName("match_route"); + + b.Property("MatchedAttributes") + .HasColumnType("jsonb") + .HasColumnName("matched_attributes"); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("VerificationRoute") + .HasColumnType("integer") + .HasColumnName("verification_route"); + + b.Property("VerifiedDatesOfBirth") + .HasColumnType("jsonb") + .HasColumnName("verified_dates_of_birth"); + + b.Property("VerifiedNames") + .HasColumnType("jsonb") + .HasColumnName("verified_names"); + + b.Property("VerifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("verified_on"); + + b.HasKey("Subject") + .HasName("pk_one_login_users"); + + b.ToTable("one_login_users", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.Person", b => + { + b.Property("PersonId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("DateOfBirth") + .HasColumnType("date") + .HasColumnName("date_of_birth"); + + b.Property("DeletedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_on"); + + b.Property("DqtContactId") + .HasColumnType("uuid") + .HasColumnName("dqt_contact_id"); + + b.Property("DqtCreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("dqt_created_on"); + + b.Property("DqtFirstName") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("dqt_first_name") + .UseCollation("case_insensitive"); + + b.Property("DqtFirstSync") + .HasColumnType("timestamp with time zone") + .HasColumnName("dqt_first_sync"); + + b.Property("DqtLastName") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("dqt_last_name") + .UseCollation("case_insensitive"); + + b.Property("DqtLastSync") + .HasColumnType("timestamp with time zone") + .HasColumnName("dqt_last_sync"); + + b.Property("DqtMiddleName") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("dqt_middle_name") + .UseCollation("case_insensitive"); + + b.Property("DqtModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("dqt_modified_on"); + + b.Property("DqtState") + .HasColumnType("integer") + .HasColumnName("dqt_state"); + + b.Property("EmailAddress") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("email_address") + .UseCollation("case_insensitive"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("first_name") + .UseCollation("case_insensitive"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("last_name") + .UseCollation("case_insensitive"); + + b.Property("MiddleName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("middle_name") + .UseCollation("case_insensitive"); + + b.Property("NationalInsuranceNumber") + .HasMaxLength(9) + .HasColumnType("character(9)") + .HasColumnName("national_insurance_number") + .IsFixedLength(); + + b.Property("Trn") + .HasMaxLength(7) + .HasColumnType("character(7)") + .HasColumnName("trn") + .IsFixedLength(); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on"); + + b.HasKey("PersonId") + .HasName("pk_persons"); + + b.HasIndex("DqtContactId") + .IsUnique() + .HasDatabaseName("ix_persons_dqt_contact_id") + .HasFilter("dqt_contact_id is not null"); + + b.HasIndex("Trn") + .IsUnique() + .HasDatabaseName("ix_persons_trn") + .HasFilter("trn is not null"); + + b.ToTable("persons", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.PersonSearchAttribute", b => + { + b.Property("PersonSearchAttributeId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("person_search_attribute_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PersonSearchAttributeId")); + + b.Property("AttributeKey") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("attribute_key") + .UseCollation("case_insensitive"); + + b.Property("AttributeType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("attribute_type") + .UseCollation("case_insensitive"); + + b.Property("AttributeValue") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("attribute_value") + .UseCollation("case_insensitive"); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("Tags") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("tags"); + + b.HasKey("PersonSearchAttributeId") + .HasName("pk_person_search_attributes"); + + b.HasIndex("PersonId") + .HasDatabaseName("ix_person_search_attributes_person_id"); + + b.HasIndex("AttributeType", "AttributeValue") + .HasDatabaseName("ix_person_search_attributes_attribute_type_and_value"); + + b.ToTable("person_search_attributes", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.QtsAwardedEmailsJob", b => + { + b.Property("QtsAwardedEmailsJobId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("qts_awarded_emails_job_id"); + + b.Property("AwardedToUtc") + .HasColumnType("timestamp with time zone") + .HasColumnName("awarded_to_utc"); + + b.Property("ExecutedUtc") + .HasColumnType("timestamp with time zone") + .HasColumnName("executed_utc"); + + b.HasKey("QtsAwardedEmailsJobId") + .HasName("pk_qts_awarded_emails_jobs"); + + b.HasIndex("ExecutedUtc") + .HasDatabaseName("ix_qts_awarded_emails_jobs_executed_utc"); + + b.ToTable("qts_awarded_emails_jobs", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.QtsAwardedEmailsJobItem", b => + { + b.Property("QtsAwardedEmailsJobId") + .HasColumnType("uuid") + .HasColumnName("qts_awarded_emails_job_id"); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("EmailAddress") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("email_address"); + + b.Property("EmailSent") + .HasColumnType("boolean") + .HasColumnName("email_sent"); + + b.Property("Personalization") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("personalization"); + + b.Property("Trn") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("character(7)") + .HasColumnName("trn") + .IsFixedLength(); + + b.HasKey("QtsAwardedEmailsJobId", "PersonId") + .HasName("pk_qts_awarded_emails_job_items"); + + b.HasIndex("Personalization") + .HasDatabaseName("ix_qts_awarded_emails_job_items_personalization"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Personalization"), "gin"); + + b.ToTable("qts_awarded_emails_job_items", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.Qualification", b => + { + b.Property("QualificationId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("qualification_id"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("DeletedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_on"); + + b.Property("DqtCreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("dqt_created_on"); + + b.Property("DqtFirstSync") + .HasColumnType("timestamp with time zone") + .HasColumnName("dqt_first_sync"); + + b.Property("DqtLastSync") + .HasColumnType("timestamp with time zone") + .HasColumnName("dqt_last_sync"); + + b.Property("DqtModifiedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("dqt_modified_on"); + + b.Property("DqtQualificationId") + .HasColumnType("uuid") + .HasColumnName("dqt_qualification_id"); + + b.Property("DqtState") + .HasColumnType("integer") + .HasColumnName("dqt_state"); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("QualificationType") + .HasColumnType("integer") + .HasColumnName("qualification_type"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on"); + + b.HasKey("QualificationId") + .HasName("pk_qualifications"); + + b.HasIndex("DqtQualificationId") + .IsUnique() + .HasDatabaseName("ix_qualifications_dqt_qualification_id") + .HasFilter("dqt_qualification_id is not null"); + + b.HasIndex("PersonId") + .HasDatabaseName("ix_qualifications_person_id"); + + b.ToTable("qualifications", (string)null); + + b.HasDiscriminator("QualificationType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.SupportTask", b => + { + b.Property("SupportTaskReference") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("support_task_reference"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("OneLoginUserSubject") + .HasColumnType("character varying(255)") + .HasColumnName("one_login_user_subject"); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("SupportTaskType") + .HasColumnType("integer") + .HasColumnName("support_task_type"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on"); + + b.Property("_data") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("data"); + + b.HasKey("SupportTaskReference") + .HasName("pk_support_tasks"); + + b.HasIndex("OneLoginUserSubject") + .HasDatabaseName("ix_support_tasks_one_login_user_subject"); + + b.HasIndex("PersonId") + .HasDatabaseName("ix_support_tasks_person_id"); + + b.ToTable("support_tasks", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsCsvExtract", b => + { + b.Property("TpsCsvExtractId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tps_csv_extract_id"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("Filename") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("filename"); + + b.HasKey("TpsCsvExtractId") + .HasName("pk_tps_csv_extracts"); + + b.ToTable("tps_csv_extracts", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsCsvExtractItem", b => + { + b.Property("TpsCsvExtractItemId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tps_csv_extract_item_id"); + + b.Property("Created") + .HasColumnType("timestamp with time zone") + .HasColumnName("created"); + + b.Property("DateOfBirth") + .HasColumnType("date") + .HasColumnName("date_of_birth"); + + b.Property("DateOfDeath") + .HasColumnType("date") + .HasColumnName("date_of_death"); + + b.Property("EmploymentEndDate") + .HasColumnType("date") + .HasColumnName("employment_end_date"); + + b.Property("EmploymentStartDate") + .HasColumnType("date") + .HasColumnName("employment_start_date"); + + b.Property("EmploymentType") + .HasColumnType("integer") + .HasColumnName("employment_type"); + + b.Property("EstablishmentEmailAddress") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("establishment_email_address"); + + b.Property("EstablishmentNumber") + .HasMaxLength(4) + .HasColumnType("character(4)") + .HasColumnName("establishment_number") + .IsFixedLength(); + + b.Property("EstablishmentPostcode") + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("establishment_postcode"); + + b.Property("ExtractDate") + .HasColumnType("date") + .HasColumnName("extract_date"); + + b.Property("Gender") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("gender"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("key"); + + b.Property("LocalAuthorityCode") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("character(3)") + .HasColumnName("local_authority_code") + .IsFixedLength(); + + b.Property("MemberEmailAddress") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("member_email_address"); + + b.Property("MemberId") + .HasColumnType("integer") + .HasColumnName("member_id"); + + b.Property("MemberPostcode") + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("member_postcode"); + + b.Property("NationalInsuranceNumber") + .IsRequired() + .HasMaxLength(9) + .HasColumnType("character(9)") + .HasColumnName("national_insurance_number") + .IsFixedLength(); + + b.Property("Result") + .HasColumnType("integer") + .HasColumnName("result"); + + b.Property("TpsCsvExtractId") + .HasColumnType("uuid") + .HasColumnName("tps_csv_extract_id"); + + b.Property("TpsCsvExtractLoadItemId") + .HasColumnType("uuid") + .HasColumnName("tps_csv_extract_load_item_id"); + + b.Property("Trn") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("character(7)") + .HasColumnName("trn") + .IsFixedLength(); + + b.Property("WithdrawalIndicator") + .HasMaxLength(1) + .HasColumnType("character(1)") + .HasColumnName("withdrawal_indicator") + .IsFixedLength(); + + b.HasKey("TpsCsvExtractItemId") + .HasName("pk_tps_csv_extract_items"); + + b.HasIndex("Key") + .HasDatabaseName("ix_tps_csv_extract_items_key"); + + b.HasIndex("TpsCsvExtractId") + .HasDatabaseName("ix_tps_csv_extract_items_tps_csv_extract_id"); + + b.HasIndex("TpsCsvExtractLoadItemId") + .HasDatabaseName("ix_tps_csv_extract_items_tps_csv_extract_load_item_id"); + + b.HasIndex("Trn") + .HasDatabaseName("ix_tps_csv_extract_items_trn"); + + b.HasIndex("LocalAuthorityCode", "EstablishmentNumber") + .HasDatabaseName("ix_tps_csv_extract_items_la_code_establishment_number"); + + b.ToTable("tps_csv_extract_items", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsCsvExtractLoadItem", b => + { + b.Property("TpsCsvExtractLoadItemId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tps_csv_extract_load_item_id"); + + b.Property("Created") + .HasColumnType("timestamp with time zone") + .HasColumnName("created"); + + b.Property("DateOfBirth") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("date_of_birth"); + + b.Property("DateOfDeath") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("date_of_death"); + + b.Property("EmploymentEndDate") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("employment_end_date"); + + b.Property("EmploymentStartDate") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("employment_start_date"); + + b.Property("Errors") + .HasColumnType("integer") + .HasColumnName("errors"); + + b.Property("EstablishmentEmailAddress") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("establishment_email_address"); + + b.Property("EstablishmentNumber") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("establishment_number"); + + b.Property("EstablishmentPostcode") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("establishment_postcode"); + + b.Property("ExtractDate") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("extract_date"); + + b.Property("FullOrPartTimeIndicator") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("full_or_part_time_indicator"); + + b.Property("Gender") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("gender"); + + b.Property("LocalAuthorityCode") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("local_authority_code"); + + b.Property("MemberEmailAddress") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("member_email_address"); + + b.Property("MemberId") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("member_id"); + + b.Property("MemberPostcode") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("member_postcode"); + + b.Property("NationalInsuranceNumber") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("national_insurance_number"); + + b.Property("TpsCsvExtractId") + .HasColumnType("uuid") + .HasColumnName("tps_csv_extract_id"); + + b.Property("Trn") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("trn"); + + b.Property("WithdrawalIndicator") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("withdrawal_indicator"); + + b.HasKey("TpsCsvExtractLoadItemId") + .HasName("pk_tps_csv_extract_load_items"); + + b.ToTable("tps_csv_extract_load_items", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsEmployment", b => + { + b.Property("TpsEmploymentId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tps_employment_id"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("EmploymentType") + .HasColumnType("integer") + .HasColumnName("employment_type"); + + b.Property("EndDate") + .HasColumnType("date") + .HasColumnName("end_date"); + + b.Property("EstablishmentId") + .HasColumnType("uuid") + .HasColumnName("establishment_id"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("key"); + + b.Property("LastExtractDate") + .HasColumnType("date") + .HasColumnName("last_extract_date"); + + b.Property("LastKnownTpsEmployedDate") + .HasColumnType("date") + .HasColumnName("last_known_tps_employed_date"); + + b.Property("NationalInsuranceNumber") + .HasMaxLength(9) + .HasColumnType("character(9)") + .HasColumnName("national_insurance_number") + .IsFixedLength(); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("PersonPostcode") + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("person_postcode"); + + b.Property("StartDate") + .HasColumnType("date") + .HasColumnName("start_date"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on"); + + b.Property("WithdrawalConfirmed") + .HasColumnType("boolean") + .HasColumnName("withdrawal_confirmed"); + + b.HasKey("TpsEmploymentId") + .HasName("pk_tps_employments"); + + b.HasIndex("EstablishmentId") + .HasDatabaseName("ix_tps_employments_establishment_id"); + + b.HasIndex("Key") + .HasDatabaseName("ix_tps_employments_key"); + + b.HasIndex("PersonId") + .HasDatabaseName("ix_tps_employments_person_id"); + + b.ToTable("tps_employments", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsEstablishment", b => + { + b.Property("TpsEstablishmentId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tps_establishment_id"); + + b.Property("EmployersName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("employers_name"); + + b.Property("EstablishmentCode") + .IsRequired() + .HasMaxLength(4) + .HasColumnType("character(4)") + .HasColumnName("establishment_code") + .IsFixedLength(); + + b.Property("LaCode") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("character(3)") + .HasColumnName("la_code") + .IsFixedLength(); + + b.Property("SchoolClosedDate") + .HasColumnType("date") + .HasColumnName("school_closed_date"); + + b.Property("SchoolGiasName") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("school_gias_name"); + + b.HasKey("TpsEstablishmentId") + .HasName("pk_tps_establishments"); + + b.HasIndex("LaCode", "EstablishmentCode") + .HasDatabaseName("ix_tps_establishments_la_code_establishment_number"); + + b.ToTable("tps_establishments", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsEstablishmentType", b => + { + b.Property("TpsEstablishmentTypeId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("tps_establishment_type_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("TpsEstablishmentTypeId")); + + b.Property("Description") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("description"); + + b.Property("EstablishmentRangeFrom") + .IsRequired() + .HasMaxLength(4) + .HasColumnType("character(4)") + .HasColumnName("establishment_range_from") + .IsFixedLength(); + + b.Property("EstablishmentRangeTo") + .IsRequired() + .HasMaxLength(4) + .HasColumnType("character(4)") + .HasColumnName("establishment_range_to") + .IsFixedLength(); + + b.Property("ShortDescription") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)") + .HasColumnName("short_description"); + + b.HasKey("TpsEstablishmentTypeId") + .HasName("pk_tps_establishment_types"); + + b.ToTable("tps_establishment_types", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TrnRequest", b => + { + b.Property("TrnRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("trn_request_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("TrnRequestId")); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("client_id"); + + b.Property("IdentityUserId") + .HasColumnType("uuid") + .HasColumnName("identity_user_id"); + + b.Property("LinkedToIdentity") + .HasColumnType("boolean") + .HasColumnName("linked_to_identity"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("request_id"); + + b.Property("TeacherId") + .HasColumnType("uuid") + .HasColumnName("teacher_id"); + + b.Property("TrnToken") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("trn_token"); + + b.HasKey("TrnRequestId") + .HasName("pk_trn_requests"); + + b.HasIndex("ClientId", "RequestId") + .IsUnique() + .HasDatabaseName("ix_trn_requests_client_id_request_id"); + + b.ToTable("trn_requests", (string)null); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.UserBase", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("Active") + .HasColumnType("boolean") + .HasColumnName("active"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("UserType") + .HasColumnType("integer") + .HasColumnName("user_type"); + + b.HasKey("UserId") + .HasName("pk_users"); + + b.ToTable("users", (string)null); + + b.HasDiscriminator("UserType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.MandatoryQualification", b => + { + b.HasBaseType("TeachingRecordSystem.Core.DataStore.Postgres.Models.Qualification"); + + b.Property("DqtMqEstablishmentId") + .HasColumnType("uuid") + .HasColumnName("dqt_mq_establishment_id"); + + b.Property("DqtSpecialismId") + .HasColumnType("uuid") + .HasColumnName("dqt_specialism_id"); + + b.Property("EndDate") + .HasColumnType("date") + .HasColumnName("end_date"); + + b.Property("ProviderId") + .HasColumnType("uuid") + .HasColumnName("mq_provider_id"); + + b.Property("Specialism") + .HasColumnType("integer") + .HasColumnName("mq_specialism"); + + b.Property("StartDate") + .HasColumnType("date") + .HasColumnName("start_date"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("mq_status"); + + b.HasDiscriminator().HasValue(0); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.ApplicationUser", b => + { + b.HasBaseType("TeachingRecordSystem.Core.DataStore.Postgres.Models.UserBase"); + + b.Property("ApiRoles") + .HasColumnType("varchar[]") + .HasColumnName("api_roles"); + + b.Property("ClientId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("client_id"); + + b.Property("ClientSecret") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("client_secret"); + + b.Property("IsOidcClient") + .HasColumnType("boolean") + .HasColumnName("is_oidc_client"); + + b.Property("OneLoginAuthenticationSchemeName") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("one_login_authentication_scheme_name"); + + b.Property("OneLoginClientId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("one_login_client_id"); + + b.Property("OneLoginPostLogoutRedirectUriPath") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("one_login_post_logout_redirect_uri_path"); + + b.Property("OneLoginPrivateKeyPem") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("one_login_private_key_pem"); + + b.Property("OneLoginRedirectUriPath") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("one_login_redirect_uri_path"); + + b.Property>("PostLogoutRedirectUris") + .HasColumnType("varchar[]") + .HasColumnName("post_logout_redirect_uris"); + + b.Property>("RedirectUris") + .HasColumnType("varchar[]") + .HasColumnName("redirect_uris"); + + b.HasIndex("ClientId") + .IsUnique() + .HasDatabaseName("ix_users_client_id") + .HasFilter("client_id is not null"); + + b.HasIndex("OneLoginAuthenticationSchemeName") + .IsUnique() + .HasDatabaseName("ix_users_one_login_authentication_scheme_name") + .HasFilter("one_login_authentication_scheme_name is not null"); + + b.HasDiscriminator().HasValue(2); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.SystemUser", b => + { + b.HasBaseType("TeachingRecordSystem.Core.DataStore.Postgres.Models.UserBase"); + + b.ToTable("users", (string)null); + + b.HasDiscriminator().HasValue(3); + + b.HasData( + new + { + UserId = new Guid("a81394d1-a498-46d8-af3e-e077596ab303"), + Active = true, + Name = "System", + UserType = 0 + }); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.User", b => + { + b.HasBaseType("TeachingRecordSystem.Core.DataStore.Postgres.Models.UserBase"); + + b.Property("AzureAdUserId") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("azure_ad_user_id"); + + b.Property("DqtUserId") + .HasColumnType("uuid") + .HasColumnName("dqt_user_id"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("email") + .UseCollation("case_insensitive"); + + b.Property("Roles") + .IsRequired() + .HasColumnType("varchar[]") + .HasColumnName("roles"); + + b.HasIndex("AzureAdUserId") + .IsUnique() + .HasDatabaseName("ix_users_azure_ad_user_id"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId") + .HasConstraintName("fk_oidc_authorizations_oidc_applications_application_id"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId") + .HasConstraintName("fk_oidc_tokens_oidc_applications_application_id"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId") + .HasConstraintName("fk_oidc_tokens_oidc_authorizations_authorization_id"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.ApiKey", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.ApplicationUser", "ApplicationUser") + .WithMany("ApiKeys") + .HasForeignKey("ApplicationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_api_key_application_user"); + + b.Navigation("ApplicationUser"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.Establishment", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.EstablishmentSource", null) + .WithMany() + .HasForeignKey("EstablishmentSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_establishments_establishment_source_id"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.EytsAwardedEmailsJobItem", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.EytsAwardedEmailsJob", "EytsAwardedEmailsJob") + .WithMany("JobItems") + .HasForeignKey("EytsAwardedEmailsJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_eyts_awarded_emails_job_items_eyts_awarded_emails_jobs_eyts"); + + b.Navigation("EytsAwardedEmailsJob"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.InductionCompletedEmailsJobItem", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.InductionCompletedEmailsJob", "InductionCompletedEmailsJob") + .WithMany("JobItems") + .HasForeignKey("InductionCompletedEmailsJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_induction_completed_emails_job_items_induction_completed_em"); + + b.Navigation("InductionCompletedEmailsJob"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.InternationalQtsAwardedEmailsJobItem", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.InternationalQtsAwardedEmailsJob", "InternationalQtsAwardedEmailsJob") + .WithMany("JobItems") + .HasForeignKey("InternationalQtsAwardedEmailsJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_international_qts_awarded_emails_job_items_international_qt"); + + b.Navigation("InternationalQtsAwardedEmailsJob"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.OneLoginUser", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.Person", "Person") + .WithOne() + .HasForeignKey("TeachingRecordSystem.Core.DataStore.Postgres.Models.OneLoginUser", "PersonId") + .HasConstraintName("fk_one_login_users_persons_person_id"); + + b.Navigation("Person"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.QtsAwardedEmailsJobItem", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.QtsAwardedEmailsJob", "QtsAwardedEmailsJob") + .WithMany("JobItems") + .HasForeignKey("QtsAwardedEmailsJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_qts_awarded_emails_job_items_qts_awarded_emails_jobs_qts_aw"); + + b.Navigation("QtsAwardedEmailsJob"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.Qualification", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.Person", null) + .WithMany() + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_qualifications_person"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.SupportTask", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.OneLoginUser", null) + .WithMany() + .HasForeignKey("OneLoginUserSubject") + .HasConstraintName("fk_support_tasks_one_login_user"); + + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.Person", null) + .WithMany() + .HasForeignKey("PersonId") + .HasConstraintName("fk_support_tasks_person"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsCsvExtractItem", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsCsvExtract", null) + .WithMany() + .HasForeignKey("TpsCsvExtractId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tps_csv_extract_items_tps_csv_extract_id"); + + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsCsvExtractLoadItem", null) + .WithMany() + .HasForeignKey("TpsCsvExtractLoadItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tps_csv_extract_items_tps_csv_extract_load_item_id"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsCsvExtractLoadItem", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsCsvExtract", null) + .WithMany() + .HasForeignKey("TpsCsvExtractId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tps_csv_extract_load_items_tps_csv_extract_id"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsEmployment", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.Establishment", null) + .WithMany() + .HasForeignKey("EstablishmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tps_employments_establishment_id"); + + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.Person", null) + .WithMany() + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tps_employments_person_id"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.MandatoryQualification", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.MandatoryQualificationProvider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .HasConstraintName("fk_qualifications_mandatory_qualification_provider"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.EytsAwardedEmailsJob", b => + { + b.Navigation("JobItems"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.InductionCompletedEmailsJob", b => + { + b.Navigation("JobItems"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.InternationalQtsAwardedEmailsJob", b => + { + b.Navigation("JobItems"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.QtsAwardedEmailsJob", b => + { + b.Navigation("JobItems"); + }); + + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.ApplicationUser", b => + { + b.Navigation("ApiKeys"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/20240807133540_RenameTpsEmployment.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/20240807133540_RenameTpsEmployment.cs new file mode 100644 index 000000000..fdeb96f83 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/20240807133540_RenameTpsEmployment.cs @@ -0,0 +1,175 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata.Conventions; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeachingRecordSystem.Core.DataStore.Postgres.Migrations +{ + /// + public partial class RenameTpsEmployment : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "person_employments"); + + migrationBuilder.RenameColumn( + name: "withdrawl_indicator", + table: "tps_csv_extract_load_items", + newName: "withdrawal_indicator"); + + migrationBuilder.RenameColumn( + name: "withdrawl_indicator", + table: "tps_csv_extract_items", + newName: "withdrawal_indicator"); + + migrationBuilder.CreateTable( + name: "tps_employments", + columns: table => new + { + tps_employment_id = table.Column(type: "uuid", nullable: false), + person_id = table.Column(type: "uuid", nullable: false), + establishment_id = table.Column(type: "uuid", nullable: false), + start_date = table.Column(type: "date", nullable: false), + end_date = table.Column(type: "date", nullable: true), + last_known_tps_employed_date = table.Column(type: "date", nullable: false), + last_extract_date = table.Column(type: "date", nullable: false), + employment_type = table.Column(type: "integer", nullable: false), + created_on = table.Column(type: "timestamp with time zone", nullable: false), + updated_on = table.Column(type: "timestamp with time zone", nullable: false), + key = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + national_insurance_number = table.Column(type: "character(9)", fixedLength: true, maxLength: 9, nullable: true), + person_postcode = table.Column(type: "character varying(10)", maxLength: 10, nullable: true), + withdrawal_confirmed = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_tps_employments", x => x.tps_employment_id); + table.ForeignKey( + name: "fk_tps_employments_establishment_id", + column: x => x.establishment_id, + principalTable: "establishments", + principalColumn: "establishment_id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_tps_employments_person_id", + column: x => x.person_id, + principalTable: "persons", + principalColumn: "person_id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_tps_employments_establishment_id", + table: "tps_employments", + column: "establishment_id"); + + migrationBuilder.CreateIndex( + name: "ix_tps_employments_key", + table: "tps_employments", + column: "key"); + + migrationBuilder.CreateIndex( + name: "ix_tps_employments_person_id", + table: "tps_employments", + column: "person_id"); + + migrationBuilder.InsertData( + table: "establishments", + columns: new[] { "establishment_id", "urn", "la_code", "la_name", "establishment_number", "establishment_name", "establishment_type_code", "establishment_type_name", "establishment_type_group_code", "establishment_type_group_name", "establishment_status_code", "establishment_status_name", "street", "locality", "address3", "town", "county", "postcode", "establishment_source_id" }, + values: new object[,] + { + { new Guid("2bd072d8-6214-4a77-b9b4-e8d77d96a030"), null, "751", null, "0000", "Multi-Academy Trusts", null, null, null, null, null, null, null, null, null, null, null, null, 2 }, + { new Guid("2b9b1c3e-8057-4a68-8250-2699368e2e98"), null, "751", null, "1570", "The Collective Community Trust", null, null, null, null, null, null, null, null, null, null, null, null, 2 }, + { new Guid("cb7ef0ee-41ce-4c2d-87dc-91aa968ca76c"), null, "751", null, "1572", "Synergy Education Trust Limited", null, null, null, null, null, null, null, null, null, null, null, null, 2 }, + { new Guid("7310b62f-454a-4d2c-8183-124acd71fd7a"), null, "751", null, "1573", "Mosaic Partnership Trust Ltd", null, null, null, null, null, null, null, null, null, null, null, null, 2 } + }); + + migrationBuilder.Procedure("p_refresh_tps_employments_person_search_attributes_v1.sql"); + migrationBuilder.Procedure("fn_delete_tps_employments_person_search_attributes_v1.sql"); + migrationBuilder.Procedure("fn_insert_tps_employments_person_search_attributes_v1.sql"); + migrationBuilder.Procedure("fn_update_tps_employments_person_search_attributes_v1.sql"); + migrationBuilder.Trigger("trg_delete_tps_employments_person_search_attributes_v1.sql"); + migrationBuilder.Trigger("trg_insert_tps_employments_person_search_attributes_v1.sql"); + migrationBuilder.Trigger("trg_update_tps_employments_person_search_attributes_v1.sql"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "tps_employments"); + + migrationBuilder.RenameColumn( + name: "withdrawal_indicator", + table: "tps_csv_extract_load_items", + newName: "withdrawl_indicator"); + + migrationBuilder.RenameColumn( + name: "withdrawal_indicator", + table: "tps_csv_extract_items", + newName: "withdrawl_indicator"); + + migrationBuilder.CreateTable( + name: "person_employments", + columns: table => new + { + person_employment_id = table.Column(type: "uuid", nullable: false), + created_on = table.Column(type: "timestamp with time zone", nullable: false), + employment_type = table.Column(type: "integer", nullable: false), + end_date = table.Column(type: "date", nullable: true), + establishment_id = table.Column(type: "uuid", nullable: false), + key = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + last_extract_date = table.Column(type: "date", nullable: false), + last_known_employed_date = table.Column(type: "date", nullable: false), + national_insurance_number = table.Column(type: "character(9)", fixedLength: true, maxLength: 9, nullable: true), + person_id = table.Column(type: "uuid", nullable: false), + person_postcode = table.Column(type: "character varying(10)", maxLength: 10, nullable: true), + start_date = table.Column(type: "date", nullable: false), + updated_on = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_person_employments", x => x.person_employment_id); + table.ForeignKey( + name: "fk_person_employments_establishment_id", + column: x => x.establishment_id, + principalTable: "establishments", + principalColumn: "establishment_id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_person_employments_person_id", + column: x => x.person_id, + principalTable: "persons", + principalColumn: "person_id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_person_employments_establishment_id", + table: "person_employments", + column: "establishment_id"); + + migrationBuilder.CreateIndex( + name: "ix_person_employments_key", + table: "person_employments", + column: "key"); + + migrationBuilder.CreateIndex( + name: "ix_person_employments_person_id", + table: "person_employments", + column: "person_id"); + + migrationBuilder.Procedure("p_refresh_person_employments_person_search_attributes_v1.sql"); + migrationBuilder.Procedure("fn_delete_person_employments_person_search_attributes_v1.sql"); + migrationBuilder.Procedure("fn_insert_person_employments_person_search_attributes_v1.sql"); + migrationBuilder.Procedure("fn_update_person_employments_person_search_attributes_v1.sql"); + migrationBuilder.Procedure("p_refresh_person_search_attributes_v3.sql"); + migrationBuilder.Trigger("trg_delete_person_employments_person_search_attributes_v1.sql"); + migrationBuilder.Trigger("trg_insert_person_employments_person_search_attributes_v1.sql"); + migrationBuilder.Trigger("trg_update_person_employments_person_search_attributes_v1.sql"); + } + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/TrsDbContextModelSnapshot.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/TrsDbContextModelSnapshot.cs index 992ae7577..32e260673 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/TrsDbContextModelSnapshot.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Migrations/TrsDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.4") + .HasAnnotation("ProductVersion", "8.0.7") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -1123,81 +1123,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("persons", (string)null); }); - modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.PersonEmployment", b => - { - b.Property("PersonEmploymentId") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("person_employment_id"); - - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_on"); - - b.Property("EmploymentType") - .HasColumnType("integer") - .HasColumnName("employment_type"); - - b.Property("EndDate") - .HasColumnType("date") - .HasColumnName("end_date"); - - b.Property("EstablishmentId") - .HasColumnType("uuid") - .HasColumnName("establishment_id"); - - b.Property("Key") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("character varying(50)") - .HasColumnName("key"); - - b.Property("LastExtractDate") - .HasColumnType("date") - .HasColumnName("last_extract_date"); - - b.Property("LastKnownEmployedDate") - .HasColumnType("date") - .HasColumnName("last_known_employed_date"); - - b.Property("NationalInsuranceNumber") - .HasMaxLength(9) - .HasColumnType("character(9)") - .HasColumnName("national_insurance_number") - .IsFixedLength(); - - b.Property("PersonId") - .HasColumnType("uuid") - .HasColumnName("person_id"); - - b.Property("PersonPostcode") - .HasMaxLength(10) - .HasColumnType("character varying(10)") - .HasColumnName("person_postcode"); - - b.Property("StartDate") - .HasColumnType("date") - .HasColumnName("start_date"); - - b.Property("UpdatedOn") - .HasColumnType("timestamp with time zone") - .HasColumnName("updated_on"); - - b.HasKey("PersonEmploymentId") - .HasName("pk_person_employments"); - - b.HasIndex("EstablishmentId") - .HasDatabaseName("ix_person_employments_establishment_id"); - - b.HasIndex("Key") - .HasDatabaseName("ix_person_employments_key"); - - b.HasIndex("PersonId") - .HasDatabaseName("ix_person_employments_person_id"); - - b.ToTable("person_employments", (string)null); - }); - modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.PersonSearchAttribute", b => { b.Property("PersonSearchAttributeId") @@ -1565,10 +1490,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnName("trn") .IsFixedLength(); - b.Property("WithdrawlIndicator") + b.Property("WithdrawalIndicator") .HasMaxLength(1) .HasColumnType("character(1)") - .HasColumnName("withdrawl_indicator") + .HasColumnName("withdrawal_indicator") .IsFixedLength(); b.HasKey("TpsCsvExtractItemId") @@ -1691,10 +1616,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("character varying(200)") .HasColumnName("trn"); - b.Property("WithdrawlIndicator") + b.Property("WithdrawalIndicator") .HasMaxLength(200) .HasColumnType("character varying(200)") - .HasColumnName("withdrawl_indicator"); + .HasColumnName("withdrawal_indicator"); b.HasKey("TpsCsvExtractLoadItemId") .HasName("pk_tps_csv_extract_load_items"); @@ -1702,6 +1627,85 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("tps_csv_extract_load_items", (string)null); }); + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsEmployment", b => + { + b.Property("TpsEmploymentId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("tps_employment_id"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_on"); + + b.Property("EmploymentType") + .HasColumnType("integer") + .HasColumnName("employment_type"); + + b.Property("EndDate") + .HasColumnType("date") + .HasColumnName("end_date"); + + b.Property("EstablishmentId") + .HasColumnType("uuid") + .HasColumnName("establishment_id"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("key"); + + b.Property("LastExtractDate") + .HasColumnType("date") + .HasColumnName("last_extract_date"); + + b.Property("LastKnownTpsEmployedDate") + .HasColumnType("date") + .HasColumnName("last_known_tps_employed_date"); + + b.Property("NationalInsuranceNumber") + .HasMaxLength(9) + .HasColumnType("character(9)") + .HasColumnName("national_insurance_number") + .IsFixedLength(); + + b.Property("PersonId") + .HasColumnType("uuid") + .HasColumnName("person_id"); + + b.Property("PersonPostcode") + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasColumnName("person_postcode"); + + b.Property("StartDate") + .HasColumnType("date") + .HasColumnName("start_date"); + + b.Property("UpdatedOn") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_on"); + + b.Property("WithdrawalConfirmed") + .HasColumnType("boolean") + .HasColumnName("withdrawal_confirmed"); + + b.HasKey("TpsEmploymentId") + .HasName("pk_tps_employments"); + + b.HasIndex("EstablishmentId") + .HasDatabaseName("ix_tps_employments_establishment_id"); + + b.HasIndex("Key") + .HasDatabaseName("ix_tps_employments_key"); + + b.HasIndex("PersonId") + .HasDatabaseName("ix_tps_employments_person_id"); + + b.ToTable("tps_employments", (string)null); + }); + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsEstablishment", b => { b.Property("TpsEstablishmentId") @@ -1974,6 +1978,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasBaseType("TeachingRecordSystem.Core.DataStore.Postgres.Models.UserBase"); + b.ToTable("users", (string)null); + b.HasDiscriminator().HasValue(3); b.HasData( @@ -2112,23 +2118,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Person"); }); - modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.PersonEmployment", b => - { - b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.Establishment", null) - .WithMany() - .HasForeignKey("EstablishmentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_person_employments_establishment_id"); - - b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.Person", null) - .WithMany() - .HasForeignKey("PersonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_person_employments_person_id"); - }); - modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.QtsAwardedEmailsJobItem", b => { b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.QtsAwardedEmailsJob", "QtsAwardedEmailsJob") @@ -2191,6 +2180,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasConstraintName("fk_tps_csv_extract_load_items_tps_csv_extract_id"); }); + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.TpsEmployment", b => + { + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.Establishment", null) + .WithMany() + .HasForeignKey("EstablishmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tps_employments_establishment_id"); + + b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.Person", null) + .WithMany() + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tps_employments_person_id"); + }); + modelBuilder.Entity("TeachingRecordSystem.Core.DataStore.Postgres.Models.MandatoryQualification", b => { b.HasOne("TeachingRecordSystem.Core.DataStore.Postgres.Models.MandatoryQualificationProvider", "Provider") diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsCsvExtractItem.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsCsvExtractItem.cs index a620c77c7..c1405034f 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsCsvExtractItem.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsCsvExtractItem.cs @@ -28,7 +28,7 @@ public class TpsCsvExtractItem public required DateOnly EmploymentStartDate { get; set; } public required DateOnly? EmploymentEndDate { get; set; } public required EmploymentType EmploymentType { get; set; } - public required string? WithdrawlIndicator { get; set; } + public required string? WithdrawalIndicator { get; set; } public required DateOnly ExtractDate { get; set; } public required DateTime Created { get; set; } public required TpsCsvExtractItemResult? Result { get; set; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsCsvExtractLoadItem.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsCsvExtractLoadItem.cs index aba47efe0..489df0704 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsCsvExtractLoadItem.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsCsvExtractLoadItem.cs @@ -22,7 +22,7 @@ public class TpsCsvExtractLoadItem public required string? EmploymentStartDate { get; set; } public required string? EmploymentEndDate { get; set; } public required string? FullOrPartTimeIndicator { get; set; } - public required string? WithdrawlIndicator { get; set; } + public required string? WithdrawalIndicator { get; set; } public required string? ExtractDate { get; set; } public required string? Gender { get; set; } public required DateTime Created { get; set; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/PersonEmployment.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsEmployment.cs similarity index 60% rename from TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/PersonEmployment.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsEmployment.cs index 11ffac24c..505c08050 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/PersonEmployment.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Models/TpsEmployment.cs @@ -1,17 +1,17 @@ namespace TeachingRecordSystem.Core.DataStore.Postgres.Models; -public class PersonEmployment +public class TpsEmployment { - public const string PersonIdIndexName = "ix_person_employments_person_id"; - public const string EstablishmentIdIndexName = "ix_person_employments_establishment_id"; - public const string KeyIndexName = "ix_person_employments_key"; + public const string PersonIdIndexName = "ix_tps_employments_person_id"; + public const string EstablishmentIdIndexName = "ix_tps_employments_establishment_id"; + public const string KeyIndexName = "ix_tps_employments_key"; - public required Guid PersonEmploymentId { get; set; } + public required Guid TpsEmploymentId { get; set; } public required Guid PersonId { get; set; } public required Guid EstablishmentId { get; set; } public required DateOnly StartDate { get; set; } public required DateOnly? EndDate { get; set; } - public required DateOnly LastKnownEmployedDate { get; set; } + public required DateOnly LastKnownTpsEmployedDate { get; set; } public required DateOnly LastExtractDate { get; set; } public required EmploymentType EmploymentType { get; set; } public required DateTime CreatedOn { get; set; } @@ -19,4 +19,5 @@ public class PersonEmployment public required string Key { get; set; } public required string? NationalInsuranceNumber { get; set; } public required string? PersonPostcode { get; set; } + public required bool WithdrawalConfirmed { get; set; } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_delete_tps_employments_person_search_attributes_v1.sql b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_delete_tps_employments_person_search_attributes_v1.sql new file mode 100644 index 000000000..f281fe335 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_delete_tps_employments_person_search_attributes_v1.sql @@ -0,0 +1,15 @@ +CREATE OR REPLACE FUNCTION fn_delete_tps_employments_person_search_attributes() + RETURNS trigger + LANGUAGE 'plpgsql' +AS $BODY$ +BEGIN + DELETE FROM + person_search_attributes psa + USING + old_tps_employments o + WHERE + psa.attribute_key = o.tps_employment_id::text; + + RETURN NULL; +END; +$BODY$ diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_insert_tps_employments_person_search_attributes_v1.sql b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_insert_tps_employments_person_search_attributes_v1.sql new file mode 100644 index 000000000..7dbf68c61 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_insert_tps_employments_person_search_attributes_v1.sql @@ -0,0 +1,14 @@ +CREATE OR REPLACE FUNCTION fn_insert_tps_employments_person_search_attributes() + RETURNS trigger + LANGUAGE 'plpgsql' +AS $BODY$ +DECLARE tps_employment_ids uuid[]; +BEGIN + + tps_employment_ids := ARRAY(SELECT tps_employment_id FROM new_tps_employments); + + CALL p_refresh_tps_employments_person_search_attributes(tps_employment_ids); + + RETURN NULL; +END; +$BODY$ diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_update_tps_employments_person_search_attributes_v1.sql b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_update_tps_employments_person_search_attributes_v1.sql new file mode 100644 index 000000000..4d3a6a9a1 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/fn_update_tps_employments_person_search_attributes_v1.sql @@ -0,0 +1,21 @@ +CREATE OR REPLACE FUNCTION fn_update_tps_employments_person_search_attributes() + RETURNS trigger + LANGUAGE 'plpgsql' +AS $BODY$ +DECLARE tps_employment_ids uuid[]; +BEGIN + + tps_employment_ids := ARRAY( + SELECT o.tps_employment_id FROM old_tps_employments o + JOIN new_tps_employments n ON o.tps_employment_id = n.tps_employment_id + WHERE n.national_insurance_number IS DISTINCT FROM o.national_insurance_number + OR n.person_postcode IS DISTINCT FROM o.person_postcode + ); + + IF (array_length(tps_employment_ids, 1) > 0) THEN + CALL p_refresh_tps_employments_person_search_attributes(tps_employment_ids); + END IF; + + RETURN NULL; +END; +$BODY$ diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/p_refresh_tps_employments_person_search_attributes_v1.sql b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/p_refresh_tps_employments_person_search_attributes_v1.sql new file mode 100644 index 000000000..f476c2a37 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Procedures/p_refresh_tps_employments_person_search_attributes_v1.sql @@ -0,0 +1,37 @@ +CREATE OR REPLACE PROCEDURE public.p_refresh_tps_employments_person_search_attributes( + IN p_tps_employment_ids uuid[]) +LANGUAGE 'plpgsql' +AS $BODY$ +BEGIN + DELETE FROM person_search_attributes WHERE attribute_key = ANY(p_tps_employment_ids::text[]); + + INSERT INTO person_search_attributes (person_id, attribute_type, attribute_value, tags, attribute_key) + WITH tps_employments_data AS ( + SELECT + person_id, + national_insurance_number, + person_postcode, + tps_employment_id + FROM + tps_employments te + WHERE + te.tps_employment_id = ANY(p_tps_employment_ids) + ), + attribs AS ( + SELECT + te.person_id, + unnest(ARRAY['NationalInsuranceNumber', 'Postcode']) attribute_type, + unnest(ARRAY[te.national_insurance_number, te.person_postcode]) attribute_value, + ARRAY['data_source:tps_employments'] tags, + te.tps_employment_id attribute_key + FROM + tps_employments_data te + ) + SELECT + * + FROM + attribs + WHERE + attribs.attribute_value IS NOT NULL; +END; +$BODY$; diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_delete_tps_employments_person_search_attributes_v1.sql b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_delete_tps_employments_person_search_attributes_v1.sql new file mode 100644 index 000000000..9b75d5293 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_delete_tps_employments_person_search_attributes_v1.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE TRIGGER trg_delete_tps_employments_person_search_attributes + AFTER DELETE + ON tps_employments + REFERENCING OLD TABLE AS old_tps_employments + FOR EACH STATEMENT + EXECUTE FUNCTION fn_delete_tps_employments_person_search_attributes(); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_insert_tps_employments_person_search_attributes_v1.sql b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_insert_tps_employments_person_search_attributes_v1.sql new file mode 100644 index 000000000..969733014 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_insert_tps_employments_person_search_attributes_v1.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE TRIGGER trg_insert_tps_employments_person_search_attributes + AFTER INSERT + ON tps_employments + REFERENCING NEW TABLE AS new_tps_employments + FOR EACH STATEMENT + EXECUTE FUNCTION fn_insert_tps_employments_person_search_attributes(); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_update_tps_employments_person_search_attributes_v1.sql b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_update_tps_employments_person_search_attributes_v1.sql new file mode 100644 index 000000000..83a707e27 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/Triggers/trg_update_tps_employments_person_search_attributes_v1.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE TRIGGER trg_update_tps_employments_person_search_attributes + AFTER UPDATE + ON tps_employments + REFERENCING OLD TABLE AS old_tps_employments NEW TABLE AS new_tps_employments + FOR EACH STATEMENT + EXECUTE FUNCTION fn_update_tps_employments_person_search_attributes(); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/TrsDbContext.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/TrsDbContext.cs index c286273e0..27128377e 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/TrsDbContext.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/DataStore/Postgres/TrsDbContext.cs @@ -72,7 +72,7 @@ public static TrsDbContext Create(string connectionString, int? commandTimeout = public DbSet TpsCsvExtractItems => Set(); - public DbSet PersonEmployments => Set(); + public DbSet TpsEmployments => Set(); public DbSet SupportTasks => Set(); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/Models/PersonEmployment.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/Models/TpsEmployment.cs similarity index 70% rename from TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/Models/PersonEmployment.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/Models/TpsEmployment.cs index 00b7fbeb9..4aef8eef7 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/Models/PersonEmployment.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/Models/TpsEmployment.cs @@ -1,28 +1,30 @@ namespace TeachingRecordSystem.Core.Events.Models; -public record PersonEmployment +public record TpsEmployment { public required Guid PersonEmploymentId { get; init; } public required Guid PersonId { get; init; } public required Guid EstablishmentId { get; init; } public required DateOnly StartDate { get; init; } public required DateOnly? EndDate { get; init; } + public required DateOnly LastKnownTpsEmployedDate { get; init; } public required EmploymentType EmploymentType { get; init; } - public required DateOnly LastKnownEmployedDate { get; init; } + public required bool WithdrawalConfirmed { get; init; } public required DateOnly LastExtractDate { get; init; } public required string? NationalInsuranceNumber { get; init; } public required string? PersonPostcode { get; init; } public required string Key { get; init; } - public static PersonEmployment FromModel(DataStore.Postgres.Models.PersonEmployment model) => new() + public static TpsEmployment FromModel(DataStore.Postgres.Models.TpsEmployment model) => new() { - PersonEmploymentId = model.PersonEmploymentId, + PersonEmploymentId = model.TpsEmploymentId, PersonId = model.PersonId, EstablishmentId = model.EstablishmentId, StartDate = model.StartDate, EndDate = model.EndDate, + LastKnownTpsEmployedDate = model.LastKnownTpsEmployedDate, EmploymentType = model.EmploymentType, - LastKnownEmployedDate = model.LastKnownEmployedDate, + WithdrawalConfirmed = model.WithdrawalConfirmed, LastExtractDate = model.LastExtractDate, NationalInsuranceNumber = model.NationalInsuranceNumber, PersonPostcode = model.PersonPostcode, diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/PersonEmploymentUpdatedEvent.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/PersonEmploymentUpdatedEvent.cs deleted file mode 100644 index 11b8aa197..000000000 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/PersonEmploymentUpdatedEvent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using TeachingRecordSystem.Core.Events.Models; - -namespace TeachingRecordSystem.Core.Events; - -public record PersonEmploymentUpdatedEvent : EventBase, IEventWithPersonId -{ - public required Guid PersonId { get; init; } - public required PersonEmployment PersonEmployment { get; init; } - public required PersonEmployment OldPersonEmployment { get; init; } - public required PersonEmploymentUpdatedEventChanges Changes { get; init; } -} - -[Flags] -public enum PersonEmploymentUpdatedEventChanges -{ - None = 0, - StartDate = 1 << 0, - EndDate = 1 << 1, - EmploymentType = 1 << 2, - EstablishmentId = 1 << 3, - LastKnownEmployedDate = 1 << 4, - LastExtractDate = 1 << 5, - NationalInsuranceNumber = 1 << 6, - PersonPostcode = 1 << 7 -} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/PersonEmploymentCreatedEvent.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/TpsEmploymentCreatedEvent.cs similarity index 50% rename from TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/PersonEmploymentCreatedEvent.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/TpsEmploymentCreatedEvent.cs index 5de809a3d..a9c39c666 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/PersonEmploymentCreatedEvent.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/TpsEmploymentCreatedEvent.cs @@ -2,8 +2,8 @@ namespace TeachingRecordSystem.Core.Events; -public record PersonEmploymentCreatedEvent : EventBase, IEventWithPersonId +public record TpsEmploymentCreatedEvent : EventBase, IEventWithPersonId { public required Guid PersonId { get; init; } - public required PersonEmployment PersonEmployment { get; init; } + public required TpsEmployment TpsEmployment { get; init; } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/TpsEmploymentUpdatedEvent.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/TpsEmploymentUpdatedEvent.cs new file mode 100644 index 000000000..378483189 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Events/TpsEmploymentUpdatedEvent.cs @@ -0,0 +1,26 @@ +using TeachingRecordSystem.Core.Events.Models; + +namespace TeachingRecordSystem.Core.Events; + +public record TpsEmploymentUpdatedEvent : EventBase, IEventWithPersonId +{ + public required Guid PersonId { get; init; } + public required TpsEmployment TpsEmployment { get; init; } + public required TpsEmployment OldTpsEmployment { get; init; } + public required TpsEmploymentUpdatedEventChanges Changes { get; init; } +} + +[Flags] +public enum TpsEmploymentUpdatedEventChanges +{ + None = 0, + StartDate = 1 << 0, + EndDate = 1 << 1, + EmploymentType = 1 << 2, + EstablishmentId = 1 << 3, + LastKnownTpsEmployedDate = 1 << 4, + LastExtractDate = 1 << 5, + NationalInsuranceNumber = 1 << 6, + PersonPostcode = 1 << 7, + WithdrawalConfirmed = 1 << 8 +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/NewPersonEmployment.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/NewTpsEmployment.cs similarity index 69% rename from TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/NewPersonEmployment.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/NewTpsEmployment.cs index 1c46c053a..86d17f790 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/NewPersonEmployment.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/NewTpsEmployment.cs @@ -1,13 +1,15 @@ namespace TeachingRecordSystem.Core.Services.WorkforceData; -public record NewPersonEmployment +public record NewTpsEmployment { - public required Guid PersonEmploymentId { get; init; } + public required Guid TpsEmploymentId { get; init; } public required Guid PersonId { get; init; } public required Guid EstablishmentId { get; init; } public required DateOnly StartDate { get; init; } - public required DateOnly LastKnownEmployedDate { get; init; } + public required DateOnly? EndDate { get; init; } + public required DateOnly LastKnownTpsEmployedDate { get; init; } public required EmploymentType EmploymentType { get; init; } + public required bool WithdrawalConfirmed { get; init; } public required DateOnly LastExtractDate { get; init; } public required string Key { get; init; } public required string NationalInsuranceNumber { get; init; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/TpsCsvExtractFileImporter.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/TpsCsvExtractFileImporter.cs index 2e86ac98f..dcd432320 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/TpsCsvExtractFileImporter.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/TpsCsvExtractFileImporter.cs @@ -59,7 +59,7 @@ INSERT INTO tps_csv_extracts ( employment_start_date, employment_end_date, full_or_part_time_indicator, - withdrawl_indicator, + withdrawal_indicator, extract_date, gender, created, @@ -197,7 +197,7 @@ public async Task CopyValidFormatDataToStaging(Guid tpsCsvExtractId, Cancellatio employment_start_date, employment_end_date, employment_type, - withdrawl_indicator, + withdrawal_indicator, extract_date, gender, created, @@ -230,7 +230,7 @@ public async Task CopyValidFormatDataToStaging(Guid tpsCsvExtractId, Cancellatio writer.Write(employmentStartDate, NpgsqlDbType.Date); writer.Write(!string.IsNullOrEmpty(item.EmploymentEndDate) ? DateOnly.ParseExact(item.EmploymentEndDate!, "dd/MM/yyyy") : (DateOnly?)null, NpgsqlDbType.Date); writer.Write((int)EmploymentTypeHelper.FromFullOrPartTimeIndicator(item.FullOrPartTimeIndicator!), NpgsqlDbType.Integer); - writer.Write(item.WithdrawlIndicator, NpgsqlDbType.Char); + writer.Write(item.WithdrawalIndicator, NpgsqlDbType.Char); writer.Write(DateOnly.ParseExact(item.ExtractDate!, "dd/MM/yyyy"), NpgsqlDbType.Date); writer.Write(item.Gender, NpgsqlDbType.Varchar); writer.Write(clock.UtcNow, NpgsqlDbType.TimestampTz); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/TpsCsvExtractProcessor.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/TpsCsvExtractProcessor.cs index 8abd7264d..c3a208f73 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/TpsCsvExtractProcessor.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/TpsCsvExtractProcessor.cs @@ -126,12 +126,14 @@ WITH unique_establishments AS ( update_extract_items AS ( SELECT x.tps_csv_extract_item_id, - gen_random_uuid() as person_employment_id, + gen_random_uuid() as tps_employment_id, p.person_id, e.establishment_id, x.employment_start_date as start_date, - x.employment_end_date as last_known_employed_date, + CASE WHEN x.withdrawal_indicator = 'W' OR AGE(x.extract_date, least(x.employment_end_date, x.extract_date)) > INTERVAL '5 months' THEN x.employment_end_date ELSE NULL END as end_date, + least(x.employment_end_date, x.extract_date) as last_known_tps_employed_date, x.employment_type, + CASE WHEN x.withdrawal_indicator = 'W' THEN TRUE ELSE FALSE END as withdrawal_confirmed, x.extract_date as last_extract_date, x.key, x.national_insurance_number, @@ -158,12 +160,12 @@ AND x.result IS NULL AND NOT EXISTS (SELECT 1 FROM - person_employments pe + tps_employments te WHERE - pe.key = x.key) + te.key = x.key) LIMIT 1000 ), - new_person_employments AS ( + new_tps_employments AS ( UPDATE tps_csv_extract_items x SET @@ -173,26 +175,30 @@ update_extract_items u WHERE x.tps_csv_extract_item_id = u.tps_csv_extract_item_id RETURNING - u.person_employment_id, + u.tps_employment_id, u.person_id, u.establishment_id, u.start_date, + u.end_date, + u.last_known_tps_employed_date, u.employment_type, - u.last_extract_date, - u.last_known_employed_date, + u.withdrawal_confirmed, + u.last_extract_date, u.key, u.national_insurance_number, u.person_postcode ) - INSERT INTO person_employments + INSERT INTO tps_employments ( - person_employment_id, + tps_employment_id, person_id, establishment_id, start_date, + end_date, + last_known_tps_employed_date, employment_type, - last_extract_date, - last_known_employed_date, + withdrawal_confirmed, + last_extract_date, key, national_insurance_number, person_postcode, @@ -204,15 +210,17 @@ INSERT INTO person_employments {clock.UtcNow} as created_on, {clock.UtcNow} as updated_on FROM - new_person_employments + new_tps_employments RETURNING - person_employment_id, + tps_employment_id, person_id, establishment_id, start_date, + end_date, + last_known_tps_employed_date, employment_type, + withdrawal_confirmed, last_extract_date, - last_known_employed_date, key, national_insurance_number, person_postcode, @@ -228,19 +236,20 @@ INSERT INTO person_employments hasRecordsToUpdate = false; using var transaction = await connection.BeginTransactionAsync(cancellationToken); dbContext.Database.UseTransaction(transaction); - await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) + await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) { hasRecordsToUpdate = true; - var personEmployment = new PersonEmployment + var personEmployment = new TpsEmployment { - PersonEmploymentId = item.PersonEmploymentId, + TpsEmploymentId = item.TpsEmploymentId, PersonId = item.PersonId, EstablishmentId = item.EstablishmentId, StartDate = item.StartDate, - EndDate = null, - LastKnownEmployedDate = item.LastKnownEmployedDate, - LastExtractDate = item.LastExtractDate, + EndDate = item.EndDate, + LastKnownTpsEmployedDate = item.LastKnownTpsEmployedDate, EmploymentType = item.EmploymentType, + WithdrawalConfirmed = item.WithdrawalConfirmed, + LastExtractDate = item.LastExtractDate, CreatedOn = item.CreatedOn, UpdatedOn = item.UpdatedOn, Key = item.Key, @@ -248,11 +257,11 @@ INSERT INTO person_employments PersonPostcode = item.PersonPostcode }; - var createdEvent = new PersonEmploymentCreatedEvent + var createdEvent = new TpsEmploymentCreatedEvent { EventId = Guid.NewGuid(), PersonId = item.PersonId, - PersonEmployment = Core.Events.Models.PersonEmployment.FromModel(personEmployment), + TpsEmployment = Core.Events.Models.TpsEmployment.FromModel(personEmployment), CreatedUtc = clock.UtcNow, RaisedBy = DataStore.Postgres.Models.SystemUser.SystemUserId }; @@ -279,23 +288,27 @@ public async Task ProcessUpdatedEmploymentHistory(Guid tpsCsvExtractId, Cancella WITH extract_items AS ( SELECT x.tps_csv_extract_item_id, - pe.person_employment_id, - pe.employment_type as current_employment_type, - pe.last_known_employed_date as current_last_known_employed_date, - pe.last_extract_date as current_last_extract_date, - pe.national_insurance_number as current_national_insurance_number, - pe.person_postcode as current_person_postcode, + te.tps_employment_id, + te.employment_type as current_employment_type, + te.end_date as current_end_date, + te.last_known_tps_employed_date as current_last_known_tps_employed_date, + te.withdrawal_confirmed as current_withdrawal_confirmed, + te.last_extract_date as current_last_extract_date, + te.national_insurance_number as current_national_insurance_number, + te.person_postcode as current_person_postcode, x.employment_type as new_employment_type, - x.employment_end_date as new_last_known_employed_date, + CASE WHEN x.withdrawal_indicator = 'W' OR AGE(x.extract_date, least(x.employment_end_date, x.extract_date)) > INTERVAL '5 months' THEN x.employment_end_date ELSE NULL END as new_end_date, + least(x.employment_end_date, x.extract_date) as new_last_known_tps_employed_date, + CASE WHEN x.withdrawal_indicator = 'W' THEN TRUE ELSE FALSE END as new_withdrawal_confirmed, x.extract_date as new_last_extract_date, x.national_insurance_number as new_national_insurance_number, x.member_postcode as new_person_postcode, x.key, - CASE WHEN pe.employment_type != x.employment_type OR pe.last_known_employed_date != x.employment_end_date OR pe.last_extract_date != x.extract_date THEN 2 ELSE 0 END as result + CASE WHEN te.employment_type != x.employment_type OR te.last_known_tps_employed_date != least(x.employment_end_date, x.extract_date) OR te.last_extract_date != x.extract_date THEN 2 ELSE 0 END as result FROM tps_csv_extract_items x JOIN - person_employments pe ON pe.key = x.key + tps_employments te ON te.key = x.key WHERE x.tps_csv_extract_id = {tpsCsvExtractId} AND x.result IS NULL @@ -310,23 +323,29 @@ extract_items u WHERE x.tps_csv_extract_item_id = u.tps_csv_extract_item_id RETURNING - u.person_employment_id, + u.tps_employment_id, + u.current_end_date, + u.current_last_known_tps_employed_date, u.current_employment_type, - u.current_last_known_employed_date, + u.current_withdrawal_confirmed, u.current_last_extract_date, u.current_national_insurance_number, - u.current_person_postcode, + u.current_person_postcode, + u.new_end_date, + u.new_last_known_tps_employed_date, u.new_employment_type, - u.new_last_known_employed_date, + u.new_withdrawal_confirmed, u.new_last_extract_date, u.new_national_insurance_number, u.new_person_postcode ) UPDATE - person_employments pe + tps_employments te SET + end_date = changes.new_end_date, + last_known_tps_employed_date = changes.new_last_known_tps_employed_date, employment_type = changes.new_employment_type, - last_known_employed_date = changes.new_last_known_employed_date, + withdrawal_confirmed = changes.new_withdrawal_confirmed, last_extract_date = changes.new_last_extract_date, national_insurance_number = changes.new_national_insurance_number, person_postcode = changes.new_person_postcode, @@ -334,24 +353,27 @@ person_employments pe FROM changes WHERE - changes.person_employment_id = pe.person_employment_id + changes.tps_employment_id = te.tps_employment_id RETURNING - pe.person_employment_id, - pe.person_id, - pe.establishment_id, - pe.start_date, - pe.end_date, - changes.current_employment_type, - changes.current_last_known_employed_date, + te.tps_employment_id, + te.person_id, + te.establishment_id, + te.start_date, + changes.current_end_date, + changes.current_last_known_tps_employed_date, + changes.current_employment_type, + changes.current_withdrawal_confirmed, changes.current_last_extract_date, changes.current_national_insurance_number, changes.current_person_postcode, + changes.new_end_date, + changes.new_last_known_tps_employed_date, changes.new_employment_type, - changes.new_last_known_employed_date, + changes.new_withdrawal_confirmed, changes.new_last_extract_date, changes.new_national_insurance_number, changes.new_person_postcode, - pe.key + te.key """; bool hasRecordsToUpdate = false; @@ -362,45 +384,49 @@ person_employments pe hasRecordsToUpdate = false; using var transaction = await connection.BeginTransactionAsync(cancellationToken); dbContext.Database.UseTransaction(transaction); - await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) + await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) { hasRecordsToUpdate = true; - var changes = PersonEmploymentUpdatedEventChanges.None | - (item.CurrentEmploymentType != item.NewEmploymentType ? PersonEmploymentUpdatedEventChanges.EmploymentType : PersonEmploymentUpdatedEventChanges.None) | - (item.CurrentLastKnownEmployedDate != item.NewLastKnownEmployedDate ? PersonEmploymentUpdatedEventChanges.LastKnownEmployedDate : PersonEmploymentUpdatedEventChanges.None) | - (item.CurrentLastExtractDate != item.NewLastExtractDate ? PersonEmploymentUpdatedEventChanges.LastExtractDate : PersonEmploymentUpdatedEventChanges.None) | - (item.CurrentNationalInsuranceNumber != item.NewNationalInsuranceNumber ? PersonEmploymentUpdatedEventChanges.NationalInsuranceNumber : PersonEmploymentUpdatedEventChanges.None) | - (item.CurrentPersonPostcode != item.NewPersonPostcode ? PersonEmploymentUpdatedEventChanges.PersonPostcode : PersonEmploymentUpdatedEventChanges.None); - - if (changes != PersonEmploymentUpdatedEventChanges.None) + var changes = TpsEmploymentUpdatedEventChanges.None | + (item.CurrentEmploymentType != item.NewEmploymentType ? TpsEmploymentUpdatedEventChanges.EmploymentType : TpsEmploymentUpdatedEventChanges.None) | + (item.CurrentEndDate != item.NewEndDate ? TpsEmploymentUpdatedEventChanges.EndDate : TpsEmploymentUpdatedEventChanges.None) | + (item.CurrentLastKnownTpsEmployedDate != item.NewLastKnownTpsEmployedDate ? TpsEmploymentUpdatedEventChanges.LastKnownTpsEmployedDate : TpsEmploymentUpdatedEventChanges.None) | + (item.CurrentWithdrawalConfirmed != item.NewWithdrawalConfirmed ? TpsEmploymentUpdatedEventChanges.WithdrawalConfirmed : TpsEmploymentUpdatedEventChanges.None) | + (item.CurrentLastExtractDate != item.NewLastExtractDate ? TpsEmploymentUpdatedEventChanges.LastExtractDate : TpsEmploymentUpdatedEventChanges.None) | + (item.CurrentNationalInsuranceNumber != item.NewNationalInsuranceNumber ? TpsEmploymentUpdatedEventChanges.NationalInsuranceNumber : TpsEmploymentUpdatedEventChanges.None) | + (item.CurrentPersonPostcode != item.NewPersonPostcode ? TpsEmploymentUpdatedEventChanges.PersonPostcode : TpsEmploymentUpdatedEventChanges.None); + + if (changes != TpsEmploymentUpdatedEventChanges.None) { - var updatedEvent = new PersonEmploymentUpdatedEvent + var updatedEvent = new TpsEmploymentUpdatedEvent { EventId = Guid.NewGuid(), - PersonId = item.PersonEmploymentId, - PersonEmployment = new() + PersonId = item.TpsEmploymentId, + TpsEmployment = new() { - PersonEmploymentId = item.PersonEmploymentId, + PersonEmploymentId = item.TpsEmploymentId, PersonId = item.PersonId, EstablishmentId = item.EstablishmentId, StartDate = item.StartDate, - EndDate = item.EndDate, + EndDate = item.NewEndDate, + LastKnownTpsEmployedDate = item.NewLastKnownTpsEmployedDate, EmploymentType = item.NewEmploymentType, - LastKnownEmployedDate = item.NewLastKnownEmployedDate, + WithdrawalConfirmed = item.NewWithdrawalConfirmed, LastExtractDate = item.NewLastExtractDate, NationalInsuranceNumber = item.NewNationalInsuranceNumber, PersonPostcode = item.NewPersonPostcode, Key = item.Key }, - OldPersonEmployment = new() + OldTpsEmployment = new() { - PersonEmploymentId = item.PersonEmploymentId, + PersonEmploymentId = item.TpsEmploymentId, PersonId = item.PersonId, EstablishmentId = item.EstablishmentId, StartDate = item.StartDate, - EndDate = item.EndDate, + EndDate = item.CurrentEndDate, + LastKnownTpsEmployedDate = item.CurrentLastKnownTpsEmployedDate, EmploymentType = item.CurrentEmploymentType, - LastKnownEmployedDate = item.CurrentLastKnownEmployedDate, + WithdrawalConfirmed = item.CurrentWithdrawalConfirmed, LastExtractDate = item.CurrentLastExtractDate, NationalInsuranceNumber = item.CurrentNationalInsuranceNumber, PersonPostcode = item.CurrentPersonPostcode, @@ -455,33 +481,33 @@ WITH unique_establishments AS ( ), establishment_changes AS ( SELECT - pe.person_employment_id, + te.tps_employment_id, e.establishment_id as current_establishment_id, e.establishment_number, e.la_code, e.establishment_type_code, e.postcode FROM - person_employments pe + tps_employments te JOIN - establishments e ON e.establishment_id = pe.establishment_id + establishments e ON e.establishment_id = te.establishment_id WHERE NOT EXISTS (SELECT 1 FROM unique_establishments e WHERE - e.establishment_id = pe.establishment_id) + e.establishment_id = te.establishment_id) LIMIT 1000 ) UPDATE - person_employments pe + tps_employments te SET establishment_id = changes.new_establishment_id, updated_on = {clock.UtcNow} FROM (SELECT - ec.person_employment_id, + ec.tps_employment_id, ec.current_establishment_id, ue.establishment_id as new_establishment_id FROM @@ -499,19 +525,20 @@ unique_establishments e2 e2.la_code = ec.la_code AND e2.establishment_number = ec.establishment_number)))) changes WHERE - pe.person_employment_id = changes.person_employment_id + te.tps_employment_id = changes.tps_employment_id RETURNING - pe.person_employment_id, - pe.person_id, + te.tps_employment_id, + te.person_id, changes.current_establishment_id, - pe.start_date, - pe.end_date, - pe.employment_type, - pe.last_known_employed_date, - pe.last_extract_date, - pe.national_insurance_number, - pe.person_postcode, - pe.key, + te.start_date, + te.end_date, + te.employment_type, + te.withdrawal_confirmed, + te.last_known_tps_employed_date, + te.last_extract_date, + te.national_insurance_number, + te.person_postcode, + te.key, changes.new_establishment_id """; @@ -523,41 +550,43 @@ unique_establishments e2 hasRecordsToUpdate = false; using var transaction = await connection.BeginTransactionAsync(cancellationToken); dbContext.Database.UseTransaction(transaction); - await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) + await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) { - var updatedEvent = new PersonEmploymentUpdatedEvent + var updatedEvent = new TpsEmploymentUpdatedEvent { EventId = Guid.NewGuid(), - PersonId = item.PersonEmploymentId, - PersonEmployment = new() + PersonId = item.TpsEmploymentId, + TpsEmployment = new() { - PersonEmploymentId = item.PersonEmploymentId, + PersonEmploymentId = item.TpsEmploymentId, PersonId = item.PersonId, EstablishmentId = item.NewEstablishmentId, StartDate = item.StartDate, EndDate = item.EndDate, + LastKnownTpsEmployedDate = item.LastKnownTpsEmployedDate, + WithdrawalConfirmed = item.WithdrawalConfirmed, EmploymentType = item.EmploymentType, - LastKnownEmployedDate = item.LastKnownEmployedDate, LastExtractDate = item.LastExtractDate, NationalInsuranceNumber = item.NationalInsuranceNumber, PersonPostcode = item.PersonPostcode, Key = item.Key }, - OldPersonEmployment = new() + OldTpsEmployment = new() { - PersonEmploymentId = item.PersonEmploymentId, + PersonEmploymentId = item.TpsEmploymentId, PersonId = item.PersonId, EstablishmentId = item.CurrentEstablishmentId, StartDate = item.StartDate, EndDate = item.EndDate, + LastKnownTpsEmployedDate = item.LastKnownTpsEmployedDate, EmploymentType = item.EmploymentType, - LastKnownEmployedDate = item.LastKnownEmployedDate, + WithdrawalConfirmed = item.WithdrawalConfirmed, LastExtractDate = item.LastExtractDate, NationalInsuranceNumber = item.NationalInsuranceNumber, PersonPostcode = item.PersonPostcode, Key = item.Key }, - Changes = PersonEmploymentUpdatedEventChanges.EstablishmentId, + Changes = TpsEmploymentUpdatedEventChanges.EstablishmentId, CreatedUtc = clock.UtcNow, RaisedBy = DataStore.Postgres.Models.SystemUser.SystemUserId }; @@ -583,37 +612,38 @@ public async Task ProcessEndedEmployments(CancellationToken cancellationToken) $""" WITH changes AS ( SELECT - person_employment_id, + tps_employment_id, end_date as current_end_date, - last_known_employed_date as new_end_date + last_known_tps_employed_date as new_end_date FROM - person_employments + tps_employments WHERE end_date IS NULL - AND AGE(last_extract_date, last_known_employed_date) > INTERVAL '5 months' + AND AGE(last_extract_date, last_known_tps_employed_date) > INTERVAL '5 months' LIMIT 1000 ) UPDATE - person_employments pe + tps_employments te SET end_date = new_end_date, updated_on = {clock.UtcNow} FROM changes WHERE - pe.person_employment_id = changes.person_employment_id + te.tps_employment_id = changes.tps_employment_id RETURNING - pe.person_employment_id, - pe.person_id, - pe.establishment_id, - pe.start_date, + te.tps_employment_id, + te.person_id, + te.establishment_id, + te.start_date, changes.current_end_date, - pe.employment_type, - pe.last_known_employed_date, - pe.last_extract_date, - pe.national_insurance_number, - pe.person_postcode, - pe.key, + te.employment_type, + te.withdrawal_confirmed, + te.last_known_tps_employed_date, + te.last_extract_date, + te.national_insurance_number, + te.person_postcode, + te.key, changes.new_end_date """; @@ -625,42 +655,44 @@ person_employments pe hasRecordsToUpdate = false; using var transaction = await connection.BeginTransactionAsync(cancellationToken); dbContext.Database.UseTransaction(transaction); - await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) + await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) { hasRecordsToUpdate = true; - var updatedEvent = new PersonEmploymentUpdatedEvent + var updatedEvent = new TpsEmploymentUpdatedEvent { EventId = Guid.NewGuid(), - PersonId = item.PersonEmploymentId, - PersonEmployment = new() + PersonId = item.TpsEmploymentId, + TpsEmployment = new() { - PersonEmploymentId = item.PersonEmploymentId, + PersonEmploymentId = item.TpsEmploymentId, PersonId = item.PersonId, EstablishmentId = item.EstablishmentId, StartDate = item.StartDate, EndDate = item.CurrentEndDate, + LastKnownTpsEmployedDate = item.LastKnownTpsEmployedDate, EmploymentType = item.EmploymentType, - LastKnownEmployedDate = item.LastKnownEmployedDate, + WithdrawalConfirmed = item.WithdrawalConfirmed, LastExtractDate = item.LastExtractDate, NationalInsuranceNumber = item.NationalInsuranceNumber, PersonPostcode = item.PersonPostcode, Key = item.Key }, - OldPersonEmployment = new() + OldTpsEmployment = new() { - PersonEmploymentId = item.PersonEmploymentId, + PersonEmploymentId = item.TpsEmploymentId, PersonId = item.PersonId, EstablishmentId = item.EstablishmentId, StartDate = item.StartDate, EndDate = item.NewEndDate, + LastKnownTpsEmployedDate = item.LastKnownTpsEmployedDate, EmploymentType = item.EmploymentType, - LastKnownEmployedDate = item.LastKnownEmployedDate, + WithdrawalConfirmed = item.WithdrawalConfirmed, LastExtractDate = item.LastExtractDate, NationalInsuranceNumber = item.NationalInsuranceNumber, PersonPostcode = item.PersonPostcode, Key = item.Key }, - Changes = PersonEmploymentUpdatedEventChanges.EndDate, + Changes = TpsEmploymentUpdatedEventChanges.EndDate, CreatedUtc = clock.UtcNow, RaisedBy = DataStore.Postgres.Models.SystemUser.SystemUserId }; @@ -686,21 +718,21 @@ public async Task BackfillNinoAndPersonPostcodeInEmploymentHistory(CancellationT $""" WITH changes AS ( SELECT - pe.person_employment_id, - pe.national_insurance_number as current_national_insurance_number, - pe.person_postcode as current_person_postcode, + te.tps_employment_id, + te.national_insurance_number as current_national_insurance_number, + te.person_postcode as current_person_postcode, x.national_insurance_number as new_national_insurance_number, x.member_postcode as new_person_postcode FROM tps_csv_extract_items x JOIN - person_employments pe ON pe.key = x.key - AND pe.last_extract_date = x.extract_date + tps_employments te ON te.key = x.key + AND te.last_extract_date = x.extract_date WHERE - pe.national_insurance_number IS NULL + te.national_insurance_number IS NULL LIMIT 1000) UPDATE - person_employments pe + tps_employments te SET national_insurance_number = changes.new_national_insurance_number, person_postcode = changes.new_person_postcode, @@ -708,21 +740,22 @@ person_employments pe FROM changes WHERE - changes.person_employment_id = pe.person_employment_id + changes.tps_employment_id = te.tps_employment_id RETURNING - pe.person_employment_id, - pe.person_id, - pe.establishment_id, - pe.start_date, - pe.end_date, - pe.employment_type, - pe.last_known_employed_date, - pe.last_extract_date, + te.tps_employment_id, + te.person_id, + te.establishment_id, + te.start_date, + te.end_date, + te.employment_type, + te.withdrawal_confirmed, + te.last_known_tps_employed_date, + te.last_extract_date, changes.current_national_insurance_number, changes.current_person_postcode, changes.new_national_insurance_number, changes.new_person_postcode, - pe.key + te.key """; bool hasRecordsToUpdate = false; @@ -733,42 +766,44 @@ person_employments pe hasRecordsToUpdate = false; using var transaction = await connection.BeginTransactionAsync(cancellationToken); dbContext.Database.UseTransaction(transaction); - await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) + await foreach (var item in dbContext.Database.SqlQuery(querySql).AsAsyncEnumerable()) { hasRecordsToUpdate = true; - var changes = PersonEmploymentUpdatedEventChanges.None | - (item.CurrentNationalInsuranceNumber != item.NewNationalInsuranceNumber ? PersonEmploymentUpdatedEventChanges.NationalInsuranceNumber : PersonEmploymentUpdatedEventChanges.None) | - (item.CurrentPersonPostcode != item.NewPersonPostcode ? PersonEmploymentUpdatedEventChanges.PersonPostcode : PersonEmploymentUpdatedEventChanges.None); + var changes = TpsEmploymentUpdatedEventChanges.None | + (item.CurrentNationalInsuranceNumber != item.NewNationalInsuranceNumber ? TpsEmploymentUpdatedEventChanges.NationalInsuranceNumber : TpsEmploymentUpdatedEventChanges.None) | + (item.CurrentPersonPostcode != item.NewPersonPostcode ? TpsEmploymentUpdatedEventChanges.PersonPostcode : TpsEmploymentUpdatedEventChanges.None); - if (changes != PersonEmploymentUpdatedEventChanges.None) + if (changes != TpsEmploymentUpdatedEventChanges.None) { - var updatedEvent = new PersonEmploymentUpdatedEvent + var updatedEvent = new TpsEmploymentUpdatedEvent { EventId = Guid.NewGuid(), - PersonId = item.PersonEmploymentId, - PersonEmployment = new() + PersonId = item.TpsEmploymentId, + TpsEmployment = new() { - PersonEmploymentId = item.PersonEmploymentId, + PersonEmploymentId = item.TpsEmploymentId, PersonId = item.PersonId, EstablishmentId = item.EstablishmentId, StartDate = item.StartDate, EndDate = item.EndDate, + LastKnownTpsEmployedDate = item.LastKnownTpsEmployedDate, EmploymentType = item.EmploymentType, - LastKnownEmployedDate = item.LastKnownEmployedDate, + WithdrawalConfirmed = item.WithdrawalConfirmed, LastExtractDate = item.LastExtractDate, NationalInsuranceNumber = item.NewNationalInsuranceNumber, PersonPostcode = item.NewPersonPostcode, Key = item.Key }, - OldPersonEmployment = new() + OldTpsEmployment = new() { - PersonEmploymentId = item.PersonEmploymentId, + PersonEmploymentId = item.TpsEmploymentId, PersonId = item.PersonId, EstablishmentId = item.EstablishmentId, StartDate = item.StartDate, EndDate = item.EndDate, + LastKnownTpsEmployedDate = item.LastKnownTpsEmployedDate, EmploymentType = item.EmploymentType, - LastKnownEmployedDate = item.LastKnownEmployedDate, + WithdrawalConfirmed = item.WithdrawalConfirmed, LastExtractDate = item.LastExtractDate, NationalInsuranceNumber = item.CurrentNationalInsuranceNumber, PersonPostcode = item.CurrentPersonPostcode, diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmployment.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmployment.cs similarity index 62% rename from TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmployment.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmployment.cs index 6ace169c1..d6ecbc5b3 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmployment.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmployment.cs @@ -1,19 +1,22 @@ namespace TeachingRecordSystem.Core.Services.WorkforceData; -public record UpdatedPersonEmployment +public record UpdatedTpsEmployment { - public required Guid PersonEmploymentId { get; init; } + public required Guid TpsEmploymentId { get; init; } public required Guid PersonId { get; init; } public required Guid EstablishmentId { get; init; } public required DateOnly StartDate { get; init; } - public required DateOnly? EndDate { get; init; } + public required DateOnly? CurrentEndDate { get; init; } + public required DateOnly CurrentLastKnownTpsEmployedDate { get; init; } public required EmploymentType CurrentEmploymentType { get; init; } - public required DateOnly CurrentLastKnownEmployedDate { get; init; } + public required bool CurrentWithdrawalConfirmed { get; init; } public required DateOnly CurrentLastExtractDate { get; init; } public required string? CurrentNationalInsuranceNumber { get; init; } public required string? CurrentPersonPostcode { get; init; } + public required DateOnly? NewEndDate { get; init; } + public required DateOnly NewLastKnownTpsEmployedDate { get; init; } public required EmploymentType NewEmploymentType { get; init; } - public required DateOnly NewLastKnownEmployedDate { get; init; } + public required bool NewWithdrawalConfirmed { get; init; } public required DateOnly NewLastExtractDate { get; init; } public required string? NewNationalInsuranceNumber { get; init; } public required string? NewPersonPostcode { get; init; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmploymentEndDate.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmploymentEndDate.cs similarity index 73% rename from TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmploymentEndDate.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmploymentEndDate.cs index da06d49a4..b6f3d2fa8 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmploymentEndDate.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmploymentEndDate.cs @@ -1,14 +1,15 @@ namespace TeachingRecordSystem.Core.Services.WorkforceData; -public record UpdatedPersonEmploymentEndDate +public record UpdatedTpsEmploymentEndDate { - public required Guid PersonEmploymentId { get; init; } + public required Guid TpsEmploymentId { get; init; } public required Guid PersonId { get; init; } public required Guid EstablishmentId { get; init; } public required DateOnly StartDate { get; init; } public required DateOnly? CurrentEndDate { get; init; } + public required DateOnly LastKnownTpsEmployedDate { get; init; } public required EmploymentType EmploymentType { get; init; } - public required DateOnly LastKnownEmployedDate { get; init; } + public required bool WithdrawalConfirmed { get; init; } public required DateOnly LastExtractDate { get; init; } public required string? NationalInsuranceNumber { get; init; } public required string? PersonPostcode { get; init; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmploymentEstablishment.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmploymentEstablishment.cs similarity index 73% rename from TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmploymentEstablishment.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmploymentEstablishment.cs index e279696f5..d9e8077c3 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmploymentEstablishment.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmploymentEstablishment.cs @@ -1,14 +1,15 @@ namespace TeachingRecordSystem.Core.Services.WorkforceData; -public record UpdatedPersonEmploymentEstablishment +public record UpdatedTpsEmploymentEstablishment { - public required Guid PersonEmploymentId { get; init; } + public required Guid TpsEmploymentId { get; init; } public required Guid PersonId { get; init; } public required Guid CurrentEstablishmentId { get; init; } public required DateOnly StartDate { get; init; } public required DateOnly? EndDate { get; init; } + public required DateOnly LastKnownTpsEmployedDate { get; init; } public required EmploymentType EmploymentType { get; init; } - public required DateOnly LastKnownEmployedDate { get; init; } + public required bool WithdrawalConfirmed { get; init; } public required DateOnly LastExtractDate { get; init; } public required string? NationalInsuranceNumber { get; set; } public required string? PersonPostcode { get; set; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmploymentNationalInsuranceNumberAndPersonPostcode.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmploymentNationalInsuranceNumberAndPersonPostcode.cs similarity index 73% rename from TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmploymentNationalInsuranceNumberAndPersonPostcode.cs rename to TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmploymentNationalInsuranceNumberAndPersonPostcode.cs index 0e957c061..90f40a037 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedPersonEmploymentNationalInsuranceNumberAndPersonPostcode.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Services/WorkforceData/UpdatedTpsEmploymentNationalInsuranceNumberAndPersonPostcode.cs @@ -1,14 +1,15 @@ namespace TeachingRecordSystem.Core.Services.WorkforceData; -public record UpdatedPersonEmploymentNationalInsuranceNumberAndPersonPostcode +public record UpdatedTpsEmploymentNationalInsuranceNumberAndPersonPostcode { - public required Guid PersonEmploymentId { get; init; } + public required Guid TpsEmploymentId { get; init; } public required Guid PersonId { get; init; } public required Guid EstablishmentId { get; init; } public required DateOnly StartDate { get; init; } public required DateOnly? EndDate { get; init; } + public required DateOnly LastKnownTpsEmployedDate { get; init; } public required EmploymentType EmploymentType { get; init; } - public required DateOnly LastKnownEmployedDate { get; init; } + public required bool WithdrawalConfirmed { get; init; } public required DateOnly LastExtractDate { get; init; } public required string? CurrentNationalInsuranceNumber { get; init; } public required string? CurrentPersonPostcode { get; init; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/TeachingRecordSystem.Core.csproj b/TeachingRecordSystem/src/TeachingRecordSystem.Core/TeachingRecordSystem.Core.csproj index 597a5590d..553f0518b 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/TeachingRecordSystem.Core.csproj +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/TeachingRecordSystem.Core.csproj @@ -7,25 +7,32 @@ + + + + + + + @@ -56,25 +63,32 @@ + + + + + + + diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/PersonMatching/PersonMatchingServiceTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/PersonMatching/PersonMatchingServiceTests.cs index 5b5c966ec..6293cedd7 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/PersonMatching/PersonMatchingServiceTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/PersonMatching/PersonMatchingServiceTests.cs @@ -71,7 +71,7 @@ public Task Match_ReturnsExpectedResult( var person = await TestData.CreatePerson(b => b.WithTrn().WithNationalInsuranceNumber().WithFirstName(firstName)); var establishment = await TestData.CreateEstablishment(localAuthorityCode: "321", establishmentNumber: "4321", establishmentStatusCode: 1); var employmentNino = TestData.GenerateChangedNationalInsuranceNumber(person.NationalInsuranceNumber!); - var personEmployment = await TestData.CreatePersonEmployment(person, establishment, new DateOnly(2023, 08, 03), new DateOnly(2024, 05, 25), EmploymentType.FullTime, new DateOnly(2024, 05, 25), employmentNino); + var personEmployment = await TestData.CreateTpsEmployment(person, establishment, new DateOnly(2023, 08, 03), new DateOnly(2024, 05, 25), EmploymentType.FullTime, new DateOnly(2024, 05, 25), employmentNino); string[][] names = nameOption switch { @@ -202,7 +202,7 @@ public Task GetSuggestedMatches_ReturnsExpectedResults(bool usePersonNino) => // Person who matches on NINO var person2 = await TestData.CreatePerson(b => b.WithNationalInsuranceNumber(usePersonNino ? nationalInsuranceNumber : alternativeNationalInsuranceNumber)); var establishment = await TestData.CreateEstablishment(localAuthorityCode: "321", establishmentNumber: "4321", establishmentStatusCode: 1); - var personEmployment = await TestData.CreatePersonEmployment(person2, establishment, new DateOnly(2023, 08, 03), new DateOnly(2024, 05, 25), EmploymentType.FullTime, new DateOnly(2024, 05, 25), usePersonNino ? alternativeNationalInsuranceNumber : nationalInsuranceNumber); + var personEmployment = await TestData.CreateTpsEmployment(person2, establishment, new DateOnly(2023, 08, 03), new DateOnly(2024, 05, 25), EmploymentType.FullTime, new DateOnly(2024, 05, 25), usePersonNino ? alternativeNationalInsuranceNumber : nationalInsuranceNumber); // Person who matches on TRN var person3 = await TestData.CreatePerson(b => b.WithTrn()); @@ -244,7 +244,7 @@ public Task GetMatchedAttributes_ReturnsExpectedResults(bool usePersonNino) => var person = await TestData.CreatePerson(b => b.WithFirstName(firstName).WithLastName(lastName).WithDateOfBirth(dateOfBirth).WithNationalInsuranceNumber(usePersonNino ? nationalInsuranceNumber : alternativeNationalInsuranceNumber)); var establishment = await TestData.CreateEstablishment(localAuthorityCode: "321", establishmentNumber: "4321", establishmentStatusCode: 1); - var personEmployment = await TestData.CreatePersonEmployment(person, establishment, new DateOnly(2023, 08, 03), new DateOnly(2024, 05, 25), EmploymentType.FullTime, new DateOnly(2024, 05, 25), usePersonNino ? alternativeNationalInsuranceNumber : nationalInsuranceNumber); + var personEmployment = await TestData.CreateTpsEmployment(person, establishment, new DateOnly(2023, 08, 03), new DateOnly(2024, 05, 25), EmploymentType.FullTime, new DateOnly(2024, 05, 25), usePersonNino ? alternativeNationalInsuranceNumber : nationalInsuranceNumber); string[][] names = [[firstName, lastName]]; DateOnly[] datesOfBirth = [dateOfBirth]; diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/WorkforceData/TpsCsvExtractFileImporterTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/WorkforceData/TpsCsvExtractFileImporterTests.cs index 4a79fe24c..5ec266b3e 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/WorkforceData/TpsCsvExtractFileImporterTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/WorkforceData/TpsCsvExtractFileImporterTests.cs @@ -685,7 +685,7 @@ public async Task CopyValidFormatDataToStaging_WithValidData_InsertsRecordWithEx EmploymentStartDate = "03/02/2023", EmploymentEndDate = "03/05/2024", FullOrPartTimeIndicator = "PTI", - WithdrawlIndicator = null, + WithdrawalIndicator = null, ExtractDate = "07/03/2024", Gender = "Male", Created = clock.UtcNow, @@ -709,7 +709,7 @@ public async Task CopyValidFormatDataToStaging_WithValidData_InsertsRecordWithEx EmploymentStartDate = "03/02/2023", EmploymentEndDate = "03/05/2024", FullOrPartTimeIndicator = "PTI", - WithdrawlIndicator = null, + WithdrawalIndicator = null, ExtractDate = "07/03/2024", Gender = "Male", Created = clock.UtcNow, diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/WorkforceData/TpsCsvExtractProcessorTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/WorkforceData/TpsCsvExtractProcessorTests.cs index 1a54cb1b6..ba2c7dd3b 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/WorkforceData/TpsCsvExtractProcessorTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Tests/Services/WorkforceData/TpsCsvExtractProcessorTests.cs @@ -104,12 +104,101 @@ public async Task ProcessNewEmploymentHistory_WhenCalledWithNewEmploymentHistory using var dbContext = TestData.DbContextFactory.CreateDbContext(); var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataAdded, i.Result)); - var employmentHistory = await dbContext.PersonEmployments.Where(e => e.PersonId == person.PersonId).ToListAsync(); + var employmentHistory = await dbContext.TpsEmployments.Where(e => e.PersonId == person.PersonId).ToListAsync(); Assert.Single(employmentHistory); var personEmployment = employmentHistory.Single(); Assert.Equal(establishment.EstablishmentId, personEmployment.EstablishmentId); + Assert.Null(personEmployment.EndDate); } + [Fact] + public async Task ProcessNewEmploymentHistory_WhenCalledWithEndDateInTheFuture_SetsLastKnownEmployedDateToExtractDate() + { + // Arrange + var person = await TestData.CreatePerson(); + var tpsCsvExtractId = Guid.NewGuid(); + var establishment = await TestData.CreateEstablishment(localAuthorityCode: "125", establishmentNumber: "1236"); + var startDate = new DateOnly(2023, 02, 03); + var endDate = new DateOnly(2024, 10, 30); + var extractDate = new DateOnly(2024, 04, 25); + await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment.LaCode, establishment.EstablishmentNumber, establishment.Postcode!, startDate, endDate, extractDate)); + + // Act + var processor = new TpsCsvExtractProcessor( + TestData.DbContextFactory, + TestData.Clock); + await processor.ProcessNewEmploymentHistory(tpsCsvExtractId, CancellationToken.None); + + // Assert + using var dbContext = TestData.DbContextFactory.CreateDbContext(); + var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); + Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataAdded, i.Result)); + var employmentHistory = await dbContext.TpsEmployments.Where(e => e.PersonId == person.PersonId).ToListAsync(); + Assert.Single(employmentHistory); + var personEmployment = employmentHistory.Single(); + Assert.Equal(establishment.EstablishmentId, personEmployment.EstablishmentId); + Assert.Equal(extractDate, personEmployment.LastKnownTpsEmployedDate); + } + + [Fact] + public async Task ProcessNewEmploymentHistory_WhenCalledWithWithdrawalIndicatorSet_SetsEndDate() + { + // Arrange + var person = await TestData.CreatePerson(); + var tpsCsvExtractId = Guid.NewGuid(); + var establishment = await TestData.CreateEstablishment(localAuthorityCode: "125", establishmentNumber: "1236"); + var startDate = new DateOnly(2023, 02, 03); + var endDate = new DateOnly(2024, 03, 30); + var extractDate = new DateOnly(2024, 04, 25); + await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment.LaCode, establishment.EstablishmentNumber, establishment.Postcode!, startDate, endDate, extractDate, withdrawalIndicator: "W")); + + // Act + var processor = new TpsCsvExtractProcessor( + TestData.DbContextFactory, + TestData.Clock); + await processor.ProcessNewEmploymentHistory(tpsCsvExtractId, CancellationToken.None); + + // Assert + using var dbContext = TestData.DbContextFactory.CreateDbContext(); + var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); + Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataAdded, i.Result)); + var employmentHistory = await dbContext.TpsEmployments.Where(e => e.PersonId == person.PersonId).ToListAsync(); + Assert.Single(employmentHistory); + var personEmployment = employmentHistory.Single(); + Assert.Equal(establishment.EstablishmentId, personEmployment.EstablishmentId); + Assert.Equal(endDate, personEmployment.EndDate); + } + + [Fact] + public async Task ProcessNewEmploymentHistory_WhenCalledWithLastKnownEmployedDateOlderThanFiveMonths_SetsEndDate() + { + // Arrange + var person = await TestData.CreatePerson(); + var tpsCsvExtractId = Guid.NewGuid(); + var establishment = await TestData.CreateEstablishment(localAuthorityCode: "125", establishmentNumber: "1236"); + var startDate = new DateOnly(2023, 02, 03); + var endDate = new DateOnly(2023, 10, 30); + var extractDate = new DateOnly(2024, 04, 25); + await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment.LaCode, establishment.EstablishmentNumber, establishment.Postcode!, startDate, endDate, extractDate)); + + // Act + var processor = new TpsCsvExtractProcessor( + TestData.DbContextFactory, + TestData.Clock); + await processor.ProcessNewEmploymentHistory(tpsCsvExtractId, CancellationToken.None); + + // Assert + using var dbContext = TestData.DbContextFactory.CreateDbContext(); + var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); + Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataAdded, i.Result)); + var employmentHistory = await dbContext.TpsEmployments.Where(e => e.PersonId == person.PersonId).ToListAsync(); + Assert.Single(employmentHistory); + var personEmployment = employmentHistory.Single(); + Assert.Equal(establishment.EstablishmentId, personEmployment.EstablishmentId); + Assert.Equal(endDate, personEmployment.EndDate); + } + + [Fact] public async Task ProcessNewEmploymentHistory_ForLaCodeAndEstablishmentNumberWithMultipleEstablishmentEntries_MatchesToTheMostOpenEstablishment() { @@ -138,7 +227,7 @@ public async Task ProcessNewEmploymentHistory_ForLaCodeAndEstablishmentNumberWit using var dbContext = TestData.DbContextFactory.CreateDbContext(); var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataAdded, i.Result)); - var employmentHistory = await dbContext.PersonEmployments.Where(e => e.PersonId == person.PersonId).ToListAsync(); + var employmentHistory = await dbContext.TpsEmployments.Where(e => e.PersonId == person.PersonId).ToListAsync(); Assert.Single(employmentHistory); var personEmployment = employmentHistory.Single(); Assert.Equal(openEstablishment.EstablishmentId, personEmployment.EstablishmentId); @@ -172,7 +261,7 @@ await TestData.CreateTpsCsvExtract( using var dbContext = TestData.DbContextFactory.CreateDbContext(); var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataAdded, i.Result)); - var employmentHistory = await dbContext.PersonEmployments.Where(e => e.PersonId == person.PersonId).ToListAsync(); + var employmentHistory = await dbContext.TpsEmployments.Where(e => e.PersonId == person.PersonId).ToListAsync(); Assert.Equal(2, employmentHistory.Count); Assert.Contains(nonHigherEducationEstablishment.EstablishmentId, employmentHistory.Select(pe => pe.EstablishmentId)); Assert.Contains(higherEductionEstablishment.EstablishmentId, employmentHistory.Select(pe => pe.EstablishmentId)); @@ -187,7 +276,7 @@ public async Task ProcessUpdatedEmploymentHistory_WhenCalledWithUpdatedEmploymen var establishment1 = await TestData.CreateEstablishment(localAuthorityCode: "126", establishmentNumber: "1237"); var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); var personPostcode = Faker.Address.UkPostCode(); - var existingPersonEmployment = await TestData.CreatePersonEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode); + var existingPersonEmployment = await TestData.CreateTpsEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode); var updatedEndDate = new DateOnly(2024, 03, 30); var updatedLastExtractDate = new DateOnly(2024, 04, 25); await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment1.LaCode, establishment1.EstablishmentNumber, establishment1.Postcode!, new DateOnly(2023, 02, 02), updatedEndDate, updatedLastExtractDate)); @@ -202,9 +291,127 @@ public async Task ProcessUpdatedEmploymentHistory_WhenCalledWithUpdatedEmploymen using var dbContext = TestData.DbContextFactory.CreateDbContext(); var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataUpdated, i.Result)); - var updatedPersonEmployment = await dbContext.PersonEmployments.SingleAsync(e => e.PersonEmploymentId == existingPersonEmployment.PersonEmploymentId); - Assert.Equal(updatedEndDate, updatedPersonEmployment.LastKnownEmployedDate); + var updatedPersonEmployment = await dbContext.TpsEmployments.SingleAsync(e => e.TpsEmploymentId == existingPersonEmployment.TpsEmploymentId); + Assert.Equal(updatedEndDate, updatedPersonEmployment.LastKnownTpsEmployedDate); + Assert.Equal(updatedLastExtractDate, updatedPersonEmployment.LastExtractDate); + Assert.Null(updatedPersonEmployment.EndDate); + } + + [Fact] + public async Task ProcessUpdatedEmploymentHistory_WhenCalledWithEndDateInTheFuture_SetsLastKnownEmployedDateToExtractDate() + { + // Arrange + var person = await TestData.CreatePerson(); + var tpsCsvExtractId = Guid.NewGuid(); + var establishment1 = await TestData.CreateEstablishment(localAuthorityCode: "126", establishmentNumber: "1237"); + var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); + var personPostcode = Faker.Address.UkPostCode(); + var existingPersonEmployment = await TestData.CreateTpsEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode); + var updatedEndDate = new DateOnly(2024, 10, 30); + var updatedLastExtractDate = new DateOnly(2024, 04, 25); + await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment1.LaCode, establishment1.EstablishmentNumber, establishment1.Postcode!, new DateOnly(2023, 02, 02), updatedEndDate, updatedLastExtractDate)); + + // Act + var processor = new TpsCsvExtractProcessor( + TestData.DbContextFactory, + TestData.Clock); + await processor.ProcessUpdatedEmploymentHistory(tpsCsvExtractId, CancellationToken.None); + + // Assert + using var dbContext = TestData.DbContextFactory.CreateDbContext(); + var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); + Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataUpdated, i.Result)); + var updatedPersonEmployment = await dbContext.TpsEmployments.SingleAsync(e => e.TpsEmploymentId == existingPersonEmployment.TpsEmploymentId); + Assert.Equal(updatedLastExtractDate, updatedPersonEmployment.LastKnownTpsEmployedDate); + } + + [Fact] + public async Task ProcessUpdatedEmploymentHistory_WhenCalledWithWithdrawalIndicatorSet_SetsEndDate() + { + // Arrange + var person = await TestData.CreatePerson(); + var tpsCsvExtractId = Guid.NewGuid(); + var establishment1 = await TestData.CreateEstablishment(localAuthorityCode: "126", establishmentNumber: "1237"); + var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); + var personPostcode = Faker.Address.UkPostCode(); + var existingPersonEmployment = await TestData.CreateTpsEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode); + var updatedEndDate = new DateOnly(2024, 03, 30); + var updatedWithdrawalIndicator = "W"; + var updatedLastExtractDate = new DateOnly(2024, 04, 25); + await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment1.LaCode, establishment1.EstablishmentNumber, establishment1.Postcode!, new DateOnly(2023, 02, 02), updatedEndDate, updatedLastExtractDate, withdrawalIndicator: updatedWithdrawalIndicator)); + + // Act + var processor = new TpsCsvExtractProcessor( + TestData.DbContextFactory, + TestData.Clock); + await processor.ProcessUpdatedEmploymentHistory(tpsCsvExtractId, CancellationToken.None); + + // Assert + using var dbContext = TestData.DbContextFactory.CreateDbContext(); + var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); + Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataUpdated, i.Result)); + var updatedPersonEmployment = await dbContext.TpsEmployments.SingleAsync(e => e.TpsEmploymentId == existingPersonEmployment.TpsEmploymentId); + Assert.Equal(updatedEndDate, updatedPersonEmployment.LastKnownTpsEmployedDate); + Assert.Equal(updatedLastExtractDate, updatedPersonEmployment.LastExtractDate); + Assert.True(updatedPersonEmployment.WithdrawalConfirmed); + Assert.Equal(updatedEndDate, updatedPersonEmployment.EndDate); + } + + [Fact] + public async Task ProcessUpdatedEmploymentHistory_WhenCalledWithLastKnownEmployedDateOlderThanFiveMonths_SetsEndDate() + { + // Arrange + var person = await TestData.CreatePerson(); + var tpsCsvExtractId = Guid.NewGuid(); + var establishment1 = await TestData.CreateEstablishment(localAuthorityCode: "126", establishmentNumber: "1237"); + var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); + var personPostcode = Faker.Address.UkPostCode(); + var existingPersonEmployment = await TestData.CreateTpsEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2023, 10, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode); + var updatedLastExtractDate = new DateOnly(2024, 04, 25); + await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment1.LaCode, establishment1.EstablishmentNumber, establishment1.Postcode!, new DateOnly(2023, 02, 02), existingPersonEmployment.LastKnownTpsEmployedDate, updatedLastExtractDate)); + + // Act + var processor = new TpsCsvExtractProcessor( + TestData.DbContextFactory, + TestData.Clock); + await processor.ProcessUpdatedEmploymentHistory(tpsCsvExtractId, CancellationToken.None); + + // Assert + using var dbContext = TestData.DbContextFactory.CreateDbContext(); + var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); + Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataUpdated, i.Result)); + var updatedPersonEmployment = await dbContext.TpsEmployments.SingleAsync(e => e.TpsEmploymentId == existingPersonEmployment.TpsEmploymentId); + Assert.Equal(updatedLastExtractDate, updatedPersonEmployment.LastExtractDate); + Assert.Equal(updatedPersonEmployment.LastKnownTpsEmployedDate, updatedPersonEmployment.EndDate); + } + + [Fact] + public async Task ProcessUpdatedEmploymentHistory_WhenCalledWithWithdrawalIndicatorNowRemoved_ResetsEndDate() + { + // Arrange + var person = await TestData.CreatePerson(); + var tpsCsvExtractId = Guid.NewGuid(); + var establishment1 = await TestData.CreateEstablishment(localAuthorityCode: "126", establishmentNumber: "1237"); + var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); + var personPostcode = Faker.Address.UkPostCode(); + var existingPersonEmployment = await TestData.CreateTpsEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode, withdrawalConfirmed: true); + var updatedLastExtractDate = new DateOnly(2024, 04, 25); + await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment1.LaCode, establishment1.EstablishmentNumber, establishment1.Postcode!, new DateOnly(2023, 02, 02), existingPersonEmployment.LastKnownTpsEmployedDate, updatedLastExtractDate)); + + // Act + var processor = new TpsCsvExtractProcessor( + TestData.DbContextFactory, + TestData.Clock); + await processor.ProcessUpdatedEmploymentHistory(tpsCsvExtractId, CancellationToken.None); + + // Assert + using var dbContext = TestData.DbContextFactory.CreateDbContext(); + var items = await dbContext.TpsCsvExtractItems.Where(i => i.TpsCsvExtractId == tpsCsvExtractId).ToListAsync(); + Assert.All(items, i => Assert.Equal(TpsCsvExtractItemResult.ValidDataUpdated, i.Result)); + var updatedPersonEmployment = await dbContext.TpsEmployments.SingleAsync(e => e.TpsEmploymentId == existingPersonEmployment.TpsEmploymentId); Assert.Equal(updatedLastExtractDate, updatedPersonEmployment.LastExtractDate); + Assert.Null(updatedPersonEmployment.EndDate); + Assert.False(updatedPersonEmployment.WithdrawalConfirmed); } [Fact] @@ -216,8 +423,8 @@ public async Task ProcessUpdatedEmploymentHistory_WhenCalledWithUpdatedEmploymen var establishment1 = await TestData.CreateEstablishment(localAuthorityCode: "126", establishmentNumber: "1237"); var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); var personPostcode = Faker.Address.UkPostCode(); - var existingPersonEmployment = await TestData.CreatePersonEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode); - await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment1.LaCode, establishment1.EstablishmentNumber, establishment1.Postcode!, existingPersonEmployment.StartDate, existingPersonEmployment.LastKnownEmployedDate, existingPersonEmployment.LastExtractDate, "FT")); + var existingPersonEmployment = await TestData.CreateTpsEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode); + await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment1.LaCode, establishment1.EstablishmentNumber, establishment1.Postcode!, existingPersonEmployment.StartDate, existingPersonEmployment.LastKnownTpsEmployedDate, existingPersonEmployment.LastExtractDate, "FT")); // Act var processor = new TpsCsvExtractProcessor( @@ -240,7 +447,7 @@ public async Task UpdateLatestEstablishmentVersions_WithEstablishmentChangingUrn var establishment2 = await TestData.CreateEstablishment(localAuthorityCode: "127", establishmentNumber: "1238", establishmentStatusCode: 1); // Open var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); var personPostcode = Faker.Address.UkPostCode(); - var existingPersonEmployment = await TestData.CreatePersonEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode); + var existingPersonEmployment = await TestData.CreateTpsEmployment(person, establishment1, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), nationalInsuranceNumber, personPostcode); // Act var processor = new TpsCsvExtractProcessor( @@ -250,7 +457,7 @@ public async Task UpdateLatestEstablishmentVersions_WithEstablishmentChangingUrn // Assert using var dbContext = TestData.DbContextFactory.CreateDbContext(); - var updatedPersonEmployment = await dbContext.PersonEmployments.SingleAsync(e => e.PersonEmploymentId == existingPersonEmployment.PersonEmploymentId); + var updatedPersonEmployment = await dbContext.TpsEmployments.SingleAsync(e => e.TpsEmploymentId == existingPersonEmployment.TpsEmploymentId); Assert.Equal(establishment2.EstablishmentId, updatedPersonEmployment.EstablishmentId); } @@ -266,8 +473,8 @@ public async Task ProcessEndedEmployments_WithLastKnownEmployedDateGreaterThanTh var lastKnownEmployedDateOutsideThreeMonthsOfExtractDate = new DateOnly(2023, 09, 30); var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); var personPostcode = Faker.Address.UkPostCode(); - var personEmploymentWhichHasEnded = await TestData.CreatePersonEmployment(person, establishment1, new DateOnly(2023, 02, 02), lastKnownEmployedDateOutsideThreeMonthsOfExtractDate, EmploymentType.FullTime, extractDate, nationalInsuranceNumber, personPostcode); - var personEmploymentWhichHasNotEnded = await TestData.CreatePersonEmployment(person, establishment2, new DateOnly(2023, 02, 02), lastKnownEmployedDateWithinThreeMonthsOfExtractDate, EmploymentType.FullTime, extractDate, nationalInsuranceNumber, personPostcode); + var personEmploymentWhichHasEnded = await TestData.CreateTpsEmployment(person, establishment1, new DateOnly(2023, 02, 02), lastKnownEmployedDateOutsideThreeMonthsOfExtractDate, EmploymentType.FullTime, extractDate, nationalInsuranceNumber, personPostcode); + var personEmploymentWhichHasNotEnded = await TestData.CreateTpsEmployment(person, establishment2, new DateOnly(2023, 02, 02), lastKnownEmployedDateWithinThreeMonthsOfExtractDate, EmploymentType.FullTime, extractDate, nationalInsuranceNumber, personPostcode); // Act var processor = new TpsCsvExtractProcessor( @@ -277,8 +484,8 @@ public async Task ProcessEndedEmployments_WithLastKnownEmployedDateGreaterThanTh // Assert using var dbContext = TestData.DbContextFactory.CreateDbContext(); - var updatedPersonEmploymentWhichShouldHaveEndDateSet = await dbContext.PersonEmployments.SingleAsync(e => e.PersonEmploymentId == personEmploymentWhichHasEnded.PersonEmploymentId); - var updatedPersonEmploymentWhichShouldNotHaveEndDateSet = await dbContext.PersonEmployments.SingleAsync(e => e.PersonEmploymentId == personEmploymentWhichHasNotEnded.PersonEmploymentId); + var updatedPersonEmploymentWhichShouldHaveEndDateSet = await dbContext.TpsEmployments.SingleAsync(e => e.TpsEmploymentId == personEmploymentWhichHasEnded.TpsEmploymentId); + var updatedPersonEmploymentWhichShouldNotHaveEndDateSet = await dbContext.TpsEmployments.SingleAsync(e => e.TpsEmploymentId == personEmploymentWhichHasNotEnded.TpsEmploymentId); Assert.Equal(lastKnownEmployedDateOutsideThreeMonthsOfExtractDate, updatedPersonEmploymentWhichShouldHaveEndDateSet.EndDate); Assert.Null(updatedPersonEmploymentWhichShouldNotHaveEndDateSet.EndDate); } @@ -292,8 +499,8 @@ public async Task BackfillNinoAndPersonPostcodeInEmploymentHistory_WhenCalledWit var establishment = await TestData.CreateEstablishment(localAuthorityCode: "129", establishmentNumber: "1241"); var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); var memberPostcode = Faker.Address.UkPostCode(); - var personEmploymentWithoutNinoAndPersonPostcode = await TestData.CreatePersonEmployment(person, establishment, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), null, null); - await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment.LaCode, establishment.EstablishmentNumber, establishment.Postcode!, personEmploymentWithoutNinoAndPersonPostcode.StartDate, personEmploymentWithoutNinoAndPersonPostcode.LastKnownEmployedDate, personEmploymentWithoutNinoAndPersonPostcode.LastExtractDate, "FT", nationalInsuranceNumber, memberPostcode: memberPostcode)); + var personEmploymentWithoutNinoAndPersonPostcode = await TestData.CreateTpsEmployment(person, establishment, new DateOnly(2023, 02, 02), new DateOnly(2024, 02, 29), EmploymentType.FullTime, new DateOnly(2024, 03, 25), null, null); + await TestData.CreateTpsCsvExtract(b => b.WithTpsCsvExtractId(tpsCsvExtractId).WithItem(person!.Trn!, establishment.LaCode, establishment.EstablishmentNumber, establishment.Postcode!, personEmploymentWithoutNinoAndPersonPostcode.StartDate, personEmploymentWithoutNinoAndPersonPostcode.LastKnownTpsEmployedDate, personEmploymentWithoutNinoAndPersonPostcode.LastExtractDate, "FT", nationalInsuranceNumber, memberPostcode: memberPostcode)); // Act var processor = new TpsCsvExtractProcessor( @@ -303,7 +510,7 @@ public async Task BackfillNinoAndPersonPostcodeInEmploymentHistory_WhenCalledWit // Assert using var dbContext = TestData.DbContextFactory.CreateDbContext(); - var updatedPersonEmployment = await dbContext.PersonEmployments.SingleAsync(e => e.PersonEmploymentId == personEmploymentWithoutNinoAndPersonPostcode.PersonEmploymentId); + var updatedPersonEmployment = await dbContext.TpsEmployments.SingleAsync(e => e.TpsEmploymentId == personEmploymentWithoutNinoAndPersonPostcode.TpsEmploymentId); Assert.Equal(nationalInsuranceNumber, updatedPersonEmployment.NationalInsuranceNumber); Assert.Equal(memberPostcode, updatedPersonEmployment.PersonPostcode); } diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreatePersonEmployment.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreatePersonEmployment.cs index f743cf369..2ec99ba6e 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreatePersonEmployment.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreatePersonEmployment.cs @@ -5,7 +5,7 @@ namespace TeachingRecordSystem.TestCommon; public partial class TestData { - public async Task CreatePersonEmployment( + public async Task CreateTpsEmployment( CreatePersonResult person, Establishment establishment, DateOnly startDate, @@ -14,21 +14,23 @@ public async Task CreatePersonEmployment( DateOnly lastExtractDate, string? nationalInsuranceNumber = null, string? personPostcode = null, - DateOnly? endDate = null) + DateOnly? endDate = null, + bool? withdrawalConfirmed = false) { var key = $"{person.Trn}.{establishment.LaCode}.{establishment.EstablishmentNumber}.{startDate:yyyyMMdd}"; var personEmployment = await WithDbContext(async dbContext => { - var personEmployment = new PersonEmployment + var personEmployment = new TpsEmployment { - PersonEmploymentId = Guid.NewGuid(), + TpsEmploymentId = Guid.NewGuid(), PersonId = person.PersonId, EstablishmentId = establishment.EstablishmentId, StartDate = startDate, EndDate = endDate, EmploymentType = employmentType, - LastKnownEmployedDate = lastKnownEmployedDate, + WithdrawalConfirmed = withdrawalConfirmed ?? false, + LastKnownTpsEmployedDate = lastKnownEmployedDate, LastExtractDate = lastExtractDate, NationalInsuranceNumber = nationalInsuranceNumber, PersonPostcode = personPostcode, @@ -37,7 +39,7 @@ public async Task CreatePersonEmployment( Key = key }; - dbContext.PersonEmployments.Add(personEmployment); + dbContext.TpsEmployments.Add(personEmployment); await dbContext.SaveChangesAsync(); return personEmployment; diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreateTpsCsvExtract.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreateTpsCsvExtract.cs index a230ccaf8..0ec9986d0 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreateTpsCsvExtract.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreateTpsCsvExtract.cs @@ -52,13 +52,14 @@ public TpsCsvExtractBuilder WithItem( string? fullOrPartTimeIndicator = null, string? nationalInsuranceNumber = null, DateOnly? dateOfBirth = null, - string? memberPostcode = null) + string? memberPostcode = null, + string? withdrawalIndicator = null) { nationalInsuranceNumber ??= Faker.Identification.UkNationalInsuranceNumber(); dateOfBirth ??= DateOnly.FromDateTime(Faker.Identification.DateOfBirth()); fullOrPartTimeIndicator ??= validFullOrPartTimeIndicatorValues[Faker.RandomNumber.Next(0, 2)]; - _items.Add(new TpsCsvExtractItem(trn, nationalInsuranceNumber, dateOfBirth.Value, localAuthorityCode, establishmentPostcode, establishmentNumber, startDate, endDate, fullOrPartTimeIndicator, extractDate, memberPostcode)); + _items.Add(new TpsCsvExtractItem(trn, nationalInsuranceNumber, dateOfBirth.Value, localAuthorityCode, establishmentPostcode, establishmentNumber, startDate, endDate, fullOrPartTimeIndicator, extractDate, memberPostcode, withdrawalIndicator)); return this; } @@ -104,7 +105,7 @@ await testData.WithDbContext(async dbContext => EmploymentStartDate = item.StartDate.ToString("dd/MM/yyyy"), EmploymentEndDate = item.EndDate.ToString("dd/MM/yyyy"), FullOrPartTimeIndicator = item.FullOrPartTimeIndicator, - WithdrawlIndicator = null, + WithdrawalIndicator = item.WithdrawalIndicator, ExtractDate = item.ExtractDate.ToString("dd/MM/yyyy"), Gender = validGenderValues[Faker.RandomNumber.Next(0, 1)], Errors = TpsCsvExtractItemLoadErrors.None, @@ -133,7 +134,7 @@ await testData.WithDbContext(async dbContext => EmploymentStartDate = item.StartDate, EmploymentEndDate = item.EndDate, EmploymentType = EmploymentTypeHelper.FromFullOrPartTimeIndicator(loadItem.FullOrPartTimeIndicator), - WithdrawlIndicator = loadItem.WithdrawlIndicator, + WithdrawalIndicator = loadItem.WithdrawalIndicator, ExtractDate = item.ExtractDate, Created = createdOn, Result = null, @@ -148,5 +149,5 @@ await testData.WithDbContext(async dbContext => } } - public record TpsCsvExtractItem(string Trn, string NationalInsuranceNumber, DateOnly DateOfBirth, string LocalAuthorityCode, string EstablishmentPostcode, string? EstablishmentNumber, DateOnly StartDate, DateOnly EndDate, string FullOrPartTimeIndicator, DateOnly ExtractDate, string? MemberPostcode); + public record TpsCsvExtractItem(string Trn, string NationalInsuranceNumber, DateOnly DateOfBirth, string LocalAuthorityCode, string EstablishmentPostcode, string? EstablishmentNumber, DateOnly StartDate, DateOnly EndDate, string FullOrPartTimeIndicator, DateOnly ExtractDate, string? MemberPostcode, string? WithdrawalIndicator); } diff --git a/docs/workforce-data-import/useful-queries.md b/docs/workforce-data-import/useful-queries.md index b44b17486..97316b8a8 100644 --- a/docs/workforce-data-import/useful-queries.md +++ b/docs/workforce-data-import/useful-queries.md @@ -12,25 +12,25 @@ A check for the reliability of the Withdrawal Indicator by establishments to ind ``` WITH withdrawal_indicator_revert_keys AS ( - SELECT - distinct key - FROM - tps_csv_extract_items x1 - WHERE - withdrawl_indicator = 'W' - AND EXISTS (SELECT - 1 - FROM - tps_csv_extract_items x2 - WHERE - x2.key = x1.key - AND x2.extract_date > x1.extract_date - AND x2.withdrawl_indicator IS NULL) + SELECT + distinct key + FROM + tps_csv_extract_items x1 + WHERE + withdrawal_indicator = 'W' + AND EXISTS (SELECT + 1 + FROM + tps_csv_extract_items x2 + WHERE + x2.key = x1.key + AND x2.extract_date > x1.extract_date + AND x2.withdrawal_indicator IS NULL) ) SELECT - COUNT(1) + COUNT(1) FROM - withdrawal_indicator_revert_keys + withdrawal_indicator_revert_keys ``` ### Get examples of records with Withdrawal Indicator 'W' that reverts in future extracts @@ -39,47 +39,47 @@ A check for the reliability of the Withdrawal Indicator by establishments to ind ``` WITH withdrawal_indicator_revert_keys AS ( - SELECT - distinct key - FROM - tps_csv_extract_items x1 - WHERE - withdrawl_indicator = 'W' - AND EXISTS (SELECT - 1 - FROM - tps_csv_extract_items x2 - WHERE - x2.key = x1.key - AND x2.extract_date > x1.extract_date - AND x2.withdrawl_indicator IS NULL) + SELECT + distinct key + FROM + tps_csv_extract_items x1 + WHERE + withdrawal_indicator = 'W' + AND EXISTS (SELECT + 1 + FROM + tps_csv_extract_items x2 + WHERE + x2.key = x1.key + AND x2.extract_date > x1.extract_date + AND x2.withdrawal_indicator IS NULL) ) SELECT - trn, - local_authority_code, - establishment_number, - employment_start_date, - employment_end_date, - CASE - WHEN employment_type = 0 THEN 'FT' - WHEN employment_type = 1 THEN 'PTR' - WHEN employment_type = 2 THEN 'PTI' - WHEN employment_type = 3 THEN 'PT' - END employment_type, - withdrawl_indicator, - extract_date + trn, + local_authority_code, + establishment_number, + employment_start_date, + employment_end_date, + CASE + WHEN employment_type = 0 THEN 'FT' + WHEN employment_type = 1 THEN 'PTR' + WHEN employment_type = 2 THEN 'PTI' + WHEN employment_type = 3 THEN 'PT' + END employment_type, + withdrawal_indicator, + extract_date FROM - tps_csv_extract_items x + tps_csv_extract_items x WHERE - EXISTS (SELECT - 1 - FROM - withdrawal_indicator_revert_keys w - WHERE - w.key = x.key) + EXISTS (SELECT + 1 + FROM + withdrawal_indicator_revert_keys w + WHERE + w.key = x.key) ORDER BY - x.key desc, - x.extract_date + x.key desc, + x.extract_date LIMIT 1000 ``` @@ -89,26 +89,26 @@ A check for the reliability of the Withdrawal Indicator by establishments to ind ``` WITH withdrawal_end_date_change_keys AS ( - SELECT - distinct key - FROM - tps_csv_extract_items x1 - WHERE - withdrawl_indicator = 'W' - AND EXISTS (SELECT - 1 - FROM - tps_csv_extract_items x2 - WHERE - x2.key = x1.key - AND x2.extract_date > x1.extract_date - AND x2.withdrawl_indicator = 'W' - AND x2.employment_end_date <> x1.employment_end_date) + SELECT + distinct key + FROM + tps_csv_extract_items x1 + WHERE + withdrawal_indicator = 'W' + AND EXISTS (SELECT + 1 + FROM + tps_csv_extract_items x2 + WHERE + x2.key = x1.key + AND x2.extract_date > x1.extract_date + AND x2.withdrawal_indicator = 'W' + AND x2.employment_end_date <> x1.employment_end_date) ) SELECT - COUNT(1) + COUNT(1) FROM - withdrawal_end_date_change_keys + withdrawal_end_date_change_keys ``` ### Get examples of records with Withdrawal Indicator 'W' that change end date in future extracts @@ -117,48 +117,48 @@ A check for the reliability of the Withdrawal Indicator by establishments to ind ``` WITH withdrawal_end_date_change_keys AS ( - SELECT - distinct key - FROM - tps_csv_extract_items x1 - WHERE - withdrawl_indicator = 'W' - AND EXISTS (SELECT - 1 - FROM - tps_csv_extract_items x2 - WHERE - x2.key = x1.key - AND x2.extract_date > x1.extract_date - AND x2.withdrawl_indicator = 'W' - AND x2.employment_end_date <> x1.employment_end_date) + SELECT + distinct key + FROM + tps_csv_extract_items x1 + WHERE + withdrawal_indicator = 'W' + AND EXISTS (SELECT + 1 + FROM + tps_csv_extract_items x2 + WHERE + x2.key = x1.key + AND x2.extract_date > x1.extract_date + AND x2.withdrawal_indicator = 'W' + AND x2.employment_end_date <> x1.employment_end_date) ) SELECT - trn, - local_authority_code, - establishment_number, - employment_start_date, - employment_end_date, - CASE - WHEN employment_type = 0 THEN 'FT' - WHEN employment_type = 1 THEN 'PTR' - WHEN employment_type = 2 THEN 'PTI' - WHEN employment_type = 3 THEN 'PT' - END employment_type, - withdrawl_indicator, - extract_date + trn, + local_authority_code, + establishment_number, + employment_start_date, + employment_end_date, + CASE + WHEN employment_type = 0 THEN 'FT' + WHEN employment_type = 1 THEN 'PTR' + WHEN employment_type = 2 THEN 'PTI' + WHEN employment_type = 3 THEN 'PT' + END employment_type, + withdrawal_indicator, + extract_date FROM - tps_csv_extract_items x + tps_csv_extract_items x WHERE - EXISTS (SELECT - 1 - FROM - withdrawal_end_date_change_keys w - WHERE - w.key = x.key) + EXISTS (SELECT + 1 + FROM + withdrawal_end_date_change_keys w + WHERE + w.key = x.key) ORDER BY - x.key desc, - x.extract_date + x.key desc, + x.extract_date LIMIT 1000 ``` @@ -168,32 +168,32 @@ A check for the reliability of the Withdrawal Indicator by establishments to ind ``` WITH withdrawal_no_change_keys AS ( - SELECT - distinct key - FROM - tps_csv_extract_items x1 - WHERE - withdrawl_indicator = 'W' - AND EXISTS (SELECT - 1 - FROM - tps_csv_extract_items x2 - WHERE - x2.key = x1.key - AND x2.extract_date > x1.extract_date) - AND NOT EXISTS (SELECT - 1 - FROM - tps_csv_extract_items x2 - WHERE - x2.key = x1.key - AND x2.extract_date > x1.extract_date - AND (x2.withdrawl_indicator IS NULL OR x2.employment_end_date <> x1.employment_end_date)) + SELECT + distinct key + FROM + tps_csv_extract_items x1 + WHERE + withdrawal_indicator = 'W' + AND EXISTS (SELECT + 1 + FROM + tps_csv_extract_items x2 + WHERE + x2.key = x1.key + AND x2.extract_date > x1.extract_date) + AND NOT EXISTS (SELECT + 1 + FROM + tps_csv_extract_items x2 + WHERE + x2.key = x1.key + AND x2.extract_date > x1.extract_date + AND (x2.withdrawal_indicator IS NULL OR x2.employment_end_date <> x1.employment_end_date)) ) SELECT - COUNT(1) + COUNT(1) FROM - withdrawal_no_change_keys + withdrawal_no_change_keys ``` ### Get counts of records where the end date hasn't changed for over 5 months and there is no further alternative employment @@ -202,40 +202,94 @@ This gives an idea of people who have potentially left the teaching profession ``` WITH latest_extracts AS ( - SELECT - * - FROM - (SELECT - trn, - local_authority_code, - establishment_number, - employment_start_date, - employment_end_date, - employment_type, - withdrawl_indicator, - extract_date, - key, - ROW_NUMBER() OVER (PARTITION BY key ORDER BY extract_date desc) as row_number - FROM - tps_csv_extract_items) x - WHERE - x.row_number = 1 + SELECT + * + FROM + (SELECT + trn, + local_authority_code, + establishment_number, + employment_start_date, + employment_end_date, + employment_type, + withdrawal_indicator, + extract_date, + key, + ROW_NUMBER() OVER (PARTITION BY key ORDER BY extract_date desc) as row_number + FROM + tps_csv_extract_items) x + WHERE + x.row_number = 1 ) SELECT - SUM(CASE WHEN withdrawl_indicator = 'W' THEN 1 ELSE 0 END) as count_with_withdrawal_indicator, - SUM(CASE WHEN withdrawl_indicator IS NULL THEN 1 ELSE 0 END) as count_without_withdrawal_indicator + SUM(CASE WHEN withdrawal_indicator = 'W' THEN 1 ELSE 0 END) as count_with_withdrawal_indicator, + SUM(CASE WHEN withdrawal_indicator IS NULL THEN 1 ELSE 0 END) as count_without_withdrawal_indicator FROM - latest_extracts x1 + latest_extracts x1 WHERE - AGE(x1.extract_date, x1.employment_end_date) > INTERVAL '5 months' - AND NOT EXISTS (SELECT - 1 - FROM - latest_extracts x2 - WHERE - x2.trn = x1.trn - AND x2.key <> x1.key - AND AGE(x2.extract_date, x2.employment_end_date) <= INTERVAL '5 months') + AGE(x1.extract_date, x1.employment_end_date) > INTERVAL '5 months' + AND NOT EXISTS (SELECT + 1 + FROM + latest_extracts x2 + WHERE + x2.trn = x1.trn + AND x2.key <> x1.key + AND AGE(x2.extract_date, x2.employment_end_date) <= INTERVAL '5 months') +``` + +### Get examples of records where the end date hasn't changed for over 5 months and there is no further alternative employment + +This gives an idea of people who have potentially left the teaching profession + +``` +WITH latest_extracts AS ( + SELECT + * + FROM + (SELECT + trn, + local_authority_code, + establishment_number, + employment_start_date, + employment_end_date, + employment_type, + withdrawal_indicator, + extract_date, + key, + ROW_NUMBER() OVER (PARTITION BY key ORDER BY extract_date desc) as row_number + FROM + tps_csv_extract_items) x + WHERE + x.row_number = 1 +) +SELECT + trn, + local_authority_code, + establishment_number, + employment_start_date, + employment_end_date, + CASE + WHEN employment_type = 0 THEN 'FT' + WHEN employment_type = 1 THEN 'PTR' + WHEN employment_type = 2 THEN 'PTI' + WHEN employment_type = 3 THEN 'PT' + END employment_type, + withdrawal_indicator, + extract_date +FROM + latest_extracts x1 +WHERE + AGE(x1.extract_date, x1.employment_end_date) > INTERVAL '5 months' + AND x1.withdrawal_indicator IS NULL + AND NOT EXISTS (SELECT + 1 + FROM + latest_extracts x2 + WHERE + x2.trn = x1.trn + AND x2.key <> x1.key + AND AGE(x2.extract_date, x2.employment_end_date) <= INTERVAL '5 months') ``` ### Get counts of records which are in the first extract and missing from multiple extracts before re-appearing in the most recent extract @@ -244,39 +298,39 @@ This is to check that it is indeed possible that records can skip multiple extra ``` WITH keys AS ( - SELECT - *, - CASE - WHEN extract_date = to_date('20240325','YYYYMMDD') THEN 1 - WHEN extract_date = to_date('20240425','YYYYMMDD') THEN 2 - WHEN extract_date = to_date('20240525','YYYYMMDD') THEN 3 - WHEN extract_date = to_date('20240626','YYYYMMDD') THEN 4 - END as extract_number - FROM - tps_csv_extract_items - WHERE - extract_date <> to_date('20240307','YYYYMMDD') + SELECT + *, + CASE + WHEN extract_date = to_date('20240325','YYYYMMDD') THEN 1 + WHEN extract_date = to_date('20240425','YYYYMMDD') THEN 2 + WHEN extract_date = to_date('20240525','YYYYMMDD') THEN 3 + WHEN extract_date = to_date('20240626','YYYYMMDD') THEN 4 + END as extract_number + FROM + tps_csv_extract_items + WHERE + extract_date <> to_date('20240307','YYYYMMDD') ) SELECT - COUNT(1) + COUNT(1) FROM - keys k1 + keys k1 WHERE - k1.extract_number = 1 - AND NOT EXISTS (SELECT - 1 - FROM - keys k2 - WHERE - k2.key = k1.key - AND k2.extract_number in (2, 3)) - AND EXISTS (SELECT - 1 - FROM - keys k4 - WHERE - k4.key = k1.key - AND k4.extract_number = 4) + k1.extract_number = 1 + AND NOT EXISTS (SELECT + 1 + FROM + keys k2 + WHERE + k2.key = k1.key + AND k2.extract_number in (2, 3)) + AND EXISTS (SELECT + 1 + FROM + keys k4 + WHERE + k4.key = k1.key + AND k4.extract_number = 4) ``` ### Get counts of employment types with Withdrawal Indicator 'W' @@ -285,17 +339,17 @@ Check that the Withrawal Indicator can also be applied to part-time employment ``` SELECT - extract_date, - SUM(CASE WHEN employment_type = 0 THEN 1 ELSE 0 END) full_time_count, - SUM(CASE WHEN employment_type = 1 THEN 1 ELSE 0 END) part_time_regular_count, - SUM(CASE WHEN employment_type = 2 THEN 1 ELSE 0 END) part_time_irregular_count, - SUM(CASE WHEN employment_type = 3 THEN 1 ELSE 0 END) part_time_count + extract_date, + SUM(CASE WHEN employment_type = 0 THEN 1 ELSE 0 END) full_time_count, + SUM(CASE WHEN employment_type = 1 THEN 1 ELSE 0 END) part_time_regular_count, + SUM(CASE WHEN employment_type = 2 THEN 1 ELSE 0 END) part_time_irregular_count, + SUM(CASE WHEN employment_type = 3 THEN 1 ELSE 0 END) part_time_count FROM - tps_csv_extract_items + tps_csv_extract_items WHERE - withdrawl_indicator = 'W' + withdrawal_indicator = 'W' GROUP BY - extract_date + extract_date ``` ### Check if we are getting data with end dates within the expected date range (i.e. 6 months of the extract date) @@ -304,13 +358,13 @@ This is a sanity check on the data ``` SELECT - extract_date, - MIN(employment_end_date) min_employment_end_date, - (date_trunc('month', extract_date) - interval '6 month')::date expected_minimum_end_date + extract_date, + MIN(employment_end_date) min_employment_end_date, + (date_trunc('month', extract_date) - interval '6 month')::date expected_minimum_end_date FROM - tps_csv_extract_items + tps_csv_extract_items GROUP BY - extract_date + extract_date ``` ### Get the count end dates which are beyond the end of the month of the extract date @@ -319,108 +373,108 @@ We appear to get records where the end date is in the future ``` WITH latest_extracts AS ( - SELECT - *, - (date_trunc('month', extract_date) + interval '1 month' - interval '1 day')::date report_end_date - FROM - (SELECT - trn, - local_authority_code, - establishment_number, - employment_start_date, - employment_end_date, - employment_type, - withdrawl_indicator, - extract_date, - key, - ROW_NUMBER() OVER (PARTITION BY key ORDER BY extract_date desc) as row_number - FROM - tps_csv_extract_items) x - WHERE - x.row_number = 1 + SELECT + *, + (date_trunc('month', extract_date) + interval '1 month' - interval '1 day')::date report_end_date + FROM + (SELECT + trn, + local_authority_code, + establishment_number, + employment_start_date, + employment_end_date, + employment_type, + withdrawal_indicator, + extract_date, + key, + ROW_NUMBER() OVER (PARTITION BY key ORDER BY extract_date desc) as row_number + FROM + tps_csv_extract_items) x + WHERE + x.row_number = 1 ) SELECT - COUNT(1) + COUNT(1) FROM - latest_extracts + latest_extracts WHERE - employment_end_date > report_end_date + employment_end_date > report_end_date ``` ### Get count of people who are in multiple full time employments at the same time ``` WITH overlapping_full_time AS ( - SELECT - distinct pe1.person_id - FROM - person_employments pe1 - WHERE - pe1.start_date <= pe1.last_known_employed_date - AND pe1.employment_type = 0 - AND EXISTS (SELECT - 1 - FROM - person_employments pe2 - WHERE - pe1.person_id = pe2.person_id - AND pe1.person_employment_id <> pe2.person_employment_id - AND pe2.employment_type = 0 - AND pe2.start_date <= pe2.last_known_employed_date - AND daterange(pe1.start_date, pe1.last_known_employed_date,'[]') && daterange(pe2.start_date, pe2.last_known_employed_date,'[]')) + SELECT + distinct te1.person_id + FROM + tps_employments te1 + WHERE + te1.start_date <= te1.last_known_employed_date + AND pe1.employment_type = 0 + AND EXISTS (SELECT + 1 + FROM + tps_employments te2 + WHERE + te1.person_id = te2.person_id + AND te1.tps_employment_id <> te2.tps_employment_id + AND te2.employment_type = 0 + AND te2.start_date <= te2.last_known_employed_date + AND daterange(te1.start_date, te1.last_known_employed_date,'[]') && daterange(te2.start_date, te2.last_known_employed_date,'[]')) ) SELECT - COUNT(1) + COUNT(1) FROM - overlapping_full_time + overlapping_full_time ``` ### Examples of people who are in multiple full time employments at the same time ``` WITH overlapping_full_time AS ( - SELECT - distinct pe1.person_id - FROM - person_employments pe1 - WHERE - pe1.start_date <= pe1.last_known_employed_date - AND pe1.employment_type = 0 - AND EXISTS (SELECT - 1 - FROM - person_employments pe2 - WHERE - pe1.person_id = pe2.person_id - AND pe1.person_employment_id <> pe2.person_employment_id - AND pe2.employment_type = 0 - AND pe2.start_date <= pe2.last_known_employed_date - AND daterange(pe1.start_date, pe1.last_known_employed_date,'[]') && daterange(pe2.start_date, pe2.last_known_employed_date,'[]')) + SELECT + distinct pe1.person_id + FROM + tps_employments te1 + WHERE + te1.start_date <= te1.last_known_employed_date + AND te1.employment_type = 0 + AND EXISTS (SELECT + 1 + FROM + tps_employments te2 + WHERE + te1.person_id = te2.person_id + AND te1.tps_employment_id <> te2.tps_employment_id + AND te2.employment_type = 0 + AND te2.start_date <= te2.last_known_employed_date + AND daterange(te1.start_date, te1.last_known_employed_date,'[]') && daterange(te2.start_date, te2.last_known_employed_date,'[]')) ) SELECT - p.trn, - e.la_code, - e.establishment_number, - substr(e.establishment_name, 1, 30), - pe.start_date, - pe.last_known_employed_date + p.trn, + e.la_code, + e.establishment_number, + substr(e.establishment_name, 1, 30), + te.start_date, + te.last_known_employed_date FROM - person_employments pe - JOIN - persons p ON p.person_id = pe.person_id - JOIN - establishments e ON e.establishment_id = pe.establishment_id + tps_employments te + JOIN + persons p ON p.person_id = te.person_id + JOIN + establishments e ON e.establishment_id = pe.establishment_id WHERE - pe.employment_type = 0 - AND EXISTS (SELECT - 1 - FROM - overlapping_full_time o - WHERE - o.person_id = pe.person_id) + pe.employment_type = 0 + AND EXISTS (SELECT + 1 + FROM + overlapping_full_time o + WHERE + o.person_id = pe.person_id) ORDER BY - p.trn, - start_date + p.trn, + start_date ``` ### Get most frequent end dates for extract @@ -429,16 +483,16 @@ Check which are the most frequent end dates for a given extract ``` SELECT - employment_end_date, - COUNT(1) + employment_end_date, + COUNT(1) FROM - tps_csv_extract_items + tps_csv_extract_items WHERE - extract_date = to_date('20240626','YYYYMMDD') + extract_date = to_date('20240626','YYYYMMDD') GROUP BY - employment_end_date + employment_end_date ORDER BY - COUNT(1) DESC + COUNT(1) DESC LIMIT 10 ``` @@ -446,70 +500,70 @@ LIMIT 10 ``` WITH updated_employment_type_keys AS ( - SELECT - distinct key - FROM - tps_csv_extract_items x1 - WHERE - EXISTS (SELECT - 1 - FROM - tps_csv_extract_items x2 - WHERE - x2.key = x1.key - AND x2.employment_type <> x1.employment_type - AND x2.extract_date > x1.extract_date) + SELECT + distinct key + FROM + tps_csv_extract_items x1 + WHERE + EXISTS (SELECT + 1 + FROM + tps_csv_extract_items x2 + WHERE + x2.key = x1.key + AND x2.employment_type <> x1.employment_type + AND x2.extract_date > x1.extract_date) ) SELECT - COUNT(key) + COUNT(key) FROM - updated_employment_type_keys + updated_employment_type_keys ``` ### Get example records which change employment type between extracts ``` WITH updated_employment_type_keys AS ( - SELECT - distinct key - FROM - tps_csv_extract_items x1 - WHERE - EXISTS (SELECT - 1 - FROM - tps_csv_extract_items x2 - WHERE - x2.key = x1.key - AND x2.employment_type <> x1.employment_type - AND x2.extract_date > x1.extract_date) + SELECT + distinct key + FROM + tps_csv_extract_items x1 + WHERE + EXISTS (SELECT + 1 + FROM + tps_csv_extract_items x2 + WHERE + x2.key = x1.key + AND x2.employment_type <> x1.employment_type + AND x2.extract_date > x1.extract_date) ) SELECT - trn, - local_authority_code, - establishment_number, - employment_start_date, - employment_end_date, - CASE - WHEN employment_type = 0 THEN 'FT' - WHEN employment_type = 1 THEN 'PTR' - WHEN employment_type = 2 THEN 'PTI' - WHEN employment_type = 3 THEN 'PT' - END employment_type, - withdrawl_indicator, - extract_date + trn, + local_authority_code, + establishment_number, + employment_start_date, + employment_end_date, + CASE + WHEN employment_type = 0 THEN 'FT' + WHEN employment_type = 1 THEN 'PTR' + WHEN employment_type = 2 THEN 'PTI' + WHEN employment_type = 3 THEN 'PT' + END employment_type, + withdrawal_indicator, + extract_date FROM - tps_csv_extract_items x + tps_csv_extract_items x WHERE - EXISTS (SELECT - 1 - FROM - updated_employment_type_keys e - WHERE - e.key = x.key) + EXISTS (SELECT + 1 + FROM + updated_employment_type_keys e + WHERE + e.key = x.key) ORDER BY - x.key desc, - x.extract_date + x.key desc, + x.extract_date LIMIT 1000 ``` @@ -565,25 +619,25 @@ unique_tps_establishments AS ( e.row_number = 1 ) SELECT - * + * FROM - unique_tps_establishments e + unique_tps_establishments e WHERE - NOT EXISTS (SELECT - 1 - FROM - unique_gias_establishments g - WHERE - g.la_code = e.la_code - AND g.establishment_number = e.establishment_code) - AND NOT EXISTS (SELECT - 1 - FROM - tps_establishment_types t - WHERE - e.establishment_code::int >= t.establishment_range_from::int - AND e.establishment_code::int <= t.establishment_range_to::int) + NOT EXISTS (SELECT + 1 + FROM + unique_gias_establishments g + WHERE + g.la_code = e.la_code + AND g.establishment_number = e.establishment_code) + AND NOT EXISTS (SELECT + 1 + FROM + tps_establishment_types t + WHERE + e.establishment_code::int >= t.establishment_range_from::int + AND e.establishment_code::int <= t.establishment_range_to::int) ORDER BY - la_code, - establishment_code -``` \ No newline at end of file + la_code, + establishment_code +``` From 97f588763a5c6760d7559316f1e9c47587e0d9dd Mon Sep 17 00:00:00 2001 From: Andrew Horth Date: Mon, 12 Aug 2024 14:34:37 +0100 Subject: [PATCH 2/3] Tweak to try and get database build working --- .github/workflows/dbml.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dbml.yml b/.github/workflows/dbml.yml index 49974d5e8..316049774 100644 --- a/.github/workflows/dbml.yml +++ b/.github/workflows/dbml.yml @@ -50,7 +50,7 @@ jobs: psql -c '\dt' | sed '1,3d' | head -n -2 | sed 's/ //g' | cut -d'|' -f 2 | tr '\n' ' ' | xargs printf -- ' -t %s' | xargs pg_dump --schema-only >trs.dump - name: Create dbml file - run: npx -p @dbml/cli sql2dbml trs.dump --postgres -o trs.dbml + run: npx -p @dbml/cli@3.6.2 sql2dbml trs.dump --postgres -o trs.dbml - name: Publish dbml file uses: actions/upload-artifact@v4 From de3572be0dd1b56331c1650b0eabfe7e160bae49 Mon Sep 17 00:00:00 2001 From: Andrew Horth Date: Mon, 12 Aug 2024 14:39:18 +0100 Subject: [PATCH 3/3] Revert "Tweak to try and get database build working" This reverts commit 97f588763a5c6760d7559316f1e9c47587e0d9dd. --- .github/workflows/dbml.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dbml.yml b/.github/workflows/dbml.yml index 316049774..49974d5e8 100644 --- a/.github/workflows/dbml.yml +++ b/.github/workflows/dbml.yml @@ -50,7 +50,7 @@ jobs: psql -c '\dt' | sed '1,3d' | head -n -2 | sed 's/ //g' | cut -d'|' -f 2 | tr '\n' ' ' | xargs printf -- ' -t %s' | xargs pg_dump --schema-only >trs.dump - name: Create dbml file - run: npx -p @dbml/cli@3.6.2 sql2dbml trs.dump --postgres -o trs.dbml + run: npx -p @dbml/cli sql2dbml trs.dump --postgres -o trs.dbml - name: Publish dbml file uses: actions/upload-artifact@v4