Kapitel 7 abgeschlossen.
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
using LehrerApp.Core.Interfaces;
|
||||
using LehrerApp.Core.Models;
|
||||
|
||||
namespace LehrerApp.Core.Services;
|
||||
|
||||
public sealed record GroupRolloverRequest(
|
||||
string Name,
|
||||
string TargetSchoolYear,
|
||||
int GradeLevel,
|
||||
IReadOnlyCollection<Guid> MembershipIds,
|
||||
bool CopyGradingScheme,
|
||||
bool ArchiveSource);
|
||||
|
||||
/// <summary>Erzeugt eine fachlich saubere Folgegruppe ohne Leistungs- oder Planungsdaten.</summary>
|
||||
public sealed class GroupRolloverService(
|
||||
IGroupRepository groups,
|
||||
IGroupMembershipRepository memberships,
|
||||
IGradingSchemeRepository gradingSchemes,
|
||||
SchoolYearService schoolYears)
|
||||
{
|
||||
public LearningGroup RollOver(LearningGroup source, GroupRolloverRequest request)
|
||||
{
|
||||
var targetSchoolYear = request.TargetSchoolYear.Trim();
|
||||
var targetStart = schoolYears.SchoolYearStart(targetSchoolYear);
|
||||
if (targetSchoolYear != schoolYears.FormatSchoolYear(targetStart.Year))
|
||||
throw new InvalidOperationException("Das Zielschuljahr muss das Format JJJJ/JJ haben, z.B. 2027/28.");
|
||||
var sourceStart = schoolYears.SchoolYearStart(source.SchoolYear);
|
||||
if (targetStart <= sourceStart)
|
||||
throw new InvalidOperationException("Das Zielschuljahr muss nach dem bisherigen Schuljahr liegen.");
|
||||
if (request.GradeLevel is < 1 or > 13)
|
||||
throw new InvalidOperationException("Die Klassenstufe muss zwischen 1 und 13 liegen.");
|
||||
if (string.IsNullOrWhiteSpace(request.Name))
|
||||
throw new InvalidOperationException("Bitte einen Gruppennamen angeben.");
|
||||
|
||||
var duplicate = groups.GetBySchoolYear(targetSchoolYear, includeInactive: true)
|
||||
.Any(g => g.Name.Equals(request.Name.Trim(), StringComparison.OrdinalIgnoreCase)
|
||||
&& g.SubjectId == source.SubjectId && g.Type == source.Type);
|
||||
if (duplicate)
|
||||
throw new InvalidOperationException(
|
||||
"Im Zielschuljahr existiert bereits eine gleichnamige Gruppe mit demselben Fach und Typ.");
|
||||
|
||||
var sourceMemberships = memberships.GetByGroup(source.Id)
|
||||
.ToDictionary(m => m.Id);
|
||||
var selected = request.MembershipIds.Distinct()
|
||||
.Select(id => sourceMemberships.GetValueOrDefault(id)
|
||||
?? throw new InvalidOperationException("Eine ausgewählte Mitgliedschaft gehört nicht mehr zur Ausgangsgruppe."))
|
||||
.ToList();
|
||||
if (selected.Count == 0)
|
||||
throw new InvalidOperationException("Bitte mindestens einen Schüler auswählen.");
|
||||
|
||||
var target = new LearningGroup
|
||||
{
|
||||
Name = request.Name.Trim(),
|
||||
Type = source.Type,
|
||||
SubjectId = source.SubjectId,
|
||||
SchoolYear = targetSchoolYear,
|
||||
GradeLevel = request.GradeLevel,
|
||||
GradingSystem = source.GradingSystem,
|
||||
HoursPerWeek = source.HoursPerWeek,
|
||||
IsActive = true,
|
||||
IsOwnClass = source.IsOwnClass,
|
||||
IsDifferentiated = source.IsDifferentiated,
|
||||
};
|
||||
|
||||
var sourceWasActive = source.IsActive;
|
||||
try
|
||||
{
|
||||
groups.Save(target);
|
||||
foreach (var membership in selected)
|
||||
{
|
||||
memberships.Save(new GroupMembership
|
||||
{
|
||||
StudentId = membership.StudentId,
|
||||
GroupId = target.Id,
|
||||
AddedOn = DateOnly.FromDateTime(DateTime.Today),
|
||||
Period = MembershipPeriod.FullYear,
|
||||
JoinedAt = targetStart,
|
||||
LeftAt = null,
|
||||
Niveau = membership.Niveau,
|
||||
});
|
||||
}
|
||||
|
||||
if (request.CopyGradingScheme && gradingSchemes.GetByGroup(source.Id) is { } scheme)
|
||||
{
|
||||
gradingSchemes.Save(new GradingScheme
|
||||
{
|
||||
GroupId = target.Id,
|
||||
ExamsPercent = scheme.ExamsPercent,
|
||||
ParticipationPercent = scheme.ParticipationPercent,
|
||||
OtherPercent = scheme.OtherPercent,
|
||||
});
|
||||
}
|
||||
|
||||
if (request.ArchiveSource && source.IsActive)
|
||||
{
|
||||
source.IsActive = false;
|
||||
groups.Save(source);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
catch
|
||||
{
|
||||
source.IsActive = sourceWasActive;
|
||||
groups.Delete(target.Id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user