public async Task CanGetMissingTimeEntries() { var now = new DateTime(2001, 1, 1); int userId = 1; int patrolId = 1; var scheduledShift = new ScheduledShift() { StartsAt = now - new TimeSpan(1, 0, 0), EndsAt = now + new TimeSpan(1, 0, 0), DurationSeconds = (int)(new TimeSpan(2, 0, 0)).TotalSeconds, PatrolId = patrolId, }; await _shiftRepository.InsertScheduledShift(scheduledShift); var scheduledShiftAssignment = new ScheduledShiftAssignment() { AssignedUserId = userId, ScheduledShiftId = scheduledShift.Id, Status = ShiftStatus.Assigned }; await _shiftRepository.InsertScheduledShiftAssignment(scheduledShiftAssignment); var entries = await _timeEntryRepository.GetMissingTimeEntries(patrolId, now); Assert.AreEqual(1, entries.Count()); }
public void CanAddShift() { var newShift = new ScheduledShift { StartHour = 12, StartMinute = 30, EndHour = 4, EndMinute = 30, RepeatsOnSaturday = true }; var response = Post("/scheduled-shifts", newShift); var allShifts = Query(db => db.Connection.GetList<ScheduledShift>().ToList()); Assert.Equal(3, allShifts.Count()); var newestShift = allShifts.Where(s => s.StartHour == 12 && s.StartMinute == 30 && s.EndHour == 4 && s.EndMinute == 30 && s.RepeatsOnSaturday).First(); Assert.Equal(response.Headers["Location"], String.Format("//scheduled-shifts/{0}", newestShift.Id)); }
public async Task RemoveWorkItemsFromShiftOccurence(ScheduledShift shift) { var now = _clock.UtcNow.UtcDateTime; var workItems = await _workItemRepository.GetWorkItems(now, 0, scheduledShiftId : shift.Id); foreach (var wi in workItems) { foreach (var asn in wi.Assignments) { await _workItemRepository.DeleteWorkItemAssignment(asn); } await _workItemRepository.DeleteWorkItem(wi); } }
public async Task SendShiftAdded(List <User> assigned, Models.Patrol patrol, ScheduledShift shift) { var localStart = shift.StartsAt.UtcToPatrolLocal(patrol); var localEnd = shift.EndsAt.UtcToPatrolLocal(patrol); var msg = new SendGridMessage() { Subject = $"Shift created on {localStart.ToShortDateString()}", PlainTextContent = $"You have a new shift on {localStart.ToShortDateString()}, {localStart.ToShortTimeString()} - {localEnd.ToShortTimeString()}.", HtmlContent = $"You have a new shift on {localStart.ToShortDateString()}, {localStart.ToShortTimeString()} - {localEnd.ToShortTimeString()}.", }; this.AddOnBehalfOf(msg, patrol); var response = await Send(msg, patrol, assigned.ToArray()); }
public async Task SendShiftRemoved(User assigned, Models.Patrol patrol, ScheduledShift shift) { var localStart = shift.StartsAt.UtcToPatrolLocal(patrol); var localEnd = shift.EndsAt.UtcToPatrolLocal(patrol); var msg = new SendGridMessage() { Subject = $"Shift on {localStart.ToShortDateString()} removed.", PlainTextContent = $"Your shift on {localStart.ToShortDateString()} has been removed.", HtmlContent = $"Your shift on {localStart.ToShortDateString()} has been removed.", }; this.AddOnBehalfOf(msg, patrol); var response = await Send(msg, patrol, assigned); }
public async Task AddWorkItemsToNewShiftOccurence(ScheduledShift shift) { var now = _clock.UtcNow.UtcDateTime; var scheduledShifts = await _shiftRepository.GetScheduledShiftAssignments(shift.PatrolId, scheduledShiftId : shift.Id); var recurringWorkItems = await _workItemRepository.GetRecurringWorkItemsForShifts(new List <int>(shift.Id)); var workItems = await _workItemRepository.GetWorkItemsForShifts(new List <int>(shift.Id)); var workItemAssignments = await _workItemRepository.GetWorkItemAssignmentsForShifts(new List <int>() { shift.Id }); var shiftRecurringWorkItems = await _workItemRepository.GetShiftRecurringWorkItemsForShifts(new List <int>(shift.Id)); var patrol = await _patrolRepository.GetPatrol(shift.PatrolId); var shiftAssignments = await _shiftRepository.GetScheduledShiftAssignments(shift.PatrolId, scheduledShiftId : shift.Id); var shiftStartLocal = shift.StartsAt.UtcToPatrolLocal(patrol); var missings = recurringWorkItems.Where(x => !workItems.Any(y => y.RecurringWorkItemId == x.Id)).ToList(); foreach (var missing in missings) { var shiftRecurrences = shiftRecurringWorkItems.Where(x => x.RecurringWorkItemId == missing.Id); foreach (var recurrence in shiftRecurrences) { var workItem = new WorkItem() { AdminGroupId = missing.AdminGroupId, CreatedAt = now, CreatedByUserId = missing.CreatedByUserId, CompletionMode = missing.CompletionMode, DescriptionMarkup = missing.DescriptionMarkup, Location = missing.Location, Name = missing.Name, PatrolId = missing.PatrolId, RecurringWorkItemId = missing.Id, ScheduledShiftId = shift.Id, ScheduledAt = new DateTime(shiftStartLocal.Year, shiftStartLocal.Month, shiftStartLocal.Day, recurrence.ScheduledAtHour, recurrence.ScheduledAtMinute, 0).UtcFromPatrolLocal(patrol) }; await _workItemRepository.InsertWorkItem(workItem); } } await RecalculateSingleShiftWorkItemAssignments(shift.Id, shiftAssignments.ToList(), workItems.ToList(), workItemAssignments.ToList(), recurringWorkItems.ToList(), shiftRecurringWorkItems.ToList()); }
public async Task CanUpdateTimeEntryScheduledShiftAssignment() { var now = new DateTime(2001, 1, 1); int userId = 1; int patrolId = 1; var entry = new TimeEntry() { ClockIn = now, PatrolId = patrolId, UserId = userId }; await _timeEntryRepository.InsertTimeEntry(entry); var scheduledShift = new ScheduledShift() { StartsAt = now, EndsAt = now + new TimeSpan(1, 0, 0), DurationSeconds = (int)(new TimeSpan(1, 0, 0)).TotalSeconds, PatrolId = patrolId, }; await _shiftRepository.InsertScheduledShift(scheduledShift); var scheduledShiftAssignment = new ScheduledShiftAssignment() { AssignedUserId = userId, ScheduledShiftId = scheduledShift.Id, Status = ShiftStatus.Assigned }; await _shiftRepository.InsertScheduledShiftAssignment(scheduledShiftAssignment); var timeEntryScheduledShiftAssignment = new TimeEntryScheduledShiftAssignment() { ScheduledShiftAssignmentId = scheduledShiftAssignment.Id, TimeEntryId = entry.Id }; await _timeEntryRepository.InsertTimeEntryScheduledShiftAssignment(timeEntryScheduledShiftAssignment); timeEntryScheduledShiftAssignment.DurationSeconds = 1000; await _timeEntryRepository.UpdateTimeEntryScheduledShiftAssignment(timeEntryScheduledShiftAssignment); var after = await _timeEntryRepository.GetTimeEntryScheduledShiftAssignment(timeEntryScheduledShiftAssignment.Id); Assert.AreEqual(1000, after.DurationSeconds); }
public async Task CanDeleteScheduledShift() { var before = new ScheduledShift() { StartsAt = new DateTime(2001, 1, 1, 8, 0, 0), EndsAt = new DateTime(2001, 1, 1, 14, 0, 0), GroupId = null, PatrolId = 1, ShiftId = null }; await _shiftRepository.InsertScheduledShift(before); await _shiftRepository.DeleteScheduledShift(before); var after = await _shiftRepository.GetScheduledShift(before.Id); Assert.IsNull(after); }
public async Task CanInsertScheduledShift() { var before = new ScheduledShift() { StartsAt = new DateTime(2001, 1, 1, 8, 0, 0), EndsAt = new DateTime(2001, 1, 1, 14, 0, 0), GroupId = null, PatrolId = 1, ShiftId = null }; await _shiftRepository.InsertScheduledShift(before); var after = await _shiftRepository.GetScheduledShift(before.Id); Assert.AreEqual(before.StartsAt, after.StartsAt); Assert.AreEqual(before.EndsAt, after.EndsAt); Assert.AreEqual(before.GroupId, after.GroupId); Assert.AreEqual(before.PatrolId, after.PatrolId); Assert.AreEqual(before.ShiftId, after.ShiftId); }
public async Task SendTrainerShiftRemoved(User trainerUser, List <User> traineeUsers, Models.Patrol patrol, ScheduledShift shift) { var localStart = shift.StartsAt.UtcToPatrolLocal(patrol); var localEnd = shift.EndsAt.UtcToPatrolLocal(patrol); var msg = new SendGridMessage() { Subject = $"Trainer Shift Removed {localStart.ToShortDateString()}", PlainTextContent = $"{trainerUser.GetFullName()}'s shift in which you wanted to train with them on {localStart.ToShortDateString()} {localStart.ToShortTimeString()} - {localEnd.ToShortTimeString()} has been removed.\n Please reschedule your training.", HtmlContent = $"{trainerUser.GetFullName()}'s shift in which you wanted to train with them on {localStart.ToShortDateString()} {localStart.ToShortTimeString()} - {localEnd.ToShortTimeString()} has been removed.<br/> Please reschedule your training.", }; this.AddOnBehalfOf(msg, patrol); var response = await Send(msg, patrol, traineeUsers.ToArray()); }
public async Task <ScheduledShift> ScheduleShift(ScheduledShiftUpdateDto dto) { var patrol = await _patrolRepository.GetPatrol(dto.PatrolId); Group group = null; IList <User> groupMembers = null; if (dto.GroupId.HasValue) { group = await _groupRepository.GetGroup(dto.GroupId.Value); groupMembers = (await _groupRepository.GetUsersInGroup(dto.GroupId.Value)).ToList(); } Shift shift = null; if (dto.StartsAt.HasValue && dto.EndsAt.HasValue && !dto.ShiftId.HasValue) { var localizedStart = dto.StartsAt.Value.UtcToPatrolLocal(patrol); var localizedEnd = dto.EndsAt.Value.UtcToPatrolLocal(patrol); var shifts = await _shiftRepository.GetShifts(dto.PatrolId, localizedStart.Hour, localizedStart.Minute, localizedEnd.Hour, localizedEnd.Minute); if (shifts.Any()) { shift = shifts.First(); dto.ShiftId = shift.Id; } } else if (dto.ShiftId.HasValue && dto.Day.HasValue) { shift = await _shiftRepository.GetShift(dto.ShiftId.Value); if (!dto.StartsAt.HasValue) { var start = new DateTime(dto.Day.Value.Year, dto.Day.Value.Month, dto.Day.Value.Day, shift.StartHour, shift.StartMinute, 0, 0, DateTimeKind.Unspecified); dto.StartsAt = start.UtcFromPatrolLocal(patrol); } if (!dto.EndsAt.HasValue) { var end = new DateTime(dto.Day.Value.Year, dto.Day.Value.Month, dto.Day.Value.Day, shift.EndHour, shift.EndMinute, 0, 0, DateTimeKind.Unspecified); dto.EndsAt = end.UtcFromPatrolLocal(patrol); } } else { throw new InvalidOperationException("Scheduled shift must provide either start+end or day+shift"); } //by this point we should have filled in the dto as best we can and can now persist ScheduledShift scheduledShift = null; List <ScheduledShiftAssignment> assignments = null; List <ScheduledShiftAssignment> newAssignments = new List <ScheduledShiftAssignment>(); if (dto.Id != default(int)) { scheduledShift = await _shiftRepository.GetScheduledShift(dto.Id); scheduledShift.StartsAt = dto.StartsAt.Value; scheduledShift.EndsAt = dto.EndsAt.Value; scheduledShift.GroupId = group != null ? (int?)group.Id : null; scheduledShift.ShiftId = shift != null ? (int?)shift.Id : null; scheduledShift.DurationSeconds = (int)(scheduledShift.EndsAt - scheduledShift.StartsAt).TotalSeconds; await _shiftRepository.UpdateScheduledShift(scheduledShift); assignments = (await _shiftRepository.GetScheduledShiftAssignmentsForScheduledShift(dto.Id)).ToList(); } else { //make sure there's not an existing shift with the same start/end var existing = await _shiftRepository.GetScheduledShifts(dto.PatrolId, dto.StartsAt.Value, dto.EndsAt.Value); if (existing.Any()) { scheduledShift = existing.First(); dto.Id = scheduledShift.Id; bool updated = false; if (!scheduledShift.GroupId.HasValue && group != null) { scheduledShift.GroupId = group.Id; updated = true; } if (!scheduledShift.ShiftId.HasValue && shift != null) { scheduledShift.ShiftId = shift.Id; updated = true; } if (updated) { scheduledShift.DurationSeconds = (int)(scheduledShift.EndsAt - scheduledShift.StartsAt).TotalSeconds; await _shiftRepository.UpdateScheduledShift(scheduledShift); } assignments = (await _shiftRepository.GetScheduledShiftAssignmentsForScheduledShift(dto.Id)).ToList(); } else { scheduledShift = new ScheduledShift() { PatrolId = dto.PatrolId, GroupId = group != null ? (int?)group.Id : null, ShiftId = shift != null ? (int?)shift.Id : null, StartsAt = dto.StartsAt.Value, EndsAt = dto.EndsAt.Value }; scheduledShift.DurationSeconds = (int)(scheduledShift.EndsAt - scheduledShift.StartsAt).TotalSeconds; await _shiftRepository.InsertScheduledShift(scheduledShift); assignments = new List <ScheduledShiftAssignment>(); //populate with work items if necassary await _shiftWorkItemService.AddWorkItemsToNewShiftOccurence(scheduledShift); } } if (group != null) { foreach (var groupMember in groupMembers) { var existing = assignments.FirstOrDefault(x => x.OriginalAssignedUserId == groupMember.Id || x.AssignedUserId == groupMember.Id || x.ClaimedByUserId == groupMember.Id); if (existing == null) { var ssa = new ScheduledShiftAssignment() { Status = ShiftStatus.Assigned, AssignedUserId = groupMember.Id, OriginalAssignedUserId = groupMember.Id, ScheduledShiftId = scheduledShift.Id }; assignments.Add(ssa); await _shiftRepository.InsertScheduledShiftAssignment(ssa); newAssignments.Add(ssa); } else if (existing.Status == ShiftStatus.Claimed && existing.ClaimedByUserId == groupMember.Id) { existing.AssignedUserId = existing.ClaimedByUserId.Value; existing.Status = ShiftStatus.Assigned; await _shiftRepository.UpdateScheduledShiftAssignment(existing); } } } if (dto.AssignUserIds != null) { foreach (var id in dto.AssignUserIds) { var existing = assignments.FirstOrDefault(x => x.OriginalAssignedUserId == id || x.AssignedUserId == id || x.ClaimedByUserId == id); if (existing == null || !id.HasValue) { var ssa = new ScheduledShiftAssignment() { Status = id.HasValue ? ShiftStatus.Assigned : ShiftStatus.Released, AssignedUserId = id, OriginalAssignedUserId = id, ScheduledShiftId = scheduledShift.Id }; assignments.Add(ssa); await _shiftRepository.InsertScheduledShiftAssignment(ssa); newAssignments.Add(ssa); } else if (existing.Status == ShiftStatus.Claimed && existing.ClaimedByUserId == id) { existing.AssignedUserId = existing.ClaimedByUserId.Value; existing.Status = ShiftStatus.Assigned; await _shiftRepository.UpdateScheduledShiftAssignment(existing); } } } var assigneeUserIds = newAssignments.Where(x => x.AssignedUserId.HasValue).Select(x => x.AssignedUserId.Value).Distinct().ToList(); if (assigneeUserIds.Count > 0) { var newAssignees = await _userRepository.GetUsers(assigneeUserIds); await _emailService.SendShiftAdded(newAssignees.ToList(), patrol, scheduledShift); } //todo: delete things that aren't there? seems risky if we found the shift by time not id etc return(await _shiftRepository.GetScheduledShift(scheduledShift.Id)); }
public Task DeleteScheduledShift(ScheduledShift shift) { return(_connection.DeleteAsync(shift)); }
public async Task InsertScheduledShift(ScheduledShift shift) { var id = (int)await _connection.InsertAsync(shift); shift.Id = id; }
public Task UpdateScheduledShift(ScheduledShift shift) { return(_connection.UpdateAsync(shift)); }
public async Task SendTraineeCancel(User trainerUser, User traineeUser, Models.Patrol patrol, ScheduledShift shift) { var localStart = shift.StartsAt.UtcToPatrolLocal(patrol); var localEnd = shift.EndsAt.UtcToPatrolLocal(patrol); var msg = new SendGridMessage() { Subject = $"{traineeUser.GetFullName()} Cancelled for {localStart.ToShortDateString()}", PlainTextContent = $"{traineeUser.GetFullName()} has cancelled their request to train with you during your shift on {localStart.ToShortDateString()} {localStart.ToShortTimeString()} - {localEnd.ToShortTimeString()}", HtmlContent = $"{traineeUser.GetFullName()} has cancelled their request to train with you during your shift on {localStart.ToShortDateString()} {localStart.ToShortTimeString()} - {localEnd.ToShortTimeString()}", }; this.AddOnBehalfOf(msg, patrol); var response = await Send(msg, patrol, trainerUser); }
public async Task SendShiftRejected(User assigned, Models.Patrol patrol, User claimed, User rejected, ScheduledShift shift) { var localStart = shift.StartsAt.UtcToPatrolLocal(patrol); var localEnd = shift.EndsAt.UtcToPatrolLocal(patrol); if (assigned == null) { var msg = new SendGridMessage() { Subject = $"Shift claim on {localStart.ToShortDateString()} to {claimed.GetFullName()} Rejected", PlainTextContent = $"Your shift claim on {localStart.ToShortDateString()} to {claimed.GetFullName()} has been rejected", HtmlContent = $"Your shift Claim on {localStart.ToShortDateString()} to {claimed.GetFullName()} has been rejected", }; this.AddOnBehalfOf(msg, patrol); var response = await Send(msg, patrol, claimed); } else { var msg = new SendGridMessage() { Subject = $"Shift swap on {localStart.ToShortDateString()} from {assigned.GetFullName()} to {claimed.GetFullName()} Rejected", PlainTextContent = $"The shift swap on {localStart.ToShortDateString()} from {assigned.GetFullName()} to {claimed.GetFullName()} has been rejected", HtmlContent = $"The shift swap on {localStart.ToShortDateString()} from {assigned.GetFullName()} to {claimed.GetFullName()} has been rejected", }; this.AddOnBehalfOf(msg, patrol); var response = await Send(msg, patrol, assigned, claimed); } }
//scheduling public async Task SendShiftClaimed(User assigned, Models.Patrol patrol, User claimed, ScheduledShift shift) { var localStart = shift.StartsAt.UtcToPatrolLocal(patrol); var localEnd = shift.EndsAt.UtcToPatrolLocal(patrol); var msg = new SendGridMessage() { Subject = $"Shift on {localStart.ToShortDateString()} Claimed by {claimed.GetFullName()}", PlainTextContent = $"The shift you released on {localStart.ToShortDateString()} has been claimed by {claimed.GetFullName()}.\nIt is now pending approval.", HtmlContent = $"The shift you released on {localStart.ToShortDateString()} has been claimed by {claimed.GetFullName()}.<br/>It is now pending approval.", }; this.AddOnBehalfOf(msg, patrol); var response = await Send(msg, patrol, assigned); }