From bb309d7c207df3e51382a8b8c0c11516b8c900dc Mon Sep 17 00:00:00 2001 From: cuqmbr Date: Wed, 28 May 2025 17:55:32 +0300 Subject: [PATCH] add account creation when adding an employee --- .../FluentValidation/CustomValidators.cs | 4 +- .../AddEmployee/AddEmployeeCommand.cs | 7 + .../AddEmployee/AddEmployeeCommandHandler.cs | 55 +- .../AddEmployeeCommandValidator.cs | 42 + .../DeleteEmployeeCommandHandler.cs | 5 +- .../UpdateEmployeeCommandHandler.cs | 40 +- .../Employees/EmployeeAccountDto.cs | 11 + src/Application/Employees/EmployeeDto.cs | 2 + .../GetEmployee/GetEmployeeQueryHandler.cs | 7 +- .../GetEmployeesPageQueryHandler.cs | 12 +- .../ViewModels/AddEmployeeViewModel.cs | 7 + src/Domain/Entities/Account.cs | 3 + src/Domain/Entities/Employee.cs | 5 + .../Controllers/EmployeesController.cs | 5 +- .../Configurations/EmployeeConfiguration.cs | 24 + ...ation_from_Employee_to_Account.Designer.cs | 1313 +++++++++++++++++ ...Add_navigation_from_Employee_to_Account.cs | 58 + .../PostgreSqlDbContextModelSnapshot.cs | 19 + 18 files changed, 1590 insertions(+), 29 deletions(-) create mode 100644 src/Application/Employees/EmployeeAccountDto.cs create mode 100644 src/Persistence/PostgreSql/Migrations/20250528141733_Add_navigation_from_Employee_to_Account.Designer.cs create mode 100644 src/Persistence/PostgreSql/Migrations/20250528141733_Add_navigation_from_Employee_to_Account.cs diff --git a/src/Application/Common/FluentValidation/CustomValidators.cs b/src/Application/Common/FluentValidation/CustomValidators.cs index 0dfbce8..aa7bb49 100644 --- a/src/Application/Common/FluentValidation/CustomValidators.cs +++ b/src/Application/Common/FluentValidation/CustomValidators.cs @@ -9,7 +9,7 @@ public static class CustomValidators { return ruleBuilder - .Matches(@"^[a-z0-9-_.]*$"); + .Matches(@"^[a-z0-9-_\.]*$"); } // According to RFC 5321. @@ -18,7 +18,7 @@ public static class CustomValidators { return ruleBuilder - .Matches(@"^[\w\.-]{1,64}@[\w\.-]{1,251}\.\w{2,4}$"); + .Matches(@"^[a-z0-9-_\.]{1,64}@[a-z0-9-_\.]{1,251}\.[a-z0-9-_]{2,4}$"); } // According to ITU-T E.164, no spaces. diff --git a/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommand.cs b/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommand.cs index 4fe21d7..073c181 100644 --- a/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommand.cs +++ b/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommand.cs @@ -20,4 +20,11 @@ public record AddEmployeeCommand : IRequest public Guid CompanyGuid { get; set; } public ICollection Documents { get; set; } + + + public string Username { get; set; } + + public string Email { get; set; } + + public string Password { get; set; } } diff --git a/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommandHandler.cs b/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommandHandler.cs index adb5fa0..610f601 100644 --- a/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommandHandler.cs +++ b/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommandHandler.cs @@ -4,6 +4,10 @@ using cuqmbr.TravelGuide.Domain.Entities; using AutoMapper; using cuqmbr.TravelGuide.Application.Common.Exceptions; using Microsoft.Extensions.Localization; +using cuqmbr.TravelGuide.Domain.Enums; +using System.Security.Cryptography; +using cuqmbr.TravelGuide.Application.Common.Services; +using System.Text; namespace cuqmbr.TravelGuide.Application.Employees.Commands.AddEmployee; @@ -13,15 +17,15 @@ public class AddEmployeeCommandHandler : private readonly UnitOfWork _unitOfWork; private readonly IMapper _mapper; private readonly IStringLocalizer _localizer; + private readonly PasswordHasherService _passwordHasher; - public AddEmployeeCommandHandler( - UnitOfWork unitOfWork, - IMapper mapper, - IStringLocalizer localizer) + public AddEmployeeCommandHandler(UnitOfWork unitOfWork, IMapper mapper, + IStringLocalizer localizer, PasswordHasherService passwordHasher) { _unitOfWork = unitOfWork; _mapper = mapper; _localizer = localizer; + _passwordHasher = passwordHasher; } public async Task Handle( @@ -52,6 +56,45 @@ public class AddEmployeeCommandHandler : throw new DuplicateEntityException(); } + + // Create new account for employee + + var account = await _unitOfWork.AccountRepository.GetOneAsync( + e => e.Email == request.Email, + cancellationToken); + + if (account != null) + { + throw new DuplicateEntityException(); + } + + + var role = (await _unitOfWork.RoleRepository.GetPageAsync( + 1, IdentityRole.Enumerations.Count(), cancellationToken)) + .Items + .First(r => r.Value.Equals(IdentityRole.CompanyEmployee)); + + var salt = RandomNumberGenerator.GetBytes(128 / 8); + var hash = await _passwordHasher.HashAsync( + Encoding.UTF8.GetBytes(request.Password), + salt, cancellationToken); + + var saltBase64 = Convert.ToBase64String(salt); + var hashBase64 = Convert.ToBase64String(hash); + + account = new Account() + { + Username = request.Username, + Email = request.Email, + PasswordHash = hashBase64, + PasswordSalt = saltBase64, + AccountRoles = new AccountRole[] { new() { RoleId = role.Id } } + }; + + account = await _unitOfWork.AccountRepository.AddOneAsync( + account, cancellationToken); + + entity = new Employee() { FirstName = request.FirstName, @@ -66,12 +109,14 @@ public class AddEmployeeCommandHandler : Information = d.Information }) .ToArray(), - Company = parentEntity + Company = parentEntity, + Account = account }; entity = await _unitOfWork.EmployeeRepository.AddOneAsync( entity, cancellationToken); + await _unitOfWork.SaveAsync(cancellationToken); _unitOfWork.Dispose(); diff --git a/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommandValidator.cs b/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommandValidator.cs index 3a9348e..5e7a87a 100644 --- a/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommandValidator.cs +++ b/src/Application/Employees/Commands/AddEmployee/AddEmployeeCommandValidator.cs @@ -1,3 +1,4 @@ +using cuqmbr.TravelGuide.Application.Common.FluentValidation; using cuqmbr.TravelGuide.Application.Common.Services; using cuqmbr.TravelGuide.Domain.Enums; using FluentValidation; @@ -79,5 +80,46 @@ public class AddEmployeeCommandValidator : AbstractValidator localizer["FluentValidation.MaximumLength"], 256)); }); + + + RuleFor(v => v.Username) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]) + .MinimumLength(1) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.MinimumLength"], + 1)) + .MaximumLength(32) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.MaximumLength"], + 32)) + .IsUsername() + .WithMessage(localizer["FluentValidation.IsUsername"]); + + RuleFor(v => v.Email) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]) + .IsEmail() + .WithMessage(localizer["FluentValidation.IsEmail"]); + + RuleFor(v => v.Password) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]) + .MinimumLength(8) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.MinimumLength"], + 8)) + .MaximumLength(64) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.MaximumLength"], + 64)); } } diff --git a/src/Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandHandler.cs b/src/Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandHandler.cs index 539ebcf..a4f6233 100644 --- a/src/Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandHandler.cs +++ b/src/Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandHandler.cs @@ -18,7 +18,7 @@ public class DeleteEmployeeCommandHandler : IRequestHandler e.Guid == request.Guid, cancellationToken); + e => e.Guid == request.Guid, e => e.Account, cancellationToken); if (entity == null) { @@ -31,6 +31,9 @@ public class DeleteEmployeeCommandHandler : IRequestHandler e.FirstName == request.FirstName && e.LastName == request.LastName && @@ -49,30 +49,34 @@ public class UpdateEmployeeCommandHandler : e.Guid != request.Guid, cancellationToken); - if (entity != null) + if (employee != null) { throw new DuplicateEntityException(); } - entity = await _unitOfWork.EmployeeRepository.GetOneAsync( + employee = await _unitOfWork.EmployeeRepository.GetOneAsync( e => e.Guid == request.Guid, e => e.Documents, cancellationToken); - if (entity == null) + if (employee == null) { throw new NotFoundException(); } + var account = await _unitOfWork.AccountRepository.GetOneAsync( + a => a.Id == employee.AccountId, cancellationToken); - entity.Guid = request.Guid; - entity.FirstName = request.FirstName; - entity.LastName = request.LastName; - entity.Patronymic = request.Patronymic; - entity.Sex = request.Sex; - entity.BirthDate = request.BirthDate; - entity.CompanyId = parentEntity.Id; - entity.Company = parentEntity; + employee.Guid = request.Guid; + employee.FirstName = request.FirstName; + employee.LastName = request.LastName; + employee.Patronymic = request.Patronymic; + employee.Sex = request.Sex; + employee.BirthDate = request.BirthDate; + employee.CompanyId = parentEntity.Id; + + employee.Company = parentEntity; + employee.Account = account; var requestEmployeeDocuments = request.Documents.Select( @@ -82,27 +86,27 @@ public class UpdateEmployeeCommandHandler : Information = d.Information }); - var commonEmployeeDocuments = entity.Documents.IntersectBy( + var commonEmployeeDocuments = employee.Documents.IntersectBy( requestEmployeeDocuments.Select( ed => (ed.DocumentType, ed.Information)), ed => (ed.DocumentType, ed.Information)); var newEmployeeDocuments = requestEmployeeDocuments.ExceptBy( - entity.Documents.Select(ed => (ed.DocumentType, ed.Information)), + employee.Documents.Select(ed => (ed.DocumentType, ed.Information)), ed => (ed.DocumentType, ed.Information)); var combinedEmployeeDocuments = commonEmployeeDocuments.UnionBy( newEmployeeDocuments, ed => (ed.DocumentType, ed.Information)); - entity.Documents = combinedEmployeeDocuments.ToList(); + employee.Documents = combinedEmployeeDocuments.ToList(); - entity = await _unitOfWork.EmployeeRepository.UpdateOneAsync( - entity, cancellationToken); + employee = await _unitOfWork.EmployeeRepository.UpdateOneAsync( + employee, cancellationToken); await _unitOfWork.SaveAsync(cancellationToken); _unitOfWork.Dispose(); - return _mapper.Map(entity); + return _mapper.Map(employee); } } diff --git a/src/Application/Employees/EmployeeAccountDto.cs b/src/Application/Employees/EmployeeAccountDto.cs new file mode 100644 index 0000000..3f603bd --- /dev/null +++ b/src/Application/Employees/EmployeeAccountDto.cs @@ -0,0 +1,11 @@ +using cuqmbr.TravelGuide.Application.Common.Mappings; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Application.Employees; + +public sealed class EmployeeAccountDto : IMapFrom +{ + public string Username { get; set; } + + public string Email { get; set; } +} diff --git a/src/Application/Employees/EmployeeDto.cs b/src/Application/Employees/EmployeeDto.cs index fec500f..ff20575 100644 --- a/src/Application/Employees/EmployeeDto.cs +++ b/src/Application/Employees/EmployeeDto.cs @@ -22,6 +22,8 @@ public sealed class EmployeeDto : IMapFrom public ICollection Documents { get; set; } + public EmployeeAccountDto Account { get; set; } + public void Mapping(MappingProfile profile) { profile.CreateMap() diff --git a/src/Application/Employees/Queries/GetEmployee/GetEmployeeQueryHandler.cs b/src/Application/Employees/Queries/GetEmployee/GetEmployeeQueryHandler.cs index f8f7517..75eba22 100644 --- a/src/Application/Employees/Queries/GetEmployee/GetEmployeeQueryHandler.cs +++ b/src/Application/Employees/Queries/GetEmployee/GetEmployeeQueryHandler.cs @@ -33,13 +33,18 @@ public class GetEmployeeQueryHandler : } - // Hydrate employees with companies + // Hydrate employee var company = await _unitOfWork.CompanyRepository.GetOneAsync( e => e.Id == entity.CompanyId, cancellationToken); entity.Company = company; + var account = await _unitOfWork.AccountRepository.GetOneAsync( + e => e.Id == entity.AccountId, cancellationToken); + + entity.Account = account; + _unitOfWork.Dispose(); diff --git a/src/Application/Employees/Queries/GetEmployeesPage/GetEmployeesPageQueryHandler.cs b/src/Application/Employees/Queries/GetEmployeesPage/GetEmployeesPageQueryHandler.cs index ba18ac0..a74cd58 100644 --- a/src/Application/Employees/Queries/GetEmployeesPage/GetEmployeesPageQueryHandler.cs +++ b/src/Application/Employees/Queries/GetEmployeesPage/GetEmployeesPageQueryHandler.cs @@ -49,7 +49,7 @@ public class GetEmployeesPageQueryHandler : cancellationToken); - // Hydrate employees with companies + // Hydrate employees var companies = await _unitOfWork.CompanyRepository.GetPageAsync( e => paginatedList.Items.Select(e => e.CompanyId).Contains(e.Id), @@ -61,6 +61,16 @@ public class GetEmployeesPageQueryHandler : companies.Items.First(c => c.Id == employee.CompanyId); } + var accounts = await _unitOfWork.AccountRepository.GetPageAsync( + e => paginatedList.Items.Select(e => e.AccountId).Contains(e.Id), + 1, paginatedList.Items.Count, cancellationToken); + + foreach (var employee in paginatedList.Items) + { + employee.Account = + accounts.Items.First(a => a.Id == employee.AccountId); + } + var mappedItems = _mapper .ProjectTo(paginatedList.Items.AsQueryable()); diff --git a/src/Application/Employees/ViewModels/AddEmployeeViewModel.cs b/src/Application/Employees/ViewModels/AddEmployeeViewModel.cs index 97ba828..c058d22 100644 --- a/src/Application/Employees/ViewModels/AddEmployeeViewModel.cs +++ b/src/Application/Employees/ViewModels/AddEmployeeViewModel.cs @@ -16,4 +16,11 @@ public sealed class AddEmployeeViewModel public Guid CompanyUuid { get; set; } public ICollection Documents { get; set; } + + + public string Username { get; set; } + + public string Email { get; set; } + + public string Password { get; set; } } diff --git a/src/Domain/Entities/Account.cs b/src/Domain/Entities/Account.cs index b477df4..a2c574a 100644 --- a/src/Domain/Entities/Account.cs +++ b/src/Domain/Entities/Account.cs @@ -13,4 +13,7 @@ public sealed class Account : EntityBase public ICollection AccountRoles { get; set; } public ICollection RefreshTokens { get; set; } + + + public Employee? Employee { get; set; } } diff --git a/src/Domain/Entities/Employee.cs b/src/Domain/Entities/Employee.cs index 3035cad..d89d2ce 100644 --- a/src/Domain/Entities/Employee.cs +++ b/src/Domain/Entities/Employee.cs @@ -22,4 +22,9 @@ public sealed class Employee : EntityBase public ICollection Documents { get; set; } public ICollection VehicleEnrollmentEmployees { get; set; } + + + public long AccountId { get; set; } + + public Account Account { get; set; } } diff --git a/src/HttpApi/Controllers/EmployeesController.cs b/src/HttpApi/Controllers/EmployeesController.cs index 4df1d5b..2af1e28 100644 --- a/src/HttpApi/Controllers/EmployeesController.cs +++ b/src/HttpApi/Controllers/EmployeesController.cs @@ -59,7 +59,10 @@ public class EmployeesController : ControllerBase Information = e.Information }).ToArray(), - CompanyGuid = viewModel.CompanyUuid + CompanyGuid = viewModel.CompanyUuid, + Username = viewModel.Username, + Email = viewModel.Email, + Password = viewModel.Password }, cancellationToken)); } diff --git a/src/Persistence/PostgreSql/Configurations/EmployeeConfiguration.cs b/src/Persistence/PostgreSql/Configurations/EmployeeConfiguration.cs index fd4ccb5..3aae0e9 100644 --- a/src/Persistence/PostgreSql/Configurations/EmployeeConfiguration.cs +++ b/src/Persistence/PostgreSql/Configurations/EmployeeConfiguration.cs @@ -77,5 +77,29 @@ public class EmployeeConfiguration : BaseConfiguration "ix_" + $"{builder.Metadata.GetTableName()}_" + $"{builder.Property(e => e.CompanyId).Metadata.GetColumnName()}"); + + + builder + .Property(e => e.AccountId) + .HasColumnName("account_id") + .HasColumnType("bigint") + .IsRequired(true); + + builder + .HasOne(e => e.Account) + .WithOne(a => a.Employee) + .HasForeignKey(e => e.AccountId) + .HasConstraintName( + "fk_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(e => e.AccountId).Metadata.GetColumnName()}") + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasIndex(e => e.AccountId) + .HasDatabaseName( + "ix_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(e => e.AccountId).Metadata.GetColumnName()}"); } } diff --git a/src/Persistence/PostgreSql/Migrations/20250528141733_Add_navigation_from_Employee_to_Account.Designer.cs b/src/Persistence/PostgreSql/Migrations/20250528141733_Add_navigation_from_Employee_to_Account.Designer.cs new file mode 100644 index 0000000..bd4a395 --- /dev/null +++ b/src/Persistence/PostgreSql/Migrations/20250528141733_Add_navigation_from_Employee_to_Account.Designer.cs @@ -0,0 +1,1313 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using cuqmbr.TravelGuide.Persistence.PostgreSql; + +#nullable disable + +namespace Persistence.PostgreSql.Migrations +{ + [DbContext(typeof(PostgreSqlDbContext))] + [Migration("20250528141733_Add_navigation_from_Employee_to_Account")] + partial class Add_navigation_from_Employee_to_Account + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("application") + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.HasSequence("account_roles_id_sequence"); + + modelBuilder.HasSequence("accounts_id_sequence"); + + modelBuilder.HasSequence("addresses_id_sequence"); + + modelBuilder.HasSequence("cities_id_sequence"); + + modelBuilder.HasSequence("companies_id_sequence"); + + modelBuilder.HasSequence("countries_id_sequence"); + + modelBuilder.HasSequence("employee_documents_id_sequence"); + + modelBuilder.HasSequence("employees_id_sequence"); + + modelBuilder.HasSequence("refresh_tokens_id_sequence"); + + modelBuilder.HasSequence("regions_id_sequence"); + + modelBuilder.HasSequence("roles_id_sequence"); + + modelBuilder.HasSequence("route_address_details_id_sequence"); + + modelBuilder.HasSequence("route_addresses_id_sequence"); + + modelBuilder.HasSequence("routes_id_sequence"); + + modelBuilder.HasSequence("ticket_groups_id_sequence"); + + modelBuilder.HasSequence("tickets_id_sequence"); + + modelBuilder.HasSequence("vehicle_enrollment_employees_id_sequence"); + + modelBuilder.HasSequence("vehicle_enrollments_id_sequence"); + + modelBuilder.HasSequence("vehicles_id_sequence"); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.accounts_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "accounts_id_sequence"); + + b.Property("Email") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("email"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("varchar(88)") + .HasColumnName("password_hash"); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("varchar(24)") + .HasColumnName("password_salt"); + + b.Property("Username") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("username"); + + b.HasKey("Id") + .HasName("pk_accounts"); + + b.HasAlternateKey("Guid") + .HasName("altk_accounts_uuid"); + + b.ToTable("accounts", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.AccountRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.account_roles_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "account_roles_id_sequence"); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("RoleId") + .HasColumnType("bigint") + .HasColumnName("role_id"); + + b.HasKey("Id") + .HasName("pk_account_roles"); + + b.HasAlternateKey("Guid") + .HasName("altk_account_roles_uuid"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_account_roles_account_id"); + + b.HasIndex("RoleId") + .HasDatabaseName("ix_account_roles_role_id"); + + b.ToTable("account_roles", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.addresses_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "addresses_id_sequence"); + + b.Property("CityId") + .HasColumnType("bigint") + .HasColumnName("city_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(128)") + .HasColumnName("name"); + + b.Property("VehicleType") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("vehicle_type"); + + b.HasKey("Id") + .HasName("pk_addresses"); + + b.HasAlternateKey("Guid") + .HasName("altk_addresses_uuid"); + + b.HasIndex("CityId") + .HasDatabaseName("ix_addresses_city_id"); + + b.ToTable("addresses", "application", t => + { + t.HasCheckConstraint("ck_addresses_vehicle_type", "vehicle_type IN ('bus', 'train', 'aircraft')"); + }); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.City", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.cities_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "cities_id_sequence"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.Property("RegionId") + .HasColumnType("bigint") + .HasColumnName("region_id"); + + b.HasKey("Id") + .HasName("pk_cities"); + + b.HasAlternateKey("Guid") + .HasName("altk_cities_uuid"); + + b.HasIndex("RegionId") + .HasDatabaseName("ix_cities_region_id"); + + b.ToTable("cities", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.companies_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "companies_id_sequence"); + + b.Property("ContactEmail") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("contact_email"); + + b.Property("ContactPhoneNumber") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("contact_phone_number"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("LegalAddress") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("legal_address"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_companies"); + + b.HasAlternateKey("Guid") + .HasName("altk_companies_uuid"); + + b.ToTable("companies", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.countries_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "countries_id_sequence"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_countries"); + + b.HasAlternateKey("Guid") + .HasName("altk_countries_uuid"); + + b.ToTable("countries", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.employees_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "employees_id_sequence"); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("BirthDate") + .HasColumnType("date") + .HasColumnName("birth_date"); + + b.Property("CompanyId") + .HasColumnType("bigint") + .HasColumnName("company_id"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("first_name"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("last_name"); + + b.Property("Patronymic") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("patronymic"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("sex"); + + b.HasKey("Id") + .HasName("pk_employees"); + + b.HasAlternateKey("Guid") + .HasName("altk_employees_uuid"); + + b.HasIndex("AccountId") + .IsUnique() + .HasDatabaseName("ix_employees_account_id"); + + b.HasIndex("CompanyId") + .HasDatabaseName("ix_employees_company_id"); + + b.ToTable("employees", "application", t => + { + t.HasCheckConstraint("ck_employees_sex", "sex IN ('male', 'female')"); + }); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.EmployeeDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.employee_documents_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "employee_documents_id_sequence"); + + b.Property("DocumentType") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("document_type"); + + b.Property("EmployeeId") + .HasColumnType("bigint") + .HasColumnName("employee_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Information") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("information"); + + b.HasKey("Id") + .HasName("pk_employee_documents"); + + b.HasAlternateKey("Guid") + .HasName("altk_employee_documents_uuid"); + + b.HasIndex("EmployeeId") + .HasDatabaseName("ix_employee_documents_employee_id"); + + b.ToTable("employee_documents", "application", t => + { + t.HasCheckConstraint("ck_employee_documents_document_type", "document_type IN ('passport')"); + }); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.refresh_tokens_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "refresh_tokens_id_sequence"); + + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + + b.Property("CreationTime") + .HasColumnType("timestamptz") + .HasColumnName("creation_time"); + + b.Property("ExpirationTime") + .HasColumnType("timestamptz") + .HasColumnName("expiration_time"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("RevocationTime") + .HasColumnType("timestamptz") + .HasColumnName("revocation_time"); + + b.Property("Value") + .IsRequired() + .HasColumnType("varchar(24)") + .HasColumnName("value"); + + b.HasKey("Id") + .HasName("pk_refresh_tokens"); + + b.HasAlternateKey("Guid") + .HasName("altk_refresh_tokens_uuid"); + + b.HasIndex("AccountId") + .HasDatabaseName("ix_refresh_tokens_account_id"); + + b.ToTable("refresh_tokens", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Region", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.regions_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "regions_id_sequence"); + + b.Property("CountryId") + .HasColumnType("bigint") + .HasColumnName("country_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_regions"); + + b.HasAlternateKey("Guid") + .HasName("altk_regions_uuid"); + + b.HasIndex("CountryId") + .HasDatabaseName("ix_regions_country_id"); + + b.ToTable("regions", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.roles_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "roles_id_sequence"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Value") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_roles"); + + b.HasAlternateKey("Guid") + .HasName("altk_roles_uuid"); + + b.ToTable("roles", "application", t => + { + t.HasCheckConstraint("ck_roles_name", "name IN ('administrator', 'user', 'company_owner', 'company_employee')"); + }); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Route", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.routes_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "routes_id_sequence"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.Property("VehicleType") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("vehicle_type"); + + b.HasKey("Id") + .HasName("pk_routes"); + + b.HasAlternateKey("Guid") + .HasName("altk_routes_uuid"); + + b.ToTable("routes", "application", t => + { + t.HasCheckConstraint("ck_routes_vehicle_type", "vehicle_type IN ('bus', 'train', 'aircraft')"); + }); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.route_addresses_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "route_addresses_id_sequence"); + + b.Property("AddressId") + .HasColumnType("bigint") + .HasColumnName("address_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Order") + .HasColumnType("smallint") + .HasColumnName("order"); + + b.Property("RouteId") + .HasColumnType("bigint") + .HasColumnName("route_id"); + + b.HasKey("Id") + .HasName("pk_route_addresses"); + + b.HasAlternateKey("Guid") + .HasName("altk_route_addresses_uuid"); + + b.HasAlternateKey("AddressId", "RouteId", "Order") + .HasName("altk_route_addresses_address_id_route_id_order"); + + b.HasIndex("AddressId") + .HasDatabaseName("ix_route_addresses_address_id"); + + b.HasIndex("RouteId") + .HasDatabaseName("ix_route_addresses_route_id"); + + b.ToTable("route_addresses", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RouteAddressDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.route_address_details_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "route_address_details_id_sequence"); + + b.Property("CostToNextAddress") + .HasColumnType("numeric(24,12)") + .HasColumnName("cost_to_next_address"); + + b.Property("CurrentAddressStopTime") + .HasColumnType("interval") + .HasColumnName("current_address_stop_time"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("RouteAddressId") + .HasColumnType("bigint") + .HasColumnName("route_address_id"); + + b.Property("TimeToNextAddress") + .HasColumnType("interval") + .HasColumnName("time_to_next_address"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("bigint") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id") + .HasName("pk_route_address_details"); + + b.HasAlternateKey("Guid") + .HasName("altk_route_address_details_uuid"); + + b.HasIndex("RouteAddressId") + .HasDatabaseName("ix_route_address_details_route_address_id"); + + b.HasIndex("VehicleEnrollmentId") + .HasDatabaseName("ix_route_address_details_vehicle_enrollment_id"); + + b.ToTable("route_address_details", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.tickets_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "tickets_id_sequence"); + + b.Property("ArrivalRouteAddressId") + .HasColumnType("bigint") + .HasColumnName("arrival_route_address_id"); + + b.Property("Cost") + .HasColumnType("numeric(24,12)") + .HasColumnName("cost"); + + b.Property("Currency") + .IsRequired() + .HasColumnType("varchar(8)") + .HasColumnName("currency"); + + b.Property("DepartureRouteAddressId") + .HasColumnType("bigint") + .HasColumnName("departure_route_address_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Order") + .HasColumnType("smallint") + .HasColumnName("order"); + + b.Property("TicketGroupId") + .HasColumnType("bigint") + .HasColumnName("ticket_group_id"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("bigint") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id") + .HasName("pk_tickets"); + + b.HasAlternateKey("Guid") + .HasName("altk_tickets_uuid"); + + b.HasIndex("ArrivalRouteAddressId"); + + b.HasIndex("DepartureRouteAddressId"); + + b.HasIndex("TicketGroupId") + .HasDatabaseName("ix_tickets_ticket_group_id"); + + b.HasIndex("VehicleEnrollmentId") + .HasDatabaseName("ix_tickets_vehicle_enrollment_id"); + + b.ToTable("tickets", "application", t => + { + t.HasCheckConstraint("ck_tickets_currency", "currency IN ('DEFAULT', 'USD', 'EUR', 'UAH')"); + }); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.TicketGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.ticket_groups_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "ticket_groups_id_sequence"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("PassangerBirthDate") + .HasColumnType("date") + .HasColumnName("passanger_birth_date"); + + b.Property("PassangerFirstName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passanger_first_name"); + + b.Property("PassangerLastName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passanger_last_name"); + + b.Property("PassangerPatronymic") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passanger_patronymic"); + + b.Property("PassangerSex") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passanger_sex"); + + b.Property("PurchaseTime") + .HasColumnType("timestamptz") + .HasColumnName("purchase_time"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("status"); + + b.Property("TravelTime") + .HasColumnType("interval") + .HasColumnName("travel_time"); + + b.HasKey("Id") + .HasName("pk_ticket_groups"); + + b.HasAlternateKey("Guid") + .HasName("altk_ticket_groups_uuid"); + + b.ToTable("ticket_groups", "application", t => + { + t.HasCheckConstraint("ck_ticket_groups_passanger_sex", "passanger_sex IN ('male', 'female')"); + + t.HasCheckConstraint("ck_ticket_groups_status", "status IN ('reserved', 'returned', 'purchased')"); + }); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.vehicles_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "vehicles_id_sequence"); + + b.Property("CompanyId") + .HasColumnType("bigint") + .HasColumnName("company_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("VehicleType") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("vehicle_type"); + + b.HasKey("Id") + .HasName("pk_vehicles"); + + b.HasAlternateKey("Guid") + .HasName("altk_vehicles_uuid"); + + b.HasIndex("CompanyId") + .HasDatabaseName("ix_vehicles_company_id"); + + b.ToTable("vehicles", "application", t => + { + t.HasCheckConstraint("ck_vehicles_vehicle_type", "vehicle_type IN ('bus', 'train', 'aircraft')"); + }); + + b.HasDiscriminator("VehicleType"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.VehicleEnrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.vehicle_enrollments_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "vehicle_enrollments_id_sequence"); + + b.Property("Currency") + .IsRequired() + .HasColumnType("varchar(8)") + .HasColumnName("currency"); + + b.Property("DepartureTime") + .HasColumnType("timestamptz") + .HasColumnName("departure_time"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("RouteId") + .HasColumnType("bigint") + .HasColumnName("route_id"); + + b.Property("VehicleId") + .HasColumnType("bigint") + .HasColumnName("vehicle_id"); + + b.HasKey("Id") + .HasName("pk_vehicle_enrollments"); + + b.HasAlternateKey("Guid") + .HasName("altk_vehicle_enrollments_uuid"); + + b.HasIndex("RouteId") + .HasDatabaseName("ix_vehicle_enrollments_route_id"); + + b.HasIndex("VehicleId") + .HasDatabaseName("ix_vehicle_enrollments_vehicle_id"); + + b.ToTable("vehicle_enrollments", "application", t => + { + t.HasCheckConstraint("ck_vehicle_enrollments_currency", "currency IN ('DEFAULT', 'USD', 'EUR', 'UAH')"); + }); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.VehicleEnrollmentEmployee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.vehicle_enrollment_employees_id_sequence')"); + + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "vehicle_enrollment_employees_id_sequence"); + + b.Property("EmployeeId") + .HasColumnType("bigint") + .HasColumnName("employee_id"); + + b.Property("Guid") + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("bigint") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id") + .HasName("pk_vehicle_enrollment_employees"); + + b.HasAlternateKey("Guid") + .HasName("altk_vehicle_enrollment_employees_uuid"); + + b.HasIndex("EmployeeId") + .HasDatabaseName("ix_vehicle_enrollment_employees_employee_id"); + + b.HasIndex("VehicleEnrollmentId") + .HasDatabaseName("ix_vehicle_enrollment_employees_vehicle_enrollment_id"); + + b.ToTable("vehicle_enrollment_employees", "application"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Aircraft", b => + { + b.HasBaseType("cuqmbr.TravelGuide.Domain.Entities.Vehicle"); + + b.Property("Capacity") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("smallint") + .HasColumnName("capacity"); + + b.Property("Model") + .IsRequired() + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("varchar(64)") + .HasColumnName("model"); + + b.Property("Number") + .IsRequired() + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("varchar(32)") + .HasColumnName("number"); + + b.ToTable(t => + { + t.HasCheckConstraint("ck_vehicles_vehicle_type", "vehicle_type IN ('bus', 'train', 'aircraft')"); + }); + + b.HasDiscriminator().HasValue("aircraft"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Bus", b => + { + b.HasBaseType("cuqmbr.TravelGuide.Domain.Entities.Vehicle"); + + b.Property("Capacity") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("smallint") + .HasColumnName("capacity"); + + b.Property("Model") + .IsRequired() + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("varchar(64)") + .HasColumnName("model"); + + b.Property("Number") + .IsRequired() + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("varchar(32)") + .HasColumnName("number"); + + b.ToTable(t => + { + t.HasCheckConstraint("ck_vehicles_vehicle_type", "vehicle_type IN ('bus', 'train', 'aircraft')"); + }); + + b.HasDiscriminator().HasValue("bus"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Train", b => + { + b.HasBaseType("cuqmbr.TravelGuide.Domain.Entities.Vehicle"); + + b.Property("Capacity") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("smallint") + .HasColumnName("capacity"); + + b.Property("Model") + .IsRequired() + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("varchar(64)") + .HasColumnName("model"); + + b.Property("Number") + .IsRequired() + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("varchar(32)") + .HasColumnName("number"); + + b.ToTable(t => + { + t.HasCheckConstraint("ck_vehicles_vehicle_type", "vehicle_type IN ('bus', 'train', 'aircraft')"); + }); + + b.HasDiscriminator().HasValue("train"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.AccountRole", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Account", "Account") + .WithMany("AccountRoles") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_roles_account_id"); + + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Role", "Role") + .WithMany("AccountRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_account_roles_role_id"); + + b.Navigation("Account"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Address", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.City", "City") + .WithMany("Addresses") + .HasForeignKey("CityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_addresses_city_id"); + + b.Navigation("City"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.City", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Region", "Region") + .WithMany("Cities") + .HasForeignKey("RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_cities_region_id"); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Employee", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Account", "Account") + .WithOne("Employee") + .HasForeignKey("cuqmbr.TravelGuide.Domain.Entities.Employee", "AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_employees_account_id"); + + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Company", "Company") + .WithMany("Employees") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_employees_company_id"); + + b.Navigation("Account"); + + b.Navigation("Company"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Employee", "Employee") + .WithMany("Documents") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_employee_documents_employee_id"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RefreshToken", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Account", "Account") + .WithMany("RefreshTokens") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_refresh_tokens_account_id"); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Region", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Country", "Country") + .WithMany("Regions") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_regions_country_id"); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Address", "Address") + .WithMany("AddressRoutes") + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_route_addresses_address_id"); + + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Route", "Route") + .WithMany("RouteAddresses") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_route_addresses_route_id"); + + b.Navigation("Address"); + + b.Navigation("Route"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RouteAddressDetail", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", "RouteAddress") + .WithMany("Details") + .HasForeignKey("RouteAddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_route_address_details_route_address_id"); + + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("RouteAddressDetails") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_route_address_details_vehicle_enrollment_id"); + + b.Navigation("RouteAddress"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Ticket", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", "ArrivalRouteAddress") + .WithMany() + .HasForeignKey("ArrivalRouteAddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", "DepartureRouteAddress") + .WithMany() + .HasForeignKey("DepartureRouteAddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.TicketGroup", "TicketGroup") + .WithMany("Tickets") + .HasForeignKey("TicketGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tickets_ticket_group_id"); + + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Tickets") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tickets_vehicle_enrollment_id"); + + b.Navigation("ArrivalRouteAddress"); + + b.Navigation("DepartureRouteAddress"); + + b.Navigation("TicketGroup"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Vehicle", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Company", "Company") + .WithMany("Vehicles") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicles_company_id"); + + b.Navigation("Company"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.VehicleEnrollment", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Route", "Route") + .WithMany("VehicleEnrollments") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicle_enrollments_route_id"); + + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Vehicle", "Vehicle") + .WithMany("Enrollments") + .HasForeignKey("VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicle_enrollments_vehicle_id"); + + b.Navigation("Route"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.VehicleEnrollmentEmployee", b => + { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Employee", "Employee") + .WithMany("VehicleEnrollmentEmployees") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicle_enrollment_employees_employee_id"); + + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("VehicleEnrollmentEmployees") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicle_enrollment_employees_vehicle_enrollment_id"); + + b.Navigation("Employee"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Account", b => + { + b.Navigation("AccountRoles"); + + b.Navigation("Employee"); + + b.Navigation("RefreshTokens"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Address", b => + { + b.Navigation("AddressRoutes"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.City", b => + { + b.Navigation("Addresses"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Company", b => + { + b.Navigation("Employees"); + + b.Navigation("Vehicles"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Country", b => + { + b.Navigation("Regions"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Employee", b => + { + b.Navigation("Documents"); + + b.Navigation("VehicleEnrollmentEmployees"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Region", b => + { + b.Navigation("Cities"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Role", b => + { + b.Navigation("AccountRoles"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Route", b => + { + b.Navigation("RouteAddresses"); + + b.Navigation("VehicleEnrollments"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", b => + { + b.Navigation("Details"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.TicketGroup", b => + { + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Vehicle", b => + { + b.Navigation("Enrollments"); + }); + + modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.VehicleEnrollment", b => + { + b.Navigation("RouteAddressDetails"); + + b.Navigation("Tickets"); + + b.Navigation("VehicleEnrollmentEmployees"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Persistence/PostgreSql/Migrations/20250528141733_Add_navigation_from_Employee_to_Account.cs b/src/Persistence/PostgreSql/Migrations/20250528141733_Add_navigation_from_Employee_to_Account.cs new file mode 100644 index 0000000..56f22dd --- /dev/null +++ b/src/Persistence/PostgreSql/Migrations/20250528141733_Add_navigation_from_Employee_to_Account.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistence.PostgreSql.Migrations +{ + /// + public partial class Add_navigation_from_Employee_to_Account : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "account_id", + schema: "application", + table: "employees", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.CreateIndex( + name: "ix_employees_account_id", + schema: "application", + table: "employees", + column: "account_id", + unique: true); + + migrationBuilder.AddForeignKey( + name: "fk_employees_account_id", + schema: "application", + table: "employees", + column: "account_id", + principalSchema: "application", + principalTable: "accounts", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "fk_employees_account_id", + schema: "application", + table: "employees"); + + migrationBuilder.DropIndex( + name: "ix_employees_account_id", + schema: "application", + table: "employees"); + + migrationBuilder.DropColumn( + name: "account_id", + schema: "application", + table: "employees"); + } + } +} diff --git a/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs b/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs index ba23151..f9e33c5 100644 --- a/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs +++ b/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs @@ -306,6 +306,10 @@ namespace Persistence.PostgreSql.Migrations NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "employees_id_sequence"); + b.Property("AccountId") + .HasColumnType("bigint") + .HasColumnName("account_id"); + b.Property("BirthDate") .HasColumnType("date") .HasColumnName("birth_date"); @@ -344,6 +348,10 @@ namespace Persistence.PostgreSql.Migrations b.HasAlternateKey("Guid") .HasName("altk_employees_uuid"); + b.HasIndex("AccountId") + .IsUnique() + .HasDatabaseName("ix_employees_account_id"); + b.HasIndex("CompanyId") .HasDatabaseName("ix_employees_company_id"); @@ -1030,6 +1038,13 @@ namespace Persistence.PostgreSql.Migrations modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Employee", b => { + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Account", "Account") + .WithOne("Employee") + .HasForeignKey("cuqmbr.TravelGuide.Domain.Entities.Employee", "AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_employees_account_id"); + b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Company", "Company") .WithMany("Employees") .HasForeignKey("CompanyId") @@ -1037,6 +1052,8 @@ namespace Persistence.PostgreSql.Migrations .IsRequired() .HasConstraintName("fk_employees_company_id"); + b.Navigation("Account"); + b.Navigation("Company"); }); @@ -1213,6 +1230,8 @@ namespace Persistence.PostgreSql.Migrations { b.Navigation("AccountRoles"); + b.Navigation("Employee"); + b.Navigation("RefreshTokens"); });