private bool AreDutiesEqual(DutySlot a, DutySlot b)
 {
     return(
         (b.SheriffId == a.SheriffId) &&
         (a.StartDate == b.StartDate) &&
         (a.EndDate == b.EndDate) &&
         (a.Id == b.Id) &&
         (a.DutyId == b.DutyId)
         );
 }
Example #2
0
        public async Task SheriffOverrideConflictRemove()
        {
            var sheriffObject = await CreateSheriffUsingDbContext();

            var newLocation = new Location {
                Name = "New PLace", AgencyId = "zfddf2342"
            };
            await Db.Location.AddAsync(newLocation);

            await Db.SaveChangesAsync();

            var startDate = DateTimeOffset.UtcNow.Date.AddHours(1);
            var endDate   = startDate.AddHours(8);

            var shift = new Shift
            {
                Id         = 9000665,
                StartDate  = startDate,
                EndDate    = endDate,
                LocationId = newLocation.Id,
                Timezone   = "America/Vancouver",
                SheriffId  = sheriffObject.Id
            };

            Db.Shift.Add(shift);

            var duty = new Duty
            {
                LocationId = newLocation.Id,
            };

            Db.Duty.Add(duty);

            await Db.SaveChangesAsync();

            var dutySlot = new DutySlot
            {
                SheriffId  = sheriffObject.Id,
                LocationId = newLocation.Id,
                StartDate  = startDate,
                EndDate    = endDate,
                DutyId     = duty.Id
            };

            Db.DutySlot.Add(dutySlot);

            var lookupCode = new LookupCode
            {
                Code        = "zz4",
                Description = "gg",
                LocationId  = newLocation.Id
            };
            await Db.LookupCode.AddAsync(lookupCode);

            await Db.SaveChangesAsync();

            var entity = new SheriffLeaveDto
            {
                LeaveTypeId = lookupCode.Id,
                SheriffId   = sheriffObject.Id,
                StartDate   = startDate,
                EndDate     = endDate
            };

            Assert.True(Db.Shift.Any(s => s.ExpiryDate == null && s.Id == shift.Id));
            Assert.True(Db.DutySlot.Any(ds => ds.ExpiryDate == null && ds.Id == dutySlot.Id));

            await _controller.AddSheriffLeave(entity, true);

            Assert.False(Db.Shift.Any(s => s.ExpiryDate == null && s.Id == shift.Id));
            Assert.False(Db.DutySlot.Any(ds => ds.ExpiryDate == null && ds.Id == dutySlot.Id));
        }
        public async Task MoveDuty()
        {
            var locationId = await CreateLocation();

            var newSheriffId = await CreateSheriff(locationId);

            //Create a duty from 9 -> 5 pm
            var startDate = DateTimeOffset.UtcNow.AddYears(5).ConvertToTimezone("America/Vancouver");

            startDate = startDate.Date.AddHours(9);
            var endDate = startDate.Date.AddHours(9 + 8);

            var fromDutySlot = new DutySlot
            {
                Id         = 50000,
                DutyId     = 50000,
                StartDate  = startDate,
                EndDate    = endDate,
                SheriffId  = newSheriffId,
                LocationId = locationId
            };

            var fromDuty = new Duty
            {
                Id         = 50000,
                LocationId = locationId,
                StartDate  = startDate,
                EndDate    = endDate,
                Timezone   = "America/Vancouver",
                DutySlots  = new List <DutySlot>
                {
                    fromDutySlot
                }
            };

            var shift = new Shift
            {
                Id         = 50000,
                LocationId = locationId,
                StartDate  = startDate,
                EndDate    = startDate.Date.AddHours(20),
                Timezone   = "America/Vancouver",
                SheriffId  = newSheriffId
            };

            await Db.Duty.AddAsync(fromDuty);

            await Db.Shift.AddAsync(shift);

            await Db.SaveChangesAsync();

            //It ends early and you'd like to move someone from 3pm -> 5pm into another duty

            //CASE works perfectly, Duty has no slots
            var toDuty = new Duty
            {
                Id         = 50001,
                LocationId = locationId,
                StartDate  = startDate,
                EndDate    = endDate,
                Timezone   = "America/Vancouver"
            };

            await Db.Duty.AddAsync(toDuty);

            await Db.SaveChangesAsync();

            var controllerResult = await _controller.MoveSheriffFromDutySlot(fromDutySlot.Id, toDuty.Id, startDate.AddHours(6));

            var newDuty = HttpResponseTest.CheckForValid200HttpResponseAndReturnValue(controllerResult);

            Assert.Equal(startDate.AddHours(6), newDuty.DutySlots.FirstOrDefault().StartDate);
            Assert.Equal(startDate.AddHours(8), newDuty.DutySlots.FirstOrDefault().EndDate);

            Db.Duty.Remove(toDuty);
            Db.Duty.Remove(fromDuty);
            await Db.SaveChangesAsync();

            fromDuty.DutySlots.First().EndDate = endDate;
            await Db.Duty.AddAsync(fromDuty);

            await Db.SaveChangesAsync();

            //CASE Duty has a blank slot (take over the slot)
            toDuty = new Duty
            {
                Id         = 50001,
                LocationId = locationId,
                StartDate  = startDate,
                EndDate    = endDate,
                Timezone   = "America/Vancouver",
                DutySlots  = new List <DutySlot>
                {
                    new DutySlot
                    {
                        Id         = 50001,
                        DutyId     = 50001,
                        StartDate  = startDate,
                        EndDate    = endDate,
                        LocationId = locationId
                    }
                }
            };
            await Db.Duty.AddAsync(toDuty);

            await Db.SaveChangesAsync();

            controllerResult = await _controller.MoveSheriffFromDutySlot(fromDutySlot.Id, toDuty.Id, startDate.AddHours(6));

            newDuty = HttpResponseTest.CheckForValid200HttpResponseAndReturnValue(controllerResult);

            Assert.Equal(startDate.AddHours(6), newDuty.DutySlots.FirstOrDefault().StartDate);
            Assert.Equal(startDate.AddHours(8), newDuty.DutySlots.FirstOrDefault().EndDate);

            Db.Duty.Remove(toDuty);
            Db.Duty.Remove(fromDuty);
            await Db.SaveChangesAsync();

            fromDuty.DutySlots.First().EndDate = endDate;
            await Db.Duty.AddAsync(fromDuty);

            await Db.SaveChangesAsync();

            //CASE A slot conflicts at 4 pm, so it should be moved from 3 -> 4pm
            toDuty = new Duty
            {
                Id         = 50001,
                LocationId = locationId,
                StartDate  = startDate,
                EndDate    = endDate,
                Timezone   = "America/Vancouver",
                DutySlots  = new List <DutySlot>
                {
                    new DutySlot
                    {
                        Id         = 50001,
                        DutyId     = 50001,
                        StartDate  = startDate.Date.AddHours(16),
                        EndDate    = startDate.Date.AddHours(17),
                        SheriffId  = newSheriffId,
                        LocationId = locationId
                    }
                }
            };
            await Db.Duty.AddAsync(toDuty);

            await Db.SaveChangesAsync();

            controllerResult = await _controller.MoveSheriffFromDutySlot(fromDutySlot.Id, toDuty.Id, startDate.AddHours(6));

            newDuty = HttpResponseTest.CheckForValid200HttpResponseAndReturnValue(controllerResult);

            Assert.Equal(startDate.AddHours(6), newDuty.DutySlots.FirstOrDefault(ds => ds.Id != 50001).StartDate);
            Assert.Equal(startDate.AddHours(7), newDuty.DutySlots.FirstOrDefault(ds => ds.Id != 50001).EndDate);

            Db.Duty.Remove(toDuty);
            Db.Duty.Remove(fromDuty);
            await Db.SaveChangesAsync();

            fromDuty.DutySlots.First().EndDate = endDate;
            await Db.Duty.AddAsync(fromDuty);

            await Db.SaveChangesAsync();

            //CASE Duty ends early so it should be only created from 3 -> 4 pm
            toDuty = new Duty
            {
                Id         = 50001,
                LocationId = locationId,
                StartDate  = startDate,
                EndDate    = startDate.Date.AddHours(16),
                Timezone   = "America/Vancouver",
            };
            await Db.Duty.AddAsync(toDuty);

            await Db.SaveChangesAsync();

            controllerResult = await _controller.MoveSheriffFromDutySlot(fromDutySlot.Id, toDuty.Id, startDate.AddHours(6));

            newDuty = HttpResponseTest.CheckForValid200HttpResponseAndReturnValue(controllerResult);

            Assert.Equal(startDate.AddHours(6), newDuty.DutySlots.FirstOrDefault().StartDate);
            Assert.Equal(startDate.AddHours(7), newDuty.DutySlots.FirstOrDefault().EndDate);

            Db.Duty.Remove(toDuty);
            Db.Duty.Remove(fromDuty);
            await Db.SaveChangesAsync();

            fromDuty.DutySlots.First().EndDate = endDate;
            await Db.Duty.AddAsync(fromDuty);

            await Db.SaveChangesAsync();

            //CASE Duty impossible to move, already someone scheduled for duty/dutyslots.
            toDuty = new Duty
            {
                Id         = 50001,
                LocationId = locationId,
                StartDate  = startDate,
                EndDate    = endDate,
                Timezone   = "America/Vancouver",
                DutySlots  = new List <DutySlot>
                {
                    new DutySlot
                    {
                        Id         = 50001,
                        DutyId     = 50001,
                        StartDate  = startDate,
                        EndDate    = endDate,
                        SheriffId  = newSheriffId,
                        LocationId = locationId
                    }
                }
            };
            await Db.Duty.AddAsync(toDuty);

            await Db.SaveChangesAsync();

            await Assert.ThrowsAsync <BusinessLayerException>(async() => await _controller.MoveSheriffFromDutySlot(fromDutySlot.Id, toDuty.Id, startDate.AddHours(6)));
        }
 private static string ConflictingSheriffAndDutySlot(Sheriff sheriff, DutySlot dutySlot)
 => $"Conflict - {nameof(Sheriff)}: {sheriff?.LastName}, {sheriff?.FirstName} - Existing {nameof(DutySlot)} conflicts: {dutySlot.StartDate.ConvertToTimezone(dutySlot.Timezone).PrintFormatDateTime(dutySlot.Timezone)} to {dutySlot.EndDate.ConvertToTimezone(dutySlot.Timezone).PrintFormatDateTime(dutySlot.Timezone)}";
        public async Task <Duty> MoveSheriffFromDutySlot(int fromDutySlotId, int toDutyId, DateTimeOffset?separationTime = null)
        {
            var fromDutySlot = await Db.DutySlot.FirstOrDefaultAsync(d => d.Id == fromDutySlotId);

            fromDutySlot.ThrowBusinessExceptionIfNull("From duty slot didn't exist.");

            var fromSheriffId = fromDutySlot.SheriffId;

            if (!fromSheriffId.HasValue)
            {
                throw new BusinessLayerException("No Sheriff Id provided.");
            }

            var toDutySlotStart = separationTime ?? DateTimeOffset.UtcNow.ConvertToTimezone(fromDutySlot.Timezone);

            toDutySlotStart = RoundUp(toDutySlotStart, TimeSpan.FromMinutes(15));

            fromDutySlot.EndDate = toDutySlotStart;

            if (fromDutySlot.StartDate == fromDutySlot.EndDate)
            {
                Db.DutySlot.Remove(fromDutySlot);
            }

            var toDuty = await Db.Duty.Include(d => d.DutySlots).FirstOrDefaultAsync(d => d.Id == toDutyId);

            if (!(toDutySlotStart < toDuty.EndDate && toDuty.StartDate < toDutySlotStart))
            {
                throw new BusinessLayerException("Duty doesn't have room. You may need to adjust the duty.");
            }

            var toDutySlotEnd = toDuty.EndDate;

            //Might be cut even shorter from existing DutySlots with sheriffs in them.
            if (toDuty.DutySlots.Any(ds =>
                                     toDutySlotStart < ds.EndDate && ds.StartDate < toDutySlotEnd && ds.SheriffId != null))
            {
                var earliestEndFromOtherSlots = toDuty.DutySlots.Where(ds =>
                                                                       toDutySlotStart < ds.EndDate && ds.StartDate < toDutySlotEnd && ds.SheriffId != null)
                                                .Min(ds => ds.StartDate);

                if (earliestEndFromOtherSlots <= toDutySlotStart)
                {
                    throw new BusinessLayerException("This already has a duty slot with a sheriff in it.");
                }

                toDutySlotEnd = toDutySlotEnd > earliestEndFromOtherSlots ? earliestEndFromOtherSlots : toDutySlotEnd;
            }

            var maxShiftEndDate = await FindContinuousEndDateOverShifts(toDuty.LocationId, fromSheriffId.Value, toDutySlotStart, toDuty.Timezone);

            toDutySlotEnd = toDutySlotEnd > maxShiftEndDate ? maxShiftEndDate : toDutySlotEnd;

            var toDutySlot = toDuty.DutySlots.FirstOrDefault(ds =>
                                                             toDutySlotStart < ds.EndDate && ds.StartDate < toDutySlotEnd && ds.SheriffId == null);

            if (toDutySlot == null)
            {
                toDutySlot = new DutySlot
                {
                    LocationId = toDuty.LocationId,
                    DutyId     = toDuty.Id,
                    Timezone   = toDuty.Timezone,
                    StartDate  = toDutySlotStart,
                    EndDate    = toDutySlotEnd,
                    SheriffId  = fromSheriffId
                };
                await Db.DutySlot.AddAsync(toDutySlot);
            }
            else
            {
                toDutySlot.StartDate = toDutySlotStart;
                toDutySlot.EndDate   = toDutySlotEnd;
                toDutySlot.SheriffId = fromSheriffId;
            }

            await Db.SaveChangesAsync();

            return(toDuty);
        }