private void Hochzaehlen(DigitalisierungsKontingent kontingent, ref DateTime termin)
 {
     for (var i = 0; i < kontingent.InAnzahlTagen; i++)
     {
         termin = termin.AddDays(1);
         EnsureIsWorkingDay(ref termin);
     }
 }
        public async Task RecalcTermine(DigitalisierungsKategorie kategorie, DigitalisierungsKontingent kontingent)
        {
            var digipool   = (await orderDataAccess.GetDigipool()).Where(d => d.Digitalisierunskategorie == (int)kategorie).ToArray();
            var newTermine = new List <DigitalisierungsTermin>();

            foreach (var digiItem in digipool)
            {
                var letzterTerminDesBenutzers = GetAeltesterDigitalisierungsTerminDesBenutzers(kategorie, digipool, digiItem);

                var grenze = letzterTerminDesBenutzers == DateTime.MinValue
                    ? DateTime.MinValue
                    : letzterTerminDesBenutzers.AddDays(-kontingent.InAnzahlTagen);
                var termineToCheck = await orderDataAccess.GetLatestDigitalisierungsTermine(digiItem.UserId, grenze, kategorie);

                var      foundEntry = termineToCheck.Where(t => t.Termin < letzterTerminDesBenutzers).OrderBy(t => t.Termin).FirstOrDefault();
                DateTime referenzTermin;
                if (foundEntry != null)
                // Wenn ja, wird der jüngste Termin als Referenz genommen
                {
                    referenzTermin = foundEntry.Termin;
                }
                else
                // Wenn nein, wird der älteste Termin aus dem digipool genommen
                {
                    referenzTermin = digipool.Where(d => d.UserId == digiItem.UserId).Min(d => d.TerminDigitalisierung);
                }

                var termin = referenzTermin;
                if (!newTermine.Any() && referenzTermin != letzterTerminDesBenutzers)
                {
                    Hochzaehlen(kontingent, ref termin);
                    EnsureIsWorkingDay(ref termin);
                }
                else
                {
                    termin = GetNextPossibleTermin(referenzTermin, newTermine.Where(t => t.UserId == digiItem.UserId).ToList(), kontingent);
                }

                var index = newTermine.FindIndex(n => n.Termin == termin && n.UserId == digiItem.UserId);
                if (index >= 0)
                {
                    newTermine[index].AnzahlAuftraege++;
                }
                else
                {
                    newTermine.Add(new DigitalisierungsTermin
                    {
                        AnzahlAuftraege = 1,
                        Termin          = termin,
                        UserId          = digiItem.UserId
                    });
                }

                Log.Debug("Neuberechnung für Auftrag {ID} beendet, vorher: {TERMINALT}, jetzt: {TERMINNEU}", digiItem.OrderItemId,
                          digiItem.TerminDigitalisierung, termin);
                await orderDataAccess.UpdateTermin(digiItem.OrderItemId, termin);
            }
        }
        private void GetPossibleTerminInternal(int anzahlAuftraege, DigitalisierungsKontingent kontingent, List <DigitalisierungsTermin> nextTermine,
                                               DateTime nextTermin, ref DateTime termin, bool isWeekendDay)
        {
            if (anzahlAuftraege < kontingent.AnzahlAuftraege)
            {
                // Es hat noch freien Platz an diesem Tag
                termin = CreateReferenzTermin(nextTermin, isWeekendDay);
                return;
            }

            termin = CreateReferenzTermin(nextTermin, isWeekendDay);
            while (anzahlAuftraege >= kontingent.AnzahlAuftraege)
            {
                Hochzaehlen(kontingent, ref termin);
                termin          = CreateReferenzTermin(termin, isWeekendDay);
                anzahlAuftraege = GetAnzahlAuftraegeAnDiesemTag(nextTermine, termin);
            }
        }
        public DateTime GetNextPossibleTermin(DateTime orderDate, List <DigitalisierungsTermin> nextTermine, DigitalisierungsKontingent kontingent)
        {
            var isWeekendDay = IsWeekendDay(orderDate);
            var termin       = CreateReferenzTermin(orderDate, isWeekendDay);

            var limite = termin.AddDays(-kontingent.InAnzahlTagen);
            var nextTermineRegardingLimit = nextTermine.Where(t => t.Termin >= limite).OrderByDescending(t => t.Termin).ToList();

            // Beträgt die Anzahl Aufträge pro Anzahl Arbeitstage (X Aufträge in Y Arbeitstage) X > 1, werden für die Berechnung des Termins  X-1 vorangehende Aufträge nicht berücksichtigt
            var toSkip = Math.Max(kontingent.AnzahlAuftraege - 1, 0);

            if (nextTermineRegardingLimit.Any() && (toSkip == 0 || toSkip == nextTermineRegardingLimit.Sum(t => t.AnzahlAuftraege)))
            {
                var nextTermin      = nextTermineRegardingLimit.First();
                var anzahlAuftraege = GetAnzahlAuftraegeAnDiesemTag(nextTermine, nextTermin.Termin);

                GetPossibleTerminInternal(anzahlAuftraege, kontingent, nextTermine, nextTermin.Termin, ref termin, isWeekendDay);
            }
            else if (toSkip > 0 && nextTermineRegardingLimit.Any())
            {
                var latestTermin = termin;

                foreach (var nextTermin in nextTermineRegardingLimit.SkipWhile((dt, i) => SkipAuftraege(i, dt, toSkip)))
                {
                    var anzahlAuftraege = GetAnzahlAuftraegeAnDiesemTag(nextTermine, nextTermin.Termin);

                    if (toSkip > 0)
                    {
                        toSkip = Math.Max(toSkip - anzahlAuftraege, 0);
                        continue;
                    }

                    if (anzahlAuftraege < kontingent.AnzahlAuftraege)
                    {
                        // Es hat noch freien Platz an diesem Tag
                        termin = CreateReferenzTermin(nextTermin.Termin, isWeekendDay);
                        EnsureIsWorkingDay(ref termin);
                        return(termin);
                    }

                    latestTermin = nextTermin.Termin;
                    break;
                }

                var anzahl = GetAnzahlAuftraegeAnDiesemTag(nextTermine, latestTermin);
                GetPossibleTerminInternal(anzahl, kontingent, nextTermine, latestTermin, ref termin, isWeekendDay);
            }

            EnsureIsWorkingDay(ref termin);
            return(termin);
        }