Skip to content

Linking events to attendance ​

There's a clean separation in GCM between what's on the calendar and who showed up. The calendar holds events β€” dates with titles, times, locations. Attendance rosters reference meetings β€” recurring templates that describe a kind of gathering. The link between them is intentional, indirect, and worth understanding before you start configuring either.

Attendance never points at an event ​

Look at the attendances table and you won't find an event_id column:

sql
attendances(
  id, member_id, meeting_id, attendance_date,
  organization_id, org_unit_id,
  ministry_id, school_id,
  created_at, updated_at, deleted_at
)

The roster row points at a meeting and an attendance date. That's it. The fact that a calendar event also exists on that date is irrelevant to the database β€” attendance lives in its own world, joined by date and meeting type, not by a foreign key into events.

This is a deliberate design choice. If attendance pointed at events, every recurring service would create thousands of "occurrence" event rows in the database to give attendance something to point at. Instead, recurring events stay as a single row with an RRULE, and meetings are reused across hundreds of dates. The cost: a Sunday Service event on the calendar and a Sunday Service meeting in attendance settings have no automatic link. They share a name and a vibe; the system doesn't know they're connected.

Two scenarios for attendance ​

In practice, attendance gets recorded in two ways, and both flow through meetings:

Recurring services β€” use meetings only ​

For your Sunday Service, Wednesday prayer, weekly cell group β€” anything that happens on a steady rhythm β€” you don't need a calendar event at all. Set up a meeting, then record attendance against it from the single attendance flow, the bulk attendance screen, or the check-in station. Each attendance row gets meeting_id = <Sunday Service> and attendance_date = <that Sunday>.

The calendar doesn't need to surface these dates, because the gathering is implied by the meeting. Members and operators know Sunday Service is at 10 AM Sunday β€” they don't need a calendar dot for it.

Special dates β€” calendar event + optional ad-hoc meeting ​

For one-off events that you also want to track attendance against β€” a leaders' retreat, a guest speaker night, an outreach campaign β€” create the calendar event for visibility, then either:

  • Reuse an existing meeting. If the retreat counts as a leadership meeting, record attendance against your existing Leadership meeting on the retreat's date. The roster lands cleanly.
  • Create a temporary meeting. If the event doesn't fit any existing meeting type, add a meeting like Special events, scope it appropriately, and record attendance against it. Tag the date in your notes if the meeting name is too generic to identify later.

Either way, the calendar event drives reminders and visual planning; the meeting drives the roster.

Why not attendance against the event directly? ​

The pragmatic answer: events and meetings have different lifecycles. An event is a date plan β€” it can be cancelled, rescheduled via an exception, or moved entirely. A roster is a historical record β€” once people are checked in, the row shouldn't move just because the planning data changed.

If attendance pointed at events:

  • Deleting a calendar event would either orphan the rosters or cascade-delete them.
  • Editing the event's date would re-date every roster, which is wrong for an audit log.
  • Recurring events would have to materialize every occurrence into a real row before attendance could reference it.

Pointing at meetings sidesteps all of that. Meetings change rarely (and when they do, scope changes are deliberate). Events change often, and that volatility is contained to the calendar surface where it belongs.

Deduplication rules ​

A few rules keep rosters clean across re-imports, duplicate check-ins, and accidental double-saves:

  1. One row per (member_id, meeting_id, attendance_date) per org. A unique index enforces this. Saving the same attendance twice β€” same member, same meeting, same date β€” silently no-ops the second save.
  2. Soft delete only. Deleting a roster row sets deleted_at rather than removing it. The unique index treats soft-deleted rows as non-conflicting, so a deleted-then-re-added row works as expected.
  3. is_sample = true rows are excluded from reports. Seeded demo attendance carries the flag so it doesn't pollute real numbers when you go live.
  4. Org and org-unit columns are stamped at insert time. They don't recompute if you move the member to another branch later β€” the historical record stays accurate to the moment of the check-in.

The check-in station and bulk attendance both rely on these rules to be idempotent: hit submit twice, scan the same QR code twice, re-upload the same CSV β€” the row count stays correct.

What about ministry and school attendance? ​

The attendances table carries optional ministry_id and school_id columns for rosters that belong to a specific ministry meeting or school session. These are set when the meeting's linked_entity_type is ministry or school, and they let the reports module split totals by ministry or by school.

For a worship-team rehearsal that counts as both a ministry meeting and a regular meeting, pick one. Don't double-record across two meeting rows β€” you'll inflate totals. The cleanest pattern is a single meeting linked to the ministry, and reports that aggregate by ministry_id.

When calendar and attendance diverge ​

If the calendar shows an event and no attendance ever gets recorded, that's fine β€” most events are informational. If attendance exists for a date with no matching calendar event, that's also fine β€” recurring meetings don't need calendar dots.

The case to watch is both exist and they describe slightly different things. A calendar event titled "Sunday Service @ 10am" and a meeting titled "Morning Service" both refer to the same gathering, but reports and reminders treat them as independent. Pick a consistent naming convention and stick to it across both surfaces β€” your future self running an attendance audit will thank you.