332 lines
13 KiB
C#
332 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using BeWo.Data.Entities;
|
|
using BS.Shared.Core;
|
|
using BS.Shared.Extensions;
|
|
using DevExpress.XtraScheduler;
|
|
using DevExpress.XtraScheduler.Compatibility;
|
|
using NHibernate.Criterion;
|
|
using NHibernate.SqlCommand;
|
|
|
|
namespace BeWo.Data.Utils
|
|
{
|
|
public class IntervalFinderHelper
|
|
{
|
|
public static List<DateTimeSpan> GenerateIntervalsForCriteria(DateTime intervalStart, DateTime intervalEnd, bool skipWeekends)
|
|
{
|
|
var result = new List<DateTimeSpan>();
|
|
|
|
if (intervalEnd <= intervalStart)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
var start = intervalStart;
|
|
var end = start.MergeDatesByDate(intervalEnd);
|
|
|
|
while (end <= intervalEnd)
|
|
{
|
|
end = start.MergeDatesByDate(intervalEnd);
|
|
|
|
if (skipWeekends && (start.Date.DayOfWeek == DayOfWeek.Saturday || start.Date.DayOfWeek == DayOfWeek.Sunday))
|
|
{
|
|
start = start.AddDays(1);
|
|
end = start.MergeDatesByDate(intervalEnd);
|
|
continue;
|
|
}
|
|
|
|
result.AddIfNotIn(new DateTimeSpan(start, end));
|
|
|
|
start = start.AddDays(1);
|
|
end = start.MergeDatesByDate(intervalEnd);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static List<ICriterion> CreateIntervalRecurrenceCriterias(List<DateTimeSpan> intervals, bool skipWeekends)
|
|
{
|
|
var result = new List<ICriterion>();
|
|
|
|
foreach(var interval in intervals)
|
|
{
|
|
var start = interval.StartDate;
|
|
var end = interval.EndDate;
|
|
|
|
result.AddIfNotIn(CreateRecurrenceCriterion(start, end));
|
|
}
|
|
|
|
// TODO: KGW
|
|
//for (var i = 0; i < totalDays; i++)
|
|
//{
|
|
// start = start.AddDays(i).MergeDatesByDate(startDate);
|
|
|
|
// if (skipWeekends && (start.DayOfWeek == DayOfWeek.Saturday || start.DayOfWeek == DayOfWeek.Sunday))
|
|
// {
|
|
// continue;
|
|
// }
|
|
|
|
// DateTime e;
|
|
// var end = start.MergeDatesByDate(endDate);
|
|
|
|
// while ((e = start.AddMinutes(duration)) <= end && start <= end.AddMinutes(-1 * duration))
|
|
// {
|
|
// result.AddIfNotIn(CreateRecurrenceCriterion(start, e));
|
|
|
|
// start = start.AddMinutes(intervalBuffer);
|
|
// }
|
|
//}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static ICriterion CreateRecurrenceCriterion(DateTime start, DateTime end)
|
|
{
|
|
var recurrenceBetween = $"'{start:yyyy-MM-dd HH:mm:ss}' BETWEEN STR_TO_DATE(SUBSTRING(RecurrenceInfo, 24, 19), '%m/%d/%Y %H:%i:%s') AND STR_TO_DATE(SUBSTRING(RecurrenceInfo, 50, 19), '%m/%d/%Y %H:%i:%s')";
|
|
var recurrenceAfter = $"'{start:yyyy-MM-dd HH:mm:ss}' > STR_TO_DATE(SUBSTRING(RecurrenceInfo, 24, 19), '%m/%d/%Y %H:%i:%s')";
|
|
|
|
var result = Restrictions.Or(
|
|
Restrictions.And(
|
|
Restrictions.Not(Restrictions.Like(nameof(SchedulerAppointment.RecurrenceInfo), "Range", MatchMode.Anywhere)),
|
|
Restrictions.Like(nameof(SchedulerAppointment.RecurrenceInfo), "OccurrenceCount=\"10\"", MatchMode.Anywhere)),
|
|
Restrictions.Or(
|
|
Restrictions.Or(
|
|
Restrictions.And(Restrictions.Lt(nameof(SchedulerAppointment.StartDate), end), Restrictions.Ge(nameof(SchedulerAppointment.EndDate), start)),
|
|
Restrictions.Or(Restrictions.And(
|
|
Restrictions.Like(nameof(SchedulerAppointment.RecurrenceInfo), "End", MatchMode.Anywhere),
|
|
Restrictions.And(Restrictions.IsNotNull(nameof(SchedulerAppointment.RecurrenceInfo)), Expression.Sql(new SqlString(recurrenceBetween)))
|
|
), Restrictions.And(
|
|
Restrictions.And(
|
|
Restrictions.IsNotNull(nameof(SchedulerAppointment.RecurrenceInfo)),
|
|
Restrictions.Not(Restrictions.Like(nameof(SchedulerAppointment.RecurrenceInfo), "End", MatchMode.Anywhere))),
|
|
Expression.Sql(recurrenceAfter)))
|
|
),
|
|
Restrictions.And(Restrictions.Eq(nameof(SchedulerAppointment.Type), 3), Expression.Sql(new SqlString(recurrenceBetween)))));
|
|
|
|
return result;
|
|
}
|
|
|
|
public static Dictionary<DateTime, List<DateTimeSpan>> CreateFreeIntervalsDictionary(IEnumerable<DateTimeSpan> freeIntervals)
|
|
{
|
|
var result = new Dictionary<DateTime, List<DateTimeSpan>>();
|
|
|
|
var groupedIntervals = from dateTimeSpan in freeIntervals group dateTimeSpan by dateTimeSpan.StartDate.Date;
|
|
|
|
var unsortedDictionary = new Dictionary<DateTime, List<DateTimeSpan>>();
|
|
|
|
foreach (var grouping in groupedIntervals)
|
|
{
|
|
var orderedList = grouping.OrderBy(dateTimeSpan => dateTimeSpan.StartDate).ToList();
|
|
|
|
unsortedDictionary.AddOrUpdateValueInDictionary(grouping.Key, orderedList);
|
|
}
|
|
|
|
var keys = unsortedDictionary.Keys.OrderBy(key => key.Date).ToList();
|
|
|
|
foreach (var key in keys)
|
|
{
|
|
result.Add(key, unsortedDictionary[key]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static List<DateTimeSpan> CreateIntervals(int duration, DateTime intervalStart, DateTime intervalEnd, bool skipWeekends, int intervalBuffer)
|
|
{
|
|
var result = new List<DateTimeSpan>();
|
|
|
|
if (duration > 0 && intervalStart < intervalEnd && intervalBuffer > 10)
|
|
{
|
|
var timeSpan = intervalEnd - intervalStart;
|
|
|
|
var totalDays = Convert.ToInt32(Math.Ceiling(timeSpan.TotalDays));
|
|
|
|
for (var i = 0; i < totalDays; i++)
|
|
{
|
|
var start = intervalStart.AddDays(i).MergeDatesByDate(intervalStart);
|
|
|
|
if (skipWeekends && (start.DayOfWeek == DayOfWeek.Saturday || start.DayOfWeek == DayOfWeek.Sunday))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
DateTime e;
|
|
// Beim Intervall (z.B. 04.12.2023 15:00 und 06.12.2023 12:00) wird nur zwischen Start- und Enduhrzeit geprüft.
|
|
// Startdatum+Enduhrzeit
|
|
// -> end = 04.12.2023 12:00
|
|
//var end = start.MergeDatesByDate(intervalEnd);
|
|
var end = intervalEnd;
|
|
|
|
while ((e = start.AddMinutes(duration)) <= end && start <= end.AddMinutes(-1 * duration))
|
|
{
|
|
result.Add(new DateTimeSpan(start, e));
|
|
|
|
start = start.AddMinutes(intervalBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Liefert DateTimeSpan-Objekte für ganze Tage zwischen zwei Datumsangaben zurück.
|
|
/// Z.B. 04.12.2023 bis 06.12.2023 liefert drei DateTimeSpan für den 04.12., 05.12. und 06.12. zurück.
|
|
/// </summary>
|
|
/// <param name="intervalStart">Intervallstart</param>
|
|
/// <param name="intervalEnd">Intervallende</param>
|
|
/// <returns>Liste der DateTimeSpan-Objekte für die Anzahl der Tage im angegebenen Intervall.</returns>
|
|
public static List<DateTimeSpan> GenereateIntervalsForChecking(DateTime intervalStart, DateTime intervalEnd)
|
|
{
|
|
var start = intervalStart.Date;
|
|
var end = intervalEnd.Date;
|
|
|
|
var result = new List<DateTimeSpan>();
|
|
|
|
if (end < start)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
var days = Convert.ToInt32((end - start).TotalDays);
|
|
|
|
if (days == 0)
|
|
{
|
|
days = 1;
|
|
}
|
|
|
|
var rest = days % 6;
|
|
var limit = (days - rest) / 6;
|
|
|
|
var s = start;
|
|
DateTime e;
|
|
|
|
for (var i = 0; i < limit; i++)
|
|
{
|
|
e = s.AddDays(5);
|
|
|
|
result.Add(new DateTimeSpan(s, e));
|
|
|
|
s = e.AddDays(1);
|
|
}
|
|
|
|
if (rest > 0)
|
|
{
|
|
e = s.AddDays(rest);
|
|
|
|
if (rest == 1)
|
|
{
|
|
e = e.AddDays(-1);
|
|
}
|
|
|
|
result.Add(new DateTimeSpan(s, e));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static IEnumerable<SchedulerAppointment> CalculateRecurrences(IEnumerable<SchedulerAppointment> appointments, DateTime start, DateTime end, IReadOnlyCollection<RecurrenceInformation> changedOccurrences, IReadOnlyCollection<RecurrenceInformation> deletedOccurrences)
|
|
{
|
|
var result = new List<SchedulerAppointment>();
|
|
|
|
var interval = new TimeInterval(start, end);
|
|
|
|
foreach (var appointment in appointments.Where(app => app.RecurrenceInfo != null && app.Type == 1 && app.StartDate.HasValue && app.EndDate.HasValue))
|
|
{
|
|
var recurrenceInfo = new RecurrenceInfo();
|
|
recurrenceInfo.FromXml(appointment.RecurrenceInfo);
|
|
var occurrenceCalculator = OccurrenceCalculator.CreateInstance(recurrenceInfo);
|
|
|
|
var pattern = StaticAppointmentFactory.CreateAppointment(AppointmentType.Pattern);
|
|
pattern.RecurrenceInfo.FromXml(appointment.RecurrenceInfo);
|
|
pattern.Start = pattern.RecurrenceInfo.Start;
|
|
pattern.End = pattern.RecurrenceInfo.End;
|
|
var patternId = pattern.RecurrenceInfo.Id.ToString();
|
|
|
|
var occurrences = occurrenceCalculator.CalcOccurrences(interval, pattern);
|
|
|
|
if (occurrences.Count > 0)
|
|
{
|
|
foreach (var app in occurrences.GetAppointments(interval))
|
|
{
|
|
if(appointment.EndDate == null || appointment.StartDate == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var index = app.RecurrenceIndex;
|
|
|
|
var duration = (appointment.EndDate.Value - appointment.StartDate.Value).TotalMinutes;
|
|
|
|
var s = app.Start;
|
|
var e = app.Start.AddMinutes(duration);
|
|
|
|
var isOutOfInterval = s.AreInBetweenDates(e, start, end) == false;
|
|
var isChangedOccurrence = changedOccurrences.Any(a => a.PatternId.Equals(patternId) && a.Index == index);
|
|
var isDeletedOccurrence = deletedOccurrences.Any(a => a.PatternId.Equals(patternId) && a.Index == index);
|
|
|
|
if (isOutOfInterval || isChangedOccurrence || isDeletedOccurrence)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var recurringAppointment = new SchedulerAppointment
|
|
{
|
|
AllDay = appointment.AllDay,
|
|
CustomerList = appointment.CustomerList,
|
|
Notice = appointment.Notice,
|
|
EmployeeList = appointment.EmployeeList,
|
|
EndDate = app.Start.AddMinutes(duration),
|
|
FormerBookingSequenceOid = appointment.FormerBookingSequenceOid,
|
|
IsPrivate = appointment.IsPrivate,
|
|
Location = appointment.Location,
|
|
Originator = appointment.Originator,
|
|
RecurrenceInfo = app.RecurrenceInfo.ToXml(),
|
|
ReminderInfo = appointment.ReminderInfo,
|
|
ResourceList = appointment.ResourceList,
|
|
StartDate = app.Start,
|
|
Subject = appointment.Subject ?? string.Empty,
|
|
Type = appointment.Type
|
|
};
|
|
|
|
result.AddIfNotIn(recurringAppointment);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static IEnumerable<SchedulerAppointment> GetRecurrencesForIntervalFinder(List<SchedulerAppointment> appointments, DateTime startDate, DateTime endDate, IReadOnlyCollection<RecurrenceInformation> changedOccurrences, IReadOnlyCollection<RecurrenceInformation> deletedOccurrences, bool skipWeekends)
|
|
{
|
|
var result = new List<SchedulerAppointment>();
|
|
|
|
var dayCount = Convert.ToInt32(Math.Ceiling((endDate - startDate).TotalDays));
|
|
|
|
if (dayCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
for (var i = 0; i < dayCount; i++)
|
|
{
|
|
var start = startDate.AddDays(i);
|
|
|
|
if (skipWeekends && (start.DayOfWeek == DayOfWeek.Saturday || start.DayOfWeek == DayOfWeek.Sunday))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var end = start.MergeDatesByDate(endDate);
|
|
|
|
var calculatedRecurrences = CalculateRecurrences(appointments, start, end, changedOccurrences, deletedOccurrences);
|
|
|
|
result.AddRange(calculatedRecurrences);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|