From e3c5f252e64d4ede62dba0e7204bbbab9727bbf2 Mon Sep 17 00:00:00 2001 From: LuukvH Date: Thu, 29 Aug 2024 22:24:11 +0200 Subject: [PATCH] feat: Add child scheduling Add basic child scheduling feature. --- .../Api/Controllers/GroupsController.cs | 2 +- .../Api/Controllers/SchedulesController.cs | 39 +++++ .../Api/Controllers/TimeSlotsController.cs | 2 +- .../Api/appsettings.Development.json | 2 +- .../Scheduling/Application/Application.csproj | 43 +++--- .../Persistence/IScheduleRepository.cs | 13 ++ .../AddSchedule/AddScheduleCommand.cs | 26 ++++ .../AddSchedule/AddScheduleCommandHandler.cs | 39 +++++ .../AddScheduleCommandValidator.cs | 26 ++++ .../GetChildSchedules/ChildScheduleListVM.cs | 23 +++ .../GetChildSchedulesQuery.cs | 13 ++ .../GetChildSchedulesQueryHandler.cs | 33 +++++ .../Application/Profiles/MappingProfile.cs | 13 ++ .../Scheduling/Domain/Entities/Schedule.cs | 16 +++ .../Domain/Entities/ScheduleRule.cs | 15 ++ .../Infrastructure/ConfigureServices.cs | 1 + .../Infrastructure/MigrationDbContext.cs | 3 +- ...240829202057_AddScheduleTables.Designer.cs | 136 ++++++++++++++++++ .../20240829202057_AddScheduleTables.cs | 67 +++++++++ .../MigrationDbContextModelSnapshot.cs | 67 ++++++++- .../Repositories/ScheduleRepository.cs | 27 ++++ .../Infrastructure/SchedulingDbContext.cs | 5 + 22 files changed, 584 insertions(+), 27 deletions(-) create mode 100644 src/Services/Scheduling/Api/Controllers/SchedulesController.cs create mode 100644 src/Services/Scheduling/Application/Contracts/Persistence/IScheduleRepository.cs create mode 100644 src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommand.cs create mode 100644 src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommandHandler.cs create mode 100644 src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommandValidator.cs create mode 100644 src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/ChildScheduleListVM.cs create mode 100644 src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/GetChildSchedulesQuery.cs create mode 100644 src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/GetChildSchedulesQueryHandler.cs create mode 100644 src/Services/Scheduling/Domain/Entities/Schedule.cs create mode 100644 src/Services/Scheduling/Domain/Entities/ScheduleRule.cs create mode 100644 src/Services/Scheduling/Infrastructure/Migrations/20240829202057_AddScheduleTables.Designer.cs create mode 100644 src/Services/Scheduling/Infrastructure/Migrations/20240829202057_AddScheduleTables.cs create mode 100644 src/Services/Scheduling/Infrastructure/Repositories/ScheduleRepository.cs diff --git a/src/Services/Scheduling/Api/Controllers/GroupsController.cs b/src/Services/Scheduling/Api/Controllers/GroupsController.cs index 19ca2422..b7015401 100644 --- a/src/Services/Scheduling/Api/Controllers/GroupsController.cs +++ b/src/Services/Scheduling/Api/Controllers/GroupsController.cs @@ -25,7 +25,7 @@ public GroupsController(IMediator mediator, ILogger logger) public async Task>> ListGroups([FromQuery] ListGroupsQuery listGroupsQuery) { var dtos = await _mediator.Send(listGroupsQuery); - Response.Headers.Add("x-Total", dtos.TotalCount.ToString()); + Response.Headers.Append("x-Total", dtos.TotalCount.ToString()); return Ok(dtos); } diff --git a/src/Services/Scheduling/Api/Controllers/SchedulesController.cs b/src/Services/Scheduling/Api/Controllers/SchedulesController.cs new file mode 100644 index 00000000..365d8a4c --- /dev/null +++ b/src/Services/Scheduling/Api/Controllers/SchedulesController.cs @@ -0,0 +1,39 @@ +using KDVManager.Services.Scheduling.Application.Features.Groups.Queries.ListGroups; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using KDVManager.Services.Scheduling.Application.Contracts.Pagination; +using KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules; +using System.Net; +using KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule; + +namespace KDVManager.Services.Scheduling.Api.Controllers; + +[ApiController] +[Route("v1/[controller]")] +public class SchedulesController : ControllerBase +{ + private readonly IMediator _mediator; + private readonly ILogger _logger; + + public SchedulesController(IMediator mediator, ILogger logger) + { + _logger = logger; + _mediator = mediator; + } + + [HttpGet("", Name = "GetChildSchedules")] + public async Task>> ListScheduleItems([FromQuery] GetChildSchedulesQuery getChildSchedulesQuery) + { + var dtos = await _mediator.Send(getChildSchedulesQuery); + return Ok(dtos); + } + + [HttpPost(Name = "AddSchedule")] + [ProducesResponseType(typeof(Guid), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(UnprocessableEntityResponse), (int)HttpStatusCode.UnprocessableEntity)] + public async Task> AddScheduleItem([FromBody] AddScheduleCommand addScheduleCommand) + { + var id = await _mediator.Send(addScheduleCommand); + return Ok(id); + } +} diff --git a/src/Services/Scheduling/Api/Controllers/TimeSlotsController.cs b/src/Services/Scheduling/Api/Controllers/TimeSlotsController.cs index 8f9db7e8..091e772a 100644 --- a/src/Services/Scheduling/Api/Controllers/TimeSlotsController.cs +++ b/src/Services/Scheduling/Api/Controllers/TimeSlotsController.cs @@ -24,7 +24,7 @@ public TimeSlotsController(IMediator mediator, ILogger logg public async Task>> ListTimeSlots([FromQuery] ListTimeSlotsQuery listTimeSlotsQuery) { var dtos = await _mediator.Send(listTimeSlotsQuery); - Response.Headers.Add("x-Total", dtos.TotalCount.ToString()); + Response.Headers.Append("x-Total", dtos.TotalCount.ToString()); return Ok(dtos); } diff --git a/src/Services/Scheduling/Api/appsettings.Development.json b/src/Services/Scheduling/Api/appsettings.Development.json index dd18e6b9..2b44ffed 100644 --- a/src/Services/Scheduling/Api/appsettings.Development.json +++ b/src/Services/Scheduling/Api/appsettings.Development.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "KDVManagerSchedulingConnectionString": "Server=127.0.0.1; port=5432; database=KDVManagerSchedulingDB; pooling=true; Integrated Security=true;" + "KDVManagerSchedulingConnectionString": "Server=127.0.0.1; port=5432; database=KDVManagerSchedulingDB; pooling=true;" }, "Auth0": { "Domain": "kdvmanager.eu.auth0.com", diff --git a/src/Services/Scheduling/Application/Application.csproj b/src/Services/Scheduling/Application/Application.csproj index 50daf2be..0de7f67f 100644 --- a/src/Services/Scheduling/Application/Application.csproj +++ b/src/Services/Scheduling/Application/Application.csproj @@ -1,22 +1,21 @@ - - - - net8.0 - - - - - - - - - - - - - - - - - - + + + net8.0 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Services/Scheduling/Application/Contracts/Persistence/IScheduleRepository.cs b/src/Services/Scheduling/Application/Contracts/Persistence/IScheduleRepository.cs new file mode 100644 index 00000000..323555e0 --- /dev/null +++ b/src/Services/Scheduling/Application/Contracts/Persistence/IScheduleRepository.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using KDVManager.Services.Scheduling.Application.Contracts.Persistence; +using KDVManager.Services.Scheduling.Domain.Entities; +using KDVManager.Services.Scheduling.Domain.Interfaces; + +namespace KDVManager.Services.Scheduling.Application.Contracts.Persistence; + +public interface IScheduleRepository : IAsyncRepository +{ + Task> GetSchedulesByChildIdAsync(Guid childId); +} diff --git a/src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommand.cs b/src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommand.cs new file mode 100644 index 00000000..e120f062 --- /dev/null +++ b/src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommand.cs @@ -0,0 +1,26 @@ +using System; +using MediatR; +using System.Collections.Generic; + +namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule; + +public class AddScheduleCommand : IRequest +{ + public Guid ChildId { get; set; } + + public Guid GroupId { get; set; } + + public DateTime StartDate { get; set; } + + public DateTime? EndDate { get; set; } + + // Collection of nested schedules + public ICollection ScheduleRules { get; set; } = new List(); + + public class ScheduleRule + { + public DayOfWeek Day { get; set; } + + public Guid TimeSlotId { get; set; } + } +} diff --git a/src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommandHandler.cs b/src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommandHandler.cs new file mode 100644 index 00000000..8ab48726 --- /dev/null +++ b/src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommandHandler.cs @@ -0,0 +1,39 @@ +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using KDVManager.Services.Scheduling.Application.Contracts.Persistence; +using KDVManager.Services.Scheduling.Domain.Entities; +using MediatR; +using Microsoft.Extensions.Logging; + +namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule; + +public class AddScheduleCommandHandler : IRequestHandler +{ + private readonly IScheduleRepository _scheduleRepository; + private readonly IMapper _mapper; + + public AddScheduleCommandHandler(IScheduleRepository scheduleRepository, IMapper mapper) + { + _scheduleRepository = scheduleRepository; + _mapper = mapper; + } + + public async Task Handle(AddScheduleCommand request, CancellationToken cancellationToken) + { + var validator = new AddScheduleCommandValidator(); + var validationResult = await validator.ValidateAsync(request); + + if (!validationResult.IsValid) + throw new Exceptions.ValidationException(validationResult); + + var schedule = _mapper.Map(request); + + schedule = await _scheduleRepository.AddAsync(schedule); + + return schedule.Id; + } +} + diff --git a/src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommandValidator.cs b/src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommandValidator.cs new file mode 100644 index 00000000..58685dcf --- /dev/null +++ b/src/Services/Scheduling/Application/Features/Schedules/Commands/AddSchedule/AddScheduleCommandValidator.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using FluentValidation; +using KDVManager.Services.Scheduling.Application.Contracts.Persistence; + +namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule; + +public class AddScheduleCommandValidator : AbstractValidator +{ + public AddScheduleCommandValidator() + { + + RuleFor(AddScheduleCommand => AddScheduleCommand.ChildId) + .NotEmpty() + .NotNull(); + + RuleFor(AddScheduleCommand => AddScheduleCommand.StartDate) + .NotEmpty() + .NotNull(); + + RuleFor(AddScheduleCommand => AddScheduleCommand.EndDate) + .GreaterThan(AddScheduleCommand => AddScheduleCommand.StartDate) + .When(AddScheduleCommand => AddScheduleCommand.EndDate.HasValue); + } +} diff --git a/src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/ChildScheduleListVM.cs b/src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/ChildScheduleListVM.cs new file mode 100644 index 00000000..288c5f7a --- /dev/null +++ b/src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/ChildScheduleListVM.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules; + +public class ChildScheduleListVM +{ + public Guid Id { get; set; } + public Guid ChildId { get; set; } + public DateTime StartDate { get; set; } + public DateTime? EndDate { get; set; } + + // Collection of nested schedules + public ICollection ScheduleRules { get; set; } = new List(); + + public class ScheduleRule + { + public DayOfWeek Day { get; set; } + + public Guid TimeSlotId { get; set; } + } +} + diff --git a/src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/GetChildSchedulesQuery.cs b/src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/GetChildSchedulesQuery.cs new file mode 100644 index 00000000..b7ebdcf6 --- /dev/null +++ b/src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/GetChildSchedulesQuery.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using KDVManager.Services.Scheduling.Domain; +using KDVManager.Services.Scheduling.Application.Contracts.Pagination; +using MediatR; + +namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules; + +public class GetChildSchedulesQuery : IRequest> +{ + public Guid ChildId { get; set; } +} + diff --git a/src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/GetChildSchedulesQueryHandler.cs b/src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/GetChildSchedulesQueryHandler.cs new file mode 100644 index 00000000..2ebf81b3 --- /dev/null +++ b/src/Services/Scheduling/Application/Features/Schedules/Queries/GetChildSchedules/GetChildSchedulesQueryHandler.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using KDVManager.Services.Scheduling.Application.Contracts.Persistence; +using KDVManager.Services.Scheduling.Application.Contracts.Pagination; +using KDVManager.Services.Scheduling.Domain.Entities; +using MediatR; + +namespace KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules; + +public class GetChildSchedulesQueryHandler : IRequestHandler> +{ + private readonly IScheduleRepository _scheduleRepository; + private readonly IMapper _mapper; + + public GetChildSchedulesQueryHandler(IMapper mapper, IScheduleRepository scheduleRepository) + { + _scheduleRepository = scheduleRepository; + _mapper = mapper; + } + + public async Task> Handle(GetChildSchedulesQuery request, CancellationToken cancellationToken) + { + var scheduleItems = await _scheduleRepository.GetSchedulesByChildIdAsync(request.ChildId); + + List childScheduleListVMs = _mapper.Map>(scheduleItems); + + return new List(childScheduleListVMs); + } +} + diff --git a/src/Services/Scheduling/Application/Profiles/MappingProfile.cs b/src/Services/Scheduling/Application/Profiles/MappingProfile.cs index 4c70a424..8af9ff6b 100644 --- a/src/Services/Scheduling/Application/Profiles/MappingProfile.cs +++ b/src/Services/Scheduling/Application/Profiles/MappingProfile.cs @@ -4,6 +4,8 @@ using KDVManager.Services.Scheduling.Application.Features.Groups.Queries.ListGroups; using KDVManager.Services.Scheduling.Application.Features.TimeSlots.Commands.AddTimeSlot; using KDVManager.Services.Scheduling.Application.Features.TimeSlots.Queries.ListTimeSlots; +using KDVManager.Services.Scheduling.Application.Features.Schedules.Commands.AddSchedule; +using KDVManager.Services.Scheduling.Application.Features.Schedules.Queries.GetChildSchedules; using KDVManager.Services.Scheduling.Domain.Entities; namespace KDVManager.Services.Scheduling.Application.Profiles; @@ -12,13 +14,24 @@ public class MappingProfile : Profile { public MappingProfile() { + // Group Mappings CreateMap(); CreateMap().ReverseMap(); CreateMap(); + // TimeSlot Mappings CreateMap(); CreateMap().ReverseMap(); CreateMap(); + + // Schedule Mappings + CreateMap() + .ForMember(dest => dest.ScheduleRules, opt => opt.MapFrom(src => src.ScheduleRules)); + CreateMap(); + + CreateMap() + .ForMember(dest => dest.ScheduleRules, opt => opt.MapFrom(src => src.ScheduleRules)); + CreateMap(); } } diff --git a/src/Services/Scheduling/Domain/Entities/Schedule.cs b/src/Services/Scheduling/Domain/Entities/Schedule.cs new file mode 100644 index 00000000..584167bc --- /dev/null +++ b/src/Services/Scheduling/Domain/Entities/Schedule.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using KDVManager.Services.Scheduling.Domain.Interfaces; + +namespace KDVManager.Services.Scheduling.Domain.Entities; + +public class Schedule : IMustHaveTenant +{ + public Guid Id { get; set; } + public Guid TenantId { get; set; } + public Guid ChildId { get; set; } + public Guid GroupId { get; set; } + public DateTime StartDate { get; set; } + public DateTime? EndDate { get; set; } + public ICollection ScheduleRules { get; set; } +} \ No newline at end of file diff --git a/src/Services/Scheduling/Domain/Entities/ScheduleRule.cs b/src/Services/Scheduling/Domain/Entities/ScheduleRule.cs new file mode 100644 index 00000000..1305c90c --- /dev/null +++ b/src/Services/Scheduling/Domain/Entities/ScheduleRule.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations; +using KDVManager.Services.Scheduling.Domain.Interfaces; + +namespace KDVManager.Services.Scheduling.Domain.Entities; + +public class ScheduleRule : IMustHaveTenant +{ + public Guid Id { get; set; } + public Guid TenantId { get; set; } + public DayOfWeek Day { get; set; } + public Guid ScheduleId { get; set; } + [Required] + public Guid TimeSlotId { get; set; } +} \ No newline at end of file diff --git a/src/Services/Scheduling/Infrastructure/ConfigureServices.cs b/src/Services/Scheduling/Infrastructure/ConfigureServices.cs index a0ecb9a3..5faf1e89 100644 --- a/src/Services/Scheduling/Infrastructure/ConfigureServices.cs +++ b/src/Services/Scheduling/Infrastructure/ConfigureServices.cs @@ -18,6 +18,7 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } diff --git a/src/Services/Scheduling/Infrastructure/MigrationDbContext.cs b/src/Services/Scheduling/Infrastructure/MigrationDbContext.cs index 36e816dd..b9714314 100644 --- a/src/Services/Scheduling/Infrastructure/MigrationDbContext.cs +++ b/src/Services/Scheduling/Infrastructure/MigrationDbContext.cs @@ -11,8 +11,9 @@ public MigrationDbContext(DbContextOptions options) : base(o } public DbSet Groups { get; set; } - public DbSet TimeSlots { get; set; } + public DbSet Schedules { get; set; } + public DbSet ScheduleRules { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/src/Services/Scheduling/Infrastructure/Migrations/20240829202057_AddScheduleTables.Designer.cs b/src/Services/Scheduling/Infrastructure/Migrations/20240829202057_AddScheduleTables.Designer.cs new file mode 100644 index 00000000..f361db4f --- /dev/null +++ b/src/Services/Scheduling/Infrastructure/Migrations/20240829202057_AddScheduleTables.Designer.cs @@ -0,0 +1,136 @@ +// +using System; +using KDVManager.Services.Scheduling.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(MigrationDbContext))] + [Migration("20240829202057_AddScheduleTables")] + partial class AddScheduleTables + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.Schedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChildId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Schedules"); + }); + + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.ScheduleRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Day") + .HasColumnType("integer"); + + b.Property("ScheduleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("TimeSlotId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ScheduleId"); + + b.ToTable("ScheduleRules"); + }); + + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.TimeSlot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EndTime") + .HasColumnType("time without time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("time without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("TimeSlots"); + }); + + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.ScheduleRule", b => + { + b.HasOne("KDVManager.Services.Scheduling.Domain.Entities.Schedule", null) + .WithMany("ScheduleRules") + .HasForeignKey("ScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.Schedule", b => + { + b.Navigation("ScheduleRules"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Services/Scheduling/Infrastructure/Migrations/20240829202057_AddScheduleTables.cs b/src/Services/Scheduling/Infrastructure/Migrations/20240829202057_AddScheduleTables.cs new file mode 100644 index 00000000..e315cd89 --- /dev/null +++ b/src/Services/Scheduling/Infrastructure/Migrations/20240829202057_AddScheduleTables.cs @@ -0,0 +1,67 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddScheduleTables : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Schedules", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TenantId = table.Column(type: "uuid", nullable: false), + ChildId = table.Column(type: "uuid", nullable: false), + GroupId = table.Column(type: "uuid", nullable: false), + StartDate = table.Column(type: "timestamp with time zone", nullable: false), + EndDate = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Schedules", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ScheduleRules", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TenantId = table.Column(type: "uuid", nullable: false), + Day = table.Column(type: "integer", nullable: false), + ScheduleId = table.Column(type: "uuid", nullable: false), + TimeSlotId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ScheduleRules", x => x.Id); + table.ForeignKey( + name: "FK_ScheduleRules_Schedules_ScheduleId", + column: x => x.ScheduleId, + principalTable: "Schedules", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ScheduleRules_ScheduleId", + table: "ScheduleRules", + column: "ScheduleId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ScheduleRules"); + + migrationBuilder.DropTable( + name: "Schedules"); + } + } +} diff --git a/src/Services/Scheduling/Infrastructure/Migrations/MigrationDbContextModelSnapshot.cs b/src/Services/Scheduling/Infrastructure/Migrations/MigrationDbContextModelSnapshot.cs index 158714dd..7a61ffeb 100644 --- a/src/Services/Scheduling/Infrastructure/Migrations/MigrationDbContextModelSnapshot.cs +++ b/src/Services/Scheduling/Infrastructure/Migrations/MigrationDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("ProductVersion", "8.0.7") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -40,6 +40,57 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Groups"); }); + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.Schedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChildId") + .HasColumnType("uuid"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Schedules"); + }); + + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.ScheduleRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Day") + .HasColumnType("integer"); + + b.Property("ScheduleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid"); + + b.Property("TimeSlotId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ScheduleId"); + + b.ToTable("ScheduleRules"); + }); + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.TimeSlot", b => { b.Property("Id") @@ -62,6 +113,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("TimeSlots"); }); + + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.ScheduleRule", b => + { + b.HasOne("KDVManager.Services.Scheduling.Domain.Entities.Schedule", null) + .WithMany("ScheduleRules") + .HasForeignKey("ScheduleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("KDVManager.Services.Scheduling.Domain.Entities.Schedule", b => + { + b.Navigation("ScheduleRules"); + }); #pragma warning restore 612, 618 } } diff --git a/src/Services/Scheduling/Infrastructure/Repositories/ScheduleRepository.cs b/src/Services/Scheduling/Infrastructure/Repositories/ScheduleRepository.cs new file mode 100644 index 00000000..23a28b89 --- /dev/null +++ b/src/Services/Scheduling/Infrastructure/Repositories/ScheduleRepository.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Threading.Tasks; +using KDVManager.Services.Scheduling.Application.Contracts.Persistence; +using KDVManager.Services.Scheduling.Domain.Entities; +using KDVManager.Services.Scheduling.Domain.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace KDVManager.Services.Scheduling.Infrastructure.Repositories; + +public class ScheduleRepository : BaseRepository, IScheduleRepository +{ + public ScheduleRepository(SchedulingDbContext dbContext) : base(dbContext) + { + } + + public async Task> GetSchedulesByChildIdAsync(Guid childId) + { + return await _dbContext.Schedules + .Where(si => si.ChildId == childId) + .OrderByDescending(si => si.StartDate) + .ThenBy(si => si.EndDate) + .Include(s => s.ScheduleRules) + .ToListAsync(); + } +} diff --git a/src/Services/Scheduling/Infrastructure/SchedulingDbContext.cs b/src/Services/Scheduling/Infrastructure/SchedulingDbContext.cs index 5216e2c6..8baf33a2 100644 --- a/src/Services/Scheduling/Infrastructure/SchedulingDbContext.cs +++ b/src/Services/Scheduling/Infrastructure/SchedulingDbContext.cs @@ -18,12 +18,17 @@ public SchedulingDbContext(DbContextOptions options, ITenan public DbSet Groups { get; set; } public DbSet TimeSlots { get; set; } + public DbSet Schedules { get; set; } + public DbSet ScheduleRules { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity().HasQueryFilter(a => a.TenantId == _tenantService.Tenant); modelBuilder.Entity().HasQueryFilter(a => a.TenantId == _tenantService.Tenant); + modelBuilder.Entity().HasQueryFilter(a => a.TenantId == _tenantService.Tenant); + modelBuilder.Entity().HasQueryFilter(a => a.TenantId == _tenantService.Tenant).HasMany(si => si.ScheduleRules); + modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); }