diff --git a/backend/Api/Api.csproj b/backend/Api/Api.csproj
index c426627c..f4186b36 100644
--- a/backend/Api/Api.csproj
+++ b/backend/Api/Api.csproj
@@ -10,28 +10,35 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
+
+
+
+ <_ContentIncludedByDefault Remove="Routes\obj\Api.csproj.nuget.dgspec.json"/>
+ <_ContentIncludedByDefault Remove="Routes\obj\project.assets.json"/>
+ <_ContentIncludedByDefault Remove="Routes\obj\project.packagespec.json"/>
diff --git a/backend/Api/Routes/ConsultantApi.cs b/backend/Api/Consultants/ConsultantController.cs
similarity index 55%
rename from backend/Api/Routes/ConsultantApi.cs
rename to backend/Api/Consultants/ConsultantController.cs
index ae384918..712fbd31 100644
--- a/backend/Api/Routes/ConsultantApi.cs
+++ b/backend/Api/Consultants/ConsultantController.cs
@@ -1,5 +1,4 @@
using Api.Cache;
-using Api.Validators;
using Core.DomainModels;
using Core.Services;
using Database.DatabaseContext;
@@ -8,71 +7,92 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
-namespace Api.Routes;
+namespace Api.Consultants;
-public static class ConsultantApi
+[Route("v0/variants")]
+[ApiController]
+public class ConsultantController : ControllerBase
{
- public static void MapConsultantApi(this RouteGroupBuilder group)
+ private readonly IMemoryCache _cache;
+ private readonly ConsultantService _consultantService;
+ private readonly ApplicationContext _context;
+
+ public ConsultantController(ApplicationContext context, IMemoryCache cache, ConsultantService consultantService)
{
- group.MapGet("/", GetAllConsultants);
- group.MapGet("/{id}", GetConsultantById);
- group.MapPost("/", AddBasicConsultant);
+ _context = context;
+ _cache = cache;
+ _consultantService = consultantService;
}
- private static Ok> GetAllConsultants(ApplicationContext context, IMemoryCache cache,
+ [HttpGet]
+ public ActionResult> Get(
[FromQuery(Name = "weeks")] int numberOfWeeks = 8,
[FromQuery(Name = "includeOccupied")] bool includeOccupied = false)
{
- var consultants = GetConsultantsWithAvailability(context, cache, numberOfWeeks)
+ var consultants = GetConsultantsWithAvailability(numberOfWeeks)
.Where(c =>
includeOccupied
- || c.HasAvailability
- ).ToList();
+ || c.IsOccupied
+ )
+ .ToList();
- return TypedResults.Ok(consultants);
+ return Ok(consultants);
}
- private static Ok GetConsultantById(ApplicationContext context, IMemoryCache cache, int id,
+ [HttpGet("{id}")]
+ public Ok GetConsultantById(int id,
[FromQuery(Name = "weeks")] int numberOfWeeks = 8)
{
- var consultants = GetConsultantsWithAvailability(context, cache, numberOfWeeks);
+ var consultants = GetConsultantsWithAvailability(numberOfWeeks);
return TypedResults.Ok(consultants.Single(c => c.Id == id));
}
- private static ConsultantReadModel MapToReadModel(this Consultant consultant, int weeks)
+ [HttpPost]
+ public async Task, ProblemHttpResult, ValidationProblem>> AddBasicConsultant(
+ [FromBody] ConsultantWriteModel basicVariant)
{
- const double tolerance = 0.1;
- var bookedHours = consultant.GetBookedHoursForWeeks(weeks);
-
- var hasAvailability = bookedHours.Any(b => b.BookedHours <= consultant.GetHoursPrWeek() - tolerance);
-
- return new ConsultantReadModel(
- consultant.Id,
- consultant.Name,
- consultant.Email,
- consultant.Competences.Select(comp => comp.Name).ToList(),
- consultant.Department.Name,
- bookedHours,
- hasAvailability);
+ try
+ {
+ var selectedDepartment = await GetDepartmentByIdAsync(basicVariant.DepartmentId);
+ if (selectedDepartment == null) return TypedResults.Problem("Department does not exist", statusCode: 400);
+
+ var consultantList = await GetAllConsultantsAsync(_context);
+ var validationResults = ConsultantValidators.ValidateUniqueness(consultantList, basicVariant);
+
+ if (validationResults.Count > 0) return TypedResults.ValidationProblem(validationResults);
+
+ var newConsultant = CreateConsultantFromModel(basicVariant, selectedDepartment);
+ await AddConsultantToDatabaseAsync(_context, newConsultant);
+ ClearConsultantCache();
+
+ return TypedResults.Created($"/variant/{newConsultant.Id}", basicVariant);
+ }
+ catch
+ {
+ // Adding exception handling later
+ return TypedResults.Problem("An error occurred while processing the request", statusCode: 500);
+ }
}
- private static List GetConsultantsWithAvailability(ApplicationContext context,
- IMemoryCache cache, int numberOfWeeks)
+ private List GetConsultantsWithAvailability(int numberOfWeeks)
{
if (numberOfWeeks == 8)
{
- cache.TryGetValue(CacheKeys.ConsultantAvailability8Weeks, out List? cachedConsultants);
+ _cache.TryGetValue(CacheKeys.ConsultantAvailability8Weeks,
+ out List? cachedConsultants);
if (cachedConsultants != null) return cachedConsultants;
}
- var consultants = LoadConsultantAvailability(context, numberOfWeeks);
+ var consultants = LoadConsultantAvailability(numberOfWeeks)
+ .Select(c => _consultantService.MapConsultantToReadModel(c, numberOfWeeks)).ToList();
- cache.Set(CacheKeys.ConsultantAvailability8Weeks, consultants);
+
+ _cache.Set(CacheKeys.ConsultantAvailability8Weeks, consultants);
return consultants;
}
- private static List LoadConsultantAvailability(ApplicationContext context, int numberOfWeeks)
+ private List LoadConsultantAvailability(int numberOfWeeks)
{
var applicableWeeks = DateService.GetNextWeeks(numberOfWeeks);
@@ -95,59 +115,23 @@ private static List LoadConsultantAvailability(ApplicationC
var maxWeekB = weeksInB.Max();
- return context.Consultant
+ return _context.Consultant
.Include(c => c.Vacations)
.Include(c => c.Competences)
.Include(c => c.PlannedAbsences.Where(pa =>
(pa.Year <= yearA && minWeekA <= pa.WeekNumber && pa.WeekNumber <= maxWeekA)
|| (yearB <= pa.Year && minWeekB <= pa.WeekNumber && pa.WeekNumber <= maxWeekB)))
.Include(c => c.Department)
- .ThenInclude(d => d.Organization)
.Include(c => c.Staffings.Where(s =>
(s.Year <= yearA && minWeekA <= s.Week && s.Week <= maxWeekA)
|| (yearB <= s.Year && minWeekB <= s.Week && s.Week <= maxWeekB)))
- .Select(c => c.MapToReadModel(numberOfWeeks))
.ToList();
}
- private static async Task, ProblemHttpResult, ValidationProblem>> AddBasicConsultant(
- ApplicationContext db,
- IMemoryCache cache,
- [FromBody] ConsultantWriteModel basicVariant)
- {
- try
- {
- var selectedDepartment = await GetDepartmentByIdAsync(db, basicVariant.DepartmentId);
- if (selectedDepartment == null)
- {
- return TypedResults.Problem("Department does not exist", statusCode: 400);
- }
-
- var consultantList = await GetAllConsultantsAsync(db);
- var validationResults = ConsultantValidators.ValidateUniqueness(consultantList, basicVariant);
-
- if (validationResults.Count > 0)
- {
- return TypedResults.ValidationProblem(validationResults);
- }
-
- var newConsultant = CreateConsultantFromModel(basicVariant, selectedDepartment);
- await AddConsultantToDatabaseAsync(db, newConsultant);
- ClearConsultantCache(cache);
-
- return TypedResults.Created($"/variant/{newConsultant.Id}", basicVariant);
- }
- catch (Exception ex)
- {
- // Adding exception handling later
- return TypedResults.Problem("An error occurred while processing the request", statusCode: 500);
- }
- }
-
- private static async Task GetDepartmentByIdAsync(ApplicationContext db, string departmentId)
+ private async Task GetDepartmentByIdAsync(string departmentId)
{
- return await db.Department.SingleOrDefaultAsync(d => d.Id == departmentId);
+ return await _context.Department.SingleOrDefaultAsync(d => d.Id == departmentId);
}
private static async Task> GetAllConsultantsAsync(ApplicationContext db)
@@ -172,15 +156,11 @@ private static async Task AddConsultantToDatabaseAsync(ApplicationContext db, Co
await db.SaveChangesAsync();
}
- private static void ClearConsultantCache(IMemoryCache cache)
+ private void ClearConsultantCache()
{
- cache.Remove(CacheKeys.ConsultantAvailability8Weeks);
+ _cache.Remove(CacheKeys.ConsultantAvailability8Weeks);
}
public record ConsultantWriteModel(string Name, string Email, string DepartmentId);
-
- private record ConsultantReadModel(int Id, string Name, string Email, List Competences, string Department,
- List Bookings, bool HasAvailability);
-
}
\ No newline at end of file
diff --git a/backend/Api/Consultants/ConsultantReadModel.cs b/backend/Api/Consultants/ConsultantReadModel.cs
new file mode 100644
index 00000000..126a1c96
--- /dev/null
+++ b/backend/Api/Consultants/ConsultantReadModel.cs
@@ -0,0 +1,6 @@
+using Core.DomainModels;
+
+namespace Api.Consultants;
+
+public record ConsultantReadModel(int Id, string Name, string Email, List Competences, string Department,
+ List Bookings, bool IsOccupied);
\ No newline at end of file
diff --git a/backend/Api/Consultants/ConsultantService.cs b/backend/Api/Consultants/ConsultantService.cs
new file mode 100644
index 00000000..0b62667f
--- /dev/null
+++ b/backend/Api/Consultants/ConsultantService.cs
@@ -0,0 +1,80 @@
+using Api.Options;
+using Core.DomainModels;
+using Core.Services;
+using Microsoft.Extensions.Options;
+
+namespace Api.Consultants;
+
+public class ConsultantService
+{
+ private readonly HolidayService _holidayService;
+ private readonly OrganizationOptions _organizationOptions;
+
+ public ConsultantService(IOptions orgOptions, HolidayService holidayService)
+ {
+ _organizationOptions = orgOptions.Value;
+ _holidayService = holidayService;
+ }
+
+ public ConsultantReadModel MapConsultantToReadModel(Consultant consultant, int weeks)
+ {
+ const double tolerance = 0.1;
+ var bookedHours = GetBookedHoursForWeeks(consultant, weeks);
+
+ var isOccupied = bookedHours.All(b => b.BookedHours >= GetHoursPrWeek() - tolerance);
+
+ return new ConsultantReadModel(
+ consultant.Id,
+ consultant.Name,
+ consultant.Email,
+ consultant.Competences.Select(comp => comp.Name).ToList(),
+ consultant.Department.Name,
+ bookedHours,
+ isOccupied
+ );
+ }
+
+ public double GetBookedHours(Consultant consultant, int year, int week)
+ {
+ var hoursPrWorkDay = _organizationOptions.HoursPerWorkday;
+
+ var holidayHours = _holidayService.GetTotalHolidaysOfWeek(year, week) * hoursPrWorkDay;
+ var vacationHours = consultant.Vacations.Count(v => DateService.DateIsInWeek(v.Date, year, week)) *
+ hoursPrWorkDay;
+
+ var plannedAbsenceHours = consultant.PlannedAbsences
+ .Where(pa => pa.Year == year && pa.WeekNumber == week)
+ .Select(pa => pa.Hours)
+ .Sum();
+
+ var staffedHours = consultant.Staffings
+ .Where(s => s.Year == year && s.Week == week)
+ .Select(s => s.Hours)
+ .Sum();
+
+ var bookedHours = holidayHours + vacationHours + plannedAbsenceHours + staffedHours;
+ return Math.Min(bookedHours, 5 * hoursPrWorkDay);
+ }
+
+ public List GetBookedHoursForWeeks(Consultant consultant, int weeksAhead)
+ {
+ return Enumerable.Range(0, weeksAhead)
+ .Select(offset =>
+ {
+ var year = DateTime.Today.AddDays(7 * offset).Year;
+ var week = DateService.GetWeekAhead(offset);
+
+ return new BookedHoursPerWeek(
+ year,
+ week,
+ GetBookedHours(consultant, year, week)
+ );
+ })
+ .ToList();
+ }
+
+ public double GetHoursPrWeek()
+ {
+ return _organizationOptions.HoursPerWorkday * 5;
+ }
+}
\ No newline at end of file
diff --git a/backend/Api/Validators/ConsultantValidators.cs b/backend/Api/Consultants/ConsultantValidators.cs
similarity index 80%
rename from backend/Api/Validators/ConsultantValidators.cs
rename to backend/Api/Consultants/ConsultantValidators.cs
index be1fd430..ff9d946b 100644
--- a/backend/Api/Validators/ConsultantValidators.cs
+++ b/backend/Api/Consultants/ConsultantValidators.cs
@@ -1,8 +1,7 @@
using System.Net.Mail;
-using Api.Routes;
using Core.DomainModels;
-namespace Api.Validators;
+namespace Api.Consultants;
public static class ConsultantValidators
{
@@ -11,8 +10,8 @@ public static bool IsValidEmail(string email)
return MailAddress.TryCreate(email, out _);
}
- public static IDictionary ValidateUniqueness(List consultantList,
- ConsultantApi.ConsultantWriteModel body)
+ public static IDictionary ValidateUniqueness(List consultantList,
+ ConsultantController.ConsultantWriteModel body)
{
var results = new Dictionary();
diff --git a/backend/Api/Consultants/HolidayService.cs b/backend/Api/Consultants/HolidayService.cs
new file mode 100644
index 00000000..dd81385d
--- /dev/null
+++ b/backend/Api/Consultants/HolidayService.cs
@@ -0,0 +1,51 @@
+using Api.Options;
+using Core.Services;
+using Microsoft.Extensions.Options;
+using PublicHoliday;
+
+namespace Api.Consultants;
+
+public class HolidayService
+{
+ private readonly OrganizationOptions _organizationOptions;
+
+ public HolidayService(IOptions options)
+ {
+ _organizationOptions = options.Value;
+ }
+
+ public int GetTotalHolidaysOfWeek(int year, int week)
+ {
+ var datesOfThisWeek = DateService.GetDatesInWorkWeek(year, week);
+ return datesOfThisWeek.Count(IsHoliday);
+ }
+
+ private bool IsHoliday(DateOnly day)
+ {
+ var holidayCountry = GetHolidayCountry();
+ var isPublicHoliday = holidayCountry.IsPublicHoliday(day.ToDateTime(TimeOnly.MinValue));
+ return isPublicHoliday || IsVariantChristmasHoliday(day);
+ }
+
+ private PublicHolidayBase GetHolidayCountry()
+ {
+ var country = _organizationOptions.Country;
+
+ return country switch
+ {
+ "norway" => new NorwayPublicHoliday(),
+ "sweden" => new SwedenPublicHoliday(),
+ _ => new NorwayPublicHoliday()
+ };
+ }
+
+ private bool IsVariantChristmasHoliday(DateOnly date)
+ {
+ if (!_organizationOptions.HasVacationInChristmas) return false;
+
+ var startDate = new DateOnly(date.Year, 12, 24);
+ var endDate = new DateOnly(date.Year, 12, 31);
+
+ return date >= startDate && date <= endDate;
+ }
+}
\ No newline at end of file
diff --git a/backend/Api/Options/OrganizationOptions.cs b/backend/Api/Options/OrganizationOptions.cs
new file mode 100644
index 00000000..f30d889b
--- /dev/null
+++ b/backend/Api/Options/OrganizationOptions.cs
@@ -0,0 +1,9 @@
+namespace Api.Options;
+
+public class OrganizationOptions
+{
+ public double HoursPerWorkday { get; set; }
+ public bool HasVacationInChristmas { get; set; }
+ public int NumberOfVacationDaysInYear { get; set; }
+ public string Country { get; set; }
+}
\ No newline at end of file
diff --git a/backend/Api/Program.cs b/backend/Api/Program.cs
index 45ccc5c4..78ad8d80 100644
--- a/backend/Api/Program.cs
+++ b/backend/Api/Program.cs
@@ -1,6 +1,6 @@
using Api.BuildHelpers;
+using Api.Consultants;
using Api.Options;
-using Api.Routes;
using Database.DatabaseContext;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
@@ -21,8 +21,14 @@
builder.Services.AddMemoryCache();
+
+builder.Services.Configure(builder.Configuration.GetSection("OrganizationSettings"));
+builder.Services.AddSingleton();
+builder.Services.AddSingleton();
+builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
+
var adOptions = builder.Configuration.GetSection("AzureAd").Get();
if (adOptions == null) throw new Exception("Required AzureAd options are missing");
@@ -34,7 +40,8 @@
var app = builder.Build();
-app.MapGroup("/v0").ApiGroupVersionBeta();
+app.UsePathBase("/v0");
+app.MapControllers();
app.UseSwagger();
app.UseSwaggerUI(c =>
diff --git a/backend/Api/Routes/RouteGroupBuilderExtensions.cs b/backend/Api/Routes/RouteGroupBuilderExtensions.cs
deleted file mode 100644
index 2bcb0e0a..00000000
--- a/backend/Api/Routes/RouteGroupBuilderExtensions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-
-namespace Api.Routes;
-
-public static class RouteGroupBuilderExtensions
-{
- private static RouteGroupBuilder MapApiGroup(this IEndpointRouteBuilder endpoints,
- [StringSyntax("Route")] string routePrefix, string? groupTag = null)
- {
- var group = endpoints
- .MapGroup(routePrefix)
- .WithOpenApi();
-
- if (groupTag is not null)
- group.WithTags(groupTag);
-
- return group;
- }
-
- public static RouteGroupBuilder ApiGroupVersionBeta(this RouteGroupBuilder group)
- {
- group.MapApiGroup("variants", "Varianter").MapConsultantApi();
- return group;
- }
-}
\ No newline at end of file
diff --git a/backend/Api/appsettings.Local.json.template b/backend/Api/appsettings.Local.json.template
index 33a8634e..e9adf5fb 100644
--- a/backend/Api/appsettings.Local.json.template
+++ b/backend/Api/appsettings.Local.json.template
@@ -5,6 +5,12 @@
"TenantId": "azure_tenant_id_for_organization",
"ApiScope": "Azure Ad -> App Registrations -> [app] -> Expose an Api"
},
+ "OrganizationSettings" :{
+ "HoursPerWorkday": 7.5 or 8,
+ "NumberOfVacationDaysInYear": 25 or 30,
+ "HasVacationInChristmas": true or false,
+ "Country": "norway or sweden"
+ },
"ConnectionStrings": {
"VibesDb": ""
}
diff --git a/backend/Api/appsettings.json b/backend/Api/appsettings.json
index fdd8de79..9cd22673 100644
--- a/backend/Api/appsettings.json
+++ b/backend/Api/appsettings.json
@@ -2,6 +2,12 @@
"AzureAd": {
"Instance": "https://login.microsoftonline.com/"
},
+ "OrganizationSettings": {
+ "HoursPerWorkday": 7.5,
+ "NumberOfVacationDaysInYear": 25,
+ "HasVacationInChristmas": true,
+ "Country": "norway"
+ },
"Logging": {
"LogLevel": {
"Default": "Information",
diff --git a/backend/Core/Core.csproj b/backend/Core/Core.csproj
index f2291091..7ea7c4f6 100644
--- a/backend/Core/Core.csproj
+++ b/backend/Core/Core.csproj
@@ -9,19 +9,19 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/backend/Core/DomainModels/Absence.cs b/backend/Core/DomainModels/Absence.cs
index ef3610b9..42f36a2f 100644
--- a/backend/Core/DomainModels/Absence.cs
+++ b/backend/Core/DomainModels/Absence.cs
@@ -9,5 +9,4 @@ public class Absence
public required string Name { get; set; }
public required bool ExcludeFromBillRate { get; set; } = false;
- public required Organization Organization { get; set; }
}
\ No newline at end of file
diff --git a/backend/Core/DomainModels/Consultant.cs b/backend/Core/DomainModels/Consultant.cs
index 1db7dd08..2e332154 100644
--- a/backend/Core/DomainModels/Consultant.cs
+++ b/backend/Core/DomainModels/Consultant.cs
@@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations.Schema;
-using Core.Services;
namespace Core.DomainModels;
@@ -28,48 +27,6 @@ public class Consultant
public List Projects { get; set; } = new();
public List Staffings { get; set; } = new();
-
- public double GetBookedHours(int year, int week)
- {
- var hoursPrWorkDay = Department.Organization.HoursPerWorkday;
- var holidayHours = Holiday.GetTotalHolidaysOfWeek(year, week) * hoursPrWorkDay;
- var vacationHours = Vacations.Count(v => DateService.DateIsInWeek(v.Date, year, week)) * hoursPrWorkDay;
-
- var plannedAbsenceHours = PlannedAbsences
- .Where(pa => pa.Year == year && pa.WeekNumber == week)
- .Select(pa => pa.Hours)
- .Sum();
-
- var staffedHours = Staffings
- .Where(s => s.Year == year && s.Week == week)
- .Select(s => s.Hours)
- .Sum();
-
- var bookedHours = holidayHours + vacationHours + plannedAbsenceHours + staffedHours;
- return Math.Min(bookedHours, 5 * hoursPrWorkDay);
- }
-
- public List GetBookedHoursForWeeks(int weeksAhead)
- {
- return Enumerable.Range(0, weeksAhead)
- .Select(offset =>
- {
- var year = DateTime.Today.AddDays(7 * offset).Year;
- var week = DateService.GetWeekAhead(offset);
-
- return new BookedHoursPerWeek(
- year,
- week,
- GetBookedHours(year, week)
- );
- })
- .ToList();
- }
-
- public double GetHoursPrWeek()
- {
- return Department.Organization.HoursPerWorkday * 5;
- }
}
public class Competence
diff --git a/backend/Core/DomainModels/Customer.cs b/backend/Core/DomainModels/Customer.cs
index 5057b840..b0666ff2 100644
--- a/backend/Core/DomainModels/Customer.cs
+++ b/backend/Core/DomainModels/Customer.cs
@@ -8,8 +8,5 @@ public class Customer
public int Id { get; set; }
public required string Name { get; set; }
-
- public required Organization Organization { get; set; }
-
public required List Projects { get; set; }
}
\ No newline at end of file
diff --git a/backend/Core/DomainModels/Department.cs b/backend/Core/DomainModels/Department.cs
index 7020f261..97d7a5c8 100644
--- a/backend/Core/DomainModels/Department.cs
+++ b/backend/Core/DomainModels/Department.cs
@@ -7,8 +7,6 @@ public class Department
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public required string Id { get; set; }
-
- public required Organization Organization { get; set; }
public required string Name { get; set; }
[JsonIgnore] public required List Consultants { get; set; }
}
\ No newline at end of file
diff --git a/backend/Core/DomainModels/Holiday.cs b/backend/Core/DomainModels/Holiday.cs
deleted file mode 100644
index 23e3a69b..00000000
--- a/backend/Core/DomainModels/Holiday.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Core.Services;
-using PublicHoliday;
-
-namespace Core.DomainModels;
-
-public static class Holiday
-{
- public static int GetTotalHolidaysOfWeek(int year, int week)
- {
- var datesOfThisWeek = DateService.GetDatesInWorkWeek(year, week);
-
- return datesOfThisWeek.Count(IsHoliday);
- }
-
- private static bool IsHoliday(DateOnly day)
- {
- var isPublicHoliday = new NorwayPublicHoliday().IsPublicHoliday(day.ToDateTime(TimeOnly.MinValue));
-
- return isPublicHoliday || IsVariantChristmasHoliday(day);
- }
-
-
- private static bool IsVariantChristmasHoliday(DateOnly date)
- {
- var startDate = new DateOnly(date.Year, 12, 24);
- var endDate = new DateOnly(date.Year, 12, 31);
-
- return date >= startDate && date <= endDate;
- }
-}
\ No newline at end of file
diff --git a/backend/Core/DomainModels/Organization.cs b/backend/Core/DomainModels/Organization.cs
deleted file mode 100644
index ea8e11fd..00000000
--- a/backend/Core/DomainModels/Organization.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.ComponentModel.DataAnnotations.Schema;
-using System.Text.Json.Serialization;
-
-namespace Core.DomainModels;
-
-public class Organization
-{
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public required string Id { get; set; }
-
- public required string Name { get; set; }
- public required double HoursPerWorkday { get; set; }
-
- [JsonIgnore] public List Departments { get; set; }
-
- public required List Customers { get; set; }
-
- public List AbsenceTypes { get; set; }
-}
\ No newline at end of file
diff --git a/backend/Database/DatabaseContext/ApplicationContext.cs b/backend/Database/DatabaseContext/ApplicationContext.cs
index 7b56ede4..98159c0a 100644
--- a/backend/Database/DatabaseContext/ApplicationContext.cs
+++ b/backend/Database/DatabaseContext/ApplicationContext.cs
@@ -13,7 +13,6 @@ public ApplicationContext(DbContextOptions options) : base(options)
public DbSet Consultant { get; set; } = null!;
public DbSet Competence { get; set; } = null!;
public DbSet Department { get; set; } = null!;
- public DbSet Organization { get; set; } = null!;
public DbSet PlannedAbsence { get; set; } = null!;
public DbSet Vacation { get; set; } = null!;
public DbSet Customer { get; set; } = null!;
@@ -30,18 +29,6 @@ protected override void ConfigureConventions(ModelConfigurationBuilder configura
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
- modelBuilder.Entity()
- .HasMany(org => org.Departments)
- .WithOne(dept => dept.Organization);
-
- modelBuilder.Entity()
- .HasMany(organization => organization.AbsenceTypes)
- .WithOne(absence => absence.Organization);
-
- modelBuilder.Entity()
- .HasMany(organization => organization.Customers)
- .WithOne(customer => customer.Organization);
-
modelBuilder.Entity()
.HasMany(customer => customer.Projects)
.WithOne(project => project.Customer);
@@ -113,9 +100,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
new() { Id = "project-mgmt", Name = "Project Management" }
});
- modelBuilder.Entity()
- .HasData(new { Name = "Variant AS", HoursPerWorkday = 7.5, Id = "variant-as" });
-
modelBuilder.Entity()
.HasData(new { Id = "trondheim", Name = "Trondheim", OrganizationId = "variant-as" });
diff --git a/backend/Database/Migrations/20231004195542_RemovedOrganization.Designer.cs b/backend/Database/Migrations/20231004195542_RemovedOrganization.Designer.cs
new file mode 100644
index 00000000..162c4307
--- /dev/null
+++ b/backend/Database/Migrations/20231004195542_RemovedOrganization.Designer.cs
@@ -0,0 +1,413 @@
+//
+using System;
+using Database.DatabaseContext;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace backend.Migrations
+{
+ [DbContext(typeof(ApplicationContext))]
+ [Migration("20231004195542_RemovedOrganization")]
+ partial class RemovedOrganization
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.10")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("CompetenceConsultant", b =>
+ {
+ b.Property("CompetencesId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ConsultantId")
+ .HasColumnType("int");
+
+ b.HasKey("CompetencesId", "ConsultantId");
+
+ b.HasIndex("ConsultantId");
+
+ b.ToTable("CompetenceConsultant");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Absence", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ExcludeFromBillRate")
+ .HasColumnType("bit");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Absence");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Competence", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Competence");
+
+ b.HasData(
+ new
+ {
+ Id = "frontend",
+ Name = "Frontend"
+ },
+ new
+ {
+ Id = "backend",
+ Name = "Backend"
+ },
+ new
+ {
+ Id = "design",
+ Name = "Design"
+ },
+ new
+ {
+ Id = "project-mgmt",
+ Name = "Project Management"
+ });
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Consultant", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Degree")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DepartmentId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("EndDate")
+ .HasColumnType("datetime2");
+
+ b.Property("GraduationYear")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("StartDate")
+ .HasColumnType("datetime2");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DepartmentId");
+
+ b.ToTable("Consultant");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Degree = "Master",
+ DepartmentId = "trondheim",
+ Email = "j@variant.no",
+ GraduationYear = 2019,
+ Name = "Jonas",
+ StartDate = new DateTime(2020, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
+ });
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Customer", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Customer");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Department", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Department");
+
+ b.HasData(
+ new
+ {
+ Id = "trondheim",
+ Name = "Trondheim"
+ });
+ });
+
+ modelBuilder.Entity("Core.DomainModels.PlannedAbsence", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AbsenceId")
+ .HasColumnType("int");
+
+ b.Property("ConsultantId")
+ .HasColumnType("int");
+
+ b.Property("Hours")
+ .HasColumnType("float");
+
+ b.Property("WeekNumber")
+ .HasColumnType("int");
+
+ b.Property("Year")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AbsenceId");
+
+ b.HasIndex("ConsultantId");
+
+ b.ToTable("PlannedAbsence");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Project", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CustomerId")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("State")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CustomerId");
+
+ b.ToTable("Project");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Staffing", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ConsultantId")
+ .HasColumnType("int");
+
+ b.Property("Hours")
+ .HasColumnType("float");
+
+ b.Property("ProjectId")
+ .HasColumnType("int");
+
+ b.Property("Week")
+ .HasColumnType("int");
+
+ b.Property("Year")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ConsultantId");
+
+ b.HasIndex("ProjectId");
+
+ b.ToTable("Staffing");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Vacation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ConsultantId")
+ .HasColumnType("int");
+
+ b.Property("Date")
+ .HasColumnType("datetime2");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ConsultantId");
+
+ b.ToTable("Vacation");
+ });
+
+ modelBuilder.Entity("CompetenceConsultant", b =>
+ {
+ b.HasOne("Core.DomainModels.Competence", null)
+ .WithMany()
+ .HasForeignKey("CompetencesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Core.DomainModels.Consultant", null)
+ .WithMany()
+ .HasForeignKey("ConsultantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Consultant", b =>
+ {
+ b.HasOne("Core.DomainModels.Department", "Department")
+ .WithMany("Consultants")
+ .HasForeignKey("DepartmentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Department");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.PlannedAbsence", b =>
+ {
+ b.HasOne("Core.DomainModels.Absence", "Absence")
+ .WithMany()
+ .HasForeignKey("AbsenceId")
+ .OnDelete(DeleteBehavior.ClientCascade)
+ .IsRequired();
+
+ b.HasOne("Core.DomainModels.Consultant", "Consultant")
+ .WithMany("PlannedAbsences")
+ .HasForeignKey("ConsultantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Absence");
+
+ b.Navigation("Consultant");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Project", b =>
+ {
+ b.HasOne("Core.DomainModels.Customer", "Customer")
+ .WithMany("Projects")
+ .HasForeignKey("CustomerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Customer");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Staffing", b =>
+ {
+ b.HasOne("Core.DomainModels.Consultant", "Consultant")
+ .WithMany("Staffings")
+ .HasForeignKey("ConsultantId")
+ .OnDelete(DeleteBehavior.ClientCascade)
+ .IsRequired();
+
+ b.HasOne("Core.DomainModels.Project", "Project")
+ .WithMany("Staffings")
+ .HasForeignKey("ProjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Consultant");
+
+ b.Navigation("Project");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Vacation", b =>
+ {
+ b.HasOne("Core.DomainModels.Consultant", "Consultant")
+ .WithMany("Vacations")
+ .HasForeignKey("ConsultantId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Consultant");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Consultant", b =>
+ {
+ b.Navigation("PlannedAbsences");
+
+ b.Navigation("Staffings");
+
+ b.Navigation("Vacations");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Customer", b =>
+ {
+ b.Navigation("Projects");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Department", b =>
+ {
+ b.Navigation("Consultants");
+ });
+
+ modelBuilder.Entity("Core.DomainModels.Project", b =>
+ {
+ b.Navigation("Staffings");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/backend/Database/Migrations/20231004195542_RemovedOrganization.cs b/backend/Database/Migrations/20231004195542_RemovedOrganization.cs
new file mode 100644
index 00000000..dcdeb30d
--- /dev/null
+++ b/backend/Database/Migrations/20231004195542_RemovedOrganization.cs
@@ -0,0 +1,142 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace backend.Migrations
+{
+ ///
+ public partial class RemovedOrganization : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_Absence_Organization_OrganizationId",
+ table: "Absence");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Customer_Organization_OrganizationId",
+ table: "Customer");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Department_Organization_OrganizationId",
+ table: "Department");
+
+ migrationBuilder.DropTable(
+ name: "Organization");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Department_OrganizationId",
+ table: "Department");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Customer_OrganizationId",
+ table: "Customer");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Absence_OrganizationId",
+ table: "Absence");
+
+ migrationBuilder.DropColumn(
+ name: "OrganizationId",
+ table: "Department");
+
+ migrationBuilder.DropColumn(
+ name: "OrganizationId",
+ table: "Customer");
+
+ migrationBuilder.DropColumn(
+ name: "OrganizationId",
+ table: "Absence");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "OrganizationId",
+ table: "Department",
+ type: "nvarchar(450)",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "OrganizationId",
+ table: "Customer",
+ type: "nvarchar(450)",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn(
+ name: "OrganizationId",
+ table: "Absence",
+ type: "nvarchar(450)",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.CreateTable(
+ name: "Organization",
+ columns: table => new
+ {
+ Id = table.Column(type: "nvarchar(450)", nullable: false),
+ HoursPerWorkday = table.Column(type: "float", nullable: false),
+ Name = table.Column(type: "nvarchar(max)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Organization", x => x.Id);
+ });
+
+ migrationBuilder.UpdateData(
+ table: "Department",
+ keyColumn: "Id",
+ keyValue: "trondheim",
+ column: "OrganizationId",
+ value: "variant-as");
+
+ migrationBuilder.InsertData(
+ table: "Organization",
+ columns: new[] { "Id", "HoursPerWorkday", "Name" },
+ values: new object[] { "variant-as", 7.5, "Variant AS" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Department_OrganizationId",
+ table: "Department",
+ column: "OrganizationId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Customer_OrganizationId",
+ table: "Customer",
+ column: "OrganizationId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Absence_OrganizationId",
+ table: "Absence",
+ column: "OrganizationId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Absence_Organization_OrganizationId",
+ table: "Absence",
+ column: "OrganizationId",
+ principalTable: "Organization",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Customer_Organization_OrganizationId",
+ table: "Customer",
+ column: "OrganizationId",
+ principalTable: "Organization",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Department_Organization_OrganizationId",
+ table: "Department",
+ column: "OrganizationId",
+ principalTable: "Organization",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ }
+ }
+}
diff --git a/backend/Database/Migrations/ApplicationContextModelSnapshot.cs b/backend/Database/Migrations/ApplicationContextModelSnapshot.cs
index 8d734bde..946bfb53 100644
--- a/backend/Database/Migrations/ApplicationContextModelSnapshot.cs
+++ b/backend/Database/Migrations/ApplicationContextModelSnapshot.cs
@@ -37,7 +37,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("CompetenceConsultant");
});
- modelBuilder.Entity("backend.Core.DomainModels.Absence", b =>
+ modelBuilder.Entity("Core.DomainModels.Absence", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -52,18 +52,12 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.IsRequired()
.HasColumnType("nvarchar(max)");
- b.Property("OrganizationId")
- .IsRequired()
- .HasColumnType("nvarchar(450)");
-
b.HasKey("Id");
- b.HasIndex("OrganizationId");
-
b.ToTable("Absence");
});
- modelBuilder.Entity("backend.Core.DomainModels.Competence", b =>
+ modelBuilder.Entity("Core.DomainModels.Competence", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -100,7 +94,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
});
});
- modelBuilder.Entity("backend.Core.DomainModels.Consultant", b =>
+ modelBuilder.Entity("Core.DomainModels.Consultant", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -151,7 +145,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
});
});
- modelBuilder.Entity("backend.Core.DomainModels.Customer", b =>
+ modelBuilder.Entity("Core.DomainModels.Customer", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -163,18 +157,12 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.IsRequired()
.HasColumnType("nvarchar(max)");
- b.Property("OrganizationId")
- .IsRequired()
- .HasColumnType("nvarchar(450)");
-
b.HasKey("Id");
- b.HasIndex("OrganizationId");
-
b.ToTable("Customer");
});
- modelBuilder.Entity("backend.Core.DomainModels.Department", b =>
+ modelBuilder.Entity("Core.DomainModels.Department", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -184,52 +172,19 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.IsRequired()
.HasColumnType("nvarchar(max)");
- b.Property("OrganizationId")
- .IsRequired()
- .HasColumnType("nvarchar(450)");
-
b.HasKey("Id");
- b.HasIndex("OrganizationId");
-
b.ToTable("Department");
b.HasData(
new
{
Id = "trondheim",
- Name = "Trondheim",
- OrganizationId = "variant-as"
+ Name = "Trondheim"
});
});
- modelBuilder.Entity("backend.Core.DomainModels.Organization", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("nvarchar(450)");
-
- b.Property("HoursPerWorkday")
- .HasColumnType("float");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("nvarchar(max)");
-
- b.HasKey("Id");
-
- b.ToTable("Organization");
-
- b.HasData(
- new
- {
- Id = "variant-as",
- HoursPerWorkday = 7.5,
- Name = "Variant AS"
- });
- });
-
- modelBuilder.Entity("backend.Core.DomainModels.PlannedAbsence", b =>
+ modelBuilder.Entity("Core.DomainModels.PlannedAbsence", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -261,7 +216,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("PlannedAbsence");
});
- modelBuilder.Entity("backend.Core.DomainModels.Project", b =>
+ modelBuilder.Entity("Core.DomainModels.Project", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -287,7 +242,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("Project");
});
- modelBuilder.Entity("backend.Core.DomainModels.Staffing", b =>
+ modelBuilder.Entity("Core.DomainModels.Staffing", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -319,7 +274,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("Staffing");
});
- modelBuilder.Entity("backend.Core.DomainModels.Vacation", b =>
+ modelBuilder.Entity("Core.DomainModels.Vacation", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -342,33 +297,22 @@ protected override void BuildModel(ModelBuilder modelBuilder)
modelBuilder.Entity("CompetenceConsultant", b =>
{
- b.HasOne("backend.Core.DomainModels.Competence", null)
+ b.HasOne("Core.DomainModels.Competence", null)
.WithMany()
.HasForeignKey("CompetencesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
- b.HasOne("backend.Core.DomainModels.Consultant", null)
+ b.HasOne("Core.DomainModels.Consultant", null)
.WithMany()
.HasForeignKey("ConsultantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
- modelBuilder.Entity("backend.Core.DomainModels.Absence", b =>
- {
- b.HasOne("backend.Core.DomainModels.Organization", "Organization")
- .WithMany("AbsenceTypes")
- .HasForeignKey("OrganizationId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Organization");
- });
-
- modelBuilder.Entity("backend.Core.DomainModels.Consultant", b =>
+ modelBuilder.Entity("Core.DomainModels.Consultant", b =>
{
- b.HasOne("backend.Core.DomainModels.Department", "Department")
+ b.HasOne("Core.DomainModels.Department", "Department")
.WithMany("Consultants")
.HasForeignKey("DepartmentId")
.OnDelete(DeleteBehavior.Cascade)
@@ -377,37 +321,15 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Navigation("Department");
});
- modelBuilder.Entity("backend.Core.DomainModels.Customer", b =>
+ modelBuilder.Entity("Core.DomainModels.PlannedAbsence", b =>
{
- b.HasOne("backend.Core.DomainModels.Organization", "Organization")
- .WithMany("Customers")
- .HasForeignKey("OrganizationId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Organization");
- });
-
- modelBuilder.Entity("backend.Core.DomainModels.Department", b =>
- {
- b.HasOne("backend.Core.DomainModels.Organization", "Organization")
- .WithMany("Departments")
- .HasForeignKey("OrganizationId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Organization");
- });
-
- modelBuilder.Entity("backend.Core.DomainModels.PlannedAbsence", b =>
- {
- b.HasOne("backend.Core.DomainModels.Absence", "Absence")
+ b.HasOne("Core.DomainModels.Absence", "Absence")
.WithMany()
.HasForeignKey("AbsenceId")
.OnDelete(DeleteBehavior.ClientCascade)
.IsRequired();
- b.HasOne("backend.Core.DomainModels.Consultant", "Consultant")
+ b.HasOne("Core.DomainModels.Consultant", "Consultant")
.WithMany("PlannedAbsences")
.HasForeignKey("ConsultantId")
.OnDelete(DeleteBehavior.Cascade)
@@ -418,9 +340,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Navigation("Consultant");
});
- modelBuilder.Entity("backend.Core.DomainModels.Project", b =>
+ modelBuilder.Entity("Core.DomainModels.Project", b =>
{
- b.HasOne("backend.Core.DomainModels.Customer", "Customer")
+ b.HasOne("Core.DomainModels.Customer", "Customer")
.WithMany("Projects")
.HasForeignKey("CustomerId")
.OnDelete(DeleteBehavior.Cascade)
@@ -429,15 +351,15 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Navigation("Customer");
});
- modelBuilder.Entity("backend.Core.DomainModels.Staffing", b =>
+ modelBuilder.Entity("Core.DomainModels.Staffing", b =>
{
- b.HasOne("backend.Core.DomainModels.Consultant", "Consultant")
+ b.HasOne("Core.DomainModels.Consultant", "Consultant")
.WithMany("Staffings")
.HasForeignKey("ConsultantId")
.OnDelete(DeleteBehavior.ClientCascade)
.IsRequired();
- b.HasOne("backend.Core.DomainModels.Project", "Project")
+ b.HasOne("Core.DomainModels.Project", "Project")
.WithMany("Staffings")
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
@@ -448,9 +370,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Navigation("Project");
});
- modelBuilder.Entity("backend.Core.DomainModels.Vacation", b =>
+ modelBuilder.Entity("Core.DomainModels.Vacation", b =>
{
- b.HasOne("backend.Core.DomainModels.Consultant", "Consultant")
+ b.HasOne("Core.DomainModels.Consultant", "Consultant")
.WithMany("Vacations")
.HasForeignKey("ConsultantId")
.OnDelete(DeleteBehavior.Cascade)
@@ -459,7 +381,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Navigation("Consultant");
});
- modelBuilder.Entity("backend.Core.DomainModels.Consultant", b =>
+ modelBuilder.Entity("Core.DomainModels.Consultant", b =>
{
b.Navigation("PlannedAbsences");
@@ -468,26 +390,17 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Navigation("Vacations");
});
- modelBuilder.Entity("backend.Core.DomainModels.Customer", b =>
+ modelBuilder.Entity("Core.DomainModels.Customer", b =>
{
b.Navigation("Projects");
});
- modelBuilder.Entity("backend.Core.DomainModels.Department", b =>
+ modelBuilder.Entity("Core.DomainModels.Department", b =>
{
b.Navigation("Consultants");
});
- modelBuilder.Entity("backend.Core.DomainModels.Organization", b =>
- {
- b.Navigation("AbsenceTypes");
-
- b.Navigation("Customers");
-
- b.Navigation("Departments");
- });
-
- modelBuilder.Entity("backend.Core.DomainModels.Project", b =>
+ modelBuilder.Entity("Core.DomainModels.Project", b =>
{
b.Navigation("Staffings");
});
diff --git a/backend/Tests/AbsenceTest.cs b/backend/Tests/AbsenceTest.cs
index 4636cd56..0b07863c 100644
--- a/backend/Tests/AbsenceTest.cs
+++ b/backend/Tests/AbsenceTest.cs
@@ -1,5 +1,8 @@
+using Api.Consultants;
+using Api.Options;
using Core.DomainModels;
using Core.Services;
+using Microsoft.Extensions.Options;
using NSubstitute;
namespace Tests;
@@ -24,9 +27,6 @@ public void AvailabilityCalculation(int vacationDays, double plannedAbsenceHours
double expectedBookedHours)
{
var department = Substitute.For();
- var organization = Substitute.For();
- organization.HoursPerWorkday = 7.5;
- department.Organization = organization;
var consultant = new Consultant
{
Id = 1,
@@ -75,17 +75,21 @@ public void AvailabilityCalculation(int vacationDays, double plannedAbsenceHours
Hours = staffedHours
});
- var availability = consultant.GetBookedHours(year, week);
- Assert.That(availability, Is.EqualTo(expectedBookedHours));
+ var organization = new OrganizationOptions();
+ organization.HoursPerWorkday = 7.5;
+ organization.HasVacationInChristmas = true;
+ var orgOptions = Options.Create(organization);
+ var holidayService = new HolidayService(orgOptions);
+
+ var bookedHours =
+ new ConsultantService(orgOptions, holidayService).GetBookedHours(consultant, year, week);
+ Assert.That(bookedHours, Is.EqualTo(expectedBookedHours));
}
[Test]
public void MultiplePlannedAbsences()
{
var department = Substitute.For();
- var organization = Substitute.For();
- organization.HoursPerWorkday = 7.5;
- department.Organization = organization;
var consultant = new Consultant
{
Id = 1,
@@ -110,7 +114,14 @@ public void MultiplePlannedAbsences()
Hours = 15
});
- var availability = consultant.GetBookedHours(year, week);
- Assert.That(availability, Is.EqualTo(7.5));
+ var organization = new OrganizationOptions();
+ organization.HoursPerWorkday = 7.5;
+ var orgOptions = Options.Create(organization);
+ var holidayService = new HolidayService(orgOptions);
+
+ var bookedHours =
+ new ConsultantService(orgOptions, holidayService).GetBookedHours(consultant, year, week);
+
+ Assert.That(bookedHours, Is.EqualTo(30));
}
}
\ No newline at end of file
diff --git a/backend/Tests/Tests.csproj b/backend/Tests/Tests.csproj
index b9517ff0..ab9f7fc9 100644
--- a/backend/Tests/Tests.csproj
+++ b/backend/Tests/Tests.csproj
@@ -10,17 +10,18 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
+
+