horilla.contrib.calendar renders CRM calendars and optional Google Calendar sync. Meetings are Activity rows with activity_type meetings.

This post is Part 25 of 28 in the Horilla CRM Technical Blog series.

What is calendar integration?

Calendar integration enables you to:

  • Unified calendar UI
  • Meeting records from activity module
  • OAuth Google sync
  • Company and user scoping

Calendar data model and meeting source

horilla.contrib.calendar provides the calendar preferences and external Google Calendar integration. Its UserCalendarPreference model stores per-user display and calendar choices. Google credentials and connection configuration are represented by GoogleCalendarConfig and GoogleIntegrationSetting; keep the distinction intact when reading or extending the feature. A configuration represents the calendar-side connection details for a user, while the integration setting controls the enabled behavior and options around that connection.

The CRM’s meeting record is not a second calendar-specific model. Meetings are Activity records whose type is meeting. That design keeps a meeting in the same activity history, ownership, permission, and follow-up flows as calls, tasks, and other customer interactions. Calendar views and the Google sync layer select and translate meeting activities; they should not create an unrelated event model that would bypass the CRM timeline.

from horilla.contrib.activity.models import Activity
meetings = Activity.objects.filter(activity_type="meeting", is_active=True)

Use the field names defined by the installed Activity model in production code; the query above conveys the relationship rather than prescribing a new field name.

Google synchronization flow

The synchronization logic is concentrated in horilla.contrib.calendar.google_calendar.sync. push_activity_to_google(activity, user) converts a qualifying CRM meeting activity into a Google Calendar event for the connected user. It must have an authorized configuration, sufficient activity data, and a meaningful destination calendar before it calls Google. A successful push stores or updates the external linkage so later edits can update the same event instead of creating duplicates.

pull_google_events_to_horilla(config, initial_sync_only=False) performs the reverse direction for a Google configuration. It reads Google events, respects the initial sync mode, and turns eligible events into Horilla meeting activity data. The config argument is intentional: synchronization is scoped to an authenticated integration, not a global import of every user’s events. Both directions need idempotent external IDs, timezone-aware timestamps, and clear rules for deletions and conflicts.

from horilla.contrib.calendar.google_calendar.sync import (
    pull_google_events_to_horilla,
    push_activity_to_google,
)
push_activity_to_google(meeting_activity, request.user)
pull_google_events_to_horilla(google_config, initial_sync_only=True)

Do not invoke a network sync synchronously from a request that saves an activity if latency or provider failures can affect the user experience. Queue or schedule it according to the deployment’s background-job policy, record actionable failures, and make retries safe. The sync code is an integration boundary: it must never assume that a remote event remains unchanged between fetch and update.

OAuth, redirects, and webhooks

Google OAuth and webhook callbacks need a public, canonical URL. Configure SITE_URL to the externally reachable HTTPS base URL, not an internal Docker hostname or development localhost value. Google uses the registered redirect URI and webhook address to return authorization responses and notify changes. A mismatch between SITE_URL, the Google Cloud console registration, and the reverse proxy’s public origin causes authorization failures that can look like a token problem.

Protect credential fields, refresh tokens, and webhook state as secrets. Never put them in a browser response, source control, or a calendar event’s visible notes. Validate OAuth state, verify webhook expectations where supported, and scope operations to the user/company represented by the stored configuration. When a user disconnects an integration, revoke or invalidate the stored authorization according to the provider policy and stop scheduled synchronization for that configuration.

Video meeting providers

horilla.contrib.meeting is the integration boundary for conferencing providers such as Zoom and Microsoft Teams. It complements calendar synchronization; it does not replace the Activity meeting record. A meeting activity can carry a provider join link or metadata created through that module, while calendar sync distributes the scheduled meeting to Google Calendar. This split is important for operational clarity: provider APIs create or manage conference details, and Google APIs manage calendar events.

When adding a provider, create or update conferencing metadata through horilla.contrib.meeting, retain the activity as the CRM source record, and pass only the supported meeting fields to calendar sync. Test a meeting created in Horilla, an update pushed to Google, a remote event pulled into Horilla, a disconnected token, and a timezone boundary. The result is a single CRM activity history with provider-specific conferencing and optional external-calendar visibility.

Benefits of Calendar in Horilla CRM

  • Reps see CRM meetings alongside external calendars
  • Fewer missed follow-ups
  • Single source for meeting activity history

Connect Google Calendar in settings and meetings from activity sync automatically — calendar is the bridge between CRM tasks and external schedules.

Continue the series

Previous: Part 24 — How to Use Keyboard Shortcuts in Horilla CRM for Faster Navigation

Next: Part 26 — How Horilla CRM Handles Background Tasks Using Celery, Beat, and APScheduler

More posts are on the Horilla Blogs; share feedback on GitHub.

Share this article