From 4ae17c5a91403cecf72dfaa218a0eb0abce7db7e Mon Sep 17 00:00:00 2001 From: cuqmbr Date: Tue, 27 May 2025 18:10:53 +0300 Subject: [PATCH] add vehicle enrollment employee management --- .../VehicleEnrollmentEmployeeRepository.cs | 7 + .../Interfaces/Persistence/UnitOfWork.cs | 2 + .../AddVehicleEnrollmentCommand.cs | 2 + .../AddVehicleEnrollmentCommandHandler.cs | 22 + .../AddVehicleEnrollmentCommandValidator.cs | 67 +- .../UpdateVehicleEnrollmentCommand.cs | 2 + .../UpdateVehicleEnrollmentCommandHandler.cs | 48 + ...UpdateVehicleEnrollmentCommandValidator.cs | 68 +- .../GetVehicleEnrollmentQueryHandler.cs | 30 +- .../GetVehicleEnrollmentsPageQuery.cs | 2 + .../GetVehicleEnrollmentsPageQueryHandler.cs | 251 ++-- .../VehicleEnrollmentDto.cs | 21 +- .../VehicleEnrollmentVehicleDto.cs | 94 ++ .../AddVehicleEnrollmentViewModel.cs | 2 + ...etVehicleEnrollmentsPageFilterViewModel.cs | 2 + .../UpdateVehicleEnrollmentViewModel.cs | 2 + src/Domain/Entities/Employee.cs | 2 + src/Domain/Entities/VehicleEnrollment.cs | 3 +- .../Entities/VehicleEnrollmentEmployee.cs | 13 + .../VehicleEnrollmentsController.cs | 9 +- .../InMemory/InMemoryUnitOfWork.cs | 4 + ...moryVehicleEnrollmentEmployeeRepository.cs | 13 + .../VehicleEnrollmentEmployeeConfiguration.cs | 70 ++ ...dd_Vehicle_Enrollment_Employee.Designer.cs | 1083 +++++++++++++++++ ...7113429_Add_Vehicle_Enrollment_Employee.cs | 73 ++ .../PostgreSqlDbContextModelSnapshot.cs | 64 + .../PostgreSql/PostgreSqlUnitOfWork.cs | 4 + ...eSqlVehicleEnrollmentEmployeeRepository.cs | 13 + 28 files changed, 1829 insertions(+), 144 deletions(-) create mode 100644 src/Application/Common/Interfaces/Persistence/Repositories/VehicleEnrollmentEmployeeRepository.cs create mode 100644 src/Application/VehicleEnrollments/VehicleEnrollmentVehicleDto.cs create mode 100644 src/Domain/Entities/VehicleEnrollmentEmployee.cs create mode 100644 src/Persistence/InMemory/Repositories/InMemoryVehicleEnrollmentEmployeeRepository.cs create mode 100644 src/Persistence/PostgreSql/Configurations/VehicleEnrollmentEmployeeConfiguration.cs create mode 100644 src/Persistence/PostgreSql/Migrations/20250527113429_Add_Vehicle_Enrollment_Employee.Designer.cs create mode 100644 src/Persistence/PostgreSql/Migrations/20250527113429_Add_Vehicle_Enrollment_Employee.cs create mode 100644 src/Persistence/PostgreSql/Repositories/PostgreSqlVehicleEnrollmentEmployeeRepository.cs diff --git a/src/Application/Common/Interfaces/Persistence/Repositories/VehicleEnrollmentEmployeeRepository.cs b/src/Application/Common/Interfaces/Persistence/Repositories/VehicleEnrollmentEmployeeRepository.cs new file mode 100644 index 0000000..6c396d2 --- /dev/null +++ b/src/Application/Common/Interfaces/Persistence/Repositories/VehicleEnrollmentEmployeeRepository.cs @@ -0,0 +1,7 @@ +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Application.Common.Interfaces + .Persistence.Repositories; + +public interface VehicleEnrollmentEmployeeRepository : + BaseRepository { } diff --git a/src/Application/Common/Interfaces/Persistence/UnitOfWork.cs b/src/Application/Common/Interfaces/Persistence/UnitOfWork.cs index de5c4ee..262822a 100644 --- a/src/Application/Common/Interfaces/Persistence/UnitOfWork.cs +++ b/src/Application/Common/Interfaces/Persistence/UnitOfWork.cs @@ -36,6 +36,8 @@ public interface UnitOfWork : IDisposable RouteAddressDetailRepository RouteAddressDetailRepository { get; } + VehicleEnrollmentEmployeeRepository VehicleEnrollmentEmployeeRepository { get; } + int Save(); Task SaveAsync(CancellationToken cancellationToken); diff --git a/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommand.cs b/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommand.cs index 8f5c49e..72ff69b 100644 --- a/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommand.cs +++ b/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommand.cs @@ -17,4 +17,6 @@ public record AddVehicleEnrollmentCommand : IRequest public Guid RouteGuid { get; set; } public ICollection RouteAddressDetails { get; set; } + + public ICollection EmployeeGuids { get; set; } } diff --git a/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommandHandler.cs b/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommandHandler.cs index 9688d98..b5d8d9c 100644 --- a/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommandHandler.cs +++ b/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommandHandler.cs @@ -158,6 +158,20 @@ public class AddVehicleEnrollmentCommandHandler : } + // Check if employee guids are valid + + var employees = (await _unitOfWork.EmployeeRepository + .GetPageAsync( + e => request.EmployeeGuids.Contains(e.Guid), + 1, request.EmployeeGuids.Count, cancellationToken)) + .Items; + + if (employees.Count < request.EmployeeGuids.Count) + { + throw new NotFoundException(); + } + + // Create entity and add to datastore. var entity = new VehicleEnrollment() @@ -166,6 +180,8 @@ public class AddVehicleEnrollmentCommandHandler : Currency = request.Currency, VehicleId = vehicle.Id, RouteId = route.Id, + Route = route, + Vehicle = vehicle, RouteAddressDetails = route.RouteAddresses .OrderBy(ra => ra.Order) .Select(ra => new RouteAddressDetail() @@ -181,6 +197,12 @@ public class AddVehicleEnrollmentCommandHandler : .CurrentAddressStopTime, RouteAddressId = ra.Id }) + .ToArray(), + VehicleEnrollmentEmployees = request.EmployeeGuids.Select(g => + new VehicleEnrollmentEmployee() + { + EmployeeId = employees.Single(e => e.Guid == g).Id + }) .ToArray() }; diff --git a/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommandValidator.cs b/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommandValidator.cs index c89f266..1df07c1 100644 --- a/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommandValidator.cs +++ b/src/Application/VehicleEnrollments/Commands/AddVehicleEnrollment/AddVehicleEnrollmentCommandValidator.cs @@ -1,3 +1,4 @@ +using cuqmbr.TravelGuide.Application.Common.FluentValidation; using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; using cuqmbr.TravelGuide.Domain.Enums; using FluentValidation; @@ -17,12 +18,12 @@ public class AddVehicleEnrollmentCommandValidator : RuleFor(v => v.DepartureTime) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]) - .Must(dt => dt >= DateTimeOffset.Now) - .WithMessage( - String.Format( - cultureService.Culture, - localizer["FluentValidation.DateTimeOffset.GreaterThanOrEqualTo"], - DateTimeOffset.Now.ToOffset(timeZoneService.TimeZone.BaseUtcOffset))); + .GreaterThanOrEqualTo(DateTimeOffset.Now) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + DateTimeOffset.Now.ToOffset(timeZoneService.TimeZone.BaseUtcOffset))); RuleFor(v => v.Currency) .Must(c => Currency.Enumerations.ContainsValue(c)) @@ -41,16 +42,36 @@ public class AddVehicleEnrollmentCommandValidator : localizer["FluentValidation.GreaterThanOrEqualTo"], 2)); - RuleFor(v => v.RouteAddressDetails) - .Must(v => v.All(rad => rad.RouteAddressGuid != Guid.Empty)) - .WithMessage(localizer["FluentValidation.NotEmpty"]) - .Must(v => v.All(rad => rad.TimeToNextAddress >= TimeSpan.Zero)) - .WithMessage(localizer["VehicleEnrollments.NegativeTime"]) - .Must(v => v.All(rad => rad.CostToNextAddress >= 0)) - .WithMessage(localizer["VehicleEnrollments.NegativeCost"]) - .Must(v => v.All(rad => rad.CurrentAddressStopTime >= TimeSpan.Zero)) - .WithMessage(localizer["VehicleEnrollments.NegativeTime"]); + RuleForEach(v => v.RouteAddressDetails).ChildRules(rad => + { + rad.RuleFor(rad => rad.RouteAddressGuid) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); + rad.RuleFor(rad => rad.TimeToNextAddress) + .GreaterThanOrEqualTo(TimeSpan.Zero) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + TimeSpan.Zero)); + + rad.RuleFor(rad => rad.CostToNextAddress) + .GreaterThanOrEqualTo(0) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + 0)); + + rad.RuleFor(rad => rad.CurrentAddressStopTime) + .GreaterThanOrEqualTo(TimeSpan.Zero) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + TimeSpan.Zero)); + }); RuleFor(v => v.VehicleGuid) .NotEmpty() @@ -59,5 +80,21 @@ public class AddVehicleEnrollmentCommandValidator : RuleFor(v => v.RouteGuid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); + + RuleFor(v => v.EmployeeGuids.Count) + .GreaterThanOrEqualTo(1) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + 1)); + + RuleFor(v => v.EmployeeGuids) + .IsUnique(g => g) + .WithMessage(localizer["FluentValidation.IsUnique"]); + + RuleForEach(v => v.EmployeeGuids) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); } } diff --git a/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommand.cs b/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommand.cs index 918eda7..3fff097 100644 --- a/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommand.cs +++ b/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommand.cs @@ -15,4 +15,6 @@ public record UpdateVehicleEnrollmentCommand : IRequest public ICollection RouteAddressDetails { get; set; } + + public ICollection EmployeeGuids { get; set; } } diff --git a/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandHandler.cs b/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandHandler.cs index d562c45..fb3aed7 100644 --- a/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandHandler.cs +++ b/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandHandler.cs @@ -136,8 +136,56 @@ public class UpdateVehicleEnrollmentCommandHandler : } + // Check if employee guids are valid + + var employees = (await _unitOfWork.EmployeeRepository + .GetPageAsync( + e => request.EmployeeGuids.Contains(e.Guid), + 1, request.EmployeeGuids.Count, cancellationToken)) + .Items; + + if (employees.Count < request.EmployeeGuids.Count) + { + throw new NotFoundException(); + } + + + // Get vehicle and hydrate vehicle enrollment + + var vehicle = await _unitOfWork.VehicleRepository + .GetOneAsync(e => e.Id == entity.VehicleId, cancellationToken); + entity.Vehicle = vehicle; + // Update entity and add to datastore. + var requestEmployeeGuids = request.EmployeeGuids; + var datastoreEmployeeGuids = (await _unitOfWork + .VehicleEnrollmentEmployeeRepository.GetPageAsync( + e => e.VehicleEnrollmentId == entity.Id, + e => e.Employee, + 1, int.MaxValue, cancellationToken)) + .Items + .Select(e => e.Employee.Guid); + + var commonEmployeeGuids = datastoreEmployeeGuids + .Intersect(requestEmployeeGuids); + + var newEmployeeGuids = requestEmployeeGuids + .Except(datastoreEmployeeGuids); + + var combinedEmployeeGuids = commonEmployeeGuids + .Union(newEmployeeGuids); + + entity.VehicleEnrollmentEmployees = combinedEmployeeGuids + .Select(g => + new VehicleEnrollmentEmployee() + { + EmployeeId = employees.Single(e => e.Guid == g).Id, + VehicleEnrollmentId = entity.Id + }) + .ToList(); + + entity.DepartureTime = request.DepartureTime; entity.Currency = request.Currency; diff --git a/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandValidator.cs b/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandValidator.cs index 1cd8966..2530e8c 100644 --- a/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandValidator.cs +++ b/src/Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandValidator.cs @@ -1,3 +1,4 @@ +using cuqmbr.TravelGuide.Application.Common.FluentValidation; using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; using cuqmbr.TravelGuide.Domain.Enums; using FluentValidation; @@ -21,12 +22,12 @@ public class UpdateVehicleEnrollmentCommandValidator : RuleFor(v => v.DepartureTime) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]) - .Must(dt => dt >= DateTimeOffset.Now) - .WithMessage( - String.Format( - cultureService.Culture, - localizer["FluentValidation.DateTimeOffset.GreaterThanOrEqualTo"], - DateTimeOffset.Now.ToOffset(timeZoneService.TimeZone.BaseUtcOffset))); + .GreaterThanOrEqualTo(DateTimeOffset.Now) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + DateTimeOffset.Now.ToOffset(timeZoneService.TimeZone.BaseUtcOffset))); RuleFor(v => v.Currency) .Must(c => Currency.Enumerations.ContainsValue(c)) @@ -45,14 +46,51 @@ public class UpdateVehicleEnrollmentCommandValidator : localizer["FluentValidation.GreaterThanOrEqualTo"], 2)); - RuleFor(v => v.RouteAddressDetails) - .Must(v => v.All(rad => rad.RouteAddressGuid != Guid.Empty)) - .WithMessage(localizer["FluentValidation.NotEmpty"]) - .Must(v => v.All(rad => rad.TimeToNextAddress >= TimeSpan.Zero)) - .WithMessage(localizer["VehicleEnrollments.NegativeTime"]) - .Must(v => v.All(rad => rad.CostToNextAddress >= 0)) - .WithMessage(localizer["VehicleEnrollments.NegativeCost"]) - .Must(v => v.All(rad => rad.CurrentAddressStopTime >= TimeSpan.Zero)) - .WithMessage(localizer["VehicleEnrollments.NegativeTime"]); + RuleForEach(v => v.RouteAddressDetails).ChildRules(rad => + { + rad.RuleFor(rad => rad.RouteAddressGuid) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); + + rad.RuleFor(rad => rad.TimeToNextAddress) + .GreaterThanOrEqualTo(TimeSpan.Zero) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + TimeSpan.Zero)); + + rad.RuleFor(rad => rad.CostToNextAddress) + .GreaterThanOrEqualTo(0) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + 0)); + + rad.RuleFor(rad => rad.CurrentAddressStopTime) + .GreaterThanOrEqualTo(TimeSpan.Zero) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + TimeSpan.Zero)); + }); + + RuleFor(v => v.EmployeeGuids.Count) + .GreaterThanOrEqualTo(1) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + 1)); + + RuleFor(v => v.EmployeeGuids) + .IsUnique(g => g) + .WithMessage(localizer["FluentValidation.IsUnique"]); + + RuleForEach(v => v.EmployeeGuids) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); } } diff --git a/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryHandler.cs b/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryHandler.cs index 5cb3804..3b1ad69 100644 --- a/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryHandler.cs +++ b/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryHandler.cs @@ -42,24 +42,44 @@ public class GetVehicleEnrollmentQueryHandler : throw new NotFoundException(); } - // Hydrate vehicle enrollment with address information + // Hydrate vehicle enrollment with address, + // vehicle, route and employee information. - var routeAddresses = await _unitOfWork.RouteAddressRepository + var routeAddresses = (await _unitOfWork.RouteAddressRepository .GetPageAsync( e => entity.RouteAddressDetails .Select(rad => rad.RouteAddressId) .Contains(e.Id), e => e.Address.City.Region.Country, - 1, entity.RouteAddressDetails.Count(), - cancellationToken); + 1, entity.RouteAddressDetails.Count(), cancellationToken)) + .Items; foreach (var rad in entity.RouteAddressDetails) { - rad.RouteAddress = routeAddresses.Items + rad.RouteAddress = routeAddresses .First(ra => ra.Id == rad.RouteAddressId); } + var vehicle = await _unitOfWork.VehicleRepository + .GetOneAsync(e => e.Id == entity.VehicleId, cancellationToken); + + var route = await _unitOfWork.RouteRepository + .GetOneAsync(e => e.Id == entity.RouteId, cancellationToken); + + var vehicleEnrollmentEmployees = + (await _unitOfWork.VehicleEnrollmentEmployeeRepository + .GetPageAsync( + e => e.VehicleEnrollmentId == entity.Id, + e => e.Employee, + 1, int.MaxValue, cancellationToken)) + .Items; + + entity.Vehicle = vehicle; + entity.Route = route; + entity.VehicleEnrollmentEmployees = + vehicleEnrollmentEmployees.ToArray(); + // Convert currency diff --git a/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsPage/GetVehicleEnrollmentsPageQuery.cs b/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsPage/GetVehicleEnrollmentsPageQuery.cs index a2c07bb..1253cd9 100644 --- a/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsPage/GetVehicleEnrollmentsPageQuery.cs +++ b/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsPage/GetVehicleEnrollmentsPageQuery.cs @@ -49,4 +49,6 @@ public record GetVehicleEnrollmentsPageQuery : public decimal? CostLessThanOrEqual { get; set; } public Currency? Currency { get; set; } + + public ICollection? EmployeeGuids { get; set; } } diff --git a/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsPage/GetVehicleEnrollmentsPageQueryHandler.cs b/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsPage/GetVehicleEnrollmentsPageQueryHandler.cs index 114aa9d..70cfc40 100644 --- a/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsPage/GetVehicleEnrollmentsPageQueryHandler.cs +++ b/src/Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsPage/GetVehicleEnrollmentsPageQueryHandler.cs @@ -35,120 +35,162 @@ public class GetVehicleEnrollmentsPageQueryHandler : CancellationToken cancellationToken) { // TODO: Add search functionality or remove it - var paginatedList = await _unitOfWork.VehicleEnrollmentRepository.GetPageAsync( - e => - // (e.Name.ToLower().Contains(request.Search.ToLower()) || - // e.City.Region.Country.Name.ToLower().Contains(request.Search.ToLower())) && - (request.RouteGuid != null - ? e.Route.Guid == request.RouteGuid - : true) && - (request.VehicleGuid != null - ? e.Vehicle.Guid >= request.VehicleGuid - : true) && - (request.NumberOfAddressesGreaterThanOrEqual != null - ? - e.RouteAddressDetails.Count() >= - request.NumberOfAddressesGreaterThanOrEqual - : true) && - (request.NumberOfAddressesLessThanOrEqual != null - ? - e.RouteAddressDetails.Count() <= - request.NumberOfAddressesLessThanOrEqual - : true) && - (request.DepartureTimeGreaterThanOrEqual != null - ? e.DepartureTime >= request.DepartureTimeGreaterThanOrEqual - : true) && - (request.DepartureTimeLessThanOrEqual != null - ? e.DepartureTime <= request.DepartureTimeLessThanOrEqual - : true) && - (request.ArrivalTimeGreaterThanOrEqual != null - ? - e.DepartureTime.AddSeconds(e.RouteAddressDetails - .Sum(rad => rad.TimeToNextAddress.TotalSeconds + - rad.CurrentAddressStopTime.TotalSeconds)) >= - request.ArrivalTimeGreaterThanOrEqual - : true) && - (request.ArrivalTimeLessThanOrEqual != null - ? - e.DepartureTime.AddSeconds(e.RouteAddressDetails - .Sum(rad => rad.TimeToNextAddress.TotalSeconds + - rad.CurrentAddressStopTime.TotalSeconds)) <= - request.ArrivalTimeLessThanOrEqual - : true) && - (request.TravelTimeGreaterThanOrEqual != null - ? - e.RouteAddressDetails - .Sum(rad => rad.TimeToNextAddress.TotalSeconds) >= - request.TravelTimeGreaterThanOrEqual.Value.TotalSeconds - : true) && - (request.TravelTimeLessThanOrEqual != null - ? - e.RouteAddressDetails - .Sum(rad => rad.TimeToNextAddress.TotalSeconds) <= - request.TravelTimeLessThanOrEqual.Value.TotalSeconds - : true) && - (request.TimeMovingGreaterThanOrEqual != null - ? - e.RouteAddressDetails - .Sum(rad => rad.TimeToNextAddress.TotalSeconds) >= - request.TimeMovingGreaterThanOrEqual.Value.TotalSeconds - : true) && - (request.TimeMovingLessThanOrEqual != null - ? - e.RouteAddressDetails - .Sum(rad => rad.TimeToNextAddress.TotalSeconds) <= - request.TimeMovingLessThanOrEqual.Value.TotalSeconds - : true) && - (request.TimeInStopsGreaterThanOrEqual != null - ? - e.RouteAddressDetails - .Sum(rad => rad.CurrentAddressStopTime.TotalSeconds) >= - request.TimeInStopsGreaterThanOrEqual.Value.TotalSeconds - : true) && - (request.TimeInStopsLessThanOrEqual != null - ? - e.RouteAddressDetails - .Sum(rad => rad.CurrentAddressStopTime.TotalSeconds) <= - request.TimeInStopsLessThanOrEqual.Value.TotalSeconds - : true) && - (request.CostGreaterThanOrEqual != null - ? e.RouteAddressDetails.Sum(rad => rad.CostToNextAddress) >= - request.CostGreaterThanOrEqual - : true) && - (request.CostLessThanOrEqual != null - ? e.RouteAddressDetails.Sum(rad => rad.CostToNextAddress) <= - request.CostLessThanOrEqual - : true) && - (request.Currency != null - ? e.Currency == request.Currency - : true), - e => e.RouteAddressDetails, - request.PageNumber, request.PageSize, - cancellationToken); + var pagedList = await _unitOfWork.VehicleEnrollmentRepository + .GetPageAsync( + e => + // (e.Name.ToLower().Contains(request.Search.ToLower()) || + // e.City.Region.Country.Name.ToLower().Contains(request.Search.ToLower())) && + (request.RouteGuid != null + ? e.Route.Guid == request.RouteGuid + : true) && + (request.VehicleGuid != null + ? e.Vehicle.Guid >= request.VehicleGuid + : true) && + (request.NumberOfAddressesGreaterThanOrEqual != null + ? + e.RouteAddressDetails.Count() >= + request.NumberOfAddressesGreaterThanOrEqual + : true) && + (request.NumberOfAddressesLessThanOrEqual != null + ? + e.RouteAddressDetails.Count() <= + request.NumberOfAddressesLessThanOrEqual + : true) && + (request.DepartureTimeGreaterThanOrEqual != null + ? e.DepartureTime >= request.DepartureTimeGreaterThanOrEqual + : true) && + (request.DepartureTimeLessThanOrEqual != null + ? e.DepartureTime <= request.DepartureTimeLessThanOrEqual + : true) && + (request.ArrivalTimeGreaterThanOrEqual != null + ? + e.DepartureTime.AddSeconds(e.RouteAddressDetails + .Sum(rad => rad.TimeToNextAddress.TotalSeconds + + rad.CurrentAddressStopTime.TotalSeconds)) >= + request.ArrivalTimeGreaterThanOrEqual + : true) && + (request.ArrivalTimeLessThanOrEqual != null + ? + e.DepartureTime.AddSeconds(e.RouteAddressDetails + .Sum(rad => rad.TimeToNextAddress.TotalSeconds + + rad.CurrentAddressStopTime.TotalSeconds)) <= + request.ArrivalTimeLessThanOrEqual + : true) && + (request.TravelTimeGreaterThanOrEqual != null + ? + e.RouteAddressDetails + .Sum(rad => rad.TimeToNextAddress.TotalSeconds) >= + request.TravelTimeGreaterThanOrEqual.Value.TotalSeconds + : true) && + (request.TravelTimeLessThanOrEqual != null + ? + e.RouteAddressDetails + .Sum(rad => rad.TimeToNextAddress.TotalSeconds) <= + request.TravelTimeLessThanOrEqual.Value.TotalSeconds + : true) && + (request.TimeMovingGreaterThanOrEqual != null + ? + e.RouteAddressDetails + .Sum(rad => rad.TimeToNextAddress.TotalSeconds) >= + request.TimeMovingGreaterThanOrEqual.Value.TotalSeconds + : true) && + (request.TimeMovingLessThanOrEqual != null + ? + e.RouteAddressDetails + .Sum(rad => rad.TimeToNextAddress.TotalSeconds) <= + request.TimeMovingLessThanOrEqual.Value.TotalSeconds + : true) && + (request.TimeInStopsGreaterThanOrEqual != null + ? + e.RouteAddressDetails + .Sum(rad => rad.CurrentAddressStopTime.TotalSeconds) >= + request.TimeInStopsGreaterThanOrEqual.Value.TotalSeconds + : true) && + (request.TimeInStopsLessThanOrEqual != null + ? + e.RouteAddressDetails + .Sum(rad => rad.CurrentAddressStopTime.TotalSeconds) <= + request.TimeInStopsLessThanOrEqual.Value.TotalSeconds + : true) && + (request.CostGreaterThanOrEqual != null + ? e.RouteAddressDetails.Sum(rad => rad.CostToNextAddress) >= + request.CostGreaterThanOrEqual + : true) && + (request.CostLessThanOrEqual != null + ? e.RouteAddressDetails.Sum(rad => rad.CostToNextAddress) <= + request.CostLessThanOrEqual + : true) && + (request.Currency != null + ? e.Currency == request.Currency + : true) && + (request.EmployeeGuids != null + ? e.VehicleEnrollmentEmployees.Any(e => request.EmployeeGuids.Contains(e.Employee.Guid)) + : true), + e => e.RouteAddressDetails, + request.PageNumber, request.PageSize, cancellationToken); + + var vehicleEnrollments = pagedList.Items; + + var vehicleIds = vehicleEnrollments.Select(e => e.VehicleId); + var vehicles = (await _unitOfWork.VehicleRepository + .GetPageAsync( + e => vehicleIds.Contains(e.Id), + 1, vehicleIds.Count(), cancellationToken)) + .Items; + + var routeIds = vehicleEnrollments.Select(e => e.RouteId); + var routes = (await _unitOfWork.RouteRepository + .GetPageAsync( + e => routeIds.Contains(e.Id), + 1, routeIds.Count(), cancellationToken)) + .Items; - // Hydrate vehicle enrollment with address information + // Hydrate vehicle enrollment with address, + // vehicle, route and employee information. - var routeAddressIds = paginatedList.Items + var routeAddressIds = vehicleEnrollments .SelectMany(ve => ve.RouteAddressDetails) .Select(rad => rad.RouteAddressId); - var routeAddresses = await _unitOfWork.RouteAddressRepository + var routeAddresses = (await _unitOfWork.RouteAddressRepository .GetPageAsync( e => routeAddressIds.Contains(e.Id), e => e.Address.City.Region.Country, - 1, paginatedList.Items.Sum(e => e.RouteAddressDetails.Count()), - cancellationToken); + 1, routeAddressIds.Count(), cancellationToken)) + .Items; - foreach (var vehicleEnrollment in paginatedList.Items) + var vehicleEnrollmentIds = vehicleEnrollments + .Select(ve => ve.Id); + + var vehicleEnrollmentEmployees = + (await _unitOfWork.VehicleEnrollmentEmployeeRepository + .GetPageAsync( + e => vehicleEnrollmentIds.Contains(e.VehicleEnrollmentId), + e => e.Employee, + 1, int.MaxValue, cancellationToken)) + .Items; + + foreach (var vehicleEnrollment in vehicleEnrollments) { foreach (var routeAddressDetail in vehicleEnrollment.RouteAddressDetails) { - routeAddressDetail.RouteAddress = routeAddresses.Items + routeAddressDetail.RouteAddress = routeAddresses .First(ra => ra.Id == routeAddressDetail.RouteAddressId); } + + vehicleEnrollment.Route = routes + .Single(e => e.Id == vehicleEnrollment.RouteId); + + vehicleEnrollment.Vehicle = vehicles + .Single(e => e.Id == vehicleEnrollment.VehicleId); + + vehicleEnrollment.VehicleEnrollmentEmployees = + vehicleEnrollmentEmployees + .Where(vee => vee.VehicleEnrollmentId == vehicleEnrollment.Id) + .ToArray(); } @@ -158,7 +200,7 @@ public class GetVehicleEnrollmentsPageQueryHandler : if (!_sessionCurrencyService.Currency.Equals(Currency.Default)) { - foreach (var ve in paginatedList.Items) + foreach (var ve in vehicleEnrollments) { foreach (var rad in ve.RouteAddressDetails) { @@ -171,17 +213,18 @@ public class GetVehicleEnrollmentsPageQueryHandler : } - var mappedItems = _mapper - .ProjectTo(paginatedList.Items.AsQueryable()); + var vehicleEnrollmentsDto = _mapper + .Map>(vehicleEnrollments) + .AsQueryable(); - mappedItems = QueryableExtension - .ApplySort(mappedItems, request.Sort); + vehicleEnrollmentsDto = QueryableExtension + .ApplySort(vehicleEnrollmentsDto, request.Sort); _unitOfWork.Dispose(); return new PaginatedList( - mappedItems.ToList(), - paginatedList.TotalCount, request.PageNumber, + vehicleEnrollmentsDto.ToList(), + pagedList.TotalCount, request.PageNumber, request.PageSize); } } diff --git a/src/Application/VehicleEnrollments/VehicleEnrollmentDto.cs b/src/Application/VehicleEnrollments/VehicleEnrollmentDto.cs index 0da091a..40ca6a3 100644 --- a/src/Application/VehicleEnrollments/VehicleEnrollmentDto.cs +++ b/src/Application/VehicleEnrollments/VehicleEnrollmentDto.cs @@ -33,8 +33,17 @@ public sealed class VehicleEnrollmentDto : IMapFrom public string Currency { get; set; } + public Guid VehicleUuid { get; set; } + + public VehicleEnrollmentVehicleDto Vehicle { get; set; } + + public Guid RouteUuid { get; set; } + public ICollection RouteAddressDetails { get; set; } + // TODO: Add collection of employee dto objects + public ICollection EmployeeUuids { get; set; } + public void Mapping(MappingProfile profile) { profile.CreateMap() @@ -45,6 +54,16 @@ public sealed class VehicleEnrollmentDto : IMapFrom d => d.DepartureTime, opt => opt .MapFrom( - s => s.DepartureTime)); + s => s.DepartureTime)) + .ForMember( + d => d.VehicleUuid, + opt => opt.MapFrom(s => s.Vehicle.Guid)) + .ForMember( + d => d.RouteUuid, + opt => opt.MapFrom(s => s.Route.Guid)) + .ForMember( + d => d.EmployeeUuids, + opt => opt.MapFrom(s => + s.VehicleEnrollmentEmployees.Select(e => e.Employee.Guid))); } } diff --git a/src/Application/VehicleEnrollments/VehicleEnrollmentVehicleDto.cs b/src/Application/VehicleEnrollments/VehicleEnrollmentVehicleDto.cs new file mode 100644 index 0000000..a810f0a --- /dev/null +++ b/src/Application/VehicleEnrollments/VehicleEnrollmentVehicleDto.cs @@ -0,0 +1,94 @@ +using cuqmbr.TravelGuide.Application.Common.Mappings; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Application.VehicleEnrollments; + +public sealed class VehicleEnrollmentVehicleDto : IMapFrom +{ + public string Uuid { get; set; } + + public string Type { get; set; } + + public string Number { get; set; } + + public string Model { get; set; } + + public short Capacity { get; set; } + + public void Mapping(MappingProfile profile) + { + profile.CreateMap() + .ForMember( + d => d.Uuid, + opt => opt.MapFrom(s => s.Guid)) + .ForMember( + d => d.Type, + opt => opt.MapFrom(s => s.VehicleType.Name)) + .ForMember( + d => d.Number, + opt => opt.MapFrom( + (s, d) => + { + if (s is Bus) + { + return ((Bus)s).Number; + } + else if (s is Aircraft) + { + return ((Aircraft)s).Number; + } + else if (s is Train) + { + return ((Train)s).Number; + } + else + { + throw new NotImplementedException(); + } + })) + .ForMember( + d => d.Model, + opt => opt.MapFrom( + (s, d) => + { + if (s is Bus) + { + return ((Bus)s).Model; + } + else if (s is Aircraft) + { + return ((Aircraft)s).Model; + } + else if (s is Train) + { + return ((Train)s).Model; + } + else + { + throw new NotImplementedException(); + } + })) + .ForMember( + d => d.Capacity, + opt => opt.MapFrom( + (s, d) => + { + if (s is Bus) + { + return ((Bus)s).Capacity; + } + else if (s is Aircraft) + { + return ((Aircraft)s).Capacity; + } + else if (s is Train) + { + return ((Train)s).Capacity; + } + else + { + throw new NotImplementedException(); + } + })); + } +} diff --git a/src/Application/VehicleEnrollments/ViewModels/AddVehicleEnrollmentViewModel.cs b/src/Application/VehicleEnrollments/ViewModels/AddVehicleEnrollmentViewModel.cs index dd09bcf..deeb095 100644 --- a/src/Application/VehicleEnrollments/ViewModels/AddVehicleEnrollmentViewModel.cs +++ b/src/Application/VehicleEnrollments/ViewModels/AddVehicleEnrollmentViewModel.cs @@ -12,4 +12,6 @@ public sealed class AddVehicleEnrollmentViewModel public Guid RouteUuid { get; set; } public ICollection RouteAddressDetails { get; set; } + + public ICollection EmployeeUuids { get; set; } } diff --git a/src/Application/VehicleEnrollments/ViewModels/GetVehicleEnrollmentsPageFilterViewModel.cs b/src/Application/VehicleEnrollments/ViewModels/GetVehicleEnrollmentsPageFilterViewModel.cs index 5cde753..511799e 100644 --- a/src/Application/VehicleEnrollments/ViewModels/GetVehicleEnrollmentsPageFilterViewModel.cs +++ b/src/Application/VehicleEnrollments/ViewModels/GetVehicleEnrollmentsPageFilterViewModel.cs @@ -43,4 +43,6 @@ public sealed class GetVehicleEnrollmentsPageFilterViewModel public decimal? CostLessThanOrEqual { get; set; } public string? Currency { get; set; } + + public ICollection? EmployeeUuids { get; set; } } diff --git a/src/Application/VehicleEnrollments/ViewModels/UpdateVehicleEnrollmentViewModel.cs b/src/Application/VehicleEnrollments/ViewModels/UpdateVehicleEnrollmentViewModel.cs index 5c61d67..e58befd 100644 --- a/src/Application/VehicleEnrollments/ViewModels/UpdateVehicleEnrollmentViewModel.cs +++ b/src/Application/VehicleEnrollments/ViewModels/UpdateVehicleEnrollmentViewModel.cs @@ -8,4 +8,6 @@ public sealed class UpdateVehicleEnrollmentViewModel public ICollection RouteAddressDetails { get; set; } + + public ICollection EmployeeUuids { get; set; } } diff --git a/src/Domain/Entities/Employee.cs b/src/Domain/Entities/Employee.cs index ec518ce..3035cad 100644 --- a/src/Domain/Entities/Employee.cs +++ b/src/Domain/Entities/Employee.cs @@ -20,4 +20,6 @@ public sealed class Employee : EntityBase public Company Company { get; set; } public ICollection Documents { get; set; } + + public ICollection VehicleEnrollmentEmployees { get; set; } } diff --git a/src/Domain/Entities/VehicleEnrollment.cs b/src/Domain/Entities/VehicleEnrollment.cs index b6c2ca0..7cfdc46 100644 --- a/src/Domain/Entities/VehicleEnrollment.cs +++ b/src/Domain/Entities/VehicleEnrollment.cs @@ -21,9 +21,10 @@ public class VehicleEnrollment : EntityBase public ICollection RouteAddressDetails { get; set; } - public ICollection Tickets { get; set; } + public ICollection VehicleEnrollmentEmployees { get; set; } + public DateTimeOffset GetDepartureTime(long DepartureRouteAddressId) { diff --git a/src/Domain/Entities/VehicleEnrollmentEmployee.cs b/src/Domain/Entities/VehicleEnrollmentEmployee.cs new file mode 100644 index 0000000..7ac51df --- /dev/null +++ b/src/Domain/Entities/VehicleEnrollmentEmployee.cs @@ -0,0 +1,13 @@ +namespace cuqmbr.TravelGuide.Domain.Entities; + +public class VehicleEnrollmentEmployee : EntityBase +{ + public long EmployeeId { get; set; } + + public Employee Employee { get; set; } + + + public long VehicleEnrollmentId { get; set; } + + public VehicleEnrollment VehicleEnrollment { get; set; } +} diff --git a/src/HttpApi/Controllers/VehicleEnrollmentsController.cs b/src/HttpApi/Controllers/VehicleEnrollmentsController.cs index b5a703c..c79fe23 100644 --- a/src/HttpApi/Controllers/VehicleEnrollmentsController.cs +++ b/src/HttpApi/Controllers/VehicleEnrollmentsController.cs @@ -75,7 +75,8 @@ public class VehicleEnrollmentsController : ControllerBase CurrentAddressStopTime = rad.CurrentAddressStopTime, RouteAddressGuid = rad.RouteAddressUuid }) - .ToArray() + .ToArray(), + EmployeeGuids = viewModel.EmployeeUuids }, cancellationToken)); } @@ -141,7 +142,8 @@ public class VehicleEnrollmentsController : ControllerBase filterQuery.CostGreaterThanOrEqual, CostLessThanOrEqual = filterQuery.CostLessThanOrEqual, - Currency = Currency.FromName(filterQuery.Currency) + Currency = Currency.FromName(filterQuery.Currency), + EmployeeGuids = filterQuery.EmployeeUuids }, cancellationToken); } @@ -224,7 +226,8 @@ public class VehicleEnrollmentsController : ControllerBase CurrentAddressStopTime = rad.CurrentAddressStopTime, RouteAddressGuid = rad.RouteAddressUuid }) - .ToArray() + .ToArray(), + EmployeeGuids = viewModel.EmployeeUuids }, cancellationToken); } diff --git a/src/Persistence/InMemory/InMemoryUnitOfWork.cs b/src/Persistence/InMemory/InMemoryUnitOfWork.cs index 78c3f1a..c04be86 100644 --- a/src/Persistence/InMemory/InMemoryUnitOfWork.cs +++ b/src/Persistence/InMemory/InMemoryUnitOfWork.cs @@ -32,6 +32,8 @@ public sealed class InMemoryUnitOfWork : UnitOfWork TicketRepository = new InMemoryTicketRepository(_dbContext); RouteAddressDetailRepository = new InMemoryRouteAddressDetailRepository(_dbContext); + VehicleEnrollmentEmployeeRepository = + new InMemoryVehicleEnrollmentEmployeeRepository(_dbContext); } public CountryRepository CountryRepository { get; init; } @@ -66,6 +68,8 @@ public sealed class InMemoryUnitOfWork : UnitOfWork public RouteAddressDetailRepository RouteAddressDetailRepository { get; init; } + public VehicleEnrollmentEmployeeRepository VehicleEnrollmentEmployeeRepository { get; init; } + public int Save() { return _dbContext.SaveChanges(); diff --git a/src/Persistence/InMemory/Repositories/InMemoryVehicleEnrollmentEmployeeRepository.cs b/src/Persistence/InMemory/Repositories/InMemoryVehicleEnrollmentEmployeeRepository.cs new file mode 100644 index 0000000..d9b8d21 --- /dev/null +++ b/src/Persistence/InMemory/Repositories/InMemoryVehicleEnrollmentEmployeeRepository.cs @@ -0,0 +1,13 @@ +using cuqmbr.TravelGuide.Application.Common.Interfaces.Persistence.Repositories; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Persistence.InMemory.Repositories; + +public sealed class InMemoryVehicleEnrollmentEmployeeRepository : + InMemoryBaseRepository, + VehicleEnrollmentEmployeeRepository +{ + public InMemoryVehicleEnrollmentEmployeeRepository( + InMemoryDbContext dbContext) + : base(dbContext) { } +} diff --git a/src/Persistence/PostgreSql/Configurations/VehicleEnrollmentEmployeeConfiguration.cs b/src/Persistence/PostgreSql/Configurations/VehicleEnrollmentEmployeeConfiguration.cs new file mode 100644 index 0000000..11b319f --- /dev/null +++ b/src/Persistence/PostgreSql/Configurations/VehicleEnrollmentEmployeeConfiguration.cs @@ -0,0 +1,70 @@ +using cuqmbr.TravelGuide.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace cuqmbr.TravelGuide.Persistence.PostgreSql.Configurations; + +public class VehicleEnrollmentEmployeeConfiguration : + BaseConfiguration +{ + public override void Configure( + EntityTypeBuilder builder) + { + builder + .ToTable("vehicle_enrollment_employees"); + + base.Configure(builder); + + + builder + .Property(vee => vee.EmployeeId) + .HasColumnName("employee_id") + .HasColumnType("bigint") + .IsRequired(true); + + builder + .HasOne(vee => vee.Employee) + .WithMany(e => e.VehicleEnrollmentEmployees) + .HasForeignKey(vee => vee.EmployeeId) + .HasConstraintName( + "fk_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(vee => vee.EmployeeId) + .Metadata.GetColumnName()}") + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasIndex(vee => vee.EmployeeId) + .HasDatabaseName( + "ix_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(vee => vee.EmployeeId) + .Metadata.GetColumnName()}"); + + + builder + .Property(vee => vee.VehicleEnrollmentId) + .HasColumnName("vehicle_enrollment_id") + .HasColumnType("bigint") + .IsRequired(true); + + builder + .HasOne(vee => vee.VehicleEnrollment) + .WithMany(ve => ve.VehicleEnrollmentEmployees) + .HasForeignKey(vee => vee.VehicleEnrollmentId) + .HasConstraintName( + "fk_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(vee => vee.VehicleEnrollmentId) + .Metadata.GetColumnName()}") + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasIndex(vee => vee.VehicleEnrollmentId) + .HasDatabaseName( + "ix_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(vee => vee.VehicleEnrollmentId) + .Metadata.GetColumnName()}"); + } +} diff --git a/src/Persistence/PostgreSql/Migrations/20250527113429_Add_Vehicle_Enrollment_Employee.Designer.cs b/src/Persistence/PostgreSql/Migrations/20250527113429_Add_Vehicle_Enrollment_Employee.Designer.cs new file mode 100644 index 0000000..c4ef7f4 --- /dev/null +++ b/src/Persistence/PostgreSql/Migrations/20250527113429_Add_Vehicle_Enrollment_Employee.Designer.cs @@ -0,0 +1,1083 @@ +// +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("20250527113429_Add_Vehicle_Enrollment_Employee")] + partial class Add_Vehicle_Enrollment_Employee + { + /// + 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("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("regions_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.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("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("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.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.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.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.Company", "Company") + .WithMany("Employees") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_employees_company_id"); + + 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.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.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.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/20250527113429_Add_Vehicle_Enrollment_Employee.cs b/src/Persistence/PostgreSql/Migrations/20250527113429_Add_Vehicle_Enrollment_Employee.cs new file mode 100644 index 0000000..768ef38 --- /dev/null +++ b/src/Persistence/PostgreSql/Migrations/20250527113429_Add_Vehicle_Enrollment_Employee.cs @@ -0,0 +1,73 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistence.PostgreSql.Migrations +{ + /// + public partial class Add_Vehicle_Enrollment_Employee : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateSequence( + name: "vehicle_enrollment_employees_id_sequence", + schema: "application"); + + migrationBuilder.CreateTable( + name: "vehicle_enrollment_employees", + schema: "application", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false, defaultValueSql: "nextval('application.vehicle_enrollment_employees_id_sequence')"), + employee_id = table.Column(type: "bigint", nullable: false), + vehicle_enrollment_id = table.Column(type: "bigint", nullable: false), + uuid = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_vehicle_enrollment_employees", x => x.id); + table.UniqueConstraint("altk_vehicle_enrollment_employees_uuid", x => x.uuid); + table.ForeignKey( + name: "fk_vehicle_enrollment_employees_employee_id", + column: x => x.employee_id, + principalSchema: "application", + principalTable: "employees", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_vehicle_enrollment_employees_vehicle_enrollment_id", + column: x => x.vehicle_enrollment_id, + principalSchema: "application", + principalTable: "vehicle_enrollments", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "ix_vehicle_enrollment_employees_employee_id", + schema: "application", + table: "vehicle_enrollment_employees", + column: "employee_id"); + + migrationBuilder.CreateIndex( + name: "ix_vehicle_enrollment_employees_vehicle_enrollment_id", + schema: "application", + table: "vehicle_enrollment_employees", + column: "vehicle_enrollment_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "vehicle_enrollment_employees", + schema: "application"); + + migrationBuilder.DropSequence( + name: "vehicle_enrollment_employees_id_sequence", + schema: "application"); + } + } +} diff --git a/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs b/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs index e4f59b1..1f16747 100644 --- a/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs +++ b/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs @@ -47,6 +47,8 @@ namespace Persistence.PostgreSql.Migrations modelBuilder.HasSequence("tickets_id_sequence"); + modelBuilder.HasSequence("vehicle_enrollment_employees_id_sequence"); + modelBuilder.HasSequence("vehicle_enrollments_id_sequence"); modelBuilder.HasSequence("vehicles_id_sequence"); @@ -691,6 +693,43 @@ namespace Persistence.PostgreSql.Migrations }); }); + 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"); @@ -950,6 +989,27 @@ namespace Persistence.PostgreSql.Migrations 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.Address", b => { b.Navigation("AddressRoutes"); @@ -975,6 +1035,8 @@ namespace Persistence.PostgreSql.Migrations modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Employee", b => { b.Navigation("Documents"); + + b.Navigation("VehicleEnrollmentEmployees"); }); modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Region", b => @@ -1009,6 +1071,8 @@ namespace Persistence.PostgreSql.Migrations b.Navigation("RouteAddressDetails"); b.Navigation("Tickets"); + + b.Navigation("VehicleEnrollmentEmployees"); }); #pragma warning restore 612, 618 } diff --git a/src/Persistence/PostgreSql/PostgreSqlUnitOfWork.cs b/src/Persistence/PostgreSql/PostgreSqlUnitOfWork.cs index 6898026..717d684 100644 --- a/src/Persistence/PostgreSql/PostgreSqlUnitOfWork.cs +++ b/src/Persistence/PostgreSql/PostgreSqlUnitOfWork.cs @@ -32,6 +32,8 @@ public sealed class PostgreSqlUnitOfWork : UnitOfWork TicketRepository = new PostgreSqlTicketRepository(_dbContext); RouteAddressDetailRepository = new PostgreSqlRouteAddressDetailRepository(_dbContext); + VehicleEnrollmentEmployeeRepository = + new PostgreSqlVehicleEnrollmentEmployeeRepository(_dbContext); } public CountryRepository CountryRepository { get; init; } @@ -66,6 +68,8 @@ public sealed class PostgreSqlUnitOfWork : UnitOfWork public RouteAddressDetailRepository RouteAddressDetailRepository { get; init; } + public VehicleEnrollmentEmployeeRepository VehicleEnrollmentEmployeeRepository { get; init; } + public int Save() { return _dbContext.SaveChanges(); diff --git a/src/Persistence/PostgreSql/Repositories/PostgreSqlVehicleEnrollmentEmployeeRepository.cs b/src/Persistence/PostgreSql/Repositories/PostgreSqlVehicleEnrollmentEmployeeRepository.cs new file mode 100644 index 0000000..ed6ca66 --- /dev/null +++ b/src/Persistence/PostgreSql/Repositories/PostgreSqlVehicleEnrollmentEmployeeRepository.cs @@ -0,0 +1,13 @@ +using cuqmbr.TravelGuide.Application.Common.Interfaces.Persistence.Repositories; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Persistence.PostgreSql.Repositories; + +public sealed class PostgreSqlVehicleEnrollmentEmployeeRepository : + PostgreSqlBaseRepository, + VehicleEnrollmentEmployeeRepository +{ + public PostgreSqlVehicleEnrollmentEmployeeRepository( + PostgreSqlDbContext dbContext) + : base(dbContext) { } +}