/// <summary>
 /// Copies the properties from another AttendanceOccurrence object to this AttendanceOccurrence object
 /// </summary>
 /// <param name="target">The target.</param>
 /// <param name="source">The source.</param>
 public static void CopyPropertiesFrom(this AttendanceOccurrence target, AttendanceOccurrence source)
 {
     target.Id = source.Id;
     target.AcceptConfirmationMessage  = source.AcceptConfirmationMessage;
     target.AnonymousAttendanceCount   = source.AnonymousAttendanceCount;
     target.DeclineConfirmationMessage = source.DeclineConfirmationMessage;
     target.DeclineReasonValueIds      = source.DeclineReasonValueIds;
     target.DidNotOccur             = source.DidNotOccur;
     target.ForeignGuid             = source.ForeignGuid;
     target.ForeignKey              = source.ForeignKey;
     target.GroupId                 = source.GroupId;
     target.LocationId              = source.LocationId;
     target.Name                    = source.Name;
     target.Notes                   = source.Notes;
     target.OccurrenceDate          = source.OccurrenceDate;
     target.OccurrenceSourceDate    = source.OccurrenceSourceDate;
     target.ScheduleId              = source.ScheduleId;
     target.ShowDeclineReasons      = source.ShowDeclineReasons;
     target.StepTypeId              = source.StepTypeId;
     target.SundayDate              = source.SundayDate;
     target.CreatedDateTime         = source.CreatedDateTime;
     target.ModifiedDateTime        = source.ModifiedDateTime;
     target.CreatedByPersonAliasId  = source.CreatedByPersonAliasId;
     target.ModifiedByPersonAliasId = source.ModifiedByPersonAliasId;
     target.Guid                    = source.Guid;
     target.ForeignId               = source.ForeignId;
 }
        /// <summary>
        /// Gets the specified occurrence record, creating it if necessary. Ensures that an AttendanceOccurrence
        /// record exists for the specified date, schedule, locationId and group. If it doesn't exist, it is
        /// created and saved to the database.
        /// NOTE: When looking for a matching occurrence, if null groupId, locationId or scheduleId is given
        /// any matching record must also not have a group, location or schedule.
        /// </summary>
        /// <param name="occurrenceDate">The occurrence date.</param>
        /// <param name="groupId">The group identifier.</param>
        /// <param name="locationId">The location identifier.</param>
        /// <param name="scheduleId">The schedule identifier.</param>
        /// <param name="includes">Allows including attendance occurrence virtual properties like Attendees.</param>
        /// <param name="attendanceTypeValueId">The attendance type identifier.</param>
        /// <returns>
        /// An existing or new attendance occurrence
        /// </returns>
        public AttendanceOccurrence GetOrAdd(DateTime occurrenceDate, int?groupId, int?locationId, int?scheduleId, string includes, int?attendanceTypeValueId)
        {
            var occurrence = Get(occurrenceDate, groupId, locationId, scheduleId, includes);

            if (occurrence == null)
            {
                // If occurrence does not yet exist, create it
                // A new context is used so the occurrence can be saved and used on multiple new attendance records that will be saved at once.
                using (var newContext = new RockContext())
                {
                    occurrence = new AttendanceOccurrence
                    {
                        OccurrenceDate        = occurrenceDate,
                        GroupId               = groupId,
                        LocationId            = locationId,
                        ScheduleId            = scheduleId,
                        AttendanceTypeValueId = attendanceTypeValueId
                    };

                    var newOccurrenceService = new AttendanceOccurrenceService(newContext);
                    newOccurrenceService.Add(occurrence);
                    newContext.SaveChanges();

                    // Query for the new occurrence using original context.
                    occurrence = Get(occurrence.Id);
                }
            }

            return(occurrence);
        }
 /// <summary>
 /// Clones this AttendanceOccurrence object to a new AttendanceOccurrence object
 /// </summary>
 /// <param name="source">The source.</param>
 /// <param name="deepCopy">if set to <c>true</c> a deep copy is made. If false, only the basic entity properties are copied.</param>
 /// <returns></returns>
 public static AttendanceOccurrence Clone(this AttendanceOccurrence source, bool deepCopy)
 {
     if (deepCopy)
     {
         return(source.Clone() as AttendanceOccurrence);
     }
     else
     {
         var target = new AttendanceOccurrence();
         target.CopyPropertiesFrom(source);
         return(target);
     }
 }
        public AttendanceOccurrence GetOrCreateAttendanceOccurrence(DateTime occurrenceDate, int scheduleId, int?locationId, int groupId)
        {
            // There is a unique constraint on OccurrenceDate, ScheduleId, LocationId and GroupId. So there is at most one record.
            var attendanceOccurrenceQuery = this.Queryable().Where(a =>
                                                                   a.OccurrenceDate == occurrenceDate.Date &&
                                                                   a.ScheduleId.HasValue && a.ScheduleId == scheduleId &&
                                                                   a.GroupId.HasValue && a.GroupId == groupId);

            if (locationId.HasValue)
            {
                attendanceOccurrenceQuery = attendanceOccurrenceQuery.Where(a => a.LocationId.HasValue && a.LocationId.Value == locationId.Value);
            }
            else
            {
                attendanceOccurrenceQuery = attendanceOccurrenceQuery.Where(a => a.LocationId.HasValue == false);
            }

            var attendanceOccurrence = attendanceOccurrenceQuery.FirstOrDefault();

            if (attendanceOccurrence != null)
            {
                return(attendanceOccurrence);
            }
            else
            {
                // if the attendance occurrence is not found, create and save it using a separate context, then get it with this context using the created attendanceOccurrence.Id
                int attendanceOccurrenceId;
                using (var rockContext = new RockContext())
                {
                    var attendanceOccurrenceService = new AttendanceOccurrenceService(rockContext);

                    if (attendanceOccurrence == null)
                    {
                        attendanceOccurrence = new AttendanceOccurrence
                        {
                            GroupId        = groupId,
                            LocationId     = locationId,
                            ScheduleId     = scheduleId,
                            OccurrenceDate = occurrenceDate
                        };

                        attendanceOccurrenceService.Add(attendanceOccurrence);
                        rockContext.SaveChanges();
                    }

                    attendanceOccurrenceId = attendanceOccurrence.Id;
                }

                return(this.Get(attendanceOccurrence.Id));
            }
        }
 /// <summary>
 /// Copies the properties from another AttendanceOccurrence object to this AttendanceOccurrence object
 /// </summary>
 /// <param name="target">The target.</param>
 /// <param name="source">The source.</param>
 public static void CopyPropertiesFrom(this AttendanceOccurrence target, AttendanceOccurrence source)
 {
     target.Id = source.Id;
     target.AnonymousAttendanceCount = source.AnonymousAttendanceCount;
     target.DidNotOccur             = source.DidNotOccur;
     target.ForeignGuid             = source.ForeignGuid;
     target.ForeignKey              = source.ForeignKey;
     target.GroupId                 = source.GroupId;
     target.LocationId              = source.LocationId;
     target.Notes                   = source.Notes;
     target.OccurrenceDate          = source.OccurrenceDate;
     target.ScheduleId              = source.ScheduleId;
     target.CreatedDateTime         = source.CreatedDateTime;
     target.ModifiedDateTime        = source.ModifiedDateTime;
     target.CreatedByPersonAliasId  = source.CreatedByPersonAliasId;
     target.ModifiedByPersonAliasId = source.ModifiedByPersonAliasId;
     target.Guid      = source.Guid;
     target.ForeignId = source.ForeignId;
 }
Example #6
0
        private void ProcessObsoleteOccurrenceFields(DbEntityEntry entry)
        {
            if (entry.State == EntityState.Modified || entry.State == EntityState.Added)
            {
                // NOTE: If they only changed StartDateTime, don't change the Occurrence record. We want to support letting StartDateTime be a different Date than the OccurrenceDate in that situation
                if (_updatedObsoleteGroupId || _updatedObsoleteLocationId || _updatedObsoleteScheduleId || _updatedObsoleteDidNotOccur)
                {
                    if (_updatedObsoleteGroupId || _updatedObsoleteLocationId || _updatedObsoleteScheduleId)
                    {
                        // if they changed or set stuff related to AttendanceOccurrence (not including DidNotOccur or StartDateTime) thru obsolete properties, find or create a Matching AttendanceOccurrence Record
                        using (var attendanceOccurrenceRockContext = new RockContext())
                        {
                            var attendanceOccurrenceService = new AttendanceOccurrenceService(attendanceOccurrenceRockContext);

                            // if GroupId,LocationId, or ScheduleId changed, use StartDateTime's Date as the OccurrenceDate to look up AttendanceOccurrence since it is really a completely different Occurrence if Group,Location or Schedule changes
                            var occurrenceDate = this.StartDateTime.Date;

                            var attendanceOccurrence = attendanceOccurrenceService.Queryable().Where(a => a.GroupId == this.GroupId && a.LocationId == this.LocationId && a.ScheduleId == this.ScheduleId && a.OccurrenceDate == occurrenceDate).FirstOrDefault();
                            if (attendanceOccurrence != null)
                            {
                                // found a matching attendanceOccurrence, so use that
                                if (_updatedObsoleteDidNotOccur && attendanceOccurrence.DidNotOccur != this.DidNotOccur)
                                {
                                    // If DidNotOccur also changed, update the DidNotOccur for the attendanceOccurrence
                                    // NOTE: This will update *all* Attendances' DidNotOccur for this AttendanceOccurrence. That is OK. That is what we want to happen.
                                    attendanceOccurrence.DidNotOccur = this.DidNotOccur;
                                    attendanceOccurrenceRockContext.SaveChanges();
                                }

                                if (attendanceOccurrence.Id != this.OccurrenceId)
                                {
                                    this.OccurrenceId = attendanceOccurrence.Id;
                                }
                            }
                            else
                            {
                                // didn't find a matching attendanceOccurrence, so create and insert a new one
                                attendanceOccurrence = new AttendanceOccurrence
                                {
                                    GroupId        = this.GroupId,
                                    LocationId     = this.LocationId,
                                    ScheduleId     = this.ScheduleId,
                                    DidNotOccur    = this.DidNotOccur,
                                    OccurrenceDate = occurrenceDate
                                };

                                attendanceOccurrenceService.Add(attendanceOccurrence);
                                attendanceOccurrenceRockContext.SaveChanges();
                                this.OccurrenceId = attendanceOccurrence.Id;
                            }
                        }
                    }
                    else if (_updatedObsoleteDidNotOccur)
                    {
                        // if they only changed DidNotOccur, but not any of the other obsolete attendanceoccurrence properties, just change the DidNotOccur on the existing AttendanceOccurrence record
                        if (this.Occurrence != null)
                        {
                            this.Occurrence.DidNotOccur = _updatedObsoleteDidNotOccurValue;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Gets future occurrence data for the selected group (including all scheduled dates).
        /// </summary>
        /// <param name="group">The group.</param>
        /// <param name="toDateTime">To date time.  If not supplied, this will default to 6 months from the current date.</param>
        /// <param name="locationIds">The location ids.</param>
        /// <param name="scheduleIds">The schedule ids.</param>
        /// <returns></returns>
        public List <AttendanceOccurrence> GetFutureGroupOccurrences(Group group, DateTime?toDateTime, string locationIds = null, string scheduleIds = null)
        {
            var locationIdList = new List <int>();

            if (!string.IsNullOrWhiteSpace(locationIds))
            {
                locationIdList = locationIds.Split(',').Select(int.Parse).ToList();
            }

            var scheduleIdList = new List <int>();

            if (!string.IsNullOrWhiteSpace(scheduleIds))
            {
                scheduleIdList = scheduleIds.Split(',').Select(int.Parse).ToList();
            }

            var qry = Queryable("Group,Schedule").AsNoTracking().Where(a => a.GroupId == group.Id);

            // Filter by date range
            var fromDate = RockDateTime.Now.Date;
            var toDate   = fromDate.AddMonths(6); // Default to 6 months in the future.

            if (toDateTime.HasValue)
            {
                toDate = toDateTime.Value.Date;
            }
            qry = qry
                  .Where(a => a.OccurrenceDate >= (fromDate))
                  .Where(a => a.OccurrenceDate < (toDate));

            // Location Filter
            if (locationIdList.Any())
            {
                qry = qry.Where(a => locationIdList.Contains(a.LocationId ?? 0));
            }

            // Schedule Filter
            if (scheduleIdList.Any())
            {
                qry = qry.Where(a => scheduleIdList.Contains(a.ScheduleId ?? 0));
            }

            var occurrences = qry.ToList();

            // Create any missing occurrences from the group's schedule (not location schedules)
            Schedule groupSchedule = null;

            if (group.ScheduleId.HasValue)
            {
                groupSchedule = group.Schedule ?? new ScheduleService(( RockContext )Context).Get(group.ScheduleId.Value);
            }

            if (groupSchedule == null)
            {
                return(occurrences);
            }

            var newOccurrences = new List <AttendanceOccurrence>();

            var existingDates = occurrences
                                .Where(o => o.ScheduleId.Equals(groupSchedule.Id))
                                .Select(o => o.OccurrenceDate.Date)
                                .Distinct()
                                .ToList();

            if (!string.IsNullOrWhiteSpace(groupSchedule.iCalendarContent))
            {
                // If schedule has an iCal schedule, get all the past occurrences
                foreach (var occurrence in groupSchedule.GetICalOccurrences(fromDate, toDate))
                {
                    var newOccurrence = new AttendanceOccurrence
                    {
                        OccurrenceDate = occurrence.Period.StartTime.Date,
                        GroupId        = group.Id,
                        Group          = group,
                        ScheduleId     = groupSchedule.Id,
                        Schedule       = groupSchedule
                    };

                    if (existingDates.Contains(newOccurrence.OccurrenceDate.Date))
                    {
                        continue;
                    }

                    newOccurrences.Add(newOccurrence);
                    existingDates.Add(newOccurrence.OccurrenceDate.Date);
                }
            }
            else
            {
                // if schedule does not have an iCal, then check for weekly schedule and calculate occurrences starting with first attendance or current week
                if (groupSchedule.WeeklyDayOfWeek.HasValue)
                {
                    var startDate = fromDate;
                    // Move start time forward to the correct day of week.
                    while (startDate.DayOfWeek != groupSchedule.WeeklyDayOfWeek.Value)
                    {
                        startDate = startDate.AddDays(1);
                    }

                    // Add the start time
                    if (groupSchedule.WeeklyTimeOfDay.HasValue)
                    {
                        startDate = startDate.Add(groupSchedule.WeeklyTimeOfDay.Value);
                    }

                    // Create occurrences up to current time
                    while (startDate < toDate)
                    {
                        if (!existingDates.Contains(startDate.Date))
                        {
                            var newOccurrence = new AttendanceOccurrence
                            {
                                OccurrenceDate = startDate,
                                GroupId        = group.Id,
                                Group          = group,
                                ScheduleId     = groupSchedule.Id,
                                Schedule       = groupSchedule
                            };

                            newOccurrences.Add(newOccurrence);
                        }
                        startDate = startDate.AddDays(7);
                    }
                }
            }

            if (newOccurrences.Any())
            {
                // Filter Exclusions
                var groupType = GroupTypeCache.Get(group.GroupTypeId);
                foreach (var exclusion in groupType.GroupScheduleExclusions)
                {
                    if (!exclusion.Start.HasValue || !exclusion.End.HasValue)
                    {
                        continue;
                    }

                    foreach (var occurrence in newOccurrences.ToList())
                    {
                        if (occurrence.OccurrenceDate >= exclusion.Start.Value.Date &&
                            occurrence.OccurrenceDate < exclusion.End.Value.Date.AddDays(1))
                        {
                            newOccurrences.Remove(occurrence);
                        }
                    }
                }
            }

            foreach (var occurrence in newOccurrences)
            {
                occurrences.Add(occurrence);
            }

            occurrences = occurrences.OrderBy(o => o.OccurrenceDate).ToList();

            return(occurrences);
        }
        /// <summary>
        /// Gets occurrence data for the selected group.
        /// </summary>
        /// <param name="group">The group.</param>
        /// <param name="fromDateTime">From date time.</param>
        /// <param name="toDateTime">To date time.</param>
        /// <param name="locationIds">The location ids.</param>
        /// <param name="scheduleIds">The schedule ids.</param>
        /// <returns></returns>
        public List <AttendanceOccurrence> GetGroupOccurrences(Group group, DateTime?fromDateTime, DateTime?toDateTime, List <int> locationIds, List <int> scheduleIds)
        {
            var qry = Queryable().Where(a => a.GroupId == group.Id);

            // Filter by date range
            if (fromDateTime.HasValue)
            {
                var fromDate = fromDateTime.Value.Date;
                qry = qry.Where(a => a.OccurrenceDate >= (fromDate));
            }

            if (toDateTime.HasValue)
            {
                var toDate = toDateTime.Value.Date;
                qry = qry.Where(a => a.OccurrenceDate < (toDate));
            }

            // Location Filter
            if (locationIds.Any())
            {
                qry = qry.Where(a => locationIds.Contains(a.LocationId ?? 0));
            }

            // Schedule Filter
            if (scheduleIds.Any())
            {
                qry = qry.Where(a => scheduleIds.Contains(a.ScheduleId ?? 0));
            }

            var occurrences = qry.ToList();

            // Create any missing occurrences from the group's schedule (not location schedules)
            Schedule groupSchedule = null;

            if (group.ScheduleId.HasValue)
            {
                groupSchedule = group.Schedule ?? new ScheduleService(( RockContext )Context).Get(group.ScheduleId.Value);
            }

            if (groupSchedule == null)
            {
                return(occurrences);
            }

            var newOccurrences = new List <AttendanceOccurrence>();

            var existingDates = occurrences
                                .Where(o => o.ScheduleId.Equals(groupSchedule.Id))
                                .Select(o => o.OccurrenceDate.Date)
                                .Distinct()
                                .ToList();

            var startDate = fromDateTime ?? RockDateTime.Today.AddMonths(-2);
            var endDate   = toDateTime ?? RockDateTime.Today.AddDays(1);

            if (!string.IsNullOrWhiteSpace(groupSchedule.iCalendarContent))
            {
                // If schedule has an iCal schedule, get all the past occurrences
                foreach (var occurrence in groupSchedule.GetICalOccurrences(startDate, endDate))
                {
                    var newOccurrence = new AttendanceOccurrence
                    {
                        OccurrenceDate = occurrence.Period.StartTime.Date,
                        GroupId        = group.Id,
                        Group          = group,
                        ScheduleId     = groupSchedule.Id,
                        Schedule       = groupSchedule
                    };

                    if (existingDates.Contains(newOccurrence.OccurrenceDate.Date))
                    {
                        continue;
                    }

                    newOccurrences.Add(newOccurrence);
                    existingDates.Add(newOccurrence.OccurrenceDate.Date);
                }
            }
            else
            {
                // if schedule does not have an iCal, then check for weekly schedule and calculate occurrences starting with first attendance or current week
                if (groupSchedule.WeeklyDayOfWeek.HasValue)
                {
                    // default to start with date 2 months earlier
                    startDate = fromDateTime ?? RockDateTime.Today.AddMonths(-2);
                    if (existingDates.Any(d => d < startDate))
                    {
                        startDate = existingDates.Min();
                    }

                    // Roll forward to the first start time that matches the day of the week.
                    while (startDate.DayOfWeek != groupSchedule.WeeklyDayOfWeek.Value)
                    {
                        startDate = startDate.AddDays(1);
                    }

                    // Add the start time
                    if (groupSchedule.WeeklyTimeOfDay.HasValue)
                    {
                        startDate = startDate.Add(groupSchedule.WeeklyTimeOfDay.Value);
                    }

                    // Create occurrences up to current time
                    while (startDate < endDate)
                    {
                        if (!existingDates.Contains(startDate.Date))
                        {
                            var newOccurrence = new AttendanceOccurrence
                            {
                                OccurrenceDate = startDate,
                                GroupId        = group.Id,
                                Group          = group,
                                ScheduleId     = groupSchedule.Id,
                                Schedule       = groupSchedule
                            };

                            newOccurrences.Add(newOccurrence);
                        }
                        startDate = startDate.AddDays(7);
                    }
                }
            }

            if (newOccurrences.Any())
            {
                // Filter Exclusions
                var groupType = GroupTypeCache.Get(group.GroupTypeId);
                foreach (var exclusion in groupType.GroupScheduleExclusions)
                {
                    if (!exclusion.Start.HasValue || !exclusion.End.HasValue)
                    {
                        continue;
                    }

                    foreach (var occurrence in newOccurrences.ToList())
                    {
                        if (occurrence.OccurrenceDate >= exclusion.Start.Value.Date &&
                            occurrence.OccurrenceDate < exclusion.End.Value.Date.AddDays(1))
                        {
                            newOccurrences.Remove(occurrence);
                        }
                    }
                }
            }

            foreach (var occurrence in newOccurrences)
            {
                occurrences.Add(occurrence);
            }

            return(occurrences);
        }
        /// <summary>
        /// Adds or updates an attendance record and will create the occurrence if needed
        /// </summary>
        /// <param name="personAliasId">The person alias identifier.</param>
        /// <param name="checkinDateTime">The checkin date time.</param>
        /// <param name="groupId">The group identifier.</param>
        /// <param name="locationId">The location identifier.</param>
        /// <param name="scheduleId">The schedule identifier.</param>
        /// <param name="campusId">The campus identifier.</param>
        /// <param name="deviceId">The device identifier.</param>
        /// <param name="searchTypeValueId">The search type value identifier.</param>
        /// <param name="searchValue">The search value.</param>
        /// <param name="searchResultGroupId">The search result group identifier.</param>
        /// <param name="attendanceCodeId">The attendance code identifier.</param>
        /// <param name="checkedInByPersonAliasId">The checked in by person alias identifier.</param>
        /// <returns></returns>
        public Attendance AddOrUpdate(int?personAliasId, DateTime checkinDateTime,
                                      int?groupId, int?locationId, int?scheduleId, int?campusId, int?deviceId,
                                      int?searchTypeValueId, string searchValue, int?searchResultGroupId, int?attendanceCodeId, int?checkedInByPersonAliasId)
        {
            // Check to see if an occurrence exists already
            var occurrenceService = new AttendanceOccurrenceService((RockContext)Context);
            var occurrence        = occurrenceService.Get(checkinDateTime.Date, groupId, locationId, scheduleId);

            if (occurrence == null)
            {
                // If occurrence does not yet exists, use a new context and create it
                using (var newContext = new RockContext())
                {
                    occurrence = new AttendanceOccurrence
                    {
                        OccurrenceDate = checkinDateTime.Date,
                        GroupId        = groupId,
                        LocationId     = locationId,
                        ScheduleId     = scheduleId,
                    };

                    var newOccurrenceService = new AttendanceOccurrenceService(newContext);
                    newOccurrenceService.Add(occurrence);
                    newContext.SaveChanges();

                    // Query for the new occurrence using original context.
                    occurrence = occurrenceService.Get(occurrence.Id);
                }
            }

            // If we still don't have an occurrence record (i.e. validation failed) return null
            if (occurrence == null)
            {
                return(null);
            }

            // Query for existing attendance record
            Attendance attendance = null;

            if (personAliasId.HasValue)
            {
                attendance = occurrence.Attendees
                             .FirstOrDefault(a =>
                                             a.PersonAliasId.HasValue &&
                                             a.PersonAliasId.Value == personAliasId.Value);
            }

            // If an attendance record doesn't exist for the occurrence, add a new record
            if (attendance == null)
            {
                attendance = ((RockContext)Context).Attendances.Create();
                {
                    attendance.Occurrence    = occurrence;
                    attendance.OccurrenceId  = occurrence.Id;
                    attendance.PersonAliasId = personAliasId;
                };
                Add(attendance);
            }

            // Update details of the attendance (do not overwrite an existing value with an empty value)
            if (campusId.HasValue)
            {
                attendance.CampusId = campusId.Value;
            }
            if (deviceId.HasValue)
            {
                attendance.DeviceId = deviceId.Value;
            }
            if (searchTypeValueId.HasValue)
            {
                attendance.SearchTypeValueId = searchTypeValueId;
            }
            if (searchValue.IsNotNullOrWhiteSpace())
            {
                attendance.SearchValue = searchValue;
            }
            if (checkedInByPersonAliasId.HasValue)
            {
                attendance.CheckedInByPersonAliasId = checkedInByPersonAliasId.Value;
            }
            if (searchResultGroupId.HasValue)
            {
                attendance.SearchResultGroupId = searchResultGroupId;
            }
            if (attendanceCodeId.HasValue)
            {
                attendance.AttendanceCodeId = attendanceCodeId;
            }
            attendance.StartDateTime = checkinDateTime;
            attendance.DidAttend     = true;

            return(attendance);
        }