Weitere Optimierung am Stundenkonto (CorrectOverlappingRecords)
This commit is contained in:
@@ -1236,9 +1236,38 @@ namespace BeWo.Report.ReportObjects
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verknüpft einen bereits verarbeiteten ("alten") Record mit seinem Zeit-Span und
|
||||
/// seiner Einfüge-Reihenfolge. Der Span wird gecacht (statt pro Paar neu berechnet) und
|
||||
/// bei In-Place-Korrekturen des Records aktualisiert; der Index stellt die exakte
|
||||
/// Verarbeitungsreihenfolge wieder her, wenn Kandidaten aus mehreren Tages-Buckets stammen.
|
||||
/// </summary>
|
||||
private sealed class OverlapEntry
|
||||
{
|
||||
public readonly StundenkontoServiceRecord Record;
|
||||
public readonly DateTimeSpan Span;
|
||||
public readonly int Index;
|
||||
|
||||
public OverlapEntry(StundenkontoServiceRecord record, DateTimeSpan span, int index)
|
||||
{
|
||||
Record = record;
|
||||
Span = span;
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void CorrectOverlappingTimes(IList<StundenkontoServiceRecord> serviceRecords)
|
||||
{
|
||||
List<StundenkontoServiceRecord> oldRecords = new List<StundenkontoServiceRecord>();
|
||||
// Tages-Index über bereits verarbeitete Records. Zwei Records können sich nur
|
||||
// überlappen, wenn sich ihre Zeitintervalle schneiden - und damit teilen sie
|
||||
// mindestens einen Kalendertag. Statt jeden neuen Record gegen ALLE alten zu
|
||||
// prüfen (O(n²)), vergleichen wir ihn nur gegen die Records der Tage, die sein
|
||||
// Span berührt. Da Korrekturen Intervalle ausschließlich verkleinern (Start wandert
|
||||
// nach vorne, Ende bleibt), ist die Ausdehnung bei der Registrierung eine sichere
|
||||
// Obermenge - es kann nie eine neue Überlappung entstehen. Durch Iteration in
|
||||
// Einfüge-Reihenfolge (Index) bleibt das Ergebnis bit-identisch zur alten Version.
|
||||
var oldRecordsByDay = new Dictionary<DateTime, List<OverlapEntry>>();
|
||||
int insertionIndex = 0;
|
||||
|
||||
foreach (var newRecord in serviceRecords)
|
||||
{
|
||||
@@ -1264,34 +1293,19 @@ namespace BeWo.Report.ReportObjects
|
||||
EndDateTime = newRecord.Start.AddMinutes((double)minuten)
|
||||
};
|
||||
|
||||
foreach (var oldRecord in oldRecords)
|
||||
foreach (var oldEntry in CollectOverlapCandidates(oldRecordsByDay, newSpan))
|
||||
{
|
||||
var oldRecord = oldEntry.Record;
|
||||
if (!CorrectOverlappingTime(oldRecord, newRecord))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var oldSpan = new DateTimeSpan
|
||||
{
|
||||
StartDateTime = oldRecord.Start,
|
||||
EndDateTime = oldRecord.Start.AddMinutes((double)oldRecord.RoundedDuration)
|
||||
};
|
||||
if (oldRecord.GroupRoundedDuration.HasValue)
|
||||
{
|
||||
oldSpan.EndDateTime = oldRecord.Start.AddMinutes((double)oldRecord.GroupRoundedDuration);
|
||||
}
|
||||
// Gecachter Span; wird bei der Korrektur unten in-place mit oldRecord synchron gehalten.
|
||||
var oldSpan = oldEntry.Span;
|
||||
|
||||
if (oldSpan.Overlaps(newSpan))
|
||||
{
|
||||
DateTime start = newSpan.StartDateTime;
|
||||
if (oldSpan.StartDateTime > start)
|
||||
start = oldSpan.StartDateTime;
|
||||
|
||||
DateTime end = newSpan.EndDateTime;
|
||||
|
||||
if (oldSpan.EndDateTime < end)
|
||||
end = oldSpan.EndDateTime;
|
||||
|
||||
|
||||
|
||||
if (newSpan.StartDateTime >= oldSpan.StartDateTime)
|
||||
{
|
||||
newSpan.StartDateTime = oldSpan.EndDateTime;
|
||||
@@ -1337,8 +1351,84 @@ namespace BeWo.Report.ReportObjects
|
||||
|
||||
}
|
||||
}
|
||||
oldRecords.Add(newRecord);
|
||||
|
||||
// newRecord wird zum "alten" Record: in alle Tages-Buckets seiner (ggf. bereits
|
||||
// gekürzten) Ausdehnung einsortieren. newSpan ist zugleich der gecachte Span.
|
||||
RegisterOldRecord(oldRecordsByDay, new OverlapEntry(newRecord, newSpan, insertionIndex++));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sammelt alle bereits verarbeiteten Records, deren Tag der übergebene Span berührt, in
|
||||
/// exakter Einfüge-Reihenfolge. Der häufigste Fall (Record innerhalb eines Tages) wird ohne
|
||||
/// Zwischenliste/Sortierung bedient; nur bei tagesübergreifenden Spans werden mehrere Buckets
|
||||
/// zusammengeführt, dedupliziert und nach Einfüge-Index sortiert.
|
||||
/// </summary>
|
||||
private static IEnumerable<OverlapEntry> CollectOverlapCandidates(
|
||||
Dictionary<DateTime, List<OverlapEntry>> oldRecordsByDay, DateTimeSpan newSpan)
|
||||
{
|
||||
DateTime firstDay = newSpan.StartDateTime.Date;
|
||||
DateTime lastDay = newSpan.EndDateTime.Date;
|
||||
if (lastDay < firstDay)
|
||||
{
|
||||
lastDay = firstDay;
|
||||
}
|
||||
|
||||
if (firstDay == lastDay)
|
||||
{
|
||||
// Ein Bucket ist bereits in Einfüge-Reihenfolge -> direkt verwenden.
|
||||
List<OverlapEntry> bucket;
|
||||
if (oldRecordsByDay.TryGetValue(firstDay, out bucket))
|
||||
{
|
||||
return bucket;
|
||||
}
|
||||
return System.Linq.Enumerable.Empty<OverlapEntry>();
|
||||
}
|
||||
|
||||
var candidates = new List<OverlapEntry>();
|
||||
var seen = new HashSet<int>();
|
||||
for (DateTime day = firstDay; day <= lastDay; day = day.AddDays(1))
|
||||
{
|
||||
List<OverlapEntry> bucket;
|
||||
if (!oldRecordsByDay.TryGetValue(day, out bucket))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (var entry in bucket)
|
||||
{
|
||||
// Ein tagesübergreifender Record steht in mehreren Buckets -> nur einmal prüfen.
|
||||
if (seen.Add(entry.Index))
|
||||
{
|
||||
candidates.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
candidates.Sort((a, b) => a.Index.CompareTo(b.Index));
|
||||
return candidates;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registriert einen Record in allen Tages-Buckets, die seine Ausdehnung berührt.
|
||||
/// </summary>
|
||||
private static void RegisterOldRecord(
|
||||
Dictionary<DateTime, List<OverlapEntry>> oldRecordsByDay, OverlapEntry entry)
|
||||
{
|
||||
DateTime firstDay = entry.Span.StartDateTime.Date;
|
||||
DateTime lastDay = entry.Span.EndDateTime.Date;
|
||||
if (lastDay < firstDay)
|
||||
{
|
||||
lastDay = firstDay;
|
||||
}
|
||||
|
||||
for (DateTime day = firstDay; day <= lastDay; day = day.AddDays(1))
|
||||
{
|
||||
List<OverlapEntry> bucket;
|
||||
if (!oldRecordsByDay.TryGetValue(day, out bucket))
|
||||
{
|
||||
bucket = new List<OverlapEntry>();
|
||||
oldRecordsByDay[day] = bucket;
|
||||
}
|
||||
bucket.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,16 +56,16 @@ namespace HwHilfswerkInklusionUndTeilhabe
|
||||
return dd;
|
||||
}
|
||||
|
||||
public override bool AddServiceRecordToDuration(ServiceRecord item)
|
||||
public override bool AddServiceRecordToDuration(StundenkontoServiceRecord item)
|
||||
{
|
||||
if (HatEmployeeAmbulantContract(item.Employee.GetValidContractForDate(item.Start.Value)))
|
||||
if (HatEmployeeAmbulantContract(item.Employee.GetValidContractForDate(item.Start)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (item.ServiceDescription != null && item.ServiceDescription.ServiceCategory != null)
|
||||
if (item.ServiceDescription != null && item.ServiceDescription.Category != null)
|
||||
{
|
||||
|
||||
if (item.ServiceDescription.ServiceCategory.Name == "Arbeitszeit")
|
||||
if (item.ServiceDescription.Category.Name == "Arbeitszeit")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -74,21 +74,21 @@ namespace HwHilfswerkInklusionUndTeilhabe
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override ServiceRecordSummary BerechneStunden(MitarbeiterstundenkontoRO.EmployeeDetail empDetail, IList<ServiceRecord> groupFilteredRecords, DateTime berichtsAnfang,
|
||||
protected override ServiceRecordSummary BerechneStunden(MitarbeiterstundenkontoRO.EmployeeDetail empDetail, IList<StundenkontoServiceRecord> groupFilteredRecords, DateTime berichtsAnfang,
|
||||
DateTime berichtsEnde)
|
||||
{
|
||||
decimal flsGesamt = 0;
|
||||
decimal arbeitszeit = 0;
|
||||
|
||||
var sum = new ServiceRecordSummary();
|
||||
sum.RecordsImBerichtszeitraum = new List<ServiceRecord>();
|
||||
sum.RecordsImBerichtszeitraum = new List<StundenkontoServiceRecord>();
|
||||
//base.CorrectOverlappingTimes(groupFilteredRecords);
|
||||
|
||||
foreach (var item in groupFilteredRecords)
|
||||
{
|
||||
if (!IsServiceRecordFehlzeit(item))
|
||||
{
|
||||
if (item.Start.Value >= berichtsAnfang && item.Start.Value < berichtsEnde)
|
||||
if (item.Start >= berichtsAnfang && item.Start < berichtsEnde)
|
||||
{
|
||||
//if (AddServiceRecordToDuration(item))
|
||||
//{
|
||||
@@ -104,23 +104,23 @@ namespace HwHilfswerkInklusionUndTeilhabe
|
||||
}
|
||||
|
||||
if (item.ServiceDescription != null &&
|
||||
item.ServiceDescription.ServiceCategory != null)
|
||||
item.ServiceDescription.Category != null)
|
||||
{
|
||||
if (item.Employee != null && HatEmployeeAmbulantContract(item.Employee.GetValidContractForDate(item.Start.Value)))
|
||||
if (item.Employee != null && HatEmployeeAmbulantContract(item.Employee.GetValidContractForDate(item.Start)))
|
||||
{
|
||||
arbeitszeit += duration;
|
||||
if (item.ServiceDescription.ServiceCategory.IsBillable)
|
||||
if (item.ServiceDescription.Category.IsBillable)
|
||||
{
|
||||
flsGesamt += duration;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.ServiceDescription.ServiceCategory.Name.ToLower().Contains("arbeitszeit"))
|
||||
if (item.ServiceDescription.Category.Name.ToLower().Contains("arbeitszeit"))
|
||||
{
|
||||
arbeitszeit += duration;
|
||||
}
|
||||
else if (item.ServiceDescription.ServiceCategory.IsBillable)
|
||||
else if (item.ServiceDescription.Category.IsBillable)
|
||||
{
|
||||
flsGesamt += duration;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user