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) ); }
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); }