Пример #1
0
        public async Task <ImportedShifts> ImportWeeklyShifts(int locationId, DateTimeOffset start)
        {
            var location = Db.Location.FirstOrDefault(l => l.Id == locationId);

            location.ThrowBusinessExceptionIfNull($"Couldn't find {nameof(Location)} with id: {locationId}.");
            var timezone = location?.Timezone;

            timezone.GetTimezone().ThrowBusinessExceptionIfNull("Timezone was invalid.");

            //We need to adjust to their start of the week, because it can differ depending on the TZ!
            var targetStartDate = start.ConvertToTimezone(timezone);
            var targetEndDate   = targetStartDate.TranslateDateForDaylightSavings(timezone, 7);

            var sheriffsAvailableAtLocation = await SheriffService.GetSheriffsForShiftAvailabilityForLocation(locationId, targetStartDate, targetEndDate);

            var sheriffIds = sheriffsAvailableAtLocation.SelectDistinctToList(s => s.Id);

            var shiftsToImport = Db.Shift
                                 .Include(s => s.Location)
                                 .Include(s => s.Sheriff)
                                 .AsNoTracking()
                                 .In(sheriffIds, s => s.SheriffId)
                                 .Where(s => s.LocationId == locationId &&
                                        s.ExpiryDate == null &&
                                        s.StartDate < targetEndDate && targetStartDate < s.EndDate
                                        );

            var importedShifts = await shiftsToImport.Select(shift => Db.DetachedClone(shift)).ToListAsync();

            foreach (var shift in importedShifts)
            {
                shift.StartDate = shift.StartDate.TranslateDateForDaylightSavings(timezone, 7);
                shift.EndDate   = shift.EndDate.TranslateDateForDaylightSavings(timezone, 7);
            }

            var overlaps = await GetShiftConflicts(importedShifts);

            var filteredImportedShifts = importedShifts.WhereToList(s => overlaps.All(o => o.Shift.Id != s.Id) &&
                                                                    !overlaps.Any(ts =>
                                                                                  s.Id != ts.Shift.Id && ts.Shift.StartDate < s.EndDate && s.StartDate < ts.Shift.EndDate &&
                                                                                  ts.Shift.SheriffId == s.SheriffId));

            filteredImportedShifts.ForEach(s => s.Id = 0);
            await Db.Shift.AddRangeAsync(filteredImportedShifts);

            await Db.SaveChangesAsync();

            return(new ImportedShifts
            {
                ConflictMessages = overlaps.SelectMany(o => o.ConflictMessages).ToList(),
                Shifts = filteredImportedShifts
            });
        }
Пример #2
0
        private async Task <List <ShiftConflict> > CheckSheriffEventsOverlap(List <Shift> shifts)
        {
            var sheriffEventConflicts = new List <ShiftConflict>();

            foreach (var shift in shifts)
            {
                var locationId = shift.LocationId;
                var sheriffs   = await SheriffService.GetSheriffsForShiftAvailabilityForLocation(locationId, shift.StartDate, shift.EndDate, shift.SheriffId);

                var sheriff          = sheriffs.FirstOrDefault();
                var validationErrors = new List <string>();
                if (sheriff == null)
                {
                    var unavailableSheriff =
                        await Db.Sheriff.AsNoTracking().FirstOrDefaultAsync(s => s.Id == shift.SheriffId);

                    validationErrors.Add($"{unavailableSheriff?.LastName}, {unavailableSheriff?.FirstName} is not active in this location for {shift.StartDate.ConvertToTimezone(shift.Timezone).PrintFormatDate()} {shift.StartDate.ConvertToTimezone(shift.Timezone).PrintFormatTime(shift.Timezone)} to {shift.EndDate.ConvertToTimezone(shift.Timezone).PrintFormatTime(shift.Timezone)}");
                }
                else
                {
                    validationErrors.AddRange(sheriff !.AwayLocation.Where(aw => aw.LocationId != shift.LocationId)
                                              .Select(aw => PrintSheriffEventConflict <SheriffAwayLocation>(aw.Sheriff,
                                                                                                            aw.StartDate,
                                                                                                            aw.EndDate,
                                                                                                            aw.Timezone)));
                    validationErrors.AddRange(sheriff.Leave.Select(aw => PrintSheriffEventConflict <SheriffLeave>(
                                                                       aw.Sheriff,
                                                                       aw.StartDate,
                                                                       aw.EndDate,
                                                                       aw.Timezone)));
                    validationErrors.AddRange(sheriff.Training.Select(aw => PrintSheriffEventConflict <SheriffTraining>(
                                                                          aw.Sheriff,
                                                                          aw.StartDate,
                                                                          aw.EndDate,
                                                                          aw.Timezone)));
                }

                if (validationErrors.Any())
                {
                    sheriffEventConflicts.Add(new ShiftConflict
                    {
                        Shift            = shift,
                        ConflictMessages = validationErrors
                    });
                }
            }
            return(sheriffEventConflicts);
        }
Пример #3
0
        /// <summary>
        /// This is used for Distribute Schedule, as well as the Shift Schedule page.
        /// </summary>
        public async Task <List <ShiftAvailability> > GetShiftAvailability(DateTimeOffset start, DateTimeOffset end, int locationId)
        {
            var sheriffs = await SheriffService.GetSheriffsForShiftAvailabilityForLocation(locationId, start, end);

            //Include sheriffs that have a shift, but their home location / away location doesn't match.
            //Grey out on the GUI if HomeLocationId and AwayLocation doesn't match.
            var sheriffIdsFromShifts = await Db.Shift.AsNoTracking()
                                       .Where(s => s.StartDate < end && start < s.EndDate && s.ExpiryDate == null &&
                                              s.LocationId == locationId)
                                       .Select(s => s.SheriffId)
                                       .ToListAsync();

            var sheriffsOutOfLocationWithShiftIds = sheriffIdsFromShifts.Except(sheriffs.Select(s => s.Id));

            //Note their AwayLocation, Leave, Training should be entirely empty, this is intentional.
            var sheriffsOutOfLocationWithShift = await
                                                 Db.Sheriff.AsNoTracking()
                                                 .Include(s => s.HomeLocation)
                                                 .In(sheriffsOutOfLocationWithShiftIds,
                                                     s => s.Id)
                                                 .Where(s => s.IsEnabled)
                                                 .ToListAsync();

            sheriffs = sheriffs.Concat(sheriffsOutOfLocationWithShift).ToList();

            var shiftsForSheriffs = await GetShiftsForSheriffs(sheriffs.Select(s => s.Id), start, end);

            var sheriffEventConflicts = new List <ShiftAvailabilityConflict>();

            sheriffs.ForEach(sheriff =>
            {
                sheriffEventConflicts.AddRange(sheriff.AwayLocation.Select(s => new ShiftAvailabilityConflict
                {
                    Conflict   = ShiftConflictType.AwayLocation,
                    SheriffId  = sheriff.Id,
                    Start      = s.StartDate,
                    End        = s.EndDate,
                    LocationId = s.LocationId,
                    Location   = s.Location,
                    Timezone   = s.Timezone,
                    Comment    = s.Comment
                }));
                sheriffEventConflicts.AddRange(sheriff.Leave.Select(s => new ShiftAvailabilityConflict
                {
                    Conflict         = ShiftConflictType.Leave,
                    SheriffId        = sheriff.Id,
                    Start            = s.StartDate,
                    End              = s.EndDate,
                    Timezone         = s.Timezone,
                    SheriffEventType = s.LeaveType?.Code,
                    Comment          = s.Comment
                }));
                sheriffEventConflicts.AddRange(sheriff.Training.Select(s => new ShiftAvailabilityConflict
                {
                    Conflict         = ShiftConflictType.Training,
                    SheriffId        = sheriff.Id,
                    Start            = s.StartDate,
                    End              = s.EndDate,
                    Timezone         = s.Timezone,
                    SheriffEventType = s.TrainingType?.Code,
                    Comment          = s.Note
                }));
            });

            var existingShiftConflicts = shiftsForSheriffs.Select(s => new ShiftAvailabilityConflict
            {
                Conflict      = ShiftConflictType.Scheduled,
                SheriffId     = s.SheriffId,
                Location      = s.Location,
                LocationId    = s.LocationId,
                Start         = s.StartDate,
                End           = s.EndDate,
                ShiftId       = s.Id,
                Timezone      = s.Timezone,
                OvertimeHours = s.OvertimeHours,
                Comment       = s.Comment
            });

            //We've already included this information in the conflicts.
            sheriffs.ForEach(s => s.AwayLocation = null);
            sheriffs.ForEach(s => s.Leave        = null);
            sheriffs.ForEach(s => s.Training     = null);

            var allShiftConflicts = sheriffEventConflicts.Concat(existingShiftConflicts).ToList();

            var lookupCode = await Db.LookupCode.AsNoTracking()
                             .Where(lc => lc.Type == LookupTypes.SheriffRank)
                             .Include(s => s.SortOrder)
                             .ToListAsync();

            return(sheriffs.SelectToList(s => new ShiftAvailability
            {
                Start = start,
                End = end,
                Sheriff = s,
                SheriffId = s.Id,
                Conflicts = allShiftConflicts.WhereToList(asc => asc.SheriffId == s.Id)
            })
                   .OrderBy(s => lookupCode.FirstOrDefault(so => so.Code == s.Sheriff.Rank)
                            ?.SortOrder.FirstOrDefault()
                            ?.SortOrder)
                   .ThenBy(s => s.Sheriff.LastName)
                   .ThenBy(s => s.Sheriff.FirstName)
                   .ToList());
        }