From 4d1f6edc2e763bc4f48693e9ae9e1434af58107e Mon Sep 17 00:00:00 2001 From: cuqmbr Date: Fri, 30 May 2025 13:14:20 +0300 Subject: [PATCH] add ticket group queries --- .../GetPaymentLinkCommandHandler.cs | 2 +- .../AddTicketGroupCommandHandler.cs | 4 +- .../GetTicketGroup/GetTicketGroupQuery.cs | 8 + .../GetTicketGroupQueryAuthorizer.cs | 31 +++ .../GetTicketGroupQueryHandler.cs | 175 +++++++++++++ .../GetTicketGroupQueryValidator.cs | 14 ++ .../GetTicketGroupsPageQuery.cs | 53 ++++ .../GetTicketGroupsPageQueryAuthorizer.cs | 31 +++ .../GetTicketGroupsPageQueryHandler.cs | 232 ++++++++++++++++++ .../GetTicketGroupsPageQueryValidator.cs | 80 ++++++ .../TicketGroups/TicketAddressDto.cs | 53 ---- src/Application/TicketGroups/TicketDto.cs | 50 ---- .../TicketGroups/TicketGroupAddressDto.cs | 87 +++++++ .../TicketGroups/TicketGroupCompanyDto.cs | 25 ++ .../TicketGroups/TicketGroupDto.cs | 123 +++++++++- .../TicketGroups/TicketGroupVehicleDto.cs | 95 +++++++ .../TicketGroupVehicleEnrollmentDto.cs | 99 ++++++++ .../GetTicketGroupsPageFilterViewModel.cs | 18 +- .../ViewModels/UpdateTicketGroupViewModel.cs | 21 -- .../Controllers/TicketGroupsController.cs | 231 +++++++---------- 20 files changed, 1152 insertions(+), 280 deletions(-) create mode 100644 src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQuery.cs create mode 100644 src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryAuthorizer.cs create mode 100644 src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryHandler.cs create mode 100644 src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryValidator.cs create mode 100644 src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQuery.cs create mode 100644 src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryAuthorizer.cs create mode 100644 src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryHandler.cs create mode 100644 src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryValidator.cs delete mode 100644 src/Application/TicketGroups/TicketAddressDto.cs delete mode 100644 src/Application/TicketGroups/TicketDto.cs create mode 100644 src/Application/TicketGroups/TicketGroupAddressDto.cs create mode 100644 src/Application/TicketGroups/TicketGroupCompanyDto.cs create mode 100644 src/Application/TicketGroups/TicketGroupVehicleDto.cs create mode 100644 src/Application/TicketGroups/TicketGroupVehicleEnrollmentDto.cs delete mode 100644 src/Application/TicketGroups/ViewModels/UpdateTicketGroupViewModel.cs diff --git a/src/Application/Payments/LiqPay/TicketGroups/Commands/GetPaymentLink/GetPaymentLinkCommandHandler.cs b/src/Application/Payments/LiqPay/TicketGroups/Commands/GetPaymentLink/GetPaymentLinkCommandHandler.cs index 48b6e00..dcdd08f 100644 --- a/src/Application/Payments/LiqPay/TicketGroups/Commands/GetPaymentLink/GetPaymentLinkCommandHandler.cs +++ b/src/Application/Payments/LiqPay/TicketGroups/Commands/GetPaymentLink/GetPaymentLinkCommandHandler.cs @@ -333,7 +333,7 @@ public class GetPaymentLinkCommandHandler : // TODO: This counts departure address stop time which is // not wrong but may be not desired. var timeToDeparture = verad - .TakeWhile(rad => rad.Id != departureRouteAddressId) + .TakeWhile(rad => rad.RouteAddressId != departureRouteAddressId) .Aggregate(TimeSpan.Zero, (sum, next) => sum + next.TimeToNextAddress + next.CurrentAddressStopTime); diff --git a/src/Application/TicketGroups/Commands/AddTicketGroup/AddTicketGroupCommandHandler.cs b/src/Application/TicketGroups/Commands/AddTicketGroup/AddTicketGroupCommandHandler.cs index d3edfc0..e6f0c3a 100644 --- a/src/Application/TicketGroups/Commands/AddTicketGroup/AddTicketGroupCommandHandler.cs +++ b/src/Application/TicketGroups/Commands/AddTicketGroup/AddTicketGroupCommandHandler.cs @@ -325,7 +325,7 @@ public class AddTicketGroupCommandHandler : // TODO: This counts departure address stop time which is // not wrong but may be not desired. var timeToDeparture = verad - .TakeWhile(rad => rad.Id != departureRouteAddressId) + .TakeWhile(rad => rad.RouteAddressId != departureRouteAddressId) .Aggregate(TimeSpan.Zero, (sum, next) => sum + next.TimeToNextAddress + next.CurrentAddressStopTime); @@ -339,7 +339,7 @@ public class AddTicketGroupCommandHandler : var costToDeparture = verad - .TakeWhile(rad => rad.Id != departureRouteAddressId) + .TakeWhile(rad => rad.RouteAddressId != departureRouteAddressId) .Aggregate((decimal)0, (sum, next) => sum + next.CostToNextAddress); diff --git a/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQuery.cs b/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQuery.cs new file mode 100644 index 0000000..b06e83a --- /dev/null +++ b/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroup; + +public record GetTicketGroupQuery : IRequest +{ + public Guid Guid { get; set; } +} diff --git a/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryAuthorizer.cs b/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryAuthorizer.cs new file mode 100644 index 0000000..4305de1 --- /dev/null +++ b/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryAuthorizer.cs @@ -0,0 +1,31 @@ +using cuqmbr.TravelGuide.Application.Common.Authorization; +using cuqmbr.TravelGuide.Application.Common.Services; +using cuqmbr.TravelGuide.Domain.Enums; +using MediatR.Behaviors.Authorization; + +namespace cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroup; + +public class GetTicketGroupQueryAuthorizer : + AbstractRequestAuthorizer +{ + private readonly SessionUserService _sessionUserService; + + public GetTicketGroupQueryAuthorizer(SessionUserService sessionUserService) + { + _sessionUserService = sessionUserService; + } + + public override void BuildPolicy(GetTicketGroupQuery request) + { + UseRequirement(new MustBeAuthenticatedRequirement + { + IsAuthenticated= _sessionUserService.IsAuthenticated + }); + + UseRequirement(new MustBeInRolesRequirement + { + RequiredRoles = [IdentityRole.Administrator], + UserRoles = _sessionUserService.Roles + }); + } +} diff --git a/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryHandler.cs b/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryHandler.cs new file mode 100644 index 0000000..f3c270c --- /dev/null +++ b/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryHandler.cs @@ -0,0 +1,175 @@ +using MediatR; +using cuqmbr.TravelGuide.Application.Common.Persistence; +using cuqmbr.TravelGuide.Application.Common.Exceptions; +using AutoMapper; +using cuqmbr.TravelGuide.Application.Common.Services; +using cuqmbr.TravelGuide.Domain.Enums; + +namespace cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroup; + +public class GetTicketGroupQueryHandler : + IRequestHandler +{ + private readonly UnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + private readonly CurrencyConverterService _currencyConverter; + + private readonly SessionCurrencyService _sessionCurrencyService; + private readonly SessionTimeZoneService _sessionTimeZoneService; + + private readonly object _lock = new(); + + public GetTicketGroupQueryHandler(UnitOfWork unitOfWork, + IMapper mapper, CurrencyConverterService currencyConverterService, + SessionCurrencyService sessionCurrencyService, + SessionTimeZoneService sessionTimeZoneService) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + _currencyConverter = currencyConverterService; + _sessionCurrencyService = sessionCurrencyService; + _sessionTimeZoneService = sessionTimeZoneService; + } + + public async Task Handle( + GetTicketGroupQuery request, + CancellationToken cancellationToken) + { + var ticketGroup = await _unitOfWork.TicketGroupRepository.GetOneAsync( + e => e.Guid == request.Guid, e => e.Tickets, + cancellationToken); + + if (ticketGroup == null) + { + throw new NotFoundException(); + } + + + // Hydrate + + var vehicleEnrollmentIds = + ticketGroup.Tickets.Select(t => t.VehicleEnrollmentId); + var vehicleEnrollments = (await _unitOfWork.VehicleEnrollmentRepository + .GetPageAsync( + ve => vehicleEnrollmentIds.Contains(ve.Id), + ve => ve.Route.RouteAddresses, + 1, vehicleEnrollmentIds.Count(), cancellationToken)) + .Items; + + var routeAddressIds = vehicleEnrollments + .SelectMany(ve => ve.Route.RouteAddresses) + .Select(ra => ra.Id); + var routeAddressDetails = (await _unitOfWork.RouteAddressDetailRepository + .GetPageAsync( + rad => routeAddressIds.Contains(rad.RouteAddressId), + 1, routeAddressIds.Count(), cancellationToken)) + .Items; + + var addressIds = vehicleEnrollments + .SelectMany(ve => ve.Route.RouteAddresses) + .Select(ra => ra.AddressId); + var addresses = (await _unitOfWork.AddressRepository + .GetPageAsync( + a => addressIds.Contains(a.Id), + a => a.City.Region.Country, + 1, addressIds.Count(), cancellationToken)) + .Items; + + var vehicleIds = vehicleEnrollments + .Select(ve => ve.VehicleId); + var vehicles = (await _unitOfWork.VehicleRepository + .GetPageAsync( + v => vehicleIds.Contains(v.Id), + v => v.Company, + 1, vehicleIds.Count(), cancellationToken)) + .Items; + + foreach (var ve in vehicleEnrollments) + { + ve.Vehicle = vehicles.Single(v => v.Id == ve.VehicleId); + + foreach (var ra in ve.Route.RouteAddresses) + { + ra.Address = addresses.Single(a => a.Id == ra.AddressId); + ra.Details = routeAddressDetails + .Where(rad => rad.RouteAddressId == ra.Id) + .ToArray(); + } + } + + + // TODO: Replace with AutoMapper resolvers + // Convert currency and apply session time zone + + var convertTasks = new List(); + + foreach (var t in ticketGroup.Tickets) + { + convertTasks.Add(Task.Factory.StartNew(() => + { + t.VehicleEnrollment.DepartureTime = + TimeZoneInfo.ConvertTime(t.VehicleEnrollment.DepartureTime, + _sessionTimeZoneService.TimeZone); + })); + + if (_sessionCurrencyService.Currency.Equals(Currency.Default)) + { + break; + } + + convertTasks.Add(Task.Factory.StartNew(() => + { + lock (_lock) + { + var convertedCost = _currencyConverter.ConvertAsync(t.Cost, + t.Currency, _sessionCurrencyService.Currency, + cancellationToken) + .Result; + + t.Cost = _sessionCurrencyService + .Currency.Round(convertedCost); + } + })); + + foreach (var rad in t.VehicleEnrollment.RouteAddressDetails) + { + convertTasks.Add(Task.Factory.StartNew(() => + { + lock (_lock) + { + var convertedCost = _currencyConverter.ConvertAsync( + rad.CostToNextAddress, t.VehicleEnrollment.Currency, + _sessionCurrencyService.Currency, cancellationToken) + .Result; + + rad.CostToNextAddress = _sessionCurrencyService + .Currency.Round(convertedCost); + } + })); + } + } + + Task.WaitAll(convertTasks); + + foreach (var t in ticketGroup.Tickets) + { + if (_sessionCurrencyService.Currency.Equals(Currency.Default)) + { + break; + } + + t.Currency = _sessionCurrencyService.Currency; + t.VehicleEnrollment.Currency = _sessionCurrencyService.Currency; + } + + + _unitOfWork.Dispose(); + + var dto = _mapper.Map(ticketGroup); + + dto.Currency = _sessionCurrencyService.Currency.Name; + + return dto; + } +} diff --git a/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryValidator.cs b/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryValidator.cs new file mode 100644 index 0000000..937166a --- /dev/null +++ b/src/Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Microsoft.Extensions.Localization; + +namespace cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroup; + +public class GetTicketGroupQueryValidator : AbstractValidator +{ + public GetTicketGroupQueryValidator(IStringLocalizer localizer) + { + RuleFor(v => v.Guid) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); + } +} diff --git a/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQuery.cs b/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQuery.cs new file mode 100644 index 0000000..2d0a9f9 --- /dev/null +++ b/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQuery.cs @@ -0,0 +1,53 @@ +using cuqmbr.TravelGuide.Application.Common.Models; +using cuqmbr.TravelGuide.Domain.Enums; +using MediatR; + +namespace cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroupsPage; + +public record GetTicketGroupsPageQuery : IRequest> +{ + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; + + public string Search { get; set; } = String.Empty; + + public string Sort { get; set; } = String.Empty; + + public HashSet? PassangerSex { get; set; } + + public DateOnly? PassangerBirthDateGreaterThanOrEqualTo { get; set; } + + public DateOnly? PassangerBirthDateLessThanOrEqualTo { get; set; } + + public DateTimeOffset? PurchaseTimeGreaterThanOrEqualTo { get; set; } + + public DateTimeOffset? PurchaseTimeLessThanOrEqualTo { get; set; } + + public HashSet? Statuses { get; set; } + + public HashSet? VehicleTypes { get; set; } + + public TimeSpan? TravelTimeGreaterThanOrEqualTo { get; set; } + + public TimeSpan? TravelTimeLessThanOrEqualTo { get; set; } + + // TODO: Add filtering parametetrs listed below. It is hard to + // be done because of pagination. + + // public decimal? CostGreaterThanOrEqualTo { get; set; } + // + // public decimal? CostLessThanOrEqualTo { get; set; } + // + // public short? NumberOfTransfersGreaterThanOrEqualTo { get; set; } + // + // public short? NumberOfTransfersLessThanOrEqualTo { get; set; } + // + // public DateTimeOffset? DepartureTimeGreaterThanOrEqualTo { get; set; } + // + // public DateTimeOffset? DepartureTimeLessThanOrEqualTo { get; set; } + // + // public DateTimeOffset? ArrivalTimeGreaterThanOrEqualTo { get; set; } + // + // public DateTimeOffset? ArrivalTimeLessThanOrEqualTo { get; set; } +} diff --git a/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryAuthorizer.cs b/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryAuthorizer.cs new file mode 100644 index 0000000..ba67439 --- /dev/null +++ b/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryAuthorizer.cs @@ -0,0 +1,31 @@ +using cuqmbr.TravelGuide.Application.Common.Authorization; +using cuqmbr.TravelGuide.Application.Common.Services; +using cuqmbr.TravelGuide.Domain.Enums; +using MediatR.Behaviors.Authorization; + +namespace cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroupsPage; + +public class GetTicketGroupsPageQueryAuthorizer : + AbstractRequestAuthorizer +{ + private readonly SessionUserService _sessionUserService; + + public GetTicketGroupsPageQueryAuthorizer(SessionUserService sessionUserService) + { + _sessionUserService = sessionUserService; + } + + public override void BuildPolicy(GetTicketGroupsPageQuery request) + { + UseRequirement(new MustBeAuthenticatedRequirement + { + IsAuthenticated= _sessionUserService.IsAuthenticated + }); + + UseRequirement(new MustBeInRolesRequirement + { + RequiredRoles = [IdentityRole.Administrator], + UserRoles = _sessionUserService.Roles + }); + } +} diff --git a/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryHandler.cs b/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryHandler.cs new file mode 100644 index 0000000..a7c0e2b --- /dev/null +++ b/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryHandler.cs @@ -0,0 +1,232 @@ +using MediatR; +using cuqmbr.TravelGuide.Application.Common.Persistence; +using AutoMapper; +using cuqmbr.TravelGuide.Application.Common.Models; +using cuqmbr.TravelGuide.Application.Common.Extensions; +using cuqmbr.TravelGuide.Application.Common.Services; +using cuqmbr.TravelGuide.Domain.Enums; + +namespace cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroupsPage; + +public class GetTicketGroupsPageQueryHandler : + IRequestHandler> +{ + private readonly UnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + private readonly CurrencyConverterService _currencyConverter; + + private readonly SessionCurrencyService _sessionCurrencyService; + private readonly SessionTimeZoneService _sessionTimeZoneService; + + private readonly object _lock = new(); + + public GetTicketGroupsPageQueryHandler(UnitOfWork unitOfWork, + IMapper mapper, CurrencyConverterService currencyConverterService, + SessionCurrencyService sessionCurrencyService, + SessionTimeZoneService sessionTimeZoneService) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + _currencyConverter = currencyConverterService; + _sessionCurrencyService = sessionCurrencyService; + _sessionTimeZoneService = sessionTimeZoneService; + } + + public async Task> Handle( + GetTicketGroupsPageQuery request, + CancellationToken cancellationToken) + { + var paginatedList = await _unitOfWork.TicketGroupRepository.GetPageAsync( + e => + (e.PassangerFirstName.ToLower().Contains(request.Search.ToLower()) || + e.PassangerLastName.ToLower().Contains(request.Search.ToLower()) || + e.PassangerPatronymic.ToLower().Contains(request.Search.ToLower()) || + (e.PassangerEmail != null ? + e.PassangerEmail.ToLower().Contains(request.Search.ToLower()) : + false)) && + (request.PassangerSex != null + ? request.PassangerSex.Contains(e.PassangerSex) + : true) && + (request.PassangerBirthDateGreaterThanOrEqualTo != null + ? e.PassangerBirthDate >= request.PassangerBirthDateGreaterThanOrEqualTo + : true) && + (request.PassangerBirthDateLessThanOrEqualTo != null + ? e.PassangerBirthDate <= request.PassangerBirthDateLessThanOrEqualTo + : true) && + (request.PurchaseTimeGreaterThanOrEqualTo != null + ? e.PurchaseTime >= request.PurchaseTimeGreaterThanOrEqualTo + : true) && + (request.PurchaseTimeLessThanOrEqualTo != null + ? e.PurchaseTime <= request.PurchaseTimeLessThanOrEqualTo + : true) && + (request.PassangerSex != null + ? request.PassangerSex.Contains(e.PassangerSex) + : true) && + (request.Statuses != null + ? request.Statuses.Contains(e.Status) + : true) && + (request.VehicleTypes != null + ? e.Tickets + .Select(t => t.VehicleEnrollment.Vehicle.VehicleType) + .Any(vt => request.VehicleTypes.Contains(vt)) + : true) && + (request.TravelTimeGreaterThanOrEqualTo != null + ? e.TravelTime >= request.TravelTimeGreaterThanOrEqualTo + : true) && + (request.TravelTimeLessThanOrEqualTo != null + ? e.TravelTime <= request.TravelTimeLessThanOrEqualTo + : true), + e => e.Tickets, + request.PageNumber, request.PageSize, cancellationToken); + + var ticketGroups = paginatedList.Items; + + + // Hydrate + + var vehicleEnrollmentIds = + ticketGroups.SelectMany(tg => tg.Tickets) + .Select(t => t.VehicleEnrollmentId); + var vehicleEnrollments = (await _unitOfWork.VehicleEnrollmentRepository + .GetPageAsync( + ve => vehicleEnrollmentIds.Contains(ve.Id), + ve => ve.Route.RouteAddresses, + 1, vehicleEnrollmentIds.Count(), cancellationToken)) + .Items; + + var routeAddressIds = vehicleEnrollments + .SelectMany(ve => ve.Route.RouteAddresses) + .Select(ra => ra.Id); + var routeAddressDetails = (await _unitOfWork.RouteAddressDetailRepository + .GetPageAsync( + rad => routeAddressIds.Contains(rad.RouteAddressId), + 1, routeAddressIds.Count(), cancellationToken)) + .Items; + + var addressIds = vehicleEnrollments + .SelectMany(ve => ve.Route.RouteAddresses) + .Select(ra => ra.AddressId); + var addresses = (await _unitOfWork.AddressRepository + .GetPageAsync( + a => addressIds.Contains(a.Id), + a => a.City.Region.Country, + 1, addressIds.Count(), cancellationToken)) + .Items; + + var vehicleIds = vehicleEnrollments + .Select(ve => ve.VehicleId); + var vehicles = (await _unitOfWork.VehicleRepository + .GetPageAsync( + v => vehicleIds.Contains(v.Id), + v => v.Company, + 1, vehicleIds.Count(), cancellationToken)) + .Items; + + foreach (var ve in vehicleEnrollments) + { + ve.Vehicle = vehicles.Single(v => v.Id == ve.VehicleId); + + foreach (var ra in ve.Route.RouteAddresses) + { + ra.Address = addresses.Single(a => a.Id == ra.AddressId); + ra.Details = routeAddressDetails + .Where(rad => rad.RouteAddressId == ra.Id) + .ToArray(); + } + } + + + // TODO: Replace with AutoMapper resolvers + // Convert currency and apply session time zone + + var convertTasks = new List(); + var processedRouteAddressDetailIds = new HashSet(); + + foreach (var t in ticketGroups.SelectMany(tg => tg.Tickets)) + { + convertTasks.Add(Task.Factory.StartNew(() => + { + t.VehicleEnrollment.DepartureTime = + TimeZoneInfo.ConvertTime(t.VehicleEnrollment.DepartureTime, + _sessionTimeZoneService.TimeZone); + })); + + if (_sessionCurrencyService.Currency.Equals(Currency.Default)) + { + break; + } + + convertTasks.Add(Task.Factory.StartNew(() => + { + lock (_lock) + { + var convertedCost = _currencyConverter.ConvertAsync(t.Cost, + t.Currency, _sessionCurrencyService.Currency, + t.TicketGroup.PurchaseTime, cancellationToken) + .Result; + + t.Cost = _sessionCurrencyService + .Currency.Round(convertedCost); + } + })); + + foreach (var rad in t.VehicleEnrollment.RouteAddressDetails) + { + convertTasks.Add(Task.Factory.StartNew(() => + { + lock (_lock) + { + if (processedRouteAddressDetailIds.Contains(rad.Id)) + { + return; + } + + var convertedCost = _currencyConverter.ConvertAsync( + rad.CostToNextAddress, t.VehicleEnrollment.Currency, + _sessionCurrencyService.Currency, + t.TicketGroup.PurchaseTime, cancellationToken) + .Result; + + rad.CostToNextAddress = _sessionCurrencyService + .Currency.Round(convertedCost); + + processedRouteAddressDetailIds.Add(rad.Id); + } + })); + } + } + + Task.WaitAll(convertTasks); + + foreach (var t in ticketGroups.SelectMany(tg => tg.Tickets)) + { + if (_sessionCurrencyService.Currency.Equals(Currency.Default)) + { + break; + } + + t.Currency = _sessionCurrencyService.Currency; + t.VehicleEnrollment.Currency = _sessionCurrencyService.Currency; + } + + + var mappedItems = + _mapper.Map>(ticketGroups); + + foreach (var item in mappedItems) + { + item.Currency = _sessionCurrencyService.Currency.Name; + } + + mappedItems = QueryableExtension + .ApplySort(mappedItems.AsQueryable(), request.Sort); + + _unitOfWork.Dispose(); + + return new PaginatedList( + mappedItems.ToList(), + paginatedList.TotalCount, request.PageNumber, + request.PageSize); + } +} diff --git a/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryValidator.cs b/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryValidator.cs new file mode 100644 index 0000000..ed16b23 --- /dev/null +++ b/src/Application/TicketGroups/Queries/GetTicketGroupsPage/GetTicketGroupsPageQueryValidator.cs @@ -0,0 +1,80 @@ +using cuqmbr.TravelGuide.Application.Common.Services; +using cuqmbr.TravelGuide.Domain.Enums; +using FluentValidation; +using Microsoft.Extensions.Localization; + +namespace cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroupsPage; + +public class GetTicketGroupsPageQueryValidator : AbstractValidator +{ + public GetTicketGroupsPageQueryValidator( + IStringLocalizer localizer, + SessionCultureService cultureService) + { + RuleFor(v => v.PageNumber) + .GreaterThanOrEqualTo(1) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + 1)); + + RuleFor(v => v.PageSize) + .GreaterThanOrEqualTo(1) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + 1)) + .LessThanOrEqualTo(50) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.LessThanOrEqualTo"], + 50)); + + RuleFor(v => v.Search) + .MaximumLength(64) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.MaximumLength"], + 64)); + + When(v => v.PassangerSex != null, () => + { + RuleForEach(v => v.PassangerSex) + .Must((v, s) => Sex.Enumerations.ContainsValue(s)) + .WithMessage( + String.Format( + localizer["FluentValidation.MustBeInEnum"], + String.Join( + ", ", + Sex.Enumerations.Values.Select(e => e.Name)))); + }); + + When(v => v.Statuses != null, () => + { + RuleForEach(v => v.Statuses) + .Must((v, s) => TicketStatus.Enumerations.ContainsValue(s)) + .WithMessage( + String.Format( + localizer["FluentValidation.MustBeInEnum"], + String.Join( + ", ", + TicketStatus.Enumerations.Values.Select(e => e.Name)))); + }); + + When(v => v.VehicleTypes != null, () => + { + RuleForEach(v => v.VehicleTypes) + .Must((v, s) => VehicleType.Enumerations.ContainsValue(s)) + .WithMessage( + String.Format( + localizer["FluentValidation.MustBeInEnum"], + String.Join( + ", ", + VehicleType.Enumerations.Values.Select(e => e.Name)))); + }); + } +} diff --git a/src/Application/TicketGroups/TicketAddressDto.cs b/src/Application/TicketGroups/TicketAddressDto.cs deleted file mode 100644 index d62e5c2..0000000 --- a/src/Application/TicketGroups/TicketAddressDto.cs +++ /dev/null @@ -1,53 +0,0 @@ -using cuqmbr.TravelGuide.Application.Common.Mappings; -using cuqmbr.TravelGuide.Domain.Entities; - -namespace cuqmbr.TravelGuide.Application.TicketGroups; - -public sealed class TicketAddressDto : IMapFrom
-{ - public Guid Uuid { get; set; } - - public string Name { get; set; } - - public double Longitude { get; set; } - - public double Latitude { get; set; } - - public Guid CountryUuid { get; set; } - - public string CountryName { get; set; } - - public Guid RegionUuid { get; set; } - - public string RegionName { get; set; } - - public Guid CityUuid { get; set; } - - public string CityName { get; set; } - - public void Mapping(MappingProfile profile) - { - profile.CreateMap() - .ForMember( - d => d.Uuid, - opt => opt.MapFrom(s => s.Guid)) - .ForMember( - d => d.CountryUuid, - opt => opt.MapFrom(s => s.City.Region.Country.Guid)) - .ForMember( - d => d.CountryName, - opt => opt.MapFrom(s => s.City.Region.Country.Name)) - .ForMember( - d => d.RegionUuid, - opt => opt.MapFrom(s => s.City.Region.Guid)) - .ForMember( - d => d.RegionName, - opt => opt.MapFrom(s => s.City.Region.Name)) - .ForMember( - d => d.CityUuid, - opt => opt.MapFrom(s => s.City.Guid)) - .ForMember( - d => d.CityName, - opt => opt.MapFrom(s => s.City.Name)); - } -} diff --git a/src/Application/TicketGroups/TicketDto.cs b/src/Application/TicketGroups/TicketDto.cs deleted file mode 100644 index 93194a5..0000000 --- a/src/Application/TicketGroups/TicketDto.cs +++ /dev/null @@ -1,50 +0,0 @@ -using cuqmbr.TravelGuide.Application.Common.Mappings; -using cuqmbr.TravelGuide.Domain.Entities; - -namespace cuqmbr.TravelGuide.Application.TicketGroups; - -public sealed class TicketDto : IMapFrom -{ - public Guid Uuid { get; set; } - - public Guid DepartureRouteAddressUuid { get; set; } - - public Guid ArrivalRouteAddressUuid { get; set; } - - public TicketAddressDto DepartureAddress { get; set; } - - public TicketAddressDto ArrivalAddress { get; set; } - - public short Order { get; set; } - - public Guid VehicleEnrollmentUuid { get; set; } - - // TODO: Add VehicleEnrollment model - - public string Currency { get; set; } - - public decimal Cost { get; set; } - - public void Mapping(MappingProfile profile) - { - profile.CreateMap() - .ForMember( - d => d.Uuid, - opt => opt.MapFrom(s => s.Guid)) - .ForMember( - d => d.DepartureRouteAddressUuid, - opt => opt.MapFrom(s => s.DepartureRouteAddress.Guid)) - .ForMember( - d => d.ArrivalRouteAddressUuid, - opt => opt.MapFrom(s => s.ArrivalRouteAddress.Guid)) - .ForMember( - d => d.DepartureAddress, - opt => opt.MapFrom(s => s.DepartureRouteAddress.Address)) - .ForMember( - d => d.ArrivalAddress, - opt => opt.MapFrom(s => s.ArrivalRouteAddress.Address)) - .ForMember( - d => d.VehicleEnrollmentUuid, - opt => opt.MapFrom(s => s.VehicleEnrollment.Guid)); - } -} diff --git a/src/Application/TicketGroups/TicketGroupAddressDto.cs b/src/Application/TicketGroups/TicketGroupAddressDto.cs new file mode 100644 index 0000000..dd86935 --- /dev/null +++ b/src/Application/TicketGroups/TicketGroupAddressDto.cs @@ -0,0 +1,87 @@ +using cuqmbr.TravelGuide.Application.Common.Mappings; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Application.TicketGroups; + +public sealed class TicketGroupAddressDto : IMapFrom +{ + public Guid Uuid { get; set; } + + public string Name { get; set; } + + public double Longitude { get; set; } + + public double Latitude { get; set; } + + public Guid CountryUuid { get; set; } + + public string CountryName { get; set; } + + public Guid RegionUuid { get; set; } + + public string RegionName { get; set; } + + public Guid CityUuid { get; set; } + + public string CityName { get; set; } + + public TimeSpan TimeToNextAddress { get; set; } + + public decimal CostToNextAddress { get; set; } + + public TimeSpan CurrentAddressStopTime { get; set; } + + public short Order { get; set; } + + public Guid RouteAddressUuid { get; set; } + + public void Mapping(MappingProfile profile) + { + profile.CreateMap() + .ForMember( + d => d.Uuid, + opt => opt.MapFrom(s => s.RouteAddress.Address.Guid)) + .ForMember( + d => d.Name, + opt => opt.MapFrom(s => s.RouteAddress.Address.Name)) + .ForMember( + d => d.Longitude, + opt => opt.MapFrom(s => s.RouteAddress.Address.Longitude)) + .ForMember( + d => d.Latitude, + opt => opt.MapFrom(s => s.RouteAddress.Address.Latitude)) + .ForMember( + d => d.CountryUuid, + opt => opt.MapFrom(s => s.RouteAddress.Address.City.Region.Country.Guid)) + .ForMember( + d => d.CountryName, + opt => opt.MapFrom(s => s.RouteAddress.Address.City.Region.Country.Name)) + .ForMember( + d => d.RegionUuid, + opt => opt.MapFrom(s => s.RouteAddress.Address.City.Region.Guid)) + .ForMember( + d => d.RegionName, + opt => opt.MapFrom(s => s.RouteAddress.Address.City.Region.Name)) + .ForMember( + d => d.CityUuid, + opt => opt.MapFrom(s => s.RouteAddress.Address.City.Guid)) + .ForMember( + d => d.CityName, + opt => opt.MapFrom(s => s.RouteAddress.Address.City.Name)) + .ForMember( + d => d.TimeToNextAddress, + opt => opt.MapFrom(s => s.TimeToNextAddress)) + .ForMember( + d => d.CostToNextAddress, + opt => opt.MapFrom(s => s.CostToNextAddress)) + .ForMember( + d => d.CurrentAddressStopTime, + opt => opt.MapFrom(s => s.CurrentAddressStopTime)) + .ForMember( + d => d.Order, + opt => opt.MapFrom(s => s.RouteAddress.Order)) + .ForMember( + d => d.RouteAddressUuid, + opt => opt.MapFrom(s => s.RouteAddress.Guid)); + } +} diff --git a/src/Application/TicketGroups/TicketGroupCompanyDto.cs b/src/Application/TicketGroups/TicketGroupCompanyDto.cs new file mode 100644 index 0000000..d0e4163 --- /dev/null +++ b/src/Application/TicketGroups/TicketGroupCompanyDto.cs @@ -0,0 +1,25 @@ +using cuqmbr.TravelGuide.Application.Common.Mappings; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Application.TicketGroups; + +public sealed class TicketGroupCompanyDto : IMapFrom +{ + public string Uuid { get; set; } + + public string Name { get; set; } + + public string LegalAddress { get; set; } + + public string ContactEmail { get; set; } + + public string ContactPhoneNumber { get; set; } + + public void Mapping(MappingProfile profile) + { + profile.CreateMap() + .ForMember( + d => d.Uuid, + opt => opt.MapFrom(s => s.Guid)); + } +} diff --git a/src/Application/TicketGroups/TicketGroupDto.cs b/src/Application/TicketGroups/TicketGroupDto.cs index ae9e0e8..6c5b3c3 100644 --- a/src/Application/TicketGroups/TicketGroupDto.cs +++ b/src/Application/TicketGroups/TicketGroupDto.cs @@ -18,14 +18,29 @@ public sealed class TicketGroupDto : IMapFrom public DateOnly PassangerBirthDate { get; set; } + public string? PassangerEmail { get; set; } + public DateTimeOffset PurchaseTime { get; set; } - public bool Returned { get; set; } + public string Status { get; set; } + + + public DateTimeOffset DepartureTime { get; set; } + + public DateTimeOffset ArrivalTime { get; set; } public TimeSpan TravelTime { get; set; } + public TimeSpan TimeInStops { get; set; } - public ICollection Tickets { get; set; } + public int NumberOfTransfers { get; set; } + + public string Currency { get; set; } + + public decimal Cost { get; set; } + + + public ICollection Enrollments { get; set; } public void Mapping(MappingProfile profile) { @@ -40,6 +55,108 @@ public sealed class TicketGroupDto : IMapFrom d => d.PurchaseTime, opt => opt .MapFrom( - s => s.PurchaseTime)); + s => s.PurchaseTime)) + .ForMember( + d => d.Status, + opt => opt.MapFrom(s => s.Status.Name)) + .ForMember( + d => d.DepartureTime, + opt => opt.MapFrom( + (s, d) => + { + var departureRouteAddressId = + s.Tickets + .OrderBy(t => t.Order) + .First() + .DepartureRouteAddressId; + return + s.Tickets + .OrderBy(t => t.Order) + .First().VehicleEnrollment + .GetDepartureTime(departureRouteAddressId); + })) + .ForMember( + d => d.ArrivalTime, + opt => opt.MapFrom( + (s, d) => + { + var arrivalRouteAddressId = + s.Tickets + .OrderBy(t => t.Order) + .First() + .ArrivalRouteAddressId; + return + s.Tickets + .OrderBy(t => t.Order) + .First().VehicleEnrollment + .GetArrivalTime(arrivalRouteAddressId); + })) + .ForMember( + d => d.TravelTime, + opt => opt.MapFrom( + (s, d) => + { + var departureRouteAddressId = + s.Tickets + .OrderBy(t => t.Order) + .First() + .DepartureRouteAddressId; + var arrivalRouteAddressId = + s.Tickets + .OrderBy(t => t.Order) + .First() + .ArrivalRouteAddressId; + var departureTime = + s.Tickets + .OrderBy(t => t.Order) + .First().VehicleEnrollment + .GetDepartureTime(departureRouteAddressId); + var arrivalTime = + s.Tickets + .OrderBy(t => t.Order) + .First().VehicleEnrollment + .GetArrivalTime(departureRouteAddressId); + return arrivalTime - departureTime; + })) + .ForMember( + d => d.TimeInStops, + opt => opt.MapFrom( + (s, d) => + { + var timePeriodsInStops = + s.Tickets.Select(t => + { + var departureRouteAddressId = + t.DepartureRouteAddressId; + var arrivalRouteAddressId = + t.ArrivalRouteAddressId; + return + t.VehicleEnrollment.GetTimeInStops( + departureRouteAddressId, + arrivalRouteAddressId); + }); + return + timePeriodsInStops + .Aggregate(TimeSpan.Zero, + (sum, next) => sum += next); + })) + .ForMember( + d => d.NumberOfTransfers, + opt => opt.MapFrom(s => s.Tickets.Count() - 1)) + .ForMember( + d => d.Cost, + opt => opt.MapFrom( + (s, d) => + { + var costs = + s.Tickets.Select(t => t.Currency.Round(t.Cost)); + return + costs + .Aggregate((decimal)0, + (sum, next) => sum += next); + })) + .ForMember( + d => d.Enrollments, + opt => opt.MapFrom(s => s.Tickets)); } } diff --git a/src/Application/TicketGroups/TicketGroupVehicleDto.cs b/src/Application/TicketGroups/TicketGroupVehicleDto.cs new file mode 100644 index 0000000..006d245 --- /dev/null +++ b/src/Application/TicketGroups/TicketGroupVehicleDto.cs @@ -0,0 +1,95 @@ +using cuqmbr.TravelGuide.Application.Common.Mappings; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Application.TicketGroups; + +public sealed class TicketGroupVehicleDto : 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/TicketGroups/TicketGroupVehicleEnrollmentDto.cs b/src/Application/TicketGroups/TicketGroupVehicleEnrollmentDto.cs new file mode 100644 index 0000000..ac1ac64 --- /dev/null +++ b/src/Application/TicketGroups/TicketGroupVehicleEnrollmentDto.cs @@ -0,0 +1,99 @@ +using cuqmbr.TravelGuide.Application.Common.Mappings; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Application.TicketGroups; + +public sealed class TicketGroupVehicleEnrollmentDto : IMapFrom +{ + public DateTimeOffset DepartureTime { get; set; } + + public DateTimeOffset ArrivalTime { get; set; } + + public TimeSpan TravelTime { get; set; } + + public TimeSpan TimeMoving { get; set; } + + public TimeSpan TimeInStops { get; set; } + + public int NumberOfStops { get; set; } + + public string Currency { get; set; } + + public decimal Cost { get; set; } + + public Guid Uuid { get; set; } + + public short Order { get; set; } + + public TicketGroupCompanyDto Company { get; set; } + + public TicketGroupVehicleDto Vehicle { get; set; } + + public ICollection Addresses { get; set; } + + public void Mapping(MappingProfile profile) + { + profile.CreateMap() + .ForMember( + d => d.Uuid, + opt => opt.MapFrom(s => s.VehicleEnrollment.Guid)) + .ForMember( + d => d.DepartureTime, + opt => opt.MapFrom( + (s, d) => + { + return s.VehicleEnrollment + .GetDepartureTime(s.DepartureRouteAddressId); + })) + .ForMember( + d => d.ArrivalTime, + opt => opt.MapFrom( + (s, d) => + { + return s.VehicleEnrollment + .GetArrivalTime(s.ArrivalRouteAddressId); + })) + .ForMember( + d => d.TravelTime, + opt => opt.MapFrom( + (s, d) => + { + var departureTime = s.VehicleEnrollment + .GetDepartureTime(s.DepartureRouteAddressId); + var arrivalTime = s.VehicleEnrollment + .GetArrivalTime(s.ArrivalRouteAddressId); + return arrivalTime - departureTime; + })) + .ForMember( + d => d.TimeInStops, + opt => opt.MapFrom( + (s, d) => + { + return s.VehicleEnrollment.GetTimeInStops( + s.DepartureRouteAddressId, s.ArrivalRouteAddressId); + })) + .ForMember( + d => d.NumberOfStops, + opt => opt.MapFrom( + (s, d) => + { + return s.VehicleEnrollment.GetNumberOfStops( + s.DepartureRouteAddressId, s.ArrivalRouteAddressId); + })) + .ForMember( + d => d.Currency, + opt => opt.MapFrom(s => s.Currency)) + .ForMember( + d => d.Cost, + opt => opt.MapFrom(s => s.Currency.Round(s.Cost))) + .ForMember( + d => d.Company, + opt => opt.MapFrom(s => s.VehicleEnrollment.Vehicle.Company)) + .ForMember( + d => d.Vehicle, + opt => opt.MapFrom(s => s.VehicleEnrollment.Vehicle)) + .ForMember( + d => d.Addresses, + opt => opt.MapFrom(s => s.VehicleEnrollment.RouteAddressDetails)); + } +} diff --git a/src/Application/TicketGroups/ViewModels/GetTicketGroupsPageFilterViewModel.cs b/src/Application/TicketGroups/ViewModels/GetTicketGroupsPageFilterViewModel.cs index 82399f3..cee2f12 100644 --- a/src/Application/TicketGroups/ViewModels/GetTicketGroupsPageFilterViewModel.cs +++ b/src/Application/TicketGroups/ViewModels/GetTicketGroupsPageFilterViewModel.cs @@ -2,11 +2,21 @@ namespace cuqmbr.TravelGuide.Application.TicketGroups.ViewModels; public sealed class GetTicketGroupsPageFilterViewModel { - public string? Sex { get; set; } + public HashSet? PassangerSex { get; set; } - public DateOnly? BirthDateGreaterThanOrEqualTo { get; set; } + public DateOnly? PassangerBirthDateGreaterThanOrEqualTo { get; set; } - public DateOnly? BirthDateLessThanOrEqualTo { get; set; } + public DateOnly? PassangerBirthDateLessThanOrEqualTo { get; set; } - public Guid? CompanyUuid { get; set; } + public DateTimeOffset? PurchaseTimeGreaterThanOrEqualTo { get; set; } + + public DateTimeOffset? PurchaseTimeLessThanOrEqualTo { get; set; } + + public HashSet? Statuses { get; set; } + + public HashSet? VehicleTypes { get; set; } + + public TimeSpan? TravelTimeGreaterThanOrEqualTo { get; set; } + + public TimeSpan? TravelTimeLessThanOrEqualTo { get; set; } } diff --git a/src/Application/TicketGroups/ViewModels/UpdateTicketGroupViewModel.cs b/src/Application/TicketGroups/ViewModels/UpdateTicketGroupViewModel.cs deleted file mode 100644 index b58310e..0000000 --- a/src/Application/TicketGroups/ViewModels/UpdateTicketGroupViewModel.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace cuqmbr.TravelGuide.Application.TicketGroups.ViewModels; - -public sealed class UpdateTicketGroupViewModel -{ - public string PassangerFirstName { get; set; } - - public string PassangerLastName { get; set; } - - public string PassangerPatronymic { get; set; } - - public string PassangerSex { get; set; } - - public DateOnly PassangerBirthDate { get; set; } - - public DateTimeOffset PurchaseTime { get; set; } - - public bool Returned { get; set; } - - - public ICollection Tickets { get; set; } -} diff --git a/src/HttpApi/Controllers/TicketGroupsController.cs b/src/HttpApi/Controllers/TicketGroupsController.cs index adf8e3d..fff0d39 100644 --- a/src/HttpApi/Controllers/TicketGroupsController.cs +++ b/src/HttpApi/Controllers/TicketGroupsController.cs @@ -3,8 +3,12 @@ using Swashbuckle.AspNetCore.Annotations; using cuqmbr.TravelGuide.Domain.Enums; using cuqmbr.TravelGuide.Application.TicketGroups; using cuqmbr.TravelGuide.Application.TicketGroups.Commands.AddTicketGroup; +using cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroup; +using cuqmbr.TravelGuide.Application.TicketGroups.Queries.GetTicketGroupsPage; using cuqmbr.TravelGuide.Application.TicketGroups.ViewModels; using cuqmbr.TravelGuide.Application.TicketGroups.Models; +using cuqmbr.TravelGuide.Application.Common.Models; +using cuqmbr.TravelGuide.Application.Common.ViewModels; namespace cuqmbr.TravelGuide.HttpApi.Controllers; @@ -12,7 +16,7 @@ namespace cuqmbr.TravelGuide.HttpApi.Controllers; public class TicketGroupsController : ControllerBase { [HttpPost] - [SwaggerOperation("Add a ticketGroup")] + [SwaggerOperation("Add a ticket group")] [SwaggerResponse( StatusCodes.Status201Created, "Object successfuly created", typeof(TicketGroupDto))] @@ -67,149 +71,84 @@ public class TicketGroupsController : ControllerBase cancellationToken)); } - // [HttpGet] - // [SwaggerOperation("Get a list of all ticketGroups")] - // [SwaggerResponse( - // StatusCodes.Status200OK, "Request successful", - // typeof(PaginatedList))] - // [SwaggerResponse( - // StatusCodes.Status400BadRequest, "Input data validation error", - // typeof(HttpValidationProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status403Forbidden, - // "Not enough privileges to perform an action", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status500InternalServerError, "Internal server error", - // typeof(ProblemDetails))] - // public async Task> GetPage( - // [FromQuery] PageQuery pageQuery, [FromQuery] SearchQuery searchQuery, - // [FromQuery] SortQuery sortQuery, - // [FromQuery] GetTicketGroupsPageFilterViewModel filterQuery, - // CancellationToken cancellationToken) - // { - // return await Mediator.Send( - // new GetTicketGroupsPageQuery() - // { - // PageNumber = pageQuery.PageNumber, - // PageSize = pageQuery.PageSize, - // Search = searchQuery.Search, - // Sort = sortQuery.Sort, - // LongitudeGreaterOrEqualThan = - // filterQuery.LongitudeGreaterOrEqualThan, - // LongitudeLessOrEqualThan = - // filterQuery.LongitudeLessOrEqualThan, - // LatitudeGreaterOrEqualThan = - // filterQuery.LatitudeGreaterOrEqualThan, - // LatitudeLessOrEqualThan = - // filterQuery.LatitudeLessOrEqualThan, - // VehicleType = VehicleType.FromName(filterQuery.VehicleType), - // CountryGuid = filterQuery.CountryUuid, - // RegionGuid = filterQuery.RegionUuid, - // CityGuid = filterQuery.CityUuid - // }, - // cancellationToken); - // } - // - // [HttpGet("{uuid:guid}")] - // [SwaggerOperation("Get a ticketGroup by uuid")] - // [SwaggerResponse( - // StatusCodes.Status200OK, "Request successful", typeof(TicketGroupDto))] - // [SwaggerResponse( - // StatusCodes.Status400BadRequest, "Input data validation error", - // typeof(HttpValidationProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status403Forbidden, - // "Not enough privileges to perform an action", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status404NotFound, "Object not found", typeof(TicketGroupDto))] - // [SwaggerResponse( - // StatusCodes.Status500InternalServerError, "Internal server error", - // typeof(ProblemDetails))] - // public async Task Get( - // [FromRoute] Guid uuid, - // CancellationToken cancellationToken) - // { - // return await Mediator.Send(new GetTicketGroupQuery() { Guid = uuid }, - // cancellationToken); - // } - // - // [HttpPut("{uuid:guid}")] - // [SwaggerOperation("Update a ticketGroup")] - // [SwaggerResponse( - // StatusCodes.Status200OK, "Request successful", typeof(TicketGroupDto))] - // [SwaggerResponse( - // StatusCodes.Status400BadRequest, "Object already exists", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status400BadRequest, "Input data validation error", - // typeof(HttpValidationProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status403Forbidden, - // "Not enough privileges to perform an action", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status404NotFound, "Object not found", typeof(TicketGroupDto))] - // [SwaggerResponse( - // StatusCodes.Status404NotFound, "Parent object not found", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status500InternalServerError, "Internal server error", - // typeof(ProblemDetails))] - // public async Task Update( - // [FromRoute] Guid uuid, - // [FromBody] UpdateTicketGroupViewModel viewModel, - // CancellationToken cancellationToken) - // { - // return await Mediator.Send( - // new UpdateTicketGroupCommand() - // { - // Guid = uuid, - // Name = viewModel.Name, - // Longitude = viewModel.Longitude, - // Latitude = viewModel.Latitude, - // VehicleType = VehicleType.FromName(viewModel.VehicleType), - // CityGuid = viewModel.CityUuid - // }, - // cancellationToken); - // } - // - // [HttpDelete("{uuid:guid}")] - // [SwaggerOperation("Delete a ticketGroup")] - // [SwaggerResponse(StatusCodes.Status204NoContent, "Request successful")] - // [SwaggerResponse( - // StatusCodes.Status400BadRequest, "Input data validation error", - // typeof(HttpValidationProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status403Forbidden, - // "Not enough privileges to perform an action", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status404NotFound, "Object not found", - // typeof(ProblemDetails))] - // [SwaggerResponse( - // StatusCodes.Status500InternalServerError, "Internal server error", - // typeof(ProblemDetails))] - // public async Task Delete( - // [FromRoute] Guid uuid, - // CancellationToken cancellationToken) - // { - // await Mediator.Send( - // new DeleteTicketGroupCommand() { Guid = uuid }, - // cancellationToken); - // return StatusCode(StatusCodes.Status204NoContent); - // } + [HttpGet] + [SwaggerOperation("Get a list of all ticket groups")] + [SwaggerResponse( + StatusCodes.Status200OK, "Request successful", + typeof(PaginatedList))] + [SwaggerResponse( + StatusCodes.Status400BadRequest, "Input data validation error", + typeof(HttpValidationProblemDetails))] + [SwaggerResponse( + StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status403Forbidden, + "Not enough privileges to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status500InternalServerError, "Internal server error", + typeof(ProblemDetails))] + public async Task> GetPage( + [FromQuery] PageQuery pageQuery, [FromQuery] SearchQuery searchQuery, + [FromQuery] SortQuery sortQuery, + [FromQuery] GetTicketGroupsPageFilterViewModel filterQuery, + CancellationToken cancellationToken) + { + return await Mediator.Send( + new GetTicketGroupsPageQuery() + { + PageNumber = pageQuery.PageNumber, + PageSize = pageQuery.PageSize, + Search = searchQuery.Search, + Sort = sortQuery.Sort, + PassangerSex = filterQuery.PassangerSex? + .Select(s => Sex.FromName(s)).ToHashSet(), + PassangerBirthDateGreaterThanOrEqualTo = + filterQuery.PassangerBirthDateGreaterThanOrEqualTo, + PassangerBirthDateLessThanOrEqualTo = + filterQuery.PassangerBirthDateLessThanOrEqualTo, + PurchaseTimeGreaterThanOrEqualTo = + filterQuery.PurchaseTimeGreaterThanOrEqualTo, + PurchaseTimeLessThanOrEqualTo = + filterQuery.PurchaseTimeLessThanOrEqualTo, + Statuses = filterQuery.Statuses? + .Select(s => TicketStatus.FromName(s)).ToHashSet(), + VehicleTypes = filterQuery.VehicleTypes? + .Select(vt => VehicleType.FromName(vt)).ToHashSet(), + TravelTimeGreaterThanOrEqualTo = + filterQuery.TravelTimeGreaterThanOrEqualTo, + TravelTimeLessThanOrEqualTo = + filterQuery.TravelTimeLessThanOrEqualTo + }, + cancellationToken); + } + + [HttpGet("{uuid:guid}")] + [SwaggerOperation("Get a ticket group by uuid")] + [SwaggerResponse( + StatusCodes.Status200OK, "Request successful", typeof(TicketGroupDto))] + [SwaggerResponse( + StatusCodes.Status400BadRequest, "Input data validation error", + typeof(HttpValidationProblemDetails))] + [SwaggerResponse( + StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status403Forbidden, + "Not enough privileges to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status404NotFound, "Object not found", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status500InternalServerError, "Internal server error", + typeof(ProblemDetails))] + public async Task Get( + [FromRoute] Guid uuid, + CancellationToken cancellationToken) + { + return await Mediator.Send(new GetTicketGroupQuery() { Guid = uuid }, + cancellationToken); + } }