/// <summary> /// Enforces the strict location threshold by removing attendances that would have ended up going into full location+schedules. /// Note: The is also checked earlier in the check-in process, so this catches ones that might have just gotten full in the last few seconds. /// </summary> /// <param name="action">The action.</param> /// <param name="checkInState">State of the check in.</param> /// <param name="attendanceService">The attendance service.</param> /// <param name="currentOccurrences">The current occurrences.</param> /// <param name="person">The person.</param> /// <param name="group">The group.</param> /// <param name="location">The location.</param> /// <param name="schedule">The schedule.</param> /// <param name="startDateTime">The start date time.</param> private void EnforceStrictLocationThreshold(WorkflowAction action, CheckInState checkInState, AttendanceService attendanceService, List <OccurrenceRecord> currentOccurrences, CheckInPerson person, CheckInGroup group, CheckInLocation location, CheckInSchedule schedule, DateTime startDateTime) { var thresHold = location.Location.SoftRoomThreshold.Value; if (checkInState.ManagerLoggedIn && location.Location.FirmRoomThreshold.HasValue && location.Location.FirmRoomThreshold.Value > location.Location.SoftRoomThreshold.Value) { thresHold = location.Location.FirmRoomThreshold.Value; } var currentOccurrence = GetCurrentOccurrence(currentOccurrences, location, schedule, startDateTime.Date); // The totalAttended is the number of people still checked in (not people who have been checked-out) // not counting the current person who may already be checked in, // + the number of people we have checked in so far (but haven't been saved yet). var attendanceQry = attendanceService.GetByDateOnLocationAndSchedule(startDateTime.Date, location.Location.Id, schedule.Schedule.Id) .AsNoTracking() .Where(a => a.EndDateTime == null); // Only process if the current person is NOT already checked-in to this location and schedule if (!attendanceQry.Where(a => a.PersonAlias.PersonId == person.Person.Id).Any()) { var totalAttended = attendanceQry.Count() + (currentOccurrence == null ? 0 : currentOccurrence.Count); // If over capacity, remove the schedule and add a warning message. if (totalAttended >= thresHold) { // Remove the schedule since the location was full for this schedule. location.Schedules.Remove(schedule); var message = new CheckInMessage() { MessageType = MessageType.Warning }; var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null); mergeFields.Add(MergeFieldKey.Person, person.Person); mergeFields.Add(MergeFieldKey.Group, group.Group); mergeFields.Add(MergeFieldKey.Location, location.Location); mergeFields.Add(MergeFieldKey.Schedule, schedule.Schedule); message.MessageText = GetAttributeValue(action, AttributeKey.NotCheckedInMessageFormat).ResolveMergeFields(mergeFields); // Now add it to the check-in state message list for others to see. checkInState.Messages.Add(message); return; } else { // Keep track of anyone who was checked in so far. if (currentOccurrence == null) { currentOccurrence = new OccurrenceRecord() { Date = startDateTime.Date, LocationId = location.Location.Id, ScheduleId = schedule.Schedule.Id }; currentOccurrences.Add(currentOccurrence); } currentOccurrence.Count += 1; } } }
/// <summary> /// Executes the specified workflow. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="action">The workflow action.</param> /// <param name="entity">The entity.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> /// <exception cref="System.NotImplementedException"></exception> public override bool Execute(RockContext rockContext, Model.WorkflowAction action, Object entity, out List <string> errorMessages) { var checkInState = GetCheckInState(entity, out errorMessages); if (checkInState != null) { AttendanceCode attendanceCode = null; bool reuseCodeForFamily = checkInState.CheckInType != null && checkInState.CheckInType.ReuseSameCode; int securityCodeAlphaNumericLength = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeAlphaNumericLength : 3; int securityCodeAlphaLength = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeAlphaLength : 0; int securityCodeNumericLength = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeNumericLength : 0; bool securityCodeNumericRandom = checkInState.CheckInType != null ? checkInState.CheckInType.SecurityCodeNumericRandom : true; var attendanceCodeService = new AttendanceCodeService(rockContext); var attendanceService = new AttendanceService(rockContext); var groupMemberService = new GroupMemberService(rockContext); var personAliasService = new PersonAliasService(rockContext); var attendanceRecords = new List <Attendance>(); checkInState.Messages.Clear(); var family = checkInState.CheckIn.CurrentFamily; if (family != null) { var currentOccurrences = new List <OccurrenceRecord>(); foreach (var person in family.GetPeople(true)) { if (reuseCodeForFamily && attendanceCode != null) { person.SecurityCode = attendanceCode.Code; } else { attendanceCode = AttendanceCodeService.GetNew(securityCodeAlphaNumericLength, securityCodeAlphaLength, securityCodeNumericLength, securityCodeNumericRandom); person.SecurityCode = attendanceCode.Code; } foreach (var groupType in person.GetGroupTypes(true)) { foreach (var group in groupType.GetGroups(true)) { if (groupType.GroupType.AttendanceRule == AttendanceRule.AddOnCheckIn && groupType.GroupType.DefaultGroupRoleId.HasValue && !groupMemberService.GetByGroupIdAndPersonId(group.Group.Id, person.Person.Id, true).Any()) { var groupMember = new GroupMember(); groupMember.GroupId = group.Group.Id; groupMember.PersonId = person.Person.Id; groupMember.GroupRoleId = groupType.GroupType.DefaultGroupRoleId.Value; groupMemberService.Add(groupMember); } foreach (var location in group.GetLocations(true)) { bool isCheckedIntoLocation = false; foreach (var schedule in location.GetSchedules(true)) { var startDateTime = schedule.CampusCurrentDateTime; // If we're enforcing strict location thresholds, then before we create an attendance record // we need to check the location-schedule's current count. if (GetAttributeValue(action, "EnforceStrictLocationThreshold").AsBoolean() && location.Location.SoftRoomThreshold.HasValue) { var thresHold = location.Location.SoftRoomThreshold.Value; if (checkInState.ManagerLoggedIn && location.Location.FirmRoomThreshold.HasValue && location.Location.FirmRoomThreshold.Value > location.Location.SoftRoomThreshold.Value) { thresHold = location.Location.FirmRoomThreshold.Value; } var currentOccurrence = GetCurrentOccurrence(currentOccurrences, location, schedule, startDateTime.Date); // The totalAttended is the number of people still checked in (not people who have been checked-out) // not counting the current person who may already be checked in, // + the number of people we have checked in so far (but haven't been saved yet). var attendanceQry = attendanceService.GetByDateOnLocationAndSchedule(startDateTime.Date, location.Location.Id, schedule.Schedule.Id) .AsNoTracking() .Where(a => a.EndDateTime == null); // Only process if the current person is NOT already checked-in to this location and schedule if (!attendanceQry.Where(a => a.PersonAlias.PersonId == person.Person.Id).Any()) { var totalAttended = attendanceQry.Count() + (currentOccurrence == null ? 0 : currentOccurrence.Count); // If over capacity, remove the schedule and add a warning message. if (totalAttended >= thresHold) { // Remove the schedule since the location was full for this schedule. location.Schedules.Remove(schedule); var message = new CheckInMessage() { MessageType = MessageType.Warning }; var mergeFields = Lava.LavaHelper.GetCommonMergeFields(null); mergeFields.Add("Person", person.Person); mergeFields.Add("Group", group.Group); mergeFields.Add("Location", location.Location); mergeFields.Add("Schedule", schedule.Schedule); message.MessageText = GetAttributeValue(action, "NotChecked-InMessageFormat").ResolveMergeFields(mergeFields); // Now add it to the check-in state message list for others to see. checkInState.Messages.Add(message); continue; } else { // Keep track of anyone who was checked in so far. if (currentOccurrence == null) { currentOccurrence = new OccurrenceRecord() { Date = startDateTime.Date, LocationId = location.Location.Id, ScheduleId = schedule.Schedule.Id }; currentOccurrences.Add(currentOccurrence); } currentOccurrence.Count += 1; } } } // Only create one attendance record per day for each person/schedule/group/location var attendance = attendanceService.Get(startDateTime, location.Location.Id, schedule.Schedule.Id, group.Group.Id, person.Person.Id); if (attendance == null) { var primaryAlias = personAliasService.GetPrimaryAlias(person.Person.Id); if (primaryAlias != null) { attendance = attendanceService.AddOrUpdate(primaryAlias.Id, startDateTime.Date, group.Group.Id, location.Location.Id, schedule.Schedule.Id, location.CampusId, checkInState.Kiosk.Device.Id, checkInState.CheckIn.SearchType.Id, checkInState.CheckIn.SearchValue, family.Group.Id, attendanceCode.Id); attendance.PersonAlias = primaryAlias; } } attendance.DeviceId = checkInState.Kiosk.Device.Id; attendance.SearchTypeValueId = checkInState.CheckIn.SearchType.Id; attendance.SearchValue = checkInState.CheckIn.SearchValue; attendance.CheckedInByPersonAliasId = checkInState.CheckIn.CheckedInByPersonAliasId; attendance.SearchResultGroupId = family.Group.Id; attendance.AttendanceCodeId = attendanceCode.Id; attendance.StartDateTime = startDateTime; attendance.EndDateTime = null; attendance.DidAttend = true; attendance.Note = group.Notes; KioskLocationAttendance.AddAttendance(attendance); isCheckedIntoLocation = true; // Keep track of attendance (Ids) for use by other actions later in the workflow pipeline attendanceRecords.Add(attendance); } // If the person was NOT checked into the location for any schedule then remove the location if (!isCheckedIntoLocation) { group.Locations.Remove(location); } } } } } } rockContext.SaveChanges(); // Now that the records are persisted, take the Ids and save them to the temp CheckInFamliy object family.AttendanceIds = attendanceRecords.Select(a => a.Id).ToList(); attendanceRecords = null; return(true); } return(false); }