Verhindern, dass Ressourcen doppelt gebucht werden mit Recht: ToDoOID=91266751

Serientermine können gelöscht werden, aber es fehlt noch eine Abfrage, ob man wirklich löschen will bei normalen Terminen.
This commit is contained in:
2026-07-13 20:32:28 +02:00
parent cf20c425f8
commit bacc7fcedd
12 changed files with 126 additions and 44 deletions

View File

@@ -198,11 +198,18 @@ namespace BeWo.Scheduler.ViewModel
var message = string.Empty; var message = string.Empty;
ServiceFacade.DoResourceServiceSync( s => message = s.ValidateSchedulerAppointment(start, end, employeeOids, customerOids, resourceOids, originatorOid, schedulerAppointmentOid, recurrenceInfoId, recurrenceIndex, shouldSkipResourceAvailablityCheck)); ServiceFacade.DoResourceServiceSync( s => message = s.ValidateSchedulerAppointment(start, end, employeeOids, customerOids, resourceOids, originatorOid, schedulerAppointmentOid, recurrenceInfoId, recurrenceIndex, shouldSkipResourceAvailablityCheck));
if(!string.IsNullOrWhiteSpace(message)) if(!string.IsNullOrWhiteSpace(message))
{ {
if(message.Contains("Die Ressource") && message.Contains("ist im ausgewählten Zeitraum") && message.Contains("bereits gebucht:") && BeWoApp.LoggedOnUser.HasRight(UserRightType.KalenderRessourcenDoppeltBuchen) is false)
{
message = message.Remove(message.Length - 31) + "Speichern nicht möglich.";
MessageBox.Show(message, "Überschneidung", MessageBoxButton.OK, MessageBoxImage.Exclamation);
return;
}
var messageBoxResult = MessageBox.Show(message, "Überschneidung", MessageBoxButton.YesNo, MessageBoxImage.Exclamation); var messageBoxResult = MessageBox.Show(message, "Überschneidung", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if(messageBoxResult.Equals(MessageBoxResult.No)) if(messageBoxResult.Equals(MessageBoxResult.No))
@@ -212,7 +219,7 @@ namespace BeWo.Scheduler.ViewModel
} }
} }
if (item.Type.Equals(AppointmentType.ChangedOccurrence) && _GeoeffneterTermin != null) if(item.Type.Equals(AppointmentType.ChangedOccurrence) && _GeoeffneterTermin != null)
{ {
areEqual = _GeoeffneterTermin.Equals(item); areEqual = _GeoeffneterTermin.Equals(item);
} }
@@ -223,7 +230,7 @@ namespace BeWo.Scheduler.ViewModel
} }
} }
if (dcList.Count <= 0) if(dcList.Count <= 0)
{ {
return; return;
} }
@@ -305,6 +312,14 @@ namespace BeWo.Scheduler.ViewModel
if(!string.IsNullOrWhiteSpace(message)) if(!string.IsNullOrWhiteSpace(message))
{ {
if(message.Contains("Die Ressource") && message.Contains("ist im ausgewählten Zeitraum") && message.Contains("bereits gebucht:") && BeWoApp.LoggedOnUser.HasRight(UserRightType.KalenderRessourcenDoppeltBuchen) is false)
{
message = message.Remove(message.Length - 31) + "Speichern nicht möglich.";
MessageBox.Show(message, "Überschneidung", MessageBoxButton.OK, MessageBoxImage.Exclamation);
return;
}
var messageBoxResult = MessageBox.Show(message, "Überschneidung", MessageBoxButton.YesNo, MessageBoxImage.Exclamation); var messageBoxResult = MessageBox.Show(message, "Überschneidung", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if(messageBoxResult.Equals(MessageBoxResult.No)) if(messageBoxResult.Equals(MessageBoxResult.No))
@@ -314,11 +329,10 @@ namespace BeWo.Scheduler.ViewModel
} }
} }
dcList.Add(vm.CommitToDataContract()); dcList.Add(vm.CommitToDataContract());
} }
if (dcList.Count <= 0) if(dcList.Count <= 0)
{ {
return; return;
} }

View File

