Skip to content

Commit

Permalink
+ (Group) Fixed issue where attendance taken with Obsidian Group Atte…
Browse files Browse the repository at this point in the history
…ndance Detail block shows 100% attendance. (Fixes #5473)
  • Loading branch information
joshuahenninger committed Jun 15, 2023
1 parent 4ff4c90 commit ddbcf61
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 32 deletions.
47 changes: 23 additions & 24 deletions Rock.Blocks/Group/GroupAttendanceDetail.cs
Expand Up @@ -572,7 +572,7 @@ public BlockActionResult GetAttendance( GroupAttendanceDetailGetAttendanceReques

var attendanceBag = GetAttendanceBag( new AttendanceBagDto
{
DidAttend = attendanceInfo.DidAttend ?? false,
DidAttend = attendanceInfo.DidAttend,
GroupMember = groupMemberDto?.GroupMember,
GroupRoleName = groupMemberDto?.GroupRoleName,
Person = attendanceInfo.Person,
Expand Down Expand Up @@ -1726,18 +1726,17 @@ private List<GroupAttendanceDetailAttendanceBag> GetAttendanceBags( RockContext
var groupRoleQuery = new GroupTypeRoleService( rockContext ).Queryable().AsNoTracking();
var primaryAliasQuery = new PersonAliasService( rockContext ).GetPrimaryAliasQuery();

// Get the query for people who attended.
IQueryable<AttendanceBagDto> attendedPeopleQuery = null;
// Get the query for people who did or didn't attend this occurrence.
IQueryable<AttendanceBagDto> existingPeopleQuery = null;
if ( occurrenceData.AttendanceOccurrence.Id > 0 )
{
// These may or may not be group members.
attendedPeopleQuery = new AttendanceService( rockContext )
existingPeopleQuery = new AttendanceService( rockContext )
.Queryable()
.AsNoTracking()
.Where( a =>
a.OccurrenceId == occurrenceData.AttendanceOccurrence.Id
&& a.DidAttend.HasValue
&& a.DidAttend.Value
&& a.PersonAliasId.HasValue )
.Select( a => new
{
Expand All @@ -1746,7 +1745,7 @@ private List<GroupAttendanceDetailAttendanceBag> GetAttendanceBags( RockContext
} )
.Select( a => new AttendanceBagDto
{
DidAttend = true,
DidAttend = a.Attendance.DidAttend,
GroupMember = a.GroupMember,
GroupRoleName = a.GroupMember != null && a.GroupMember.GroupRole != null ? a.GroupMember.GroupRole.Name : null,
Person = a.Attendance.PersonAlias.Person,
Expand All @@ -1755,14 +1754,14 @@ private List<GroupAttendanceDetailAttendanceBag> GetAttendanceBags( RockContext
} );
}

// Get the people who did not attend from a Person EntitySet (if specified) or from the unattended group members.
IQueryable<AttendanceBagDto> unattendedPeopleQuery = null;
// Get the new people, who can be included in the occurrence but weren't, from a Person EntitySet (if specified) or from the DidAttend=null group members.
IQueryable<AttendanceBagDto> newPeopleQuery = null;
var entitySetId = this.EntitySetIdPageParameter;
if ( entitySetId.HasValue )
{
// These may or may not be group members.
var entitySetService = new EntitySetService( rockContext );
unattendedPeopleQuery = entitySetService
newPeopleQuery = entitySetService
.GetEntityQuery<Person>( entitySetId.Value )
.AsNoTracking()
.Select( p => new
Expand All @@ -1772,7 +1771,7 @@ private List<GroupAttendanceDetailAttendanceBag> GetAttendanceBags( RockContext
} )
.Select( p => new AttendanceBagDto
{
DidAttend = false,
DidAttend = null,
GroupMember = p.GroupMember,
GroupRoleName = p.GroupMember != null && p.GroupMember.GroupRole != null ? p.GroupMember.GroupRole.Name : null,
Person = p.Person,
Expand All @@ -1782,10 +1781,10 @@ private List<GroupAttendanceDetailAttendanceBag> GetAttendanceBags( RockContext
}
else
{
unattendedPeopleQuery = groupMembersQuery
newPeopleQuery = groupMembersQuery
.Select( m => new AttendanceBagDto
{
DidAttend = false,
DidAttend = null,
GroupMember = m,
GroupRoleName = m.GroupRole != null ? m.GroupRole.Name : null,
Person = m.Person,
Expand All @@ -1796,26 +1795,26 @@ private List<GroupAttendanceDetailAttendanceBag> GetAttendanceBags( RockContext

var lavaItemTemplate = this.ListItemDetailsTemplate;

// Union the attended and unattended people and project the results to a roster attendance bag.
// Union the new/existing people and project the results to attendance bags.

if (attendedPeopleQuery == null && unattendedPeopleQuery == null )
if (existingPeopleQuery == null && newPeopleQuery == null )
{
return new List<GroupAttendanceDetailAttendanceBag>();
}

IQueryable<AttendanceBagDto> query;
if ( attendedPeopleQuery == null )
if ( existingPeopleQuery == null )
{
query = unattendedPeopleQuery;
query = newPeopleQuery;
}
else if ( unattendedPeopleQuery == null )
else if ( newPeopleQuery == null )
{
query = attendedPeopleQuery;
query = existingPeopleQuery;
}
else
{
var distinctUnattendedPeople = unattendedPeopleQuery.Where( u => !attendedPeopleQuery.Any( a => a.PersonAliasId == u.PersonAliasId ) );
query = attendedPeopleQuery.Union( distinctUnattendedPeople );
var distinctNewPeople = newPeopleQuery.Where( u => !existingPeopleQuery.Any( a => a.PersonAliasId == u.PersonAliasId ) );
query = existingPeopleQuery.Union( distinctNewPeople );
}

return query.ToList()
Expand Down Expand Up @@ -1888,7 +1887,7 @@ private static Attendance CreateAttendanceInstance( int? personAliasId, int? cam
/// </summary>
private class AttendanceBagDto
{
internal bool DidAttend { get; set; }
internal bool? DidAttend { get; set; }

internal Person Person { get; set; }

Expand Down Expand Up @@ -2256,7 +2255,7 @@ internal bool Save( OccurrenceData occurrenceData, GroupAttendanceDetailGetOrCre
attendee.PersonAliasId,
campusId.Value,
startDateTime,
attendee.DidAttend );
attendee.DidAttend ?? false );

// Check that the attendance record is valid
if ( !attendance.IsValid )
Expand All @@ -2267,10 +2266,10 @@ internal bool Save( OccurrenceData occurrenceData, GroupAttendanceDetailGetOrCre

occurrenceData.AttendanceOccurrence.Attendees.Add( attendance );
}
else
else if ( attendee.DidAttend.HasValue )
{
// Otherwise, only record that they attended -- don't change their attendance startDateTime.
attendance.DidAttend = attendee.DidAttend;
attendance.DidAttend = attendee.DidAttend.Value;
}
}
}
Expand Down
Expand Up @@ -66,7 +66,7 @@

const props = defineProps({
modelValue: {
type: Boolean as PropType<boolean>,
type: Boolean as PropType<boolean | undefined | null>,
required: true
},

Expand Down
Expand Up @@ -124,7 +124,7 @@ export const NoFilter = createFilter(_ => true);
/**
* A filter that returns `true` if `attendance.didAttend == true`.
*/
export const DidAttendFilter = createFilter(attendance => attendance.didAttend);
export const DidAttendFilter = createFilter(attendance => !!attendance.didAttend);

// Cache "last name starts with" filters.
const lastNameStartsWithFilters: Record<string, IAttendanceFilter> = {};
Expand Down
Expand Up @@ -758,11 +758,30 @@
locationGuid: locationGuid.value,
scheduleGuid: scheduleGuid.value,

// This will ensure the expected attendance is updated on the existing or newly created occurrence.
updatedAttendances: attendances.value.filter(attendance => attendance.personAliasId === bag.personAliasId),
// Pass all attendances...
// If a new occurrence is created,
// then attendances with a null didAttend value
// will be created with a false didAttend value.
// Otherwise, if another device already created the occurrence,
// then only attendances with a true/false didAttend value
// will be updated, while those with null didAttend values will be ignored.
updatedAttendances: attendances.value,
});
}
else if (attendances.value.some(a => (a.didAttend ?? null) === null)) {
// There are new people to add to the occurrence so
// invoke get/create to save multiple attendances.
await getOrCreateAttendanceOccurrence({
attendanceOccurrenceDate: attendanceForDateIsoString.value,
locationGuid: locationGuid.value,
scheduleGuid: scheduleGuid.value,

// Pass the attendance that changed and any new people (didAttend is null).
updatedAttendances: attendances.value.filter(attendance => attendance.personAliasId === bag.personAliasId || (attendance.didAttend ?? null === null))
});
}
else {
// Mark the single attendance that has changed.
await invokeBlockAction("MarkAttendance", { bag });
}
}
Expand Down
Expand Up @@ -32,7 +32,7 @@ export type GroupAttendanceDetailAttendanceBag = {
campusGuid?: Guid | null;

/** Indicates whether the Person has attended. */
didAttend: boolean;
didAttend?: boolean | null;

/** Gets or sets the item template used for rendering the Attendance in the Group Attendance Detail block. */
itemTemplate?: string | null;
Expand Down
Expand Up @@ -29,7 +29,7 @@ export type GroupAttendanceDetailMarkAttendanceRequestBag = {
attendanceOccurrenceGuid?: Guid | null;

/** Gets or sets a value indicating whether the Person attended. */
didAttend: boolean;
didAttend?: boolean | null;

/** Gets or sets the person alias identifier. */
personAliasId: number;
Expand Down
Expand Up @@ -47,7 +47,7 @@ public class GroupAttendanceDetailAttendanceBag
/// <summary>
/// Indicates whether the Person has attended.
/// </summary>
public bool DidAttend { get; set; }
public bool? DidAttend { get; set; }

/// <summary>
/// Gets or sets the Campus that a Person's family belongs to.
Expand Down
Expand Up @@ -37,6 +37,6 @@ public class GroupAttendanceDetailMarkAttendanceRequestBag
/// <summary>
/// Gets or sets a value indicating whether the Person attended.
/// </summary>
public bool DidAttend { get; set; }
public bool? DidAttend { get; set; }
}
}

0 comments on commit ddbcf61

Please sign in to comment.