Kapitel 7 abgeschlossen.
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using LehrerApp.Core.Interfaces;
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Core.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace LehrerApp.Desktop.ViewModels.Groups;
|
||||
|
||||
public partial class GroupRolloverDialogViewModel : ObservableObject
|
||||
{
|
||||
private readonly LearningGroup _source;
|
||||
private readonly GroupRolloverService _rollover;
|
||||
private readonly SchoolYearService _schoolYears;
|
||||
|
||||
[ObservableProperty] private string _name;
|
||||
[ObservableProperty] private string _targetSchoolYear;
|
||||
[ObservableProperty] private int _gradeLevel;
|
||||
[ObservableProperty] private bool _copyGradingScheme;
|
||||
[ObservableProperty] private bool _archiveSource = true;
|
||||
[ObservableProperty] private string _nameError = "";
|
||||
[ObservableProperty] private string _schoolYearError = "";
|
||||
[ObservableProperty] private string _selectionError = "";
|
||||
[ObservableProperty] private string _generalError = "";
|
||||
|
||||
public string SourceSummary => $"{_source.Name} · {_source.SchoolYear} · Stufe {_source.GradeLevel}";
|
||||
public bool HasGradingScheme { get; }
|
||||
public int SelectedCount => Students.Count(s => s.IsSelected);
|
||||
public string SelectedCountText => $"{SelectedCount} von {Students.Count} Schülern ausgewählt";
|
||||
public ObservableCollection<GroupRolloverStudentItem> Students { get; } = [];
|
||||
public LearningGroup? Result { get; private set; }
|
||||
|
||||
public GroupRolloverDialogViewModel(LearningGroup source,
|
||||
IStudentRepository students, IGroupMembershipRepository memberships,
|
||||
IGradingSchemeRepository gradingSchemes, SchoolYearService schoolYears,
|
||||
GroupRolloverService rollover)
|
||||
{
|
||||
_source = source;
|
||||
_rollover = rollover;
|
||||
_schoolYears = schoolYears;
|
||||
_name = source.Name;
|
||||
_targetSchoolYear = NextSchoolYear(source.SchoolYear, schoolYears);
|
||||
_gradeLevel = Math.Min(13, source.GradeLevel + 1);
|
||||
HasGradingScheme = gradingSchemes.GetByGroup(source.Id) is not null;
|
||||
_copyGradingScheme = HasGradingScheme;
|
||||
|
||||
var sourceEnd = schoolYears.SchoolYearEnd(source.SchoolYear);
|
||||
foreach (var membership in memberships.GetByGroup(source.Id)
|
||||
.Select(m => (Membership: m, Student: students.GetById(m.StudentId)))
|
||||
.Where(x => x.Student is not null)
|
||||
.OrderBy(x => x.Student!.LastName).ThenBy(x => x.Student!.FirstName))
|
||||
{
|
||||
var canSelect = membership.Student!.IsActive;
|
||||
var isPreselected = canSelect
|
||||
&& GroupMembershipService.IsActiveOn(membership.Membership, sourceEnd);
|
||||
var item = new GroupRolloverStudentItem(
|
||||
membership.Student, membership.Membership, isPreselected, canSelect);
|
||||
item.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName != nameof(GroupRolloverStudentItem.IsSelected)) return;
|
||||
SelectionError = "";
|
||||
OnPropertyChanged(nameof(SelectedCount));
|
||||
OnPropertyChanged(nameof(SelectedCountText));
|
||||
};
|
||||
Students.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Save()
|
||||
{
|
||||
NameError = "";
|
||||
SchoolYearError = "";
|
||||
SelectionError = "";
|
||||
GeneralError = "";
|
||||
var valid = true;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Name))
|
||||
{
|
||||
NameError = "Gruppenname erforderlich.";
|
||||
valid = false;
|
||||
}
|
||||
try
|
||||
{
|
||||
var targetText = TargetSchoolYear.Trim();
|
||||
var targetStart = _schoolYears.SchoolYearStart(targetText);
|
||||
if (targetText != _schoolYears.FormatSchoolYear(targetStart.Year))
|
||||
{
|
||||
SchoolYearError = "Format JJJJ/JJ, z.B. 2027/28.";
|
||||
valid = false;
|
||||
}
|
||||
else if (targetStart <= _schoolYears.SchoolYearStart(_source.SchoolYear))
|
||||
{
|
||||
SchoolYearError = "Das Zielschuljahr muss nach dem bisherigen liegen.";
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is FormatException or ArgumentOutOfRangeException or IndexOutOfRangeException)
|
||||
{
|
||||
SchoolYearError = "Format JJJJ/JJ, z.B. 2027/28.";
|
||||
valid = false;
|
||||
}
|
||||
if (SelectedCount == 0)
|
||||
{
|
||||
SelectionError = "Bitte mindestens einen Schüler auswählen.";
|
||||
valid = false;
|
||||
}
|
||||
if (!valid) return;
|
||||
|
||||
try
|
||||
{
|
||||
Result = _rollover.RollOver(_source, new GroupRolloverRequest(
|
||||
Name, TargetSchoolYear.Trim(), GradeLevel,
|
||||
Students.Where(s => s.IsSelected).Select(s => s.MembershipId).ToList(),
|
||||
CopyGradingScheme, ArchiveSource));
|
||||
}
|
||||
catch (Exception ex) when (ex is InvalidOperationException or FormatException or ArgumentOutOfRangeException)
|
||||
{
|
||||
GeneralError = ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
private static string NextSchoolYear(string source, SchoolYearService schoolYears)
|
||||
{
|
||||
var start = schoolYears.SchoolYearStart(source);
|
||||
return schoolYears.FormatSchoolYear(start.Year + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class GroupRolloverStudentItem : ObservableObject
|
||||
{
|
||||
[ObservableProperty] private bool _isSelected;
|
||||
|
||||
public Guid MembershipId { get; }
|
||||
public string FullName { get; }
|
||||
public string NiveauText { get; }
|
||||
public string MembershipStatus { get; }
|
||||
public bool CanSelect { get; }
|
||||
|
||||
public GroupRolloverStudentItem(Student student, GroupMembership membership,
|
||||
bool isSelected, bool canSelect)
|
||||
{
|
||||
MembershipId = membership.Id;
|
||||
FullName = student.FullName;
|
||||
NiveauText = membership.Niveau?.ToString() ?? "–";
|
||||
CanSelect = canSelect;
|
||||
_isSelected = isSelected;
|
||||
MembershipStatus = BuildStatus(student, membership, isSelected);
|
||||
}
|
||||
|
||||
partial void OnIsSelectedChanged(bool value)
|
||||
{
|
||||
if (!CanSelect && value) IsSelected = false;
|
||||
}
|
||||
|
||||
private static string BuildStatus(Student student, GroupMembership membership, bool isPreselected)
|
||||
{
|
||||
if (!student.IsActive) return "Schüler inaktiv · nicht übernehmbar";
|
||||
if (isPreselected) return "Bis Schuljahresende aktiv · vorausgewählt";
|
||||
if (membership.LeftAt is { } leftAt) return $"Ausgetreten am {leftAt:dd.MM.yyyy} · nicht vorausgewählt";
|
||||
if (membership.Period == MembershipPeriod.H1Only) return "Nur 1. Halbjahr · nicht vorausgewählt";
|
||||
return "Am Schuljahresende nicht aktiv · nicht vorausgewählt";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user