From 0abdfcd683bb7fc4dedaa9de215e833cfe0a0a6166e8240b4f54676a7c548eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= <93217193+Daniel-Garmig@users.noreply.github.com> Date: Thu, 10 Sep 2026 11:53:41 +0200 Subject: [PATCH] GuestDocument - Improvements to GuestDocument creation: - IssueDate is now Nullable. Is not required for DNI and NIE document types. - Improvements in SupportNumber for NIE documents. - Address search can now be skipped. --- .../Components/Forms/GuestDocForm.razor | 132 +++++---- ...ocument_IssueDate_MakeNullable.Designer.cs | 252 ++++++++++++++++++ ...10_GuestDocument_IssueDate_MakeNullable.cs | 42 +++ .../GuestLogContextModelSnapshot.cs | 2 +- .../Models/Entities/GuestDocument.cs | 2 +- src/Somnia.GuestLog/Models/EntityMapping.cs | 2 +- .../Entity/GuestDocumentEntityService.cs | 2 +- src/Somnia.GuestLog/Utils/RegexUtils.cs | 4 +- .../Validators/GuestDocumentDTOValidator.cs | 12 +- 9 files changed, 397 insertions(+), 53 deletions(-) create mode 100644 src/Somnia.GuestLog/Infrastructure/Migrations/20260910093310_GuestDocument_IssueDate_MakeNullable.Designer.cs create mode 100644 src/Somnia.GuestLog/Infrastructure/Migrations/20260910093310_GuestDocument_IssueDate_MakeNullable.cs diff --git a/src/Somnia.GuestLog/Components/Forms/GuestDocForm.razor b/src/Somnia.GuestLog/Components/Forms/GuestDocForm.razor index 92a6ee1..2920f3e 100644 --- a/src/Somnia.GuestLog/Components/Forms/GuestDocForm.razor +++ b/src/Somnia.GuestLog/Components/Forms/GuestDocForm.razor @@ -6,53 +6,61 @@ @inject IDataListService dataListService; @inject IValidator guestDocValidator; +@inject IUserMessageService userMessage; - + - @LanguageLocale.control_GuestDocForm_sectionDocument + + @LanguageLocale.control_GuestDocForm_sectionDocument + - - @foreach (string docType in dataListService.GetDataListByName(GuestConsts.DataList_DocumentTypes)) + + @foreach (string docType in + dataListService.GetDataListByName(GuestConsts.DataList_DocumentTypes)) { @dataListService.GetLocalizedValue(GuestConsts.DataList_DocumentTypes, docType) } - + - + - - + - @LanguageLocale.control_GuestDocForm_sectionGuest + + @LanguageLocale.control_GuestDocForm_sectionGuest + - - - + - - @foreach (string gender in dataListService.GetDataListByName(GuestConsts.DataList_GenderOptions)) + + @foreach (string gender in + dataListService.GetDataListByName(GuestConsts.DataList_GenderOptions)) { @dataListService.GetLocalizedValue(GuestConsts.DataList_GenderOptions, gender) @@ -77,50 +88,58 @@ - @LanguageLocale.control_GuestDocForm_sectionContact + + @LanguageLocale.control_GuestDocForm_sectionContact + + Label="@LanguageLocale.dto_GuestDocument_PhoneNumber" InputType="InputType.Telephone" /> + Label="@LanguageLocale.dto_GuestDocument_EmailAddress" InputType="InputType.Email" /> - @LanguageLocale.control_GuestDocForm_sectionAddress + + @LanguageLocale.control_GuestDocForm_sectionAddress + - @if (!ReadOnly) + @if (!ReadOnly) { - - @LanguageLocale.control_GuestDocForm_btnSearchAddress - + + + @LanguageLocale.control_GuestDocForm_btnSearchAddress + + + } - + + Label="@LanguageLocale.general_address_city" ReadOnly="@ReadOnlyAddress" /> + Label="@LanguageLocale.general_address_province" ReadOnly="@ReadOnlyAddress" /> + Label="@LanguageLocale.general_address_state" ReadOnly="@ReadOnlyAddress" /> - + + Label="@LanguageLocale.general_address_zipCode" ReadOnly="@ReadOnlyAddress" /> @@ -155,7 +174,20 @@ private Dictionary? countryDict = null; - private bool isSuportNumberRequired { get => Model?.Nationality == GuestConsts.CountryCode_ESP; } + private bool isSuportNumberRequired + { + get => Model?.DocumentType == GuestConsts.DocumentType_DNI + || Model?.DocumentType == GuestConsts.DocumentType_NIE; + } + private PatternMask SuportNumberPatternMask + { + get => Model?.DocumentType switch + { + GuestConsts.DocumentType_DNI => new PatternMask("aaa000000"), + GuestConsts.DocumentType_NIE => new PatternMask("a00000000"), + _ => new PatternMask("") + }; + } private string CreateTimeString { get @@ -193,7 +225,7 @@ countryDict = new(countryCodeSet.Count); //Init dict. - foreach(string code in dataListService.GetDataListByName(GuestConsts.DataList_CountryCodes)) + foreach (string code in dataListService.GetDataListByName(GuestConsts.DataList_CountryCodes)) { countryDict.Add(code, dataListService.GetLocalizedValue(GuestConsts.DataList_CountryCodes, code)); } @@ -218,7 +250,7 @@ } AddressResultDTO? resultData = await reference.GetReturnValueAsync(); - if (resultData is null) + if (resultData is null) { return; } @@ -237,6 +269,14 @@ ReadOnlyAddress = ReadOnly; } + private async Task BtnSkipAddressSearch() + { + userMessage.ShowMessage("Address search skipped! Address is editable now", MessageKind.Info); + + //Make Address editable (if the form is editable). + ReadOnlyAddress = ReadOnly; + } + #endregion } \ No newline at end of file diff --git a/src/Somnia.GuestLog/Infrastructure/Migrations/20260910093310_GuestDocument_IssueDate_MakeNullable.Designer.cs b/src/Somnia.GuestLog/Infrastructure/Migrations/20260910093310_GuestDocument_IssueDate_MakeNullable.Designer.cs new file mode 100644 index 0000000..f9e41f2 --- /dev/null +++ b/src/Somnia.GuestLog/Infrastructure/Migrations/20260910093310_GuestDocument_IssueDate_MakeNullable.Designer.cs @@ -0,0 +1,252 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Somnia.GuestLog.Infrastructure; + +#nullable disable + +namespace Somnia.GuestLog.Infrastructure.Migrations +{ + [DbContext(typeof(GuestLogContext))] + [Migration("20260910093310_GuestDocument_IssueDate_MakeNullable")] + partial class GuestDocument_IssueDate_MakeNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("GuestDocumentStayEntry", b => + { + b.Property("StayEntryId") + .HasColumnType("bigint"); + + b.Property("GuestDocumentsId") + .HasColumnType("bigint"); + + b.Property("GuestDocumentsVersion") + .HasColumnType("integer"); + + b.HasKey("StayEntryId", "GuestDocumentsId", "GuestDocumentsVersion"); + + b.HasIndex("GuestDocumentsId", "GuestDocumentsVersion"); + + b.ToTable("GuestDocumentStayEntry"); + }); + + modelBuilder.Entity("Somnia.GuestLog.Models.Entities.GuestDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Version") + .HasColumnType("integer"); + + b.Property("BirthDate") + .HasColumnType("date"); + + b.Property("CreateTime") + .HasColumnType("timestamp(3) with time zone"); + + b.Property("DocumentNum") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("DocumentType") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("EmailAddress") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Gender") + .HasMaxLength(1) + .HasColumnType("character varying(1)"); + + b.Property("IsLastVersion") + .HasColumnType("boolean"); + + b.Property("IssueDate") + .HasColumnType("date"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Nationality") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("character varying(3)"); + + b.Property("PhoneNumber") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("SupportNum") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Surname") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Surname2") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UpdateTime") + .HasColumnType("timestamp(3) with time zone"); + + b.ComplexProperty(typeof(Dictionary), "AddressDetails", "Somnia.GuestLog.Models.Entities.GuestDocument.AddressDetails#GuestAddress", b1 => + { + b1.Property("Address") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b1.Property("AddressCity") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b1.Property("AddressCountry") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("character varying(3)"); + + b1.Property("AddressProvince") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b1.Property("AddressState") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b1.Property("ZipCode") + .HasMaxLength(12) + .HasColumnType("character varying(12)"); + }); + + b.HasKey("Id", "Version"); + + b.ToTable("GuestDocument", (string)null); + }); + + modelBuilder.Entity("Somnia.GuestLog.Models.Entities.StayEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("DateIn") + .HasColumnType("timestamp(0) with time zone"); + + b.Property("DateOut") + .HasColumnType("timestamp(0) with time zone"); + + b.Property("PaymentMethod") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("character varying(5)"); + + b.Property("RoomCount") + .HasColumnType("integer"); + + b.Property("RoomNumber") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UpdateTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("StayEntry", (string)null); + }); + + modelBuilder.Entity("Somnia.GuestLog.Models.Entities.StayExportFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ExportDetails") + .HasColumnType("jsonb"); + + b.Property("ExportFormat") + .HasColumnType("integer"); + + b.Property("ExportResult") + .HasColumnType("integer"); + + b.Property("ExportTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExportedPath") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.ComplexProperty(typeof(Dictionary), "ExportParams", "Somnia.GuestLog.Models.Entities.StayExportFile.ExportParams#StayExportParams", b1 => + { + b1.IsRequired(); + + b1.Property("ExportPeriodEnd"); + + b1.Property("ExportPeriodStart"); + + b1.Property("ExportType"); + + b1 + .ToJson("ExportParams") + .HasColumnType("jsonb"); + }); + + b.HasKey("Id"); + + b.ToTable("StayExportFile", (string)null); + }); + + modelBuilder.Entity("GuestDocumentStayEntry", b => + { + b.HasOne("Somnia.GuestLog.Models.Entities.StayEntry", null) + .WithMany() + .HasForeignKey("StayEntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Somnia.GuestLog.Models.Entities.GuestDocument", null) + .WithMany() + .HasForeignKey("GuestDocumentsId", "GuestDocumentsVersion") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Somnia.GuestLog/Infrastructure/Migrations/20260910093310_GuestDocument_IssueDate_MakeNullable.cs b/src/Somnia.GuestLog/Infrastructure/Migrations/20260910093310_GuestDocument_IssueDate_MakeNullable.cs new file mode 100644 index 0000000..69a09c7 --- /dev/null +++ b/src/Somnia.GuestLog/Infrastructure/Migrations/20260910093310_GuestDocument_IssueDate_MakeNullable.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Somnia.GuestLog.Infrastructure.Migrations +{ + /// + public partial class GuestDocument_IssueDate_MakeNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "IssueDate", + table: "GuestDocument", + type: "date", + nullable: true, + oldClrType: typeof(DateOnly), + oldType: "date"); + + //Make null any IssueDate column with "default" value: + migrationBuilder.Sql("""UPDATE "GuestDocument" SET "IssueDate" = NULL WHERE "IssueDate" = '1970-01-01';"""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + //Set value to all IssueDate null columns. + migrationBuilder.Sql("""UPDATE "GuestDocument" SET "IssueDate" = '1970-01-01' WHERE "IssueDate" IS NULL;"""); + + migrationBuilder.AlterColumn( + name: "IssueDate", + table: "GuestDocument", + type: "date", + nullable: false, + oldClrType: typeof(DateOnly), + oldType: "date", + oldNullable: true); + } + } +} diff --git a/src/Somnia.GuestLog/Infrastructure/Migrations/GuestLogContextModelSnapshot.cs b/src/Somnia.GuestLog/Infrastructure/Migrations/GuestLogContextModelSnapshot.cs index f4b45a2..81201db 100644 --- a/src/Somnia.GuestLog/Infrastructure/Migrations/GuestLogContextModelSnapshot.cs +++ b/src/Somnia.GuestLog/Infrastructure/Migrations/GuestLogContextModelSnapshot.cs @@ -82,7 +82,7 @@ namespace Somnia.GuestLog.Infrastructure.Migrations b.Property("IsLastVersion") .HasColumnType("boolean"); - b.Property("IssueDate") + b.Property("IssueDate") .HasColumnType("date"); b.Property("Name") diff --git a/src/Somnia.GuestLog/Models/Entities/GuestDocument.cs b/src/Somnia.GuestLog/Models/Entities/GuestDocument.cs index 4e7776d..88e974f 100644 --- a/src/Somnia.GuestLog/Models/Entities/GuestDocument.cs +++ b/src/Somnia.GuestLog/Models/Entities/GuestDocument.cs @@ -8,7 +8,7 @@ public class GuestDocument : BaseEntity public required string DocumentType { get; set; } public required string DocumentNum { get; set; } - public required DateOnly IssueDate { get; set; } + public required DateOnly? IssueDate { get; set; } public string? SupportNum { get; set; } public required string Nationality { get; set; } diff --git a/src/Somnia.GuestLog/Models/EntityMapping.cs b/src/Somnia.GuestLog/Models/EntityMapping.cs index 4af4e7b..f5284be 100644 --- a/src/Somnia.GuestLog/Models/EntityMapping.cs +++ b/src/Somnia.GuestLog/Models/EntityMapping.cs @@ -14,7 +14,7 @@ internal static class EntityMapping Version = entity.Version, DocumentType = entity.DocumentType, DocumentNum = entity.DocumentNum, - IssueDate = entity.IssueDate.ToDateTime(TimeOnly.MinValue), + IssueDate = entity.IssueDate?.ToDateTime(TimeOnly.MinValue) ?? null, SupportNum = entity.SupportNum, Nationality = entity.Nationality, Name = entity.Name, diff --git a/src/Somnia.GuestLog/Services/Entity/GuestDocumentEntityService.cs b/src/Somnia.GuestLog/Services/Entity/GuestDocumentEntityService.cs index c55f9e3..50eb131 100644 --- a/src/Somnia.GuestLog/Services/Entity/GuestDocumentEntityService.cs +++ b/src/Somnia.GuestLog/Services/Entity/GuestDocumentEntityService.cs @@ -44,7 +44,7 @@ public class GuestDocumentEntityService( Version = GuestConsts.GuestDocument_FirstVersion, DocumentType = dto.DocumentType!, DocumentNum = dto.DocumentNum!, - IssueDate = DateOnly.FromDateTime(dto.IssueDate!.Value), + IssueDate = dto.IssueDate != null ? DateOnly.FromDateTime(dto.IssueDate!.Value) : null, SupportNum = dto.SupportNum, Nationality = dto.Nationality!, Name = dto.Name!, diff --git a/src/Somnia.GuestLog/Utils/RegexUtils.cs b/src/Somnia.GuestLog/Utils/RegexUtils.cs index f3dafd6..94120ae 100644 --- a/src/Somnia.GuestLog/Utils/RegexUtils.cs +++ b/src/Somnia.GuestLog/Utils/RegexUtils.cs @@ -11,7 +11,9 @@ public static partial class RegexUtils public static partial Regex RegexNieFormat(); [GeneratedRegex("^[A-Z]{3}[0-9]{6}$")] - public static partial Regex RegexGuestDocSuportNumber(); + public static partial Regex RegexGuestDocSupportNumberDNI(); + [GeneratedRegex("^E[0-9]{8}$")] + public static partial Regex RegexGuestDocSupportNumberNIE(); [GeneratedRegex(@"^\+?\d{1,4}?[-.\s]?\(?\d{1,3}?\)?[-.\s]?\d{1,4}[-.\s]?\d{1,4}[-.\s]?\d{1,9}$")] public static partial Regex RegexPhoneNumber(); diff --git a/src/Somnia.GuestLog/Validators/GuestDocumentDTOValidator.cs b/src/Somnia.GuestLog/Validators/GuestDocumentDTOValidator.cs index a9491aa..6ef7d45 100644 --- a/src/Somnia.GuestLog/Validators/GuestDocumentDTOValidator.cs +++ b/src/Somnia.GuestLog/Validators/GuestDocumentDTOValidator.cs @@ -22,14 +22,22 @@ public class GuestDocumentDTOValidator : ValueValidator .Length(6, 32); RuleFor(x => x.IssueDate) + .Cascade(CascadeMode.Stop) .NotEmpty() + .When(x => x.Nationality != GuestConsts.CountryCode_ESP) .LessThanOrEqualTo(DateTime.Today); RuleFor(x => x.SupportNum) .Cascade(CascadeMode.Stop) .NotEmpty() - .Matches(RegexUtils.RegexGuestDocSuportNumber()).WithMessage("Invalid Format. Expected 'AAA000000'.") - .When(x => x.Nationality == GuestConsts.CountryCode_ESP); + .Matches(RegexUtils.RegexGuestDocSupportNumberDNI()).WithMessage("Invalid Format for DNI. Expected 'AAA000000'.") + .When(x => x.DocumentType == GuestConsts.DocumentType_DNI); + + RuleFor(x => x.SupportNum) + .Cascade(CascadeMode.Stop) + .NotEmpty() + .Matches(RegexUtils.RegexGuestDocSupportNumberNIE()).WithMessage("Invalid Format for NIE. Expected 'E00000000'.") + .When(x => x.DocumentType == GuestConsts.DocumentType_NIE); RuleFor(x => x.Name) .NotEmpty()