diff --git a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs index ad14287..ba4f218 100644 --- a/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs +++ b/TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs @@ -356,7 +356,7 @@ public async Task GetOrganizerEvents_WhenPaginationSucceeds_ShouldReturnPaginate ); var organizerEvents = organizer.Events.AsQueryable(); - eventRepositoryMock.Setup(p => p.GetEventsByOranizer(organizer)).Returns(organizerEvents); + eventRepositoryMock.Setup(p => p.GetEventsByOranizerAsync(organizer)).ReturnsAsync(organizerEvents); paginationServiceMock .Setup(p => p.PaginateAsync(organizerEvents, pageSize, page)) @@ -425,7 +425,7 @@ public async Task GetOrganizerEvents_WhenPaginationFails_ShouldPropagateError() var ticketServiceMock = new Mock(); var organizerEvents = organizer.Events.AsQueryable(); - eventRepositoryMock.Setup(p => p.GetEventsByOranizer(organizer)).Returns(organizerEvents); + eventRepositoryMock.Setup(p => p.GetEventsByOranizerAsync(organizer)).ReturnsAsync(organizerEvents); paginationServiceMock .Setup(p => p.PaginateAsync(organizerEvents, pageSize, page)) @@ -477,7 +477,7 @@ public async Task GetEventsAsync_WhenPaginationSucceeds_ShouldReturnPaginatedEve ); var eventsQueryable = events.AsQueryable(); - eventRepositoryMock.Setup(p => p.GetEvents()).Returns(eventsQueryable); + eventRepositoryMock.Setup(p => p.GetEventsAsync()).ReturnsAsync(eventsQueryable); paginationServiceMock .Setup(p => p.PaginateAsync(eventsQueryable, pageSize, page)) @@ -541,7 +541,7 @@ public async Task GetEventsAsync_WhenPaginationFails_ShouldPropagateError() var ticketServiceMock = new Mock(); var eventsQueryable = events.AsQueryable(); - eventRepositoryMock.Setup(p => p.GetEvents()).Returns(eventsQueryable); + eventRepositoryMock.Setup(p => p.GetEventsAsync()).ReturnsAsync(eventsQueryable); paginationServiceMock .Setup(p => p.PaginateAsync(eventsQueryable, pageSize, page)) @@ -583,7 +583,7 @@ public async Task GetEventsPaginationDetailsAsync_WhenSuccessful_ShouldReturnPag var ticketServiceMock = new Mock(); var eventsQueryable = events.AsQueryable(); - eventRepositoryMock.Setup(p => p.GetEvents()).Returns(eventsQueryable); + eventRepositoryMock.Setup(p => p.GetEventsAsync()).ReturnsAsync(eventsQueryable); var paginationDetails = new PaginationDetails(1, 3); paginationServiceMock @@ -625,7 +625,7 @@ public async Task GetEventsPaginationDetailsAsync_WhenFails_ShouldReturnError() var ticketServiceMock = new Mock(); var eventsQueryable = events.AsQueryable(); - eventRepositoryMock.Setup(p => p.GetEvents()).Returns(eventsQueryable); + eventRepositoryMock.Setup(p => p.GetEventsAsync()).ReturnsAsync(eventsQueryable); paginationServiceMock .Setup(p => p.GetPaginationDetailsAsync(eventsQueryable, pageSize)) diff --git a/TickAPI/TickAPI/Events/Abstractions/IEventRepository.cs b/TickAPI/TickAPI/Events/Abstractions/IEventRepository.cs index 4a2103e..0dcf319 100644 --- a/TickAPI/TickAPI/Events/Abstractions/IEventRepository.cs +++ b/TickAPI/TickAPI/Events/Abstractions/IEventRepository.cs @@ -8,8 +8,8 @@ namespace TickAPI.Events.Abstractions; public interface IEventRepository { public Task AddNewEventAsync(Event @event); - public IQueryable GetEvents(); - public IQueryable GetEventsByOranizer(Organizer organizer); + public Task> GetEventsAsync(); + public Task> GetEventsByOranizerAsync(Organizer organizer); public Task> GetEventByIdAsync(Guid eventId); public Task SaveEventAsync(Event ev); public Task> GetEventByIdAndOrganizerAsync(Guid eventId, Organizer organizer); diff --git a/TickAPI/TickAPI/Events/Repositories/BaseEventRepository.cs b/TickAPI/TickAPI/Events/Repositories/BaseEventRepository.cs new file mode 100644 index 0000000..a855d85 --- /dev/null +++ b/TickAPI/TickAPI/Events/Repositories/BaseEventRepository.cs @@ -0,0 +1,100 @@ +using Microsoft.EntityFrameworkCore; +using TickAPI.Common.Results; +using TickAPI.Common.Results.Generic; +using TickAPI.Common.TickApiDbContext; +using TickAPI.Events.Abstractions; +using TickAPI.Events.Models; +using TickAPI.Organizers.Models; + +namespace TickAPI.Events.Repositories; + +public class BaseEventRepository : IEventRepository +{ + private readonly TickApiDbContext _tickApiDbContext; + + public BaseEventRepository(TickApiDbContext tickApiDbContext) + { + _tickApiDbContext = tickApiDbContext; + } + + public async Task AddNewEventAsync(Event @event) + { + _tickApiDbContext.Events.Add(@event); + await _tickApiDbContext.SaveChangesAsync(); + } + + public async Task> GetEventsAsync() + { + return _tickApiDbContext.Events + .Include(e => e.Address) + .Include(e => e.TicketTypes) + .Include(e => e.Categories); + } + + public async Task> GetEventsByOranizerAsync(Organizer organizer) + { + return _tickApiDbContext.Events + .Include(e => e.Address) + .Include(e => e.TicketTypes) + .Include(e => e.Categories) + .Where(e => e.Organizer.Id == organizer.Id); + } + + public async Task> GetEventByIdAsync(Guid eventId) + { + var @event = await _tickApiDbContext.Events + .Include(e => e.Address) + .Include(e => e.TicketTypes) + .Include(e => e.Categories) + .FirstOrDefaultAsync(e => e.Id == eventId); + + if (@event == null) + { + return Result.Failure(StatusCodes.Status404NotFound, $"event with id {eventId} not found"); + } + + return Result.Success(@event); + } + + public async Task SaveEventAsync(Event ev) + { + var fromDb = await GetEventByIdAsync(ev.Id); + if (fromDb.IsError) + return Result.PropagateError(fromDb); + await _tickApiDbContext.SaveChangesAsync(); + return Result.Success(); + } + + public async Task> GetEventByIdAndOrganizerAsync(Guid eventId, Organizer organizer) + { + var organizerEvents = GetEventsByOranizerAsync(organizer); + var ev = await (await organizerEvents).Where(e => e.Id == eventId).FirstAsync(); + if (ev is null) + { + return Result.Failure(StatusCodes.Status404NotFound, $"Event with id {eventId} not found for organizer with id {organizer.Id}"); + } + return Result.Success(ev); + } + + public async Task GetEventRevenue(Guid eventId) + { + var query = from tickets in _tickApiDbContext.Tickets + join _ticketTypes in _tickApiDbContext.TicketTypes on tickets.Type.Id equals _ticketTypes.Id + join events in _tickApiDbContext.Events on _ticketTypes.Event.Id equals events.Id + where events.Id == eventId + select new { price = _ticketTypes.Price }; + var val = await query.SumAsync(x => x.price); + return val; + } + + public async Task GetEventSoldTicketsCount(Guid eventId) + { + var query = from tickets in _tickApiDbContext.Tickets + join _ticketTypes in _tickApiDbContext.TicketTypes on tickets.Type.Id equals _ticketTypes.Id + join events in _tickApiDbContext.Events on _ticketTypes.Event.Id equals events.Id + where events.Id == eventId + select new { id = tickets.Id }; + var val = await query.CountAsync(); + return val; + } +} \ No newline at end of file diff --git a/TickAPI/TickAPI/Events/Repositories/EventRepository.cs b/TickAPI/TickAPI/Events/Repositories/EventRepository.cs index 60c95aa..65d2772 100644 --- a/TickAPI/TickAPI/Events/Repositories/EventRepository.cs +++ b/TickAPI/TickAPI/Events/Repositories/EventRepository.cs @@ -10,91 +10,110 @@ namespace TickAPI.Events.Repositories; public class EventRepository : IEventRepository { + private readonly BaseEventRepository _baseEventRepository; private readonly TickApiDbContext _tickApiDbContext; - public EventRepository(TickApiDbContext tickApiDbContext) + public EventRepository(BaseEventRepository baseEventRepository, TickApiDbContext tickApiDbContext) { + _baseEventRepository = baseEventRepository; _tickApiDbContext = tickApiDbContext; } - - public async Task AddNewEventAsync(Event @event) + + public Task AddNewEventAsync(Event @event) { - _tickApiDbContext.Events.Add(@event); - await _tickApiDbContext.SaveChangesAsync(); + return _baseEventRepository.AddNewEventAsync(@event); } - public IQueryable GetEvents() + public async Task> GetEventsAsync() { - return _tickApiDbContext.Events - .Include(e => e.Address) - .Include(e => e.TicketTypes) - .Include(e => e.Categories); + var events = await _baseEventRepository.GetEventsAsync(); + await UpdateEventsStatuses(events); + return events; } - public IQueryable GetEventsByOranizer(Organizer organizer) + public async Task> GetEventsByOranizerAsync(Organizer organizer) { - return _tickApiDbContext.Events - .Include(e => e.Address) - .Include(e => e.TicketTypes) - .Include(e => e.Categories) - .Where(e => e.Organizer.Id == organizer.Id); + var events = await _baseEventRepository.GetEventsByOranizerAsync(organizer); + await UpdateEventsStatuses(events); + return events; } public async Task> GetEventByIdAsync(Guid eventId) { - var @event = await _tickApiDbContext.Events - .Include(e => e.Address) - .Include(e => e.TicketTypes) - .Include(e => e.Categories) - .FirstOrDefaultAsync(e => e.Id == eventId); - - if (@event == null) - { - return Result.Failure(StatusCodes.Status404NotFound, $"event with id {eventId} not found"); - } - - return Result.Success(@event); + var evResult = await _baseEventRepository.GetEventByIdAsync(eventId); + if (evResult.IsError) + return evResult; + var ev = evResult.Value!; + await UpdateEventStatuses([ev.Id]); + return Result.Success(ev); } - public async Task SaveEventAsync(Event ev) + public Task SaveEventAsync(Event ev) { - var fromDb = await GetEventByIdAsync(ev.Id); - if (fromDb.IsError) - return Result.PropagateError(fromDb); - await _tickApiDbContext.SaveChangesAsync(); - return Result.Success(); + return _baseEventRepository.SaveEventAsync(ev); } public async Task> GetEventByIdAndOrganizerAsync(Guid eventId, Organizer organizer) { - var organizerEvents = GetEventsByOranizer(organizer); - var ev = await organizerEvents.Where(e => e.Id == eventId).FirstAsync(); - if (ev is null) - { - return Result.Failure(StatusCodes.Status404NotFound, $"Event with id {eventId} not found for organizer with id {organizer.Id}"); - } + var evResult = await _baseEventRepository.GetEventByIdAndOrganizerAsync(eventId, organizer); + if (evResult.IsError) + return evResult; + var ev = evResult.Value!; + await UpdateEventStatuses([ev.Id]); return Result.Success(ev); } - public async Task GetEventRevenue(Guid eventId) + public Task GetEventRevenue(Guid eventId) + { + return _baseEventRepository.GetEventRevenue(eventId); + } + + public Task GetEventSoldTicketsCount(Guid eventId) { - var query = from tickets in _tickApiDbContext.Tickets - join _ticketTypes in _tickApiDbContext.TicketTypes on tickets.Type.Id equals _ticketTypes.Id - join events in _tickApiDbContext.Events on _ticketTypes.Event.Id equals events.Id - where events.Id == eventId - select new { price = _ticketTypes.Price }; - var val = await query.SumAsync(x => x.price); - return val; + return _baseEventRepository.GetEventSoldTicketsCount(eventId); } - - public async Task GetEventSoldTicketsCount(Guid eventId) + + private async Task UpdateEventStatuses(List guids) + { + var eventsQuery = _tickApiDbContext.Events + .Where(e => guids.Contains(e.Id)); + + await UpdateEventsStatuses(eventsQuery); + } + + private async Task UpdateEventsStatuses(IQueryable eventsQuery) { - var query = from tickets in _tickApiDbContext.Tickets - join _ticketTypes in _tickApiDbContext.TicketTypes on tickets.Type.Id equals _ticketTypes.Id - join events in _tickApiDbContext.Events on _ticketTypes.Event.Id equals events.Id - where events.Id == eventId - select new { id = tickets.Id }; - var val = await query.CountAsync(); - return val; + var now = DateTime.UtcNow; + var events = await eventsQuery + .Include(e => e.TicketTypes) + .ThenInclude(tt => tt.Tickets) + .ToListAsync(); + + foreach (var ev in events) + { + if (ev.EndDate < now) + { + ev.EventStatus = EventStatus.Finished; + } + else if (ev.StartDate <= now && ev.EndDate >= now) + { + ev.EventStatus = EventStatus.InProgress; + } + else + { + var totalTickets = ev.TicketTypes.Sum(tt => tt.MaxCount); + var soldTickets = ev.TicketTypes.Sum(tt => tt.Tickets.Count); + if (totalTickets > 0 && soldTickets >= totalTickets) + { + ev.EventStatus = EventStatus.SoldOut; + } + else + { + ev.EventStatus = EventStatus.TicketsAvailable; + } + } + } + + await _tickApiDbContext.SaveChangesAsync(); } -} \ No newline at end of file +} diff --git a/TickAPI/TickAPI/Events/Services/EventService.cs b/TickAPI/TickAPI/Events/Services/EventService.cs index 546da79..641544d 100644 --- a/TickAPI/TickAPI/Events/Services/EventService.cs +++ b/TickAPI/TickAPI/Events/Services/EventService.cs @@ -118,27 +118,27 @@ public async Task> CreateNewEventAsync(string name, string descri public async Task>> GetOrganizerEventsAsync(Organizer organizer, int page, int pageSize, EventFiltersDto? eventFilters = null) { - var organizerEvents = _eventRepository.GetEventsByOranizer(organizer); + var organizerEvents = await _eventRepository.GetEventsByOranizerAsync(organizer); var filteredOrganizerEvents = ApplyEventFilters(organizerEvents, eventFilters); return await GetPaginatedEventsAsync(filteredOrganizerEvents, page, pageSize); } public async Task> GetOrganizerEventsPaginationDetailsAsync(Organizer organizer, int pageSize) { - var organizerEvents = _eventRepository.GetEventsByOranizer(organizer); + var organizerEvents = await _eventRepository.GetEventsByOranizerAsync(organizer); return await _paginationService.GetPaginationDetailsAsync(organizerEvents, pageSize); } public async Task>> GetEventsAsync(int page, int pageSize, EventFiltersDto? eventFilters = null) { - var events = _eventRepository.GetEvents(); + var events = await _eventRepository.GetEventsAsync(); var filteredEvents = ApplyEventFilters(events, eventFilters); return await GetPaginatedEventsAsync(filteredEvents, page, pageSize); } public async Task> GetEventsPaginationDetailsAsync(int pageSize) { - var events = _eventRepository.GetEvents(); + var events = await _eventRepository.GetEventsAsync(); return await _paginationService.GetPaginationDetailsAsync(events, pageSize); } diff --git a/TickAPI/TickAPI/Program.cs b/TickAPI/TickAPI/Program.cs index 5a983d6..197d41c 100644 --- a/TickAPI/TickAPI/Program.cs +++ b/TickAPI/TickAPI/Program.cs @@ -110,6 +110,7 @@ // Add event services. builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Add address services. builder.Services.AddScoped();