@@ -43,13 +43,12 @@ namespace BeWoPlanerMobil.Controllers
#if DEBUG #if DEBUG
Debug.WriteLine($"+++>Logout aufrufende Methode: {new StackTrace().GetFrame(1).GetMethod().Name}"); Debug.WriteLine($"+++>Logout aufrufende Methode: {new StackTrace().GetFrame(1).GetMethod().Name}");
#endif #endif
MobileSessionFacade.LogUserOut(); MobileSessionFacade.LogUserOut();
FormsAuthentication.SignOut(); FormsAuthentication.SignOut();
AbstractModel.TimeOfLastAction = null; AbstractModel.TimeOfLastAction = null;
return RedirectToActionPermanent("Index", "Login", new {tenant = MobileSessionFacade.Tenant}); return RedirectToActionPermanent("Index", "Login", new {tenant = MobileSessionFacade.Tenant, message = ViewData[LoginController.LoginErrorIndex]?.ToString() });
} }
protected virtual void InitModel(AbstractModel model) protected virtual void InitModel(AbstractModel model)

View File

@@ -489,17 +489,24 @@ namespace BeWoPlanerMobil.Controllers
foreach(var appointment in appointmentsToInsert) foreach(var appointment in appointmentsToInsert)
{ {
appointment.CustomerList = AppointmentModel.SelectedCustomers; if(appointment.Type != (int) AppointmentType.DeletedOccurrence)
appointment.ResourceList = AppointmentModel.SelectedResources;
appointment.EmployeeList = new List<Employee2SchedulerAppointmentDC>();
foreach(var selectedEmployee in AppointmentModel.SelectedEmployees ?? new List<CompactEmployeeDC>())
{ {
appointment.EmployeeList.Add(new Employee2SchedulerAppointmentDC { Employee = selectedEmployee }); // Beim Löschen eines Serientermins wird hier ein Insert für die gelöschte Ausnahme erstellt
} appointment.CustomerList = AppointmentModel.SelectedCustomers;
appointment.ResourceList = AppointmentModel.SelectedResources;
appointment.EmployeeList = new List<Employee2SchedulerAppointmentDC>();
appointment.Originator = Model.LoggedInUser.Employee; foreach(var selectedEmployee in AppointmentModel.SelectedEmployees ?? new List<CompactEmployeeDC>())
{
appointment.EmployeeList.Add(new Employee2SchedulerAppointmentDC { Employee = selectedEmployee });
}
appointment.Originator = Model.LoggedInUser.Employee;
}
else
{
appointment.NewSchedulerAppointmentVersion = null;
}
KalenderService.InsertSchedulerAppointment(appointment); KalenderService.InsertSchedulerAppointment(appointment);
ViewData["EditableAppointmentModel"] = appointment; ViewData["EditableAppointmentModel"] = appointment;
@@ -532,10 +539,11 @@ namespace BeWoPlanerMobil.Controllers
KalenderService.UpdateSchedulerAppointments(new List<SchedulerAppointmentDC> { appointment }); KalenderService.UpdateSchedulerAppointments(new List<SchedulerAppointmentDC> { appointment });
} }
KalenderService.DeleteSchedulerAppointments( var oid2Version = appointmentsToRemove
appointmentsToRemove .Where(appointment => appointment.SchedulerAppointmentOid.HasValue && appointment.NewSchedulerAppointmentVersion.HasValue)
.Where(appointment => appointment.SchedulerAppointmentOid.HasValue && appointment.NewSchedulerAppointmentVersion.HasValue) .ToDictionary(k => k.SchedulerAppointmentOid.Value, v => v.NewSchedulerAppointmentVersion.Value);
.ToDictionary(k => k.SchedulerAppointmentOid.Value, v => v.NewSchedulerAppointmentVersion.Value));
KalenderService.DeleteSchedulerAppointments(oid2Version);
ReloadAppointments(); ReloadAppointments();
} }
@@ -1139,9 +1147,13 @@ namespace BeWoPlanerMobil.Controllers
Model.ResourceCategories2Resources.Values.DoForEach(l => l.DoForEach(s => list.AddIfNotIn(s))); Model.ResourceCategories2Resources.Values.DoForEach(l => l.DoForEach(s => list.AddIfNotIn(s)));
Model.SelectedEmployees = Model.PossibleEmployees.Where(employee => Model.SelectedEmployeeOids.Contains(employee.EmployeeOid)).ToList(); //Model.SelectedEmployees = Model.PossibleEmployees.Where(employee => Model.SelectedEmployeeOids.Contains(employee.EmployeeOid)).ToList();
Model.SelectedCustomers = Model.PossibleCustomers.Where(customer => Model.SelectedCustomerOids.Contains(customer.CustomerOid)).ToList(); //Model.SelectedCustomers = Model.PossibleCustomers.Where(customer => Model.SelectedCustomerOids.Contains(customer.CustomerOid)).ToList();
Model.SelectedResources = list.Where(resource => resource.ResourceOid.HasValue && Model.SelectedResourceOids.Contains(resource.ResourceOid.Value)).ToList(); //Model.SelectedResources = list.Where(resource => resource.ResourceOid.HasValue && Model.SelectedResourceOids.Contains(resource.ResourceOid.Value)).ToList();
AppointmentModel.SelectedEmployees = Model.PossibleEmployees.Where(employee => Model.SelectedEmployeeOids.Contains(employee.EmployeeOid)).ToList();
AppointmentModel.SelectedCustomers = Model.PossibleCustomers.Where(customer => Model.SelectedCustomerOids.Contains(customer.CustomerOid)).ToList();
AppointmentModel.SelectedResources = list.Where(resource => resource.ResourceOid.HasValue && Model.SelectedResourceOids.Contains(resource.ResourceOid.Value)).ToList();
Model.SelectedEmployeesForFiltering = Model.PossibleEmployees.Where(employee => Model.SelectedEmployeeOidsForFiltering.Contains(employee.EmployeeOid)).ToList(); Model.SelectedEmployeesForFiltering = Model.PossibleEmployees.Where(employee => Model.SelectedEmployeeOidsForFiltering.Contains(employee.EmployeeOid)).ToList();
Model.SelectedCustomersForFiltering = Model.PossibleCustomers.Where(customer => Model.SelectedCustomerOidsForFiltering.Contains(customer.CustomerOid)).ToList(); Model.SelectedCustomersForFiltering = Model.PossibleCustomers.Where(customer => Model.SelectedCustomerOidsForFiltering.Contains(customer.CustomerOid)).ToList();

View File

@@ -146,7 +146,7 @@ namespace BeWoPlanerMobil.Controllers
} }
public ActionResult Index(string tenant, string returnUrl) public ActionResult Index(string tenant, string returnUrl, string message = null)
{ {
if(returnUrl != null) if(returnUrl != null)
{ {
@@ -184,6 +184,7 @@ namespace BeWoPlanerMobil.Controllers
return Redirect(url); return Redirect(url);
} }
ViewData[LoginErrorIndex] = message;
MobileSessionFacade.Tenant = tenant; MobileSessionFacade.Tenant = tenant;
return View("Index"); return View("Index");
} }

View File

@@ -36,6 +36,8 @@ namespace BeWoPlanerMobil.Models
public int? RecurrenceIndex { get; set; } public int? RecurrenceIndex { get; set; }
public long? NewSchedulerAppointmentVersion { get; set; }
public bool ShowEmployeeColors { get; set; } public bool ShowEmployeeColors { get; set; }
public List<ServiceRecordDC> ServiceRecordList { get; set; } public List<ServiceRecordDC> ServiceRecordList { get; set; }
@@ -214,6 +216,8 @@ namespace BeWoPlanerMobil.Models
TaskDescription = appointment.TaskDescription; TaskDescription = appointment.TaskDescription;
OriginatorOid = appointment.Originator.EmployeeOid; OriginatorOid = appointment.Originator.EmployeeOid;
NewSchedulerAppointmentVersion = appointment.NewSchedulerAppointmentVersion;
} }
public virtual void Assign(AppointmentModel source) public virtual void Assign(AppointmentModel source)
@@ -252,6 +256,7 @@ namespace BeWoPlanerMobil.Models
TaskDescription = source.TaskDescription; TaskDescription = source.TaskDescription;
OriginatorOid = source.OriginatorOid; OriginatorOid = source.OriginatorOid;
NewSchedulerAppointmentVersion = source.NewSchedulerAppointmentVersion;
} }
} }
} }

View File

@@ -13,6 +13,28 @@
newAptBtn.empty(); newAptBtn.empty();
newAptBtn.append("<span>Neuer Termin</span>"); newAptBtn.append("<span>Neuer Termin</span>");
} }
const observer = new MutationObserver((mutations, obs) => {
const el0 = document.getElementById("appointmentRecurrenceForm_AptRecCtl_Monthly_WmeWeekOfMnth_DDD_L_LBI0T0");
const el1 = document.getElementById("appointmentRecurrenceForm_AptRecCtl_Monthly_WmeWeekOfMnth_DDD_L_LBI1T0");
const el2 = document.getElementById("appointmentRecurrenceForm_AptRecCtl_Monthly_WmeWeekOfMnth_DDD_L_LBI2T0");
const el3 = document.getElementById("appointmentRecurrenceForm_AptRecCtl_Monthly_WmeWeekOfMnth_DDD_L_LBI3T0");
const el4 = document.getElementById("appointmentRecurrenceForm_AptRecCtl_Monthly_WmeWeekOfMnth_DDD_L_LBI4T0");
const dropdown = document.getElementById("appointmentRecurrenceForm_AptRecCtl_Monthly_WmeWeekOfMnth_I");
if(el0 && el1 && el2 && el3 && el4 && dropdown) {
$(el0).text("Ersten");
$(el1).text("Zweiten");
$(el2).text("Dritten");
$(el3).text("Vierten");
$(el4).text("Letzten");
obs.disconnect();
}
});
observer.observe(document.body, {childList: true, subtree: true});
}); });
function UpdateSchedulerHeight() { function UpdateSchedulerHeight() {
@@ -41,7 +63,7 @@
if(editFormPopup.css("top") !== undefined) { if(editFormPopup.css("top") !== undefined) {
editFormPopup.css("top", "50px"); editFormPopup.css("top", "50px");
const recurrence = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Recurrence')"); const recurrence = $("#appointmentRecurrenceForm_ChkRecurrence label:contains('Recurrence')");
const daily = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Daily')"); const daily = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Daily')");
const weekly = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Weekly')"); const weekly = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Weekly')");
const monthly = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Monthly')"); const monthly = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Monthly')");
@@ -52,6 +74,32 @@
weekly.text("Wöchentlich"); weekly.text("Wöchentlich");
monthly.text("Monatlich"); monthly.text("Monatlich");
yearly.text("Jährlich"); yearly.text("Jährlich");
$("#appointmentRecurrenceForm_AptRecCtl_Daily_mainDiv label:contains('Every weekday')").text("Jeden Wochentag");
$("#appointmentRecurrenceForm_AptRecCtl_Daily_mainDiv label:contains('Every')").text("Alle");
$("#appointmentRecurrenceForm_AptRecCtl_Daily_mainDiv span:contains('day(s)')").text("Tage");
$("#appointmentRecurrenceForm_AptRecCtl_Weekly_mainDiv label:contains('Recur every')").text("Alle");
$("#appointmentRecurrenceForm_AptRecCtl_Weekly_mainDiv span:contains('week(s) on:')").text("Wochen am:");
$("#appointmentRecurrenceForm_AptRecCtl_Monthly_mainDiv label:contains('Day')").text("Tag");
$("#appointmentRecurrenceForm_AptRecCtl_Monthly_ctl02").text("im");
$("#appointmentRecurrenceForm_AptRecCtl_Monthly_mainDiv span:contains('month(s)')").text("Monat");
$("#appointmentRecurrenceForm_AptRecCtl_Monthly_mainDiv label:contains('The')").text("Am");
$("#appointmentRecurrenceForm_AptRecCtl_Monthly_ctl04").text("jeden");
$("#appointmentRecurrenceForm_AptRecCtl_Monthly_mainDiv span:contains('month(s)')").text("Monats");
$("#appointmentRecurrenceForm_AptRecCtl_Monthly_WmeWeekOfMnth_DDD_L_LBI1T0").text("zweiten");
$("#appointmentRecurrenceForm_AptRecCtl_Yearly_mainDiv label:contains('Every')").text("Jeden");
$("#appointmentRecurrenceForm_AptRecCtl_Yearly_mainDiv label:contains('The')").text("Am");
$("#appointmentRecurrenceForm_AptRecCtl_Yearly_mainDiv label:contains('of')").text("im");
$("#appointmentRecurrenceForm_AptRecCtl_RangeCtl_mainDiv label:contains('No end date')").text("Ohne Enddatum");
$("#appointmentRecurrenceForm_AptRecCtl_RangeCtl_mainDiv label:contains('End after:')").text("Endet nach:");
$("#appointmentRecurrenceForm_AptRecCtl_RangeCtl_mainDiv span:contains('occurrences')").text("Wiederholungen");
$("#appointmentRecurrenceForm_AptRecCtl_RangeCtl_mainDiv label:contains('End by:')").text("Endet am:");
} }
if(editFormPopup.outerWidth() !== undefined) { if(editFormPopup.outerWidth() !== undefined) {
@@ -616,12 +664,13 @@
} }
<div class="d-flex justify-content-center align-items-center"> <div class="d-flex justify-content-center align-items-center">
@*@if(Html.IsInDebugMode()) @*@if(Html.IsInDebugMode())
{ {
<button type="button" class="btn btn-bewo-dev m-1" onclick='scheduler.PerformCallback({ apptID: "", actionId: "debug_DummyAppointments", args: null }, () => { performReload(true);hideSpinner(); });'> <button type="button" class="btn btn-bewo-dev m-1" onclick='scheduler.PerformCallback({ apptID: "", actionId: "debug_DummyAppointments", args: null }, () => { performReload(true);hideSpinner(); });'>
Testtermine Testtermine
</button> </button>
}*@ }*@
<h4 id="scheduler-log"></h4>
<button class="btn btn-sm btn-primary dropdown-toggle m-1" type="button" data-toggle="dropdown"> <button class="btn btn-sm btn-primary dropdown-toggle m-1" type="button" data-toggle="dropdown">
Filtern Filtern
</button> </button>

View File

@@ -58,6 +58,8 @@ namespace BeWoPlanerMobil.Views.DevExpressScheduler
appointmentStorage.CustomFieldMappings.Add(new ASPxAppointmentCustomFieldMapping("CanBeViewed", "CanBeViewed")); appointmentStorage.CustomFieldMappings.Add(new ASPxAppointmentCustomFieldMapping("CanBeViewed", "CanBeViewed"));
appointmentStorage.CustomFieldMappings.Add(new ASPxAppointmentCustomFieldMapping("CanBeDeleted", "CanBeDeleted")); appointmentStorage.CustomFieldMappings.Add(new ASPxAppointmentCustomFieldMapping("CanBeDeleted", "CanBeDeleted"));
appointmentStorage.CustomFieldMappings.Add(new ASPxAppointmentCustomFieldMapping("NewSchedulerAppointmentVersion", "NewSchedulerAppointmentVersion"));
return appointmentStorage; return appointmentStorage;
} }

View File

@@ -380,7 +380,8 @@
CompletedDate = container.Appointment.CustomFields["CompletedDate"] is DateTime completedDate ? completedDate : null, CompletedDate = container.Appointment.CustomFields["CompletedDate"] is DateTime completedDate ? completedDate : null,
OriginatorOid = (container.Appointment.CustomFields["Originator"] as CompactEmployeeDC)?.EmployeeOid, OriginatorOid = (container.Appointment.CustomFields["Originator"] as CompactEmployeeDC)?.EmployeeOid,
RecurrenceInfo = container.Appointment.RecurrenceInfo?.ToXml(), RecurrenceInfo = container.Appointment.RecurrenceInfo?.ToXml(),
RecurrenceIndex = container.Appointment.RecurrenceIndex RecurrenceIndex = container.Appointment.RecurrenceIndex,
NewSchedulerAppointmentVersion = container.Appointment.CustomFields["NewSchedulerAppointmentVersion"] as long?
}; };
var appointmentModel = ViewData["EditableAppointmentModel"] != null ? (AppointmentModel)ViewData["EditableAppointmentModel"] : newModel; var appointmentModel = ViewData["EditableAppointmentModel"] != null ? (AppointmentModel)ViewData["EditableAppointmentModel"] : newModel;

View File

@@ -24,7 +24,7 @@
<div class="modal-dialog col-sm-8"> <div class="modal-dialog col-sm-8">
<div class="modal-content border-0"> <div class="modal-content border-0">
@if(ViewData[LoginController.LoginErrorIndex] != null || ViewData[AbstractBaseController.IsSessionTimedOut] != null) @if(ViewData[LoginController.LoginErrorIndex] != null || TempData[LoginController.LoginErrorIndex] != null || ViewData[AbstractBaseController.IsSessionTimedOut] != null)
{ {
<div class="alert alert-danger mx-3" role="alert"> <div class="alert alert-danger mx-3" role="alert">
@ViewData[LoginController.LoginErrorIndex] @ViewData[LoginController.LoginErrorIndex]

View File

@@ -4579,8 +4579,8 @@ WHERE sc.Billable = 1 and sr.StartDate >= '{0:yyyy-MM-dd}' and sr.StartDate < '{
} }
public string ValidateSchedulerAppointment(DateTime start, DateTime end, IEnumerable<long> employees, IEnumerable<long> customers, List<long> resources, long originator, long? appointmentOid, Guid? recurrenceId = null, int occurrenceIndex = 0, bool shouldSkipResourceAvailability = false) public string ValidateSchedulerAppointment(DateTime start, DateTime end, IEnumerable<long> employees, IEnumerable<long> customers, List<long> resources, long originator, long? appointmentOid, Guid? recurrenceId = null, int occurrenceIndex = 0, bool shouldSkipResourceAvailability = false)
{ {
var resourceList = resources; var resourceList = resources;
if (shouldSkipResourceAvailability) if (shouldSkipResourceAvailability)
{ {
resourceList = null; resourceList = null;

View File

@@ -10,7 +10,6 @@ namespace BeWo.Service.Plugins
{ {
public class ResourceService : AbstractIDSpecificDefaultClass<ResourceService> public class ResourceService : AbstractIDSpecificDefaultClass<ResourceService>
{ {
public virtual string ValidateSchedulerAppointment(DateTime start, DateTime end, IEnumerable<long> employees, IEnumerable<long> customers, List<long> resources, long originator, long? appointmentOid, Guid? recurrenceId, int occurrenceIndex = 0, bool shouldSkipResourceAvailability = false) public virtual string ValidateSchedulerAppointment(DateTime start, DateTime end, IEnumerable<long> employees, IEnumerable<long> customers, List<long> resources, long originator, long? appointmentOid, Guid? recurrenceId, int occurrenceIndex = 0, bool shouldSkipResourceAvailability = false)
{ {
return DAOFactory.SearchDAO.ValidateSchedulerAppointment(start, end, employees, customers, resources, originator, appointmentOid, recurrenceId, occurrenceIndex); return DAOFactory.SearchDAO.ValidateSchedulerAppointment(start, end, employees, customers, resources, originator, appointmentOid, recurrenceId, occurrenceIndex);

View File

@@ -493,20 +493,20 @@ namespace BS.Shared
ServiceRecord_AllowEditForOtherEmployees = 20602, ServiceRecord_AllowEditForOtherEmployees = 20602,
KalenderAnsehen = 20700, KalenderAnsehen = 20700,
KalenderMitarbeitertermineAnsehen = 20701, KalenderMitarbeitertermineAnsehen = 20701,
KalenderMitarbeitertermineAnlegen = 20702, KalenderMitarbeitertermineAnlegen = 20702,
KalenderMitarbeitertermineAendern = 20703, KalenderMitarbeitertermineAendern = 20703,
KalenderKliententermineAlleAnsehen = 20704, KalenderKliententermineAlleAnsehen = 20704,
KalenderKliententermineAnlegen = 20706, KalenderKliententermineAnlegen = 20706,
KalenderKliententermineAendern = 20707, KalenderKliententermineAendern = 20707,
KalenderRessourcentermineAnsehen = 20708, KalenderRessourcentermineAnsehen = 20708,
KalenderRessourcentermineAnlegen = 20709, KalenderRessourcentermineAnlegen = 20709,
KalenderRessourcentermineAendern = 20710, KalenderRessourcentermineAendern = 20710,
KalenderRessourcentermineAndererAendern = 20711, KalenderRessourcentermineAndererAendern = 20711,
KalenderInZeiterfassungUebernehmen = 20720,
KalenderInZeiterfassungUebernehmen = 20720, KalenderInAbwesenheitenUebernehmen = 20721,
KalenderInAbwesenheitenUebernehmen = 20721, KalenderRessourcenDoppeltBuchen = 20722,
AllowChangeOwnPassword = 20800, AllowChangeOwnPassword = 20800,