From 23b8757ff5cfa092e01b7cfbacd690ff4ed133a8 Mon Sep 17 00:00:00 2001 From: Lyndon Jetten Date: Wed, 5 Jun 2024 14:45:33 +0200 Subject: [PATCH] =?UTF-8?q?FaC:=20Neues=20Scheduling-Modul=20hinzugef?= =?UTF-8?q?=C3=BCgt,=20aber=20noch=20lange=20nicht=20fertig.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ownChat: Neues TimeStamp-Objekt wird unterstützt und der Chat funktioniert wieder. --- BeWo/BeWo.csproj | 72 + BeWo/Core/BeWoUtils.cs | 34 + BeWo/Core/Config/AppSettings.cs | 15 + BeWo/Core/Service/Cache.cs | 46 + BeWo/MainControl.xaml | 37 + BeWo/MainControl.xaml.cs | 68 +- .../CalendarDisabledUnderConstruction.png | Bin 0 -> 8679 bytes .../BitmapToBitmapSourceConverter.cs | 18 +- .../AppointmentBorderThicknessConverter.cs | 28 + .../CustomerAndResourceBackgroundConverter.cs | 148 + .../DayViewDayCountVisibilityConverter.cs | 29 + .../EmployeeBackgroundColorConverter.cs | 25 + ...mployeeListConfirmationToolTipConverter.cs | 47 + .../InfoButtonVisibilityConverter.cs | 20 + .../ParticipationRequestListViewConverter.cs | 49 + .../RecurringAppointmentDurationConverter.cs | 208 + .../Converter/RequestVisibilityConverter.cs | 73 + .../SchedulingBackgroundConverter.cs | 42 + .../Converter/SchedulingCheckBoxConverter.cs | 35 + ...chedulingInformationVisibilityConverter.cs | 21 + .../SchedulingParticipationBrushConverter.cs | 54 + ...edulingRemoveElementVisibilityConverter.cs | 30 + ...chedulingTextFromIDataContractConverter.cs | 32 + .../Converter/WidthAdditionMultiConverter.cs | 22 + .../SchedulingUtils/AppointmentColorHelper.cs | 117 + .../CategoryDependencyObject.cs | 15 + .../EmployeeDependencyObject.cs | 15 + .../SchedulingUtils/ListItemHelper.cs | 19 + .../ResourceDependencyObject.cs | 15 + .../SchedulingUtils/TreeViewItemHelper.cs | 31 + BeWo/Scheduling/SchedulingUtils/Utils.cs | 193 + BeWo/Scheduling/View/AppointmentEditView.xaml | 534 ++ .../View/AppointmentEditView.xaml.cs | 580 ++ .../View/AppointmentRecurrenceEditView.xaml | 100 + .../AppointmentRecurrenceEditView.xaml.cs | 53 + ...currenceParticipationConfirmationView.xaml | 23 + ...renceParticipationConfirmationView.xaml.cs | 77 + .../Scheduling/View/OpenAppointmentsView.xaml | 172 + .../View/OpenAppointmentsView.xaml.cs | 261 + BeWo/Scheduling/View/SchedulingView.xaml | 994 ++++ BeWo/Scheduling/View/SchedulingView.xaml.cs | 1989 +++++++ .../ViewModel/AppointmentWindowVM.cs | 222 + .../ViewModel/SchedulingAppointmentListVM.cs | 699 +++ .../ViewModel/SchedulingAppointmentVM.cs | 875 +++ BeWo/ServiceProxy/Generated.cs | 4856 +++++++++-------- BeWo/Styles/OrangeBlack.xaml | 86 +- BeWo/View/Detail/AbsenceReasonView.xaml | 1 + BeWo/View/Detail/AbsenceReasonView.xaml.cs | 1 + BeWo/View/Detail/AbsenceTimesView.xaml.cs | 2 + .../AccountingTransactionBookingView.xaml.cs | 2 + .../Detail/AccountingTransactionView.xaml.cs | 2 + .../Detail/AdditionalServiceAddView.xaml.cs | 2 + .../AdditionalServiceRegionView.xaml.cs | 2 + .../View/Detail/AdditionalServiceView.xaml.cs | 2 + .../AssessmentSheetCategoryView.xaml.cs | 2 +- .../Detail/AssessmentSheetValueView.xaml.cs | 2 +- BeWo/View/Detail/AufenthaltsView.xaml.cs | 2 +- .../Detail/AuszahlungsartenAddView.xaml.cs | 2 +- BeWo/View/Detail/BudgetView.xaml.cs | 2 +- BeWo/View/Detail/CustomerView.xaml.cs | 2 +- BeWo/View/Detail/CustomerView2.xaml.cs | 2 +- .../View/Detail/DokumentationstypView.xaml.cs | 2 +- BeWo/View/Detail/EmployeeView.xaml.cs | 2 +- BeWo/View/Detail/EmploymentTypeView.xaml.cs | 2 +- BeWo/View/Detail/EnvironmentRoleView.xaml.cs | 2 +- .../View/Detail/MedikamentenartenView.xaml.cs | 2 +- BeWo/View/Detail/NationalityView.xaml.cs | 2 +- .../Zeiterfassung/ServiceRecordView2.xaml.cs | 1 + BeWo/View/HomeView.xaml.cs | 5 + BeWo/View/Master/SchedulingMasterView.xaml | 58 + BeWo/View/Master/SchedulingMasterView.xaml.cs | 26 + BeWo/ViewModel/AccountingBookingVM.cs | 2 + .../ListViewModel/ServiceRecordListVM.cs | 2 + BeWo/ownChat/ChatDokumentationsView.xaml.cs | 217 +- BeWoPlanerMobil/BeWoPlanerMobil.csproj | 3 + BeWoPlanerMobil/BeWoPlanerMobil.csproj.user | 2 +- BeWoPlanerMobil/Content/style.css | 137 + BeWoPlanerMobil/Content/style.css.map | 2 +- BeWoPlanerMobil/Content/style.scss | 2 +- BeWoPlanerMobil/Controllers/MainController.cs | 350 +- BeWoPlanerMobil/Global.asax.cs | 10 +- BeWoPlanerMobil/Models/MainModel.cs | 9 + .../Scripts/ownSoft-Scripts/group-booking.js | 38 +- .../utils/password-security.js | 142 +- .../ownSoft-Scripts/view-scripts/main.js | 8 +- BeWoPlanerMobil/Util/GoalTreeItem.cs | 6 +- .../Category2ServiceDescriptionPartial.cshtml | 85 + .../Views/Main/GoalTreePartial.cshtml | 10 +- .../Views/Main/GroupBookingFormPartial.cshtml | 414 ++ .../Views/Main/GroupBookingPartial.cshtml | 427 +- .../Views/Main/GroupBookingScCbRelList.cshtml | 82 + BeWoPlanerMobil/Views/Main/Main.cshtml | 47 +- .../Main/ServiceRecordListPartial.cshtml | 86 +- .../Views/Main/SingleBookingPartial.cshtml | 32 +- BeWoPlanerMobil/Views/Shared/_Layout.cshtml | 344 +- BeWoPlanerMobil/Web.config | 9 +- Data/Access/SearchDAO.cs | 21 + Service/Plugins/PluginLoader.cs | 2 +- Service/ServiceContracts/ICustomerService.cs | 4 + Service/ServiceContracts/IResourceService.cs | 24 + Service/ServiceContracts/IValueListService.cs | 8 + .../CustomerServiceImp.cs | 14 + .../ResourceServiceImp.cs | 120 +- .../ValueListServiceImp.cs | 26 + Shared/Core/ObservableDictionary.cs | 5 + Shared/Core/SettingsKeys.cs | 196 +- .../ClientPartials/CompactEmployeeDC.cs | 2 + Shared/Extensions/ICollectionExtensions.cs | 17 +- 108 files changed, 12550 insertions(+), 3564 deletions(-) create mode 100644 BeWo/Ressources/Icons/CalendarDisabledUnderConstruction.png create mode 100644 BeWo/Scheduling/Converter/AppointmentBorderThicknessConverter.cs create mode 100644 BeWo/Scheduling/Converter/CustomerAndResourceBackgroundConverter.cs create mode 100644 BeWo/Scheduling/Converter/DayViewDayCountVisibilityConverter.cs create mode 100644 BeWo/Scheduling/Converter/EmployeeBackgroundColorConverter.cs create mode 100644 BeWo/Scheduling/Converter/EmployeeListConfirmationToolTipConverter.cs create mode 100644 BeWo/Scheduling/Converter/InfoButtonVisibilityConverter.cs create mode 100644 BeWo/Scheduling/Converter/ParticipationRequestListViewConverter.cs create mode 100644 BeWo/Scheduling/Converter/RecurringAppointmentDurationConverter.cs create mode 100644 BeWo/Scheduling/Converter/RequestVisibilityConverter.cs create mode 100644 BeWo/Scheduling/Converter/SchedulingBackgroundConverter.cs create mode 100644 BeWo/Scheduling/Converter/SchedulingCheckBoxConverter.cs create mode 100644 BeWo/Scheduling/Converter/SchedulingInformationVisibilityConverter.cs create mode 100644 BeWo/Scheduling/Converter/SchedulingParticipationBrushConverter.cs create mode 100644 BeWo/Scheduling/Converter/SchedulingRemoveElementVisibilityConverter.cs create mode 100644 BeWo/Scheduling/Converter/SchedulingTextFromIDataContractConverter.cs create mode 100644 BeWo/Scheduling/Converter/WidthAdditionMultiConverter.cs create mode 100644 BeWo/Scheduling/SchedulingUtils/AppointmentColorHelper.cs create mode 100644 BeWo/Scheduling/SchedulingUtils/CategoryDependencyObject.cs create mode 100644 BeWo/Scheduling/SchedulingUtils/EmployeeDependencyObject.cs create mode 100644 BeWo/Scheduling/SchedulingUtils/ListItemHelper.cs create mode 100644 BeWo/Scheduling/SchedulingUtils/ResourceDependencyObject.cs create mode 100644 BeWo/Scheduling/SchedulingUtils/TreeViewItemHelper.cs create mode 100644 BeWo/Scheduling/SchedulingUtils/Utils.cs create mode 100644 BeWo/Scheduling/View/AppointmentEditView.xaml create mode 100644 BeWo/Scheduling/View/AppointmentEditView.xaml.cs create mode 100644 BeWo/Scheduling/View/AppointmentRecurrenceEditView.xaml create mode 100644 BeWo/Scheduling/View/AppointmentRecurrenceEditView.xaml.cs create mode 100644 BeWo/Scheduling/View/OccurrenceParticipationConfirmationView.xaml create mode 100644 BeWo/Scheduling/View/OccurrenceParticipationConfirmationView.xaml.cs create mode 100644 BeWo/Scheduling/View/OpenAppointmentsView.xaml create mode 100644 BeWo/Scheduling/View/OpenAppointmentsView.xaml.cs create mode 100644 BeWo/Scheduling/View/SchedulingView.xaml create mode 100644 BeWo/Scheduling/View/SchedulingView.xaml.cs create mode 100644 BeWo/Scheduling/ViewModel/AppointmentWindowVM.cs create mode 100644 BeWo/Scheduling/ViewModel/SchedulingAppointmentListVM.cs create mode 100644 BeWo/Scheduling/ViewModel/SchedulingAppointmentVM.cs create mode 100644 BeWo/View/Master/SchedulingMasterView.xaml create mode 100644 BeWo/View/Master/SchedulingMasterView.xaml.cs create mode 100644 BeWoPlanerMobil/Views/Main/Category2ServiceDescriptionPartial.cshtml create mode 100644 BeWoPlanerMobil/Views/Main/GroupBookingFormPartial.cshtml create mode 100644 BeWoPlanerMobil/Views/Main/GroupBookingScCbRelList.cshtml diff --git a/BeWo/BeWo.csproj b/BeWo/BeWo.csproj index 9643dfa4d..e7d8a1791 100644 --- a/BeWo/BeWo.csproj +++ b/BeWo/BeWo.csproj @@ -129,6 +129,7 @@ + @@ -212,6 +213,26 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -493,6 +514,10 @@ MSBuild:UpdateDesignTimeXaml + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -844,6 +869,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + AppointmentEditView.xaml + + + AppointmentRecurrenceEditView.xaml + + + OccurrenceParticipationConfirmationView.xaml + + + OpenAppointmentsView.xaml + + + SchedulingView.xaml + SchulbegleitenderDienstEmployeeView.xaml @@ -1225,6 +1291,9 @@ DokumenteView.xaml + + SchedulingMasterView.xaml + TwoFactorAuthMessageDialog.xaml @@ -3108,6 +3177,9 @@ + + + diff --git a/BeWo/Core/BeWoUtils.cs b/BeWo/Core/BeWoUtils.cs index beae06e1a..aebbe9a58 100644 --- a/BeWo/Core/BeWoUtils.cs +++ b/BeWo/Core/BeWoUtils.cs @@ -60,6 +60,9 @@ using Control = System.Windows.Controls.Control; using Utils = BS.Shared.Core.Utils; using Brush = System.Windows.Media.Brush; using BeWo.View.Detail.Zeiterfassung; +using DevExpress.Xpf.Scheduling; +using CommandBarStyle = DevExpress.Xpf.DocumentViewer.CommandBarStyle; +using ImageSource = System.Windows.Media.ImageSource; namespace BeWo.Core { @@ -2097,6 +2100,37 @@ namespace BeWo.Core newView.ShowDialog(); } + // Zeiterfassungseintrag aus dem neuen Kalendermodul erstellen + public static void CreateZeiterfassung(DateTime start, DateTime end, List customer, List employees, String notice, AppointmentItem appointment, Action schedulerCallback) + { + ChatDokumentationsView newView; + + if(customer.Count == 0) + { + // Normale Buchung + newView = new ChatDokumentationsView(notice, null, null, start, end, schedulerCallback, appointment); + } + else if(customer.Count == 1 && employees.Count < 2) + { + var compactKlient = ServiceFacade.DoCustomerServiceSync(s => s.GetCompactCustomerDC(customer[0].CustomerOid)); + + // Normale Buchung + newView = new ChatDokumentationsView(notice, compactKlient, employees.Count == 1 ? employees[0] : null, start, end, schedulerCallback, appointment); + } + else + { + if(employees.Count == 0) + { + employees.Add(BeWoApp.CompactLoggedOnEmployee); + } + + // Gruppenbuchungen + newView = new ChatDokumentationsView(appointment, notice, start, end, customer, employees, schedulerCallback); + } + + newView.ShowDialog(); + } + public static void CreateZeiterfassung(DateTime start, DateTime end, List customer, List employees, String notice) { ChatDokumentationsView newView; diff --git a/BeWo/Core/Config/AppSettings.cs b/BeWo/Core/Config/AppSettings.cs index df315e236..5d44738d7 100644 --- a/BeWo/Core/Config/AppSettings.cs +++ b/BeWo/Core/Config/AppSettings.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using BeWo.View.Navigation.Filter; using BS.Shared; +using DevExpress.Xpf.Scheduling; using DevExpress.XtraScheduler; namespace BeWo.Core.Config @@ -462,6 +463,20 @@ namespace BeWo.Core.Config } } + private ViewType _SchedulingViewType; + public ViewType SchedulingViewType + { + get => _SchedulingViewType; + set + { + if(_SchedulingViewType != value) + { + _SchedulingViewType = value; + IsDirty = true; + } + } + } + private int _SchedulerTimelineViewDayCount; public int SchedulerTimelineViewDayCount { diff --git a/BeWo/Core/Service/Cache.cs b/BeWo/Core/Service/Cache.cs index 03b91b21b..837c44ef3 100644 --- a/BeWo/Core/Service/Cache.cs +++ b/BeWo/Core/Service/Cache.cs @@ -590,5 +590,51 @@ namespace BeWo.Core.Service }); }); } + + + // Für neuen SchedulingView hinzugefügt + private List _FilteredCustomers; + public void GetAllActiveCustomersCompactForEmployee(long employeeOid, bool forceReload, Action> callback) + { + if(_FilteredCustomers is null || forceReload) + { + ServiceFacade.DoEmployeeServiceAsync(s => s.GetActiveCompactCustomersForEmployee(employeeOid), customers => + { + customers.Sort((x, y) => $"{x.LastName}, {x.FirstName}".CompareTo($"{y.LastName}, {y.FirstName}")); + _FilteredCustomers = customers; + callback(customers); + }); + } + else + { + callback(_FilteredCustomers); + } + } + public void ClearFilteredCustomers() + { + _FilteredCustomers = null; + } + + private List _FilteredEmployees; + public void GetAllActiveCompactEmployeesForEmployee(long employeeOid, bool forceReload, Action> callback) + { + if(_FilteredEmployees is null || forceReload) + { + ServiceFacade.DoEmployeeServiceAsync(s => s.GetActiveCompactEmployeesForEmployee(employeeOid), employees => + { + employees.Sort((x, y) => $"{x.LastName}, {x.FirstName}".CompareTo($"{y.LastName}, {y.FirstName}")); + _FilteredEmployees = employees; + callback(employees); + }); + } + else + { + callback(_FilteredEmployees); + } + } + public void ClearFilteredEmployees() + { + _FilteredEmployees = null; + } } } \ No newline at end of file diff --git a/BeWo/MainControl.xaml b/BeWo/MainControl.xaml index 20728c592..e5de4b7c2 100644 --- a/BeWo/MainControl.xaml +++ b/BeWo/MainControl.xaml @@ -294,6 +294,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + (); @@ -201,7 +215,8 @@ namespace BeWo } } - } else if(ActiveView?.GetType() == typeof(HomeView)) + } + else if(ActiveView?.GetType() == typeof(HomeView)) { var homeDragPanelView = (HomeView) ActiveView; homeDragPanelView.UpdateRequestsHyperLink(); @@ -268,6 +283,9 @@ namespace BeWo case UIContext.CustomerTeam: view = new CustomerTeamNavigationView(); break; + case UIContext.Scheduling: + view = new SchedulingMasterView(); + break; } _Views[uiContext] = view; } @@ -1757,5 +1775,13 @@ namespace BeWo NavigateTo(GetViewForUIContext(UIContext.Vertretungen)); } } + + private void Button_Scheduling_Click(object sender, RoutedEventArgs e) + { + if(DoSaveCheck()) + { + NavigateTo(GetViewForUIContext(UIContext.Scheduling)); + } + } } } \ No newline at end of file diff --git a/BeWo/Ressources/Icons/CalendarDisabledUnderConstruction.png b/BeWo/Ressources/Icons/CalendarDisabledUnderConstruction.png new file mode 100644 index 0000000000000000000000000000000000000000..343ba124eacb717ad6c3c6b6786747d011560874 GIT binary patch literal 8679 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 selectedItems) || !selectedItems.Any()) + { + return new LinearGradientBrush(new GradientStopCollection { new GradientStop(farbe, 1) }, new Point(.5, 0), new Point(.5, 1)); + } + + if(!(values[1] is CustomFieldCollection customViewInfo)) + { + return new LinearGradientBrush(new GradientStopCollection { new GradientStop(farbe, 1) }, new Point(.5, 0), new Point(.5, 1)); + } + + var customFieldStorage = (CustomFieldStorage) customViewInfo[nameof(CustomFieldStorage)]; + + var isTask = customFieldStorage.IsTask; + + if(isTask) + { + farbe = Color.FromRgb(153, 59, 59); + } + + var aptCustomers = customFieldStorage.CustomerList; + var aptResources = customFieldStorage.ResourceList; + + var gradientCollection = new GradientStopCollection(); + var farbKollektion = new List(); + + if(selectedItems.Any(aptCustomers.Contains)) + { + farbKollektion.Add(Color.FromRgb(59, 119, 153)); + } + + if(selectedItems.Any(aptResources.Contains)) + { + farbKollektion.Add(Color.FromRgb(4, 180, 208)); + } + + switch(farbKollektion.Count) + { + case 1: + if(BeWoApp.AppSettings.ShowMultipleResourceColors) + { + AddResourceColors(gradientCollection, aptResources, false); + } + else + { + var first = aptResources.FirstOrDefault(); + + gradientCollection.Add(first != null ? new GradientStop((Color) (ColorConverter.ConvertFromString(first.Color) ?? Color.FromRgb(4, 180, 208)), 1) : new GradientStop(farbKollektion[0], 1)); + } + + break; + case 2: + // 1. Klientenfarbe + gradientCollection.Add(new GradientStop(farbKollektion[0], 0.5)); + + // 2. Resourcenfarben + if(BeWoApp.AppSettings.ShowMultipleResourceColors) + { + AddResourceColors(gradientCollection, aptResources, true); + } + else + { + var first2 = aptResources.FirstOrDefault(); + + if(first2 != null) + { + gradientCollection.Add(new GradientStop((Color) (ColorConverter.ConvertFromString(first2.Color) ?? Color.FromRgb(4, 180, 208)), 0.5)); + } + } + + break; + default: + gradientCollection.Add(new GradientStop(farbe, 1)); + break; + } + + return new LinearGradientBrush(gradientCollection, new Point(.5, 0), new Point(.5, 1)); + } + catch(Exception) + { + return new LinearGradientBrush(new GradientStopCollection { new GradientStop(farbe, 1) }, new Point(.5, 0), new Point(.5, 1)); + } + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + private static void AddResourceColors(GradientStopCollection gradientCollection, IReadOnlyList resourceList, bool hasCustomers) + { + var previousOffset = hasCustomers ? 0.5 : 0; + + var numberOfResources = resourceList.Count; + + if(numberOfResources == 0) + { + return; + } + + if(numberOfResources > 4) + { + numberOfResources = 4; + } + + var iterator = hasCustomers == false ? 1d / numberOfResources : previousOffset / numberOfResources; + + for(var i = 0; i < numberOfResources; i++) + { + var resource = resourceList[i]; + + var currentOffset = previousOffset; + var nextOffset = currentOffset + iterator; + + if(nextOffset > 1) + { + nextOffset = 1; + } + + previousOffset = nextOffset; + + gradientCollection.Add(new GradientStop((Color) (ColorConverter.ConvertFromString(resource.Color) ?? Color.FromRgb(4, 180, 208)), currentOffset)); + gradientCollection.Add(new GradientStop((Color) (ColorConverter.ConvertFromString(resource.Color) ?? Color.FromRgb(4, 180, 208)), nextOffset)); + } + } + } +} diff --git a/BeWo/Scheduling/Converter/DayViewDayCountVisibilityConverter.cs b/BeWo/Scheduling/Converter/DayViewDayCountVisibilityConverter.cs new file mode 100644 index 000000000..d02850fd4 --- /dev/null +++ b/BeWo/Scheduling/Converter/DayViewDayCountVisibilityConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace BeWo.Scheduling.Converter +{ + public class DayViewDayCountVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(value is DevExpress.Xpf.Scheduling.ViewType.DayView && parameter is string stringParameter && stringParameter == "DayCount") + { + return Visibility.Visible; + } + else if(value is DevExpress.Xpf.Scheduling.ViewType.TimelineView && parameter is string strParameter && strParameter == "IntervalCount") + { + return Visibility.Visible; + } + + return Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/EmployeeBackgroundColorConverter.cs b/BeWo/Scheduling/Converter/EmployeeBackgroundColorConverter.cs new file mode 100644 index 000000000..05b0006fd --- /dev/null +++ b/BeWo/Scheduling/Converter/EmployeeBackgroundColorConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; + +namespace BeWo.Scheduling.Converter +{ + public class EmployeeBackgroundColorConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if(values.Length == 3 && values[0] is bool showEmployeeBackgroundColor && values[1] is LinearGradientBrush backgroundBrush && values[2] is SolidColorBrush defaultBackgroundColor) + { + return showEmployeeBackgroundColor ? new SolidColorBrush(backgroundBrush.GradientStops[0].Color) : defaultBackgroundColor; + } + + return new SolidColorBrush(Color.FromRgb(192, 255, 208)); + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/EmployeeListConfirmationToolTipConverter.cs b/BeWo/Scheduling/Converter/EmployeeListConfirmationToolTipConverter.cs new file mode 100644 index 000000000..cb085fe2b --- /dev/null +++ b/BeWo/Scheduling/Converter/EmployeeListConfirmationToolTipConverter.cs @@ -0,0 +1,47 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; +using BS.Shared; +using BS.Shared.DataContracts; + +namespace BeWo.Scheduling.Converter +{ + public class EmployeeListConfirmationToolTipConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(value is Employee2SchedulerAppointmentDC e2s) + { + if(parameter?.Equals("EmployeeName") ?? false) + { + return e2s.Employee?.DetailDescription; + } + + var color = Color.FromRgb(255, 255, 255); + + switch(e2s.ParticipationAnswer) + { + case ParticipationAnswer.Vorbehalt: + color = Color.FromRgb(185, 39, 217); + break; + case ParticipationAnswer.Zusage: + color = Color.FromRgb(72, 212, 78); + break; + case ParticipationAnswer.Absage: + color = Color.FromRgb(171, 0, 48); + break; + } + + return new SolidColorBrush(color); + } + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/BeWo/Scheduling/Converter/InfoButtonVisibilityConverter.cs b/BeWo/Scheduling/Converter/InfoButtonVisibilityConverter.cs new file mode 100644 index 000000000..ae3f392fc --- /dev/null +++ b/BeWo/Scheduling/Converter/InfoButtonVisibilityConverter.cs @@ -0,0 +1,20 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace BeWo.Scheduling.Converter +{ + public class InfoButtonVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return BeWoApp.AppSettings.ShowInfoButtonInCalender ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/ParticipationRequestListViewConverter.cs b/BeWo/Scheduling/Converter/ParticipationRequestListViewConverter.cs new file mode 100644 index 000000000..c6481511a --- /dev/null +++ b/BeWo/Scheduling/Converter/ParticipationRequestListViewConverter.cs @@ -0,0 +1,49 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Windows.Data; +using BeWo.Scheduling.ViewModel; +using BS.Shared; + +namespace BeWo.Scheduling.Converter +{ + public class ParticipationRequestListViewConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(value is SchedulingAppointmentVM appointment) + { + if(appointment.LabelKey is null) + { + return $"{appointment.Start.ToShortDateString()}; {appointment.Subject}"; + } + + var participationAnswer = string.Empty; + + var correspondingRelation = appointment.EmployeeList.ToList().Find(e2a => e2a.Employee2SchedulerAppointmentOid == appointment.LabelKey); + + switch(correspondingRelation.ParticipationAnswer) + { + case ParticipationAnswer.Zusage: + participationAnswer = "zugesagt"; + break; + case ParticipationAnswer.Vorbehalt: + participationAnswer = "mit Vorbehalt zugesagt"; + break; + case ParticipationAnswer.Absage: + participationAnswer = "abgesagt"; + break; + } + + return $"{appointment.Start.ToShortDateString()}: {correspondingRelation.Employee.FirstNameLastName} hat {participationAnswer}"; + } + + return string.Empty; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/RecurringAppointmentDurationConverter.cs b/BeWo/Scheduling/Converter/RecurringAppointmentDurationConverter.cs new file mode 100644 index 000000000..491ee1341 --- /dev/null +++ b/BeWo/Scheduling/Converter/RecurringAppointmentDurationConverter.cs @@ -0,0 +1,208 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using BeWo.Scheduling.ViewModel; +using DevExpress.XtraScheduler; + +namespace BeWo.Scheduling.Converter +{ + public class RecurringAppointmentDurationConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(value is null) + { + return null; + } + + var appointment = (SchedulingAppointmentVM) value; + + if(appointment.RecurrenceInfo is null) + { + return string.Empty; + } + + IRecurrenceInfo recurrenceInfo = new RecurrenceInfo(); + recurrenceInfo.FromXml(appointment.RecurrenceInfo); + + var rangeString = string.Empty; + var suffix = string.Empty; + var periodicity = recurrenceInfo.Periodicity; + var intervalPlural = string.Empty; + var weekDays = string.Empty; + string weekOfMonth; + + switch(recurrenceInfo.Range) + { + case RecurrenceRange.EndByDate: + rangeString += $"bis zum {recurrenceInfo.End.ToShortDateString()} "; + break; + case RecurrenceRange.OccurrenceCount: + rangeString += $"{recurrenceInfo.OccurrenceCount} mal "; + break; + } + + switch(recurrenceInfo.Type) + { + case RecurrenceType.Daily: + if(periodicity == 1) + { + suffix = recurrenceInfo.WeekDays.Equals(WeekDays.WorkDays) ? "jeden Arbeitstag " : "täglich "; + } + + intervalPlural = " Tage "; + + break; + case RecurrenceType.Weekly: + if(periodicity == 1) + { + suffix = "wöchentlich "; + } + + intervalPlural = " Wochen "; + + weekDays = TranslateWorkDays(recurrenceInfo.WeekDays.ToString(), true); + + break; + case RecurrenceType.Yearly: + weekDays = TranslateWorkDays(recurrenceInfo.WeekDays.ToString(), false); + weekOfMonth = TranslateWeekOfMonth(recurrenceInfo.WeekOfMonth); + var tag = recurrenceInfo.DayNumber; + + suffix = "jährlich "; + + if(recurrenceInfo.WeekOfMonth != WeekOfMonth.None) + { + suffix += $"jeden {weekOfMonth}{weekDays}im {GetMonthByInt(recurrenceInfo.Month)}"; + } + else + { + suffix += $"am {tag}. {GetMonthByInt(recurrenceInfo.Month)}"; + } + + weekDays = string.Empty; + + break; + case RecurrenceType.Monthly: + string monatlichesInterval; + + if(recurrenceInfo.WeekOfMonth != WeekOfMonth.None) + { + weekOfMonth = TranslateWeekOfMonth(recurrenceInfo.WeekOfMonth); + weekDays = TranslateWorkDays(recurrenceInfo.WeekDays.ToString(), false); + + suffix = $"jeden {weekOfMonth}{weekDays}"; + + monatlichesInterval = periodicity == 1 ? "jedes Monats " : $"jedes {periodicity}. Monats "; + + suffix += monatlichesInterval; + weekDays = ""; + } + else + { + monatlichesInterval = periodicity == 1 ? "jedes Monats " : $"jedes {periodicity}. Monats "; + + suffix = $"am {recurrenceInfo.DayNumber}. {monatlichesInterval}"; + weekDays = string.Empty; + } + + break; + } + + if(periodicity > 1 && recurrenceInfo.Type != RecurrenceType.Monthly) + { + suffix = $"alle {periodicity}{intervalPlural}"; + } + + return $"Dieser Termin wird {rangeString}{suffix}{weekDays}wiederholt"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + private static string TranslateWorkDays(string weekDays, bool mitGenitivS) + { + weekDays = weekDays.Replace("Monday", mitGenitivS ? "Montags" : "Montag"); + weekDays = weekDays.Replace("Tuesday", mitGenitivS ? "Dienstags" : "Dienstag"); + weekDays = weekDays.Replace("Wednesday", mitGenitivS ? "Mittwochs" : "Mittwoch"); + weekDays = weekDays.Replace("Thursday", mitGenitivS ? "Donnerstags" : "Donnerstag"); + weekDays = weekDays.Replace("Friday", mitGenitivS ? "Freitags" : "Freitag"); + weekDays = weekDays.Replace("Saturday", mitGenitivS ? "Samstags" : "Samstag"); + weekDays = weekDays.Replace("Sunday", mitGenitivS ? "Sonntags" : "Sonntag"); + weekDays = weekDays.Replace("EveryDay", "jeden Tag"); + + var lastComma = weekDays.LastIndexOf(','); + if(lastComma != -1) + { + weekDays = weekDays.Remove(lastComma, 1).Insert(lastComma, " und"); + } + + return weekDays + " "; + } + + private static string TranslateWeekOfMonth(WeekOfMonth weekOfMonth) + { + var result = " "; + + switch(weekOfMonth) + { + case WeekOfMonth.First: + result = "ersten "; + break; + case WeekOfMonth.Second: + result = "zweiten "; + break; + case WeekOfMonth.Third: + result = "dritten "; + break; + case WeekOfMonth.Fourth: + result = "vierten "; + break; + case WeekOfMonth.Last: + result = "letzten "; + break; + case WeekOfMonth.None: + return result; + default: + return result; + } + + return result; + } + + private static string GetMonthByInt(int month) + { + switch(month) + { + case 1: + return "Januar "; + case 2: + return "Februar "; + case 3: + return "März "; + case 4: + return "April "; + case 5: + return "Mai "; + case 6: + return "Juni "; + case 7: + return "Juli "; + case 8: + return "August "; + case 9: + return "September "; + case 10: + return "Oktober "; + case 11: + return "November "; + case 12: + return "Dezember "; + default: + return " "; + } + } + } +} diff --git a/BeWo/Scheduling/Converter/RequestVisibilityConverter.cs b/BeWo/Scheduling/Converter/RequestVisibilityConverter.cs new file mode 100644 index 000000000..7d2dca4bf --- /dev/null +++ b/BeWo/Scheduling/Converter/RequestVisibilityConverter.cs @@ -0,0 +1,73 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using BeWo.Scheduling.ViewModel; +using DevExpress.XtraScheduler; + +namespace BeWo.Scheduling.Converter +{ + public class RequestVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(value is null) + { + return Visibility.Collapsed; + } + + var appointment = (SchedulingAppointmentVM) value; + + var hasNoEnd = false; + var liesInPast = appointment.Start < DateTime.Now && appointment.End < DateTime.Now; + + if(!(appointment.RecurrenceInfo is null)) + { + IRecurrenceInfo recurrenceInfo = new RecurrenceInfo(); + recurrenceInfo.FromXml(appointment.RecurrenceInfo); + + hasNoEnd = recurrenceInfo.Range == RecurrenceRange.NoEndDate; + liesInPast = recurrenceInfo.Start < DateTime.Now && recurrenceInfo.End < DateTime.Now; + } + + if(parameter?.Equals("ZusagenStack") ?? false) + { + if(appointment.LabelKey.HasValue) + { + return Visibility.Collapsed; + } + + var shouldBeVisible = !liesInPast; + + if(!(appointment.RecurrenceInfo is null)) + { + shouldBeVisible = !liesInPast || hasNoEnd; + } + + return shouldBeVisible ? Visibility.Visible : Visibility.Collapsed; + } + + if(parameter?.Equals("OKBtn") ?? false) + { + return (appointment.LabelKey.HasValue || liesInPast) && !hasNoEnd ? Visibility.Visible : Visibility.Collapsed; + } + + if(parameter?.Equals("TerminVerstrichen") ?? false) + { + return liesInPast && !hasNoEnd ? Visibility.Visible : Visibility.Collapsed; + } + + if(parameter?.Equals("SerienterminLabel") ?? false) + { + return !(appointment.RecurrenceInfo is null) ? Visibility.Visible : Visibility.Collapsed; + } + + return appointment.LabelKey.HasValue || liesInPast ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/SchedulingBackgroundConverter.cs b/BeWo/Scheduling/Converter/SchedulingBackgroundConverter.cs new file mode 100644 index 000000000..bf49375c9 --- /dev/null +++ b/BeWo/Scheduling/Converter/SchedulingBackgroundConverter.cs @@ -0,0 +1,42 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; +using BS.Shared.DataContracts; +using BS.Shared.DataContracts.Compact; + +namespace BeWo.Scheduling.Converter +{ + public class SchedulingBackgroundConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var employeeBrush = new SolidColorBrush(Color.FromArgb(255, 69, 153, 59)); + var customerBrush = new SolidColorBrush(Color.FromArgb(255, 59, 119, 153)); + var resourceBrush = new SolidColorBrush(Color.FromArgb(255, 4, 180, 208)); + var supportConceptBrush = new SolidColorBrush(Color.FromArgb(255, 153, 59, 59)); + + if(value is CompactEmployeeDC || value is Employee2SchedulerAppointmentDC) + { + return employeeBrush; + } + + if(value is CompactCustomerDC) + { + return customerBrush; + } + + if(value is CompactSupportConceptDC) + { + return supportConceptBrush; + } + + return resourceBrush; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/SchedulingCheckBoxConverter.cs b/BeWo/Scheduling/Converter/SchedulingCheckBoxConverter.cs new file mode 100644 index 000000000..653a59ed9 --- /dev/null +++ b/BeWo/Scheduling/Converter/SchedulingCheckBoxConverter.cs @@ -0,0 +1,35 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Windows.Data; +using BeWo.Scheduling.ViewModel; + +namespace BeWo.Scheduling.Converter +{ + public class SchedulingCheckBoxConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if(values.Length < 2 || parameter == null) + { + return false; + } + + var oid = (long) values[0]; + var viewModel = (SchedulingAppointmentListVM) values[1]; + + switch(parameter.ToString()) + { + case "Klienten": + return viewModel.SelectedCustomers.Any(item => item.CustomerOid == oid); + default: + return false; + } + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/SchedulingInformationVisibilityConverter.cs b/BeWo/Scheduling/Converter/SchedulingInformationVisibilityConverter.cs new file mode 100644 index 000000000..bd9514312 --- /dev/null +++ b/BeWo/Scheduling/Converter/SchedulingInformationVisibilityConverter.cs @@ -0,0 +1,21 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using BS.Shared.DataContracts.Compact; + +namespace BeWo.Scheduling.Converter +{ + public class SchedulingInformationVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value is CompactCustomerDC ? Visibility.Visible : (object) Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/SchedulingParticipationBrushConverter.cs b/BeWo/Scheduling/Converter/SchedulingParticipationBrushConverter.cs new file mode 100644 index 000000000..6a29c16c1 --- /dev/null +++ b/BeWo/Scheduling/Converter/SchedulingParticipationBrushConverter.cs @@ -0,0 +1,54 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; +using BS.Shared; +using BS.Shared.DataContracts; + +namespace BeWo.Scheduling.Converter +{ + public class SchedulingParticipationBrushConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var offen = new SolidColorBrush(Color.FromRgb(255, 255, 255)); + var zusage = new SolidColorBrush(Color.FromRgb(72, 212, 78)); + var absage = new SolidColorBrush(Color.FromRgb(171, 0, 48)); + var mitVorbehalt = new SolidColorBrush(Color.FromRgb(185, 39, 217)); + + if((string) parameter == "Border") + if(value is Employee2SchedulerAppointmentDC) + { + return new SolidColorBrush(Colors.Black); + } + else + { + return new SolidColorBrush(Colors.Transparent); + } + + if(value is Employee2SchedulerAppointmentDC wert) + { + switch(wert.ParticipationAnswer) + { + case ParticipationAnswer.Zusage: + return zusage; + case ParticipationAnswer.Absage: + return absage; + case ParticipationAnswer.Vorbehalt: + return mitVorbehalt; + } + } + else + { + return new SolidColorBrush(Colors.Transparent); + } + + return offen; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/SchedulingRemoveElementVisibilityConverter.cs b/BeWo/Scheduling/Converter/SchedulingRemoveElementVisibilityConverter.cs new file mode 100644 index 000000000..204ef32ed --- /dev/null +++ b/BeWo/Scheduling/Converter/SchedulingRemoveElementVisibilityConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace BeWo.Scheduling.Converter +{ + public class SchedulingRemoveElementVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + //if(value == null) + //{ + return Visibility.Visible; + //} + + //var isNew = SchedulerAppointmentEditForm.IsNew; + + //var isAllowedToChange = BeWoUtils.CheckSchedulerRights(SchedulerAppointmentEditForm.CurrentAppointment, isNew, SchedulerRightsCheckType.Edit); + + + //return isAllowedToChange ? Visibility.Visible : Visibility.Hidden; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/SchedulingTextFromIDataContractConverter.cs b/BeWo/Scheduling/Converter/SchedulingTextFromIDataContractConverter.cs new file mode 100644 index 000000000..b472105e6 --- /dev/null +++ b/BeWo/Scheduling/Converter/SchedulingTextFromIDataContractConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using BS.Shared.DataContracts; + +namespace BeWo.Scheduling.Converter +{ + public class SchedulingTextFromIDataContractConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(value is ResourceDC resourceDC) + { + return resourceDC.Name; + } + + if(value is Employee2SchedulerAppointmentDC dc) + { + return dc.Employee.SimpleDescription; + } + + var filterableDC = value as IFilterableDC; + + return filterableDC?.SimpleDescription; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/Converter/WidthAdditionMultiConverter.cs b/BeWo/Scheduling/Converter/WidthAdditionMultiConverter.cs new file mode 100644 index 000000000..fcaf6857e --- /dev/null +++ b/BeWo/Scheduling/Converter/WidthAdditionMultiConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace BeWo.Scheduling.Converter +{ + public class WidthAdditionMultiConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + var w1 = System.Convert.ToDouble(values[0]); + var w2 = System.Convert.ToDouble(values[1]); + + return w1 + w2; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/BeWo/Scheduling/SchedulingUtils/AppointmentColorHelper.cs b/BeWo/Scheduling/SchedulingUtils/AppointmentColorHelper.cs new file mode 100644 index 000000000..9c5021ac3 --- /dev/null +++ b/BeWo/Scheduling/SchedulingUtils/AppointmentColorHelper.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Media; + +namespace BeWo.Scheduling.SchedulingUtils +{ + public class AppointmentColorHelper + { + public LinearGradientBrush ResourceAndCustomerBackground { get; set; } + public LinearGradientBrush EmployeeBackground { get; set; } + + public AppointmentColorHelper(Color employeeColor, List resourceColors, bool hasCustomers, bool isTask) + { + var defaultColor = isTask ? Color.FromRgb(153, 59, 59) : Color.FromRgb(192, 255, 208); + + EmployeeBackground = new LinearGradientBrush(new GradientStopCollection { new GradientStop(employeeColor, 1) }, new Point(0, 0), new Point(0, 1)); + + if(resourceColors is null) + { + resourceColors = new List(); + } + + // Weder Ressourcen noch Klienten vorhanden + if(!resourceColors.Any() && !hasCustomers) + { + ResourceAndCustomerBackground = new LinearGradientBrush(new GradientStopCollection { new GradientStop(defaultColor, 1) }, new Point(.5, 0), new Point(.5, 1)); + } + + var colorList = new List(); + var gradientStopCollection = new GradientStopCollection(); + + if(hasCustomers) + { + colorList.Add(Color.FromRgb(59, 119, 153)); + } + + if(resourceColors.Any()) + { + colorList.Add(Color.FromRgb(4, 180, 208)); + } + + switch(colorList.Count) + { + case 1: + if(BeWoApp.AppSettings.ShowMultipleResourceColors) + { + AddResourceColors(gradientStopCollection, resourceColors, hasCustomers); + } + else + { + var color = resourceColors.Count >= 1 ? resourceColors[0] : Color.FromRgb(4, 180, 208); + + var gradientStop = new GradientStop(color, 1); + gradientStopCollection.Add(gradientStop); + } + break; + case 2: + // 1. Klientenfarbe + gradientStopCollection.Add(new GradientStop(colorList[0], .5)); + + // 2. Ressourcenfarben + if(BeWoApp.AppSettings.ShowMultipleResourceColors) + { + AddResourceColors(gradientStopCollection, resourceColors, hasCustomers); + } + else + { + var color = resourceColors.Count >= 1 ? resourceColors[0] : Color.FromRgb(4, 180, 208); + + gradientStopCollection.Add(new GradientStop(color, .5)); + } + break; + default: + gradientStopCollection.Add(new GradientStop(defaultColor, 1)); + break; + } + + ResourceAndCustomerBackground = new LinearGradientBrush(gradientStopCollection, new Point(.5, 0), new Point(.5, 1)); + } + + private static void AddResourceColors(GradientStopCollection gradientStopCollection, List resourceColors, bool hasCustomers) + { + var previousOffset = hasCustomers ? .5 : 0; + + var numberOfResources = resourceColors.Count; + + if(numberOfResources == 0) + { + return; + } + + if(numberOfResources > 4) + { + numberOfResources = 4; + } + + var iterator = hasCustomers ? previousOffset / numberOfResources : 1d / numberOfResources; + + for(var i = 0; i < numberOfResources; i++) + { + var currentOffset = previousOffset; + var nextOffset = currentOffset + iterator; + + if(nextOffset > 1) + { + nextOffset = 1; + } + + previousOffset = nextOffset; + + gradientStopCollection.Add(new GradientStop(resourceColors[i], currentOffset)); + gradientStopCollection.Add(new GradientStop(resourceColors[i], nextOffset)); + } + } + } +} diff --git a/BeWo/Scheduling/SchedulingUtils/CategoryDependencyObject.cs b/BeWo/Scheduling/SchedulingUtils/CategoryDependencyObject.cs new file mode 100644 index 000000000..7cc9af49a --- /dev/null +++ b/BeWo/Scheduling/SchedulingUtils/CategoryDependencyObject.cs @@ -0,0 +1,15 @@ +using System.Windows; +using BS.Shared.DataContracts; + +namespace BeWo.Scheduling.SchedulingUtils +{ + public class CategoryDependencyObject : DependencyObject + { + public ValueListEntryDC ResourceCategory { get; } + + public CategoryDependencyObject(ValueListEntryDC category) + { + ResourceCategory = category; + } + } +} diff --git a/BeWo/Scheduling/SchedulingUtils/EmployeeDependencyObject.cs b/BeWo/Scheduling/SchedulingUtils/EmployeeDependencyObject.cs new file mode 100644 index 000000000..cad20a211 --- /dev/null +++ b/BeWo/Scheduling/SchedulingUtils/EmployeeDependencyObject.cs @@ -0,0 +1,15 @@ +using System.Windows; +using BS.Shared.DataContracts.Compact; + +namespace BeWo.Scheduling.SchedulingUtils +{ + public class EmployeeDependencyObject : DependencyObject + { + public CompactEmployeeDC Employee { get; } + + public EmployeeDependencyObject(CompactEmployeeDC employee) + { + Employee = employee; + } + } +} diff --git a/BeWo/Scheduling/SchedulingUtils/ListItemHelper.cs b/BeWo/Scheduling/SchedulingUtils/ListItemHelper.cs new file mode 100644 index 000000000..6a447920f --- /dev/null +++ b/BeWo/Scheduling/SchedulingUtils/ListItemHelper.cs @@ -0,0 +1,19 @@ +using System.Windows; + +namespace BeWo.Scheduling.SchedulingUtils +{ + public class ListItemHelper : DependencyObject + { + public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached("IsChecked", typeof(bool?), typeof(ListItemHelper), new PropertyMetadata(false, null)); + + public static void SetIsChecked(DependencyObject element, bool? isChecked) + { + element.SetValue(IsCheckedProperty, isChecked); + } + + public static bool? GetIsChecked(DependencyObject element) + { + return (bool?) element.GetValue(IsCheckedProperty); + } + } +} diff --git a/BeWo/Scheduling/SchedulingUtils/ResourceDependencyObject.cs b/BeWo/Scheduling/SchedulingUtils/ResourceDependencyObject.cs new file mode 100644 index 000000000..7d05f40e7 --- /dev/null +++ b/BeWo/Scheduling/SchedulingUtils/ResourceDependencyObject.cs @@ -0,0 +1,15 @@ +using System.Windows; +using BS.Shared.DataContracts; + +namespace BeWo.Scheduling.SchedulingUtils +{ + public class ResourceDependencyObject : DependencyObject + { + public ResourceDC DataContract { get; } + + public ResourceDependencyObject(ResourceDC resource) + { + DataContract = resource; + } + } +} diff --git a/BeWo/Scheduling/SchedulingUtils/TreeViewItemHelper.cs b/BeWo/Scheduling/SchedulingUtils/TreeViewItemHelper.cs new file mode 100644 index 000000000..89255a263 --- /dev/null +++ b/BeWo/Scheduling/SchedulingUtils/TreeViewItemHelper.cs @@ -0,0 +1,31 @@ +using System.Windows; + +namespace BeWo.Scheduling.SchedulingUtils +{ + public class TreeViewItemHelper : DependencyObject + { + public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached("IsChecked", typeof(bool?), typeof(TreeViewItemHelper), new PropertyMetadata(false, null)); + + public static void SetIsChecked(DependencyObject element, bool? isChecked) + { + element.SetValue(IsCheckedProperty, isChecked); + } + + public static bool? GetIsChecked(DependencyObject element) + { + return (bool?) element.GetValue(IsCheckedProperty); + } + + public static readonly DependencyProperty ParentProperty = DependencyProperty.RegisterAttached("Parent", typeof(object), typeof(TreeViewItemHelper)); + + public static void SetParent(DependencyObject element, object Parent) + { + element.SetValue(ParentProperty, Parent); + } + + public static object GetParent(DependencyObject element) + { + return element.GetValue(ParentProperty); + } + } +} diff --git a/BeWo/Scheduling/SchedulingUtils/Utils.cs b/BeWo/Scheduling/SchedulingUtils/Utils.cs new file mode 100644 index 000000000..b1a756f24 --- /dev/null +++ b/BeWo/Scheduling/SchedulingUtils/Utils.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using BeWo.Scheduling.ViewModel; +using BS.Shared; +using BS.Shared.Core; +using BS.Shared.DataContracts; +using BS.Shared.DataContracts.Compact; +using BS.Shared.Extensions; +using DevExpress.Xpf.Scheduling; +using DevExpress.XtraScheduler; + +namespace BeWo.Scheduling.SchedulingUtils +{ + public static class Utils + { + private static readonly Regex HexColorRegex = new Regex("^#([a-fA-F0-9]{8}|[a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); + + public static bool Categories2ResourcesEquals(ObservableDictionary> dictionary1, ObservableDictionary> dictionary2) + { + if(dictionary1 is null && dictionary2 != null || dictionary2 is null && dictionary1 != null) + { + return false; + } + + return dictionary1 is null && dictionary2 is null || dictionary1 != null && dictionary2 != null && dictionary1.Count == dictionary2.Count && dictionary1.Except(dictionary2).Any(); + } + + public static List ConvertAbsenceTimesToAppointments(List absenceTimes, DateTime end, List allEmployees) + { + var result = new List(); + + foreach(var absenceTime in absenceTimes) + { + if(absenceTime.AbsenceTimeOid == null || absenceTime.Start == null) + { + continue; + } + + var subject = absenceTime.Reason.Description; + var employee = absenceTime.EmployeeOid != null ? allEmployees.FirstOrDefault(f => f.EmployeeOid == absenceTime.EmployeeOid.Value) : null; + + if(employee != null) + { + subject += $" ({employee.SimpleDescription})"; + } + + var absenceTimeEnd = absenceTime.End?.AddTicks(1); + + result.AddIfNotIn(new SchedulingAppointmentVM(true, subject, absenceTime.Start.Value, absenceTime.End ?? end, employee, absenceTime.Start, absenceTimeEnd)); + } + + return result; + } + + public static string GetDeleteMessageForAppointment(AppointmentItem appointment) + { + if(appointment is null) + { + return "Möchten Sie den gewählten Termin wirklich löschen?"; + } + + var subject = appointment.Subject; + + var interval = GetAppointmentTimeString(appointment); + + return $"Möchten Sie den gewählten Termin \"{subject}\" vom {interval} wirklich löschen?"; + } + + public static string GetAppointmentTimeString(AppointmentItem appointment) + { + if(appointment is null) + { + return string.Empty; + } + + var start = appointment.Start; + var end = appointment.End; + + // 1. 17. August 2021 09:00 Uhr bis 10:00 Uhr + // 2. 17. August 2021 + // 3. 17. August 2021 09:00 Uhr bis 19. August 2021 10 Uhr + // 4. 17. August 2021 bis 19. August 2021 + + return start.Date == end.Date ? + appointment.AllDay ? $"{start:dd. MMMM yyyy}" : $"{start:dd. MMMM yyyy HH:mm} Uhr bis {end:HH:mm} Uhr" : + appointment.AllDay ? $"{start:dd. MMMM yyyy} bis zum {end:dd. MMMM yyyy}" : $"{start: dd. MMMM yyyy HH:mm} Uhr bis zum {end:dd. MMMM yyyy HH:mm} Uhr"; + } + + public static string GetDeactivationMessageForAppointments(IReadOnlyList appointments2Deactivate) + { + var message = string.Empty; + + if(appointments2Deactivate.Count > 1) + { + message = "Möchten Sie folgende Termine wirklich löschen?\n\n"; + + foreach(var appointment2Deactivate in appointments2Deactivate.OrderBy(o => o.Start)) + { + var interval = GetAppointmentTimeString(appointment2Deactivate); + var subject = appointment2Deactivate.Subject; + + message += $"{interval} \"{subject}\"\n"; + } + } + else + { + var first = appointments2Deactivate.FirstOrDefault(); + + if(first is null) + { + return message; + } + + var interval = GetAppointmentTimeString(first); + var subject = first.Subject; + + message = $"Möchten Sie den gewählten Termin \"{subject}\" vom {interval} wirklich löschen?"; + } + + return message; + } + + public static AppointmentType GetAppointmentTypeFromViewModel(int eventType) + { + switch(eventType) + { + case 0: + return AppointmentType.Normal; + case 1: + return AppointmentType.Pattern; + case 2: + return AppointmentType.Occurrence; + case 3: + return AppointmentType.ChangedOccurrence; + case 4: + return AppointmentType.DeletedOccurrence; + default: + return AppointmentType.Normal; + } + } + + public static bool IsValidColorHexString(string hexString) + { + return !(hexString is null) && HexColorRegex.IsMatch(hexString); + } + + public static StatusUpdatesAndAppointments SeperateStatusUpdatesAndAppointments(List statusUpdatesAndAppointments) + { + var result = new StatusUpdatesAndAppointments(); + + foreach(var appointment in statusUpdatesAndAppointments) + { + if(!(appointment.Originator?.Equals(BeWoApp.CompactLoggedOnEmployee) ?? false) && (appointment.EmployeeList?.Any(e2a => e2a.Employee.Equals(BeWoApp.CompactLoggedOnEmployee) && e2a.ParticipationAnswer == ParticipationAnswer.Offen) ?? false)) + { + result.Appointments.AddIfNotIn(new SchedulingAppointmentVM(appointment)); + } + else if((appointment.Originator?.Equals(BeWoApp.CompactLoggedOnEmployee) ?? false) && (appointment.EmployeeList?.Any(e2a => e2a.IsPChanged && e2a.ParticipationAnswer != ParticipationAnswer.Offen && e2a.ParticipationAnswer != ParticipationAnswer.Verstrichen && !e2a.Employee.Equals(BeWoApp.CompactLoggedOnEmployee)) ?? false)) + { + var vm = new SchedulingAppointmentVM(appointment); + + foreach(var employee2Appointment in appointment.EmployeeList.Where(e2a => e2a.IsPChanged && e2a.ParticipationAnswer != ParticipationAnswer.Offen && e2a.ParticipationAnswer != ParticipationAnswer.Verstrichen && !e2a.Employee.Equals(BeWoApp.CompactLoggedOnEmployee))) + { + if(result.StatusUpdates.All(a => a.LabelKey != employee2Appointment.Employee2SchedulerAppointmentOid)) + { + if(employee2Appointment.Employee2SchedulerAppointmentOid.HasValue) + { + vm.LabelKey = employee2Appointment.Employee2SchedulerAppointmentOid; + } + } + } + + result.StatusUpdates.AddIfNotIn(vm); + } + } + + return result; + } + } + + public class StatusUpdatesAndAppointments + { + public List StatusUpdates { get; } + public List Appointments { get; } + + public StatusUpdatesAndAppointments() + { + StatusUpdates = new List(); + Appointments = new List(); + } + } +} diff --git a/BeWo/Scheduling/View/AppointmentEditView.xaml b/BeWo/Scheduling/View/AppointmentEditView.xaml new file mode 100644 index 000000000..8f59624a7 --- /dev/null +++ b/BeWo/Scheduling/View/AppointmentEditView.xaml @@ -0,0 +1,534 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +