Skip to content

fix: stop duplicate calendar entries from the booking .ics - #368

Open
rahul286 wants to merge 3 commits into
developfrom
fix/duplicate-invitations
Open

rahul286 wants to merge 3 commits into
developfrom
fix/duplicate-invitations

Conversation

@rahul286

Copy link
Copy Markdown
Member

Problem

Each booking sends attendees two invites: Google's own (sendUpdates="all") and an app email with an invite.ics. The .ics used a random uuid4() UID, so calendar clients could not tell it was the same event. Accepting both put the meeting on the calendar twice, and a reschedule added another .ics entry while the old one stayed at the old time.

Fix

  • helpers/ics_file.py: new get_ics_identity(event) returns the UID and SEQUENCE for the .ics:
    • Synced to Google: fetches the Google event and reuses its iCalUID and sequence, so clients merge the .ics with Google's invite and move it in place on reschedule. If the fetch fails, it falls back to <google_calendar_event_id>@google.com with no SEQUENCE, and the error is logged.
    • Not synced: a stable <event name>@<site> UID, with SEQUENCE set to the modified timestamp so it grows on each reschedule.
  • The .ics now sets METHOD:REQUEST when it has an ORGANIZER, and METHOD:PUBLISH when the organizer is hidden (hide_ics_organizer).
  • send_meet_email looks up the UID and SEQUENCE once and reuses them for both the organiser and booker emails. That is one extra Google events.get per send.

Tests

tests/helpers/test_ics_file.py adds tests for:

  • the Google UID and sequence being reused
  • SEQUENCE increasing on reschedule while the UID stays the same
  • the fallback when the Google fetch fails
  • a stable UID when the event is not synced to Google
  • the METHOD choice

The full app suite passes locally (185 tests).

For reviewers

  • Only the .ics changes. Attendees still get two emails (Google's and the app's), but should now see one calendar entry.
  • Not checked yet against real mail clients (Gmail, Outlook, Apple Calendar). It would help if someone could book and then reschedule against a real Google account and confirm only one entry remains.

🤖 Generated with Claude Code

The .ics attached to booking emails used a random UID, so calendar
clients could not match it to the invite Google sends for the same
event. Accepting both left the meeting on the calendar twice, and a
reschedule left a stale copy at the old time.

The .ics now reuses the Google event's iCalUID and sequence (one
events.get per send_meet_email run). Events not synced to Google get a
stable UID per Event and a SEQUENCE that grows with each save. METHOD
is REQUEST when an ORGANIZER is present, PUBLISH otherwise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 12, 2026 11:04
…roup

The endpoint enqueues a background job, which escapes the GET rollback,
so Frappe's semgrep rule whitelisted-side-effect-on-get now blocks it.
Its only caller (appointment_group.js) uses frappe.call, which posts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Generated scheduling requests lack attendees and may use an organizer inconsistent with the Google event.

3 open findings
What changed in this PR

Adds stable ICS identities so calendar clients can merge booking updates with Google Calendar events.

Changes:

  • Reuses Google UID/sequence or generates stable local identities.
  • Selects ICS METHOD based on organizer presence.
  • Reuses one identity across outgoing emails and adds tests.
File Description
helpers/​ics_file.py Generates stable ICS identity and method.
overrides/​event_override.py Reuses identity across attachments.
tests/​helpers/​test_ics_file.py Tests UID, sequence, fallback, and method behavior.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread frappe_appointment/helpers/ics_file.py Outdated
Comment thread frappe_appointment/helpers/ics_file.py Outdated
Comment thread frappe_appointment/overrides/event_override.py Outdated
Review follow-up:
- Google-backed .ics files reuse the Google event's ORGANIZER and
  ATTENDEE list, so the REQUEST matches Google's own invite for the
  same UID. METHOD is REQUEST only when both are known, else PUBLISH.
- hide_ics_organizer also drops the attendee list, since Google lists
  the organiser among the attendees.
- send_meet_email looks up the identity only when an email with an
  .ics is actually sent, and once for both emails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants