using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Windows; using System.Xml; using BeWo.Scheduler.Utils; using BeWo.Scheduler.View; using BeWo.ServiceProxy; using BeWo.ViewModel.ListViewModel; using BS.Shared; using BS.Shared.DataContracts; using BS.Shared.DataContracts.Compact; using BS.Shared.Extensions; using DevExpress.Xpf.Scheduler; using DevExpress.XtraScheduler; using DevExpress.XtraScheduler.Native; using SchedulerControl = DevExpress.Xpf.Scheduler.SchedulerControl; using SchedulerStorage = DevExpress.Xpf.Scheduler.SchedulerStorage; namespace BeWo.Scheduler.ViewModel { public class SchedulerAppointmentListVM : AbstractDCListMapperVM, ISchedulerViewModel { public bool ShouldLockOverlappingAppointmentCheck { get; set; } //protected override SchedulerAppointmentVM CreateVM(SchedulerAppointmentDC pDC) //{ // var vm = base.CreateVM(pDC); // if (!pDC.SchedulerAppointmentOid.HasValue && vm.EmployeeList.Count == 0) // { // var e2sVm = new Employee2SchedulerAppointmentDC(); // e2sVm.Employee = BeWoApp.LoggedOnUser.Employee; // vm.EmployeeList.Add(e2sVm); // } // return vm; //} public SchedulerSettings GetDefaultSchedulerSettings() { var settings = new SchedulerSettings(AppointmentKind.General, AppointmentViewType.WorkWeek); return settings; } private BindingList _AppointmentList; public BindingList Appointments { get { if (_AppointmentList != null) { return _AppointmentList; } _AppointmentList = new BindingList(); _AppointmentList.AddingNew += AppointmentListAddingNew; foreach (var item in VMList) { _AppointmentList.Add(item); } return _AppointmentList; } } private static void AppointmentListAddingNew(object sender, AddingNewEventArgs e) { var dc = new SchedulerAppointmentDC { EmployeeList = new List(), CustomerList = new List(), ResourceList = new List(), ServiceRecordList = new List(), SupportConceptList = new List(), Status = 0 }; var neu = new SchedulerAppointmentVM(dc); e.NewObject = neu; } private Dictionary>> _ChangedOccurenceCustomFields; public Dictionary>> ChangedOccurenceCustomFields { get; } public List AllCustomers { get; } public List AllEmployees { get; } public Dictionary> Categories2Resources { get; } private Appointment _GeoeffneterTermin; public SchedulerAppointmentListVM(List appList, List allCustomers, List allEmployees, Dictionary> lCategoriesToResources) : base(appList) { Categories2Resources = lCategoriesToResources; AllCustomers = allCustomers; AllEmployees = allEmployees; // TODO: Hier Rechte beachten! InitChangedOccurrencyCustomFields(appList.Where(t => t.Type == 3).ToList()); InitViewModel(); } private void InitViewModel() { foreach (var item in VMList) { item.CustomFields.Add(nameof(CustomFieldStorage), new CustomFieldStorage(item)); item.Id = item.DataContract.SchedulerAppointmentOid; if (!item.IsPrivate || item.Originator.EmployeeOid.Equals(BeWoApp.LoggedOnEmployee.EmployeeOid) || item.EmployeeList.Select(s => s.Employee).Contains(BeWoApp.CompactLoggedOnEmployee)) { continue; } item.Description = "Privater Termin"; item.Subject = $"Privat ({item.Originator})"; } } public void UpdateViewModel(IEnumerable updatedDcList) { foreach (var dc in updatedDcList) { SchedulerAppointmentVM found = null; foreach (var vm in Appointments.OfType().Where(vm => vm.DataContract != null && vm.DataContract.Equals(dc))) { found = vm; } if (found != null) { found.DataContract = dc; } } } public void SaveAppointments(SchedulerControl control, IEnumerable list) { var areEqual = false; var dcList = new List(); foreach (var item in list) { if (!(item.GetSourceObject(control.GetCoreStorage()) is SchedulerAppointmentVM vm)) { continue; } UpdateSchedulerAppointment(item, vm); // Wird ein Serientermin bearbeitet, erfolgt ein Insert und ein Update. Beim Insert erfolgt bereits die Überschneidungsprüfung und muss hier nicht wiederholt werden. var shouldSkipResourceAvailablityCheck = false; if(_RecurrenceInfoId != null && item.RecurrenceInfo?.Id is Guid recurrenceId) { shouldSkipResourceAvailablityCheck = recurrenceId.Equals(_RecurrenceInfoId); } /* * EventTypes: * 0: Normal * 1: Pattern * 2: Occurrence * 3: ChangedOccurrence * 4: DeletedOccurrence * * * * Wozu ist das gut? * !ShouldLockOverlappingAppointmentCheck && */ if(!shouldSkipResourceAvailablityCheck && !vm.IsTask && vm.EventType != 4 && !(vm.EventType == 3 && vm.DataContract?.SchedulerAppointmentOid == null)) { var start = vm.Start; var end = vm.End; var employeeOids = vm.EmployeeList.Select(emp => emp.Employee.EmployeeOid).ToList(); var customerOids = vm.CustomerList.Select(cc => cc.CustomerOid).ToList(); var resourceOids = vm.ResourceList.Where(r => r.ResourceOid.HasValue).Select(r => r.ResourceOid.Value).ToList(); var originatorOid = vm.Originator.EmployeeOid; var schedulerAppointmentOid = vm.DataContract?.SchedulerAppointmentOid; var recurrenceInfoId = vm.RecurrenceId; var recurrenceIndex = vm.RecurrenceIndex; var message = string.Empty; ServiceFacade.DoResourceServiceSync( s => message = s.ValidateSchedulerAppointment(start, end, employeeOids, customerOids, resourceOids, originatorOid, schedulerAppointmentOid, recurrenceInfoId, recurrenceIndex, shouldSkipResourceAvailablityCheck)); if(!string.IsNullOrWhiteSpace(message)) { var messageBoxResult = MessageBox.Show(message, "Überschneidung", MessageBoxButton.YesNo, MessageBoxImage.Exclamation); if(messageBoxResult.Equals(MessageBoxResult.No)) { return; } } } if (item.Type.Equals(AppointmentType.ChangedOccurrence) && _GeoeffneterTermin != null) { areEqual = _GeoeffneterTermin.Equals(item); } if(!areEqual) { dcList.Add(vm.CommitToDataContract()); } } if (dcList.Count <= 0) { return; } NewSchedulerView.IgnoreChangeEvents = true; var mostRecentAppointments = new List(); ServiceFacade.DoResourceServiceSync(sync => mostRecentAppointments = sync.UpdateSchedulerAppointments(dcList)); UpdateViewModel(mostRecentAppointments); _RecurrenceInfoId = null; control.ActiveView.LayoutChanged(); NewSchedulerView.IgnoreChangeEvents = false; } private Guid? _RecurrenceInfoId; public void InsertAppointments(SchedulerControl control, IEnumerable list) { var dcList = new List(); foreach (var item in list) { if (!(item.GetSourceObject(control.GetCoreStorage()) is SchedulerAppointmentVM vm)) { if(item.IsException && item.CF_HasServiceRecordEntry()) { var dc = new SchedulerAppointmentDC { IsPrivate = item.CF_IsPrivate(), Subject = item.Subject, Description = item.Description, StartDate = item.Start, EndDate = item.End, Originator = item.CF_Originator(), LabelKey = (long?) item.LabelKey, AllDay = item.AllDay, Status = item.StatusId, Type = (int) item.Type, Location = item.Location, RecurrenceInfo = item.RecurrenceInfo.ToXml(), ReminderInfo = item.Reminder?.ToXml(), CustomerList = item.CF_CustomerList(), EmployeeList = item.CF_EmployeeList().ToList(), ResourceList = item.CF_ResourceList(), SupportConceptList = item.CF_SupportConceptList(), ServiceRecordList = item.CF_ServiceRecordList(), RecurrenceIndex = item.RecurrenceIndex, RecurrenceId = item.RecurrenceInfo.Id.ToString(), HasServiceRecordEntry = item.CF_HasServiceRecordEntry() }; vm = new SchedulerAppointmentVM(dc); } else { continue; } } if(item.RecurrenceInfo != null) { _RecurrenceInfoId = (Guid) item.RecurrenceInfo.Id; } UpdateSchedulerAppointment(item, vm); if(!vm.IsTask && vm.EventType != 4 && !ShouldLockOverlappingAppointmentCheck) { var start = vm.Start; var end = vm.End; var employeeOids = vm.EmployeeList.Select(emp => emp.Employee.EmployeeOid).ToList(); var customerOids = vm.CustomerList.Select(cc => cc.CustomerOid).ToList(); var resourceOids = vm.ResourceList.Where(r => r.ResourceOid.HasValue).Select(r => r.ResourceOid.Value).ToList(); var originatorOid = vm.Originator.EmployeeOid; var schedulerAppointmentOid = vm.DataContract?.SchedulerAppointmentOid; var recurrenceInfoId = vm.RecurrenceId; var recurrenceIndex = vm.RecurrenceIndex; var message = string.Empty; ServiceFacade.DoResourceServiceSync(s => message = s.ValidateSchedulerAppointment(start, end, employeeOids, customerOids, resourceOids, originatorOid, schedulerAppointmentOid, recurrenceInfoId, recurrenceIndex, false)); if(!string.IsNullOrWhiteSpace(message)) { var messageBoxResult = MessageBox.Show(message, "Überschneidung", MessageBoxButton.YesNo, MessageBoxImage.Exclamation); if(messageBoxResult.Equals(MessageBoxResult.No)) { return; } } } dcList.Add(vm.CommitToDataContract()); } if (dcList.Count <= 0) { return; } NewSchedulerView.IgnoreChangeEvents = true; var mostRecentAppointments = new List(); ServiceFacade.DoResourceServiceSync(sync => mostRecentAppointments = sync.InsertSchedulerAppointments(dcList)); UpdateViewModel(mostRecentAppointments); control.ActiveView.LayoutChanged(); NewSchedulerView.IgnoreChangeEvents = false; } private void UpdateSchedulerAppointment(Appointment app, SchedulerAppointmentVM vm) { vm.EmployeeList = app.CF_EmployeeList(); vm.CustomerList = app.CF_CustomerList(); vm.ResourceList = app.CF_ResourceList(); vm.Originator = app.CF_Originator(); vm.IsPrivate = app.CF_IsPrivate(); vm.IsTask = app.CF_IsTask(); vm.DueDate = app.CF_DueDate(); vm.CompletedDate = app.CF_CompletedDate(); vm.CompletedNotice = app.CF_CompletedNotice(); vm.TaskDescription = app.CF_TaskDescription(); vm.SupportConceptList = app.CF_SupportConceptList(); vm.HasServiceRecordEntry = app.CF_HasServiceRecordEntry(); vm.ServiceRecordList = app.CF_ServiceRecordList(); if(vm.IsTask) { vm.AllDay = true; } if(!vm.CustomFields.ContainsKey(nameof(CustomFieldStorage))) { vm.CustomFields.Add(nameof(CustomFieldStorage), new CustomFieldStorage(vm)); } if (vm.CommitToDataContract() != null && vm.CommitToDataContract().SchedulerAppointmentOid.HasValue && vm.EmployeeList != null && vm.EmployeeList.Count > 0) { var alt = new SchedulerAppointmentDC(); ServiceFacade.DoResourceServiceSync(s => alt = s.GetSchedulerAppointmentsById(new List {vm.CommitToDataContract().SchedulerAppointmentOid.Value}).First()); var alts = alt.StartDate; var alte = alt.EndDate; if(alts != vm.Start || alte != vm.End) { var neu = vm.EmployeeList; vm.EmployeeList = new ObservableCollection(neu.DoForEach(f => { if(!f.Employee.Equals(vm.Originator)) { f.ParticipationAnswer = ParticipationAnswer.Offen; } }).ToList()); } } if (vm.ResourceList == null || vm.ResourceList.Count <= 0) return; if (app.RecurrenceInfo != null) { var index = app.RecurrenceIndex.ToString(); var id = new Guid(app.RecurrenceInfo.Id.ToString()); if (_ChangedOccurenceCustomFields.ContainsKey(id) && _ChangedOccurenceCustomFields[id].ContainsKey(index)) { if (app.Type.Equals(AppointmentType.ChangedOccurrence)) { app.CustomFields[nameof(CustomFieldStorage)] = _ChangedOccurenceCustomFields[id][index][nameof(CustomFieldStorage)]; } } } app.LabelKey = vm.LabelKey; } AbstractAppointmentEditForm ISchedulerViewModel.GetEditAppointmentForm(SchedulerControl control, Appointment appointment) { _GeoeffneterTermin = control.Storage.CreateAppointment(AppointmentType.Normal); _GeoeffneterTermin.AllDay = appointment.AllDay; _GeoeffneterTermin.Start = appointment.Start; _GeoeffneterTermin.End = appointment.End; _GeoeffneterTermin.Description = appointment.Description; foreach (CustomField cf in appointment.CustomFields) { _GeoeffneterTermin.CustomFields[cf.Name] = cf.Value; } var isTask = _GeoeffneterTermin.CF_IsTask(); if (!appointment.Type.Equals(AppointmentType.ChangedOccurrence)) { return new SchedulerAppointmentEditForm(this, control, appointment, AllEmployees, AllCustomers, Categories2Resources, isTask); } var index = appointment.RecurrenceIndex; var id = new Guid(appointment.RecurrenceInfo.Id.ToString()); if (_ChangedOccurenceCustomFields.ContainsKey(id) && _ChangedOccurenceCustomFields[id].ContainsKey(index.ToString())) { var specialCustomFields = _ChangedOccurenceCustomFields[id][index.ToString()]; foreach (var cf in specialCustomFields) { appointment.CustomFields[cf.Key] = cf.Value; _GeoeffneterTermin.CustomFields[cf.Key] = cf.Value; } } return new SchedulerAppointmentEditForm(this, control, appointment, AllEmployees, AllCustomers, Categories2Resources, isTask); } public void ApplyChangesToChangedOccurencyCustomFields(ObservableCollection employees, List customers, List resources, CompactEmployeeDC originator, bool isPrivate, string index, Guid id, bool isTask, string taskDescription, DateTime? completedDate, DateTime? dueDate, string completedNotice, List supportConcepts, string subject, bool isAllDay, List serviceRecords) { if (!_ChangedOccurenceCustomFields.ContainsKey(id) || !_ChangedOccurenceCustomFields[id].ContainsKey(index)) { return; } _ChangedOccurenceCustomFields[id][index][nameof(CustomFieldStorage)] = new CustomFieldStorage(employees, customers, resources, originator, isPrivate, isTask, taskDescription, dueDate, completedDate, completedNotice, supportConcepts, subject, isAllDay, serviceRecords); } public void AddCustomFieldsMapping(SchedulerStorage schedulerStorage) { var customFieldStorageMapping = new SchedulerCustomFieldMapping(nameof(CustomFieldStorage), nameof(CustomFieldStorage)); schedulerStorage.AppointmentStorage.CustomFieldMappings.Add(customFieldStorageMapping); } public void InitNewAppointment(Appointment appointment) { appointment.StatusId = 0; appointment.CustomFields[nameof(CustomFieldStorage)] = new CustomFieldStorage { Originator = BeWoApp.CompactLoggedOnEmployee }; } public void InitNewTask(Appointment pAppointment, DateTime? pDueDate, IEnumerable pSelectedEmployees, List pSelectedCustomers, List pSelectedResources, List pSelectedSupportConcepts) { pAppointment.CustomFields[nameof(CustomFieldStorage)] = new CustomFieldStorage { Originator = BeWoApp.CompactLoggedOnEmployee, EmployeeList = new ObservableCollection(pSelectedEmployees ?? new List()), CustomerList = pSelectedCustomers ?? new List(), ResourceList = pSelectedResources ?? new List(), SupportConceptList = pSelectedSupportConcepts ?? new List(), TaskDescription = string.Empty, DueDate = pDueDate, IsTask = true }; pAppointment.StatusId = 0; } public bool HideAppointment(string filterCategory, object selectedObject, Appointment appointment, bool showPrivateAppointments, bool showAbsenceTimes, bool pShowTasks) { if (appointment.GetCustomFieldStorage() == null) { return false; } var wirdAngezeigt = true; // -> wird standardmäßig angezeigt var isPrivate = appointment.CF_IsPrivate(); var isAbsenceTime = appointment.CF_IsAbsenceTime(); var isTask = appointment.CF_IsTask(); // Ist Abwesenheit, es sollen keine privaten Termine angezeigt werden und es sollen keine Abwesenheiten angezeigt werden if(isAbsenceTime && !showPrivateAppointments && !showAbsenceTimes) { return !showAbsenceTimes; } if(showPrivateAppointments && !isPrivate) { return true; } if (!pShowTasks && isTask) { return true; } var ersteller = appointment.CF_Originator(); var employeeList = appointment.CF_EmployeeList(); var customerList = appointment.CF_CustomerList(); var resourceList = appointment.CF_ResourceList(); if (isPrivate) { if (ersteller == null || !ersteller.EmployeeOid.Equals(BeWoApp.LoggedOnEmployee.EmployeeOid)) { return true; } } switch (filterCategory) { case "NurEigene": var istEigenerTermin = ersteller != null && ersteller.EmployeeOid.Equals(BeWoApp.LoggedOnEmployee.EmployeeOid) && employeeList != null && employeeList.Count == 0 || employeeList != null && employeeList.Any(a => a.Employee.EmployeeOid.Equals(BeWoApp.LoggedOnEmployee.EmployeeOid)); wirdAngezeigt = istEigenerTermin; break; case "NachAuswahl": var dictionary = (Dictionary) selectedObject; var ressourcen = (List) dictionary["Ressourcen"]; var mitarbeiter = (List) dictionary["Mitarbeiter"]; var klienten = (List) dictionary["Klienten"]; var cfe = employeeList?.Select(s => s.Employee).ToList(); var cfr = resourceList ?? new List(); var cfc = customerList ?? new List(); var hasOnlyOriginatorInCommon = employeeList != null && ersteller != null && employeeList.Any() == false && mitarbeiter.Contains(ersteller); var m = cfe.Intersect(mitarbeiter).ToList(); var r = cfr.Intersect(ressourcen).ToList(); var k = cfc.Intersect(klienten).ToList(); wirdAngezeigt = (m.Any() || k.Any() || r.Any()) || hasOnlyOriginatorInCommon; break; case "Ebenen": var liste = (List) selectedObject; // mkr var appEList = employeeList.Where(w => !w.Employee.Equals(appointment.CF_Originator())).ToList(); // Ersteller und Rechte beachten var istEigen = ersteller.EmployeeOid.Equals(BeWoApp.LoggedOnEmployee.EmployeeOid); var mVorhanden = appEList.Any(a2app => AllEmployees.Contains(a2app.Employee)); var kVorhanden = customerList.Any(AllCustomers.Contains); wirdAngezeigt = liste[0] && mVorhanden || liste[1] && resourceList.Any() || liste[2] && kVorhanden; // Nur Ansehen if (!istEigen && !BeWoApp.LoggedOnUser.HasRight(UserRightType.KalenderMitarbeitertermineAnsehen) && liste[0] && mVorhanden && !appEList.Select(e2sa => e2sa.Employee).Any(a => a.EmployeeOid.Equals(BeWoApp.LoggedOnEmployee.EmployeeOid))) { wirdAngezeigt = false; } if (!istEigen && !BeWoApp.LoggedOnUser.HasRight(UserRightType.KalenderKliententermineAlleAnsehen) && liste[2] && kVorhanden) { wirdAngezeigt = false; } if (!istEigen && !BeWoApp.LoggedOnUser.HasRight(UserRightType.KalenderRessourcentermineAnsehen) && liste[1] && resourceList.Any()) { wirdAngezeigt = false; } break; } // return false -> Termin wird angezeigt! return !wirdAngezeigt; } public void InitChangedOccurrencyCustomFields(IEnumerable appList) { _ChangedOccurenceCustomFields = new Dictionary>>(); foreach (var termin in appList) { var ri = new RecurrenceInfo(); ri.FromXml(termin.RecurrenceInfo); string index; using (var reader = XmlReader.Create(new StringReader(termin.RecurrenceInfo))) { reader.ReadToFollowing("RecurrenceInfo"); reader.MoveToAttribute("Index"); index = reader.Value; } if (index.Equals("")) { index = "0"; } var customFieldCollection = new Dictionary { [nameof(CustomFieldStorage)] = new CustomFieldStorage(termin) }; var id = new Guid(ri.Id.ToString()); if(!_ChangedOccurenceCustomFields.ContainsKey(id)) { _ChangedOccurenceCustomFields.Add(id, new Dictionary> { { index, customFieldCollection } }); } else if(!_ChangedOccurenceCustomFields[id].ContainsKey(index)) { _ChangedOccurenceCustomFields[id].Add(index, customFieldCollection); } else { _ChangedOccurenceCustomFields[id][index] = customFieldCollection; } } } //TODO: alle relevanten Eigenschaften beachten und eigene AreEqual Extension schreiben (erledigt) (https://www.devexpress.com/Support/Center/Question/Details/T270666) public override bool Equals(object pObj) { var obj = (SchedulerAppointmentListVM) pObj; if (obj == null) { return false; } var appointmentsAreEqual = Appointments == null && obj.Appointments == null || Appointments != null && Appointments.AreEqual(obj.Appointments); var allCustomersAreEqual = AllCustomers == null && obj.AllCustomers == null || AllCustomers != null && obj.AllCustomers != null && AllCustomers.Count == obj.AllCustomers.Count && !AllCustomers .Where( (t, i) => !( t.CustomerOid.Equals(obj.AllCustomers[i].CustomerOid) && t.CustomerVersion.Equals(obj.AllCustomers[i].CustomerVersion) ) ).Any(); var allEmployeesAreEqual = AllEmployees == null && obj.AllEmployees == null || AllEmployees != null && obj.AllEmployees != null && AllEmployees.Count == obj.AllEmployees.Count && !AllEmployees .Where( (t, i) => !( t.EmployeeOid.Equals(obj.AllEmployees[i].EmployeeOid) && t.EmployeeVersion.Equals(obj.AllEmployees[i].EmployeeVersion) ) ).Any(); var areEqual = VMList.AreEqual(obj.VMList) && IsDirty == obj.IsDirty && allCustomersAreEqual && allEmployeesAreEqual && appointmentsAreEqual && Equals(Categories2Resources, obj.Categories2Resources) && Equals(ChangedOccurenceCustomFields, obj.ChangedOccurenceCustomFields); return areEqual; } } }