Pibical

CalDav Integration between Frappe/ERPNExt and NextCloud

Install Pibical

bench get-app https://github.com/pibico/pibical

Add the Frappe Gems badge to your README

Maintain Pibical? Paste this into your README:

[![Listed on Frappe Gems](https://frappegems.com/api/method/frappe_gems.seo.badge?app=pibico%2Fpibical)](https://frappegems.com/gems/apps/pibico/pibical)

About Pibical

# PibiCal **Bidirectional Calendar Synchronization for Frappe/ERPNext and CalDAV Servers** [![Version](https://img.shields.io/badge/version-2.0-blue.svg)](https://github.com/pibico/pibical) [![Frappe](https://img.shields.io/badge/Frappe-v15-orange.svg)](https://frappeframework.com) [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) PibiCal enables seamless, real-time synchronization of calendar events between your Frappe/ERPNext instance and CalDAV-compatible servers (NextCloud, ownCloud, etc.). It supports bidirectional sync, recurring events, timezone management, and more. --- ## 📋 Table of Contents - [Features](#-features) - [Version Compatibility](#-version-compatibility) - [Requirements](#-requirements) - [Installation](#-installation) - [Configuration](#-configuration) - [Usage](#-usage) - [Architecture](#-architecture) - [Code Structure](#-code-structure) - [API Reference](#-api-reference) - [Performance](#-performance) - [Troubleshooting](#-troubleshooting) - [Development](#-development) - [Recent Updates](#-recent-updates-v20) - [Contributing](#-contributing) - [License](#-license) --- ## ✨ Features ### Core Capabilities - **✅ Bidirectional Sync** - Changes in Frappe/ERPNext ↔️ CalDAV servers sync automatically - **✅ Timezone Support** - Events properly converted between user timezones and UTC - **✅ Recurring Events** - Weekly, monthly, yearly patterns with end dates - **✅ Event Status** - Open, Completed, Cancelled states synchronized - **✅ All-Day Events** - Properly handled as date-only events - **✅ Multiple Calendars** - Support for multiple CalDAV calendars per user - **✅ Event Invitations** - Send calendar invitations with .ics attachments ### Synchronization Details | Feature | Frappe → CalDAV | CalDAV → Frappe | |---------|----------------|-----------------| | **Speed** | Instant | Every 3 minutes | | **Event Creation** | ✅ Supported | ✅ Supported | | **Event Updates** | ✅ Supported | ✅ Supported | | **Event Deletion** | ✅ Supported | ⚠️ Manual only | | **Recurring Events** | ✅ Supported | ✅ Supported | | **Timezone Handling** | ✅ Automatic | ✅ Automatic | ### Limitations - ❌ **Private Events**: Only "Public" events are synchronized - ❌ **Attachments**: File attachments are not synced - ⚠️ **Deletion Sync**: CalDAV → Frappe deletions require manual cleanup - ⚠️ **Participants**: Frappe → CalDAV only (disabled by default) --- ## 📦 Version Compatibility | Frappe Version | PibiCal Branch | Status | Notes | |----------------|----------------|--------|-------| | **v15** | `develop` | ✅ **Active Development** | Recommended for new installations | | **v13** | `version-13` | ✅ Stable | Production ready | | **v12** | `version-12` | ⚠️ Legacy | No longer maintained | **Current Version**: 2.0 (December 2025) --- ## 🔧 Requirements ### System Requirements - **Frappe/ERPNext**: v15 (for this branch) or v13 (version-13 branch) - **CalDAV Server**: NextCloud, ownCloud, or any CalDAV-compatible server - **SSL/TLS**: Must be enabled (wildcard certificates NOT supported) - **Python**: 3.10+ (included with Frappe) ### Python Dependencies ```txt frappe >= 15.0.0 caldav >= 0.9.0 icalendar >= 4.0.0 ``` These are automatically installed via `requirements.txt`. ### Server Requirements - **CalDAV Endpoint**: Valid CalDAV server URL - **Authentication**: Username and password/app-specific token - **Permissions**: Read/write access to calendars - **Network**: Stable connection between Frappe and CalDAV server --- ## 🚀 Installation ### Standard Installation ```bash # Navigate to your frappe-bench directory cd ~/frappe-bench # Download the app (Frappe v15) bench get-app pibical https://github.com/pibico/pibical.git --branch develop # Install on your site bench --site your-site-name install-app pibical # Restart bench to apply changes bench restart ``` ### For Frappe v13 ```bash bench get-app pibical https://github.com/pibico/pibical.git --branch version-13 bench --site your-site-name install-app pibical bench restart ``` ### Updating ```bash # Update the app cd ~/frappe-bench bench update --apps pibical --no-backup # If you encounter dependency issues bench update --requirements # Restart bench restart ``` ### Uninstalling ```bash bench --site your-site-name uninstall-app pibical bench remove-app pibical ``` --- ## ⚙️ Configuration ### Step 1: Configure CalDAV Credentials Each user must configure their CalDAV credentials: 1. **Navigate**: User List → Select User → Edit 2. **Scroll to**: CalDAV Credentials section 3. **Fill in**: | Field | Description | Example | |-------|-------------|---------| | **CalDAV URL** | CalDAV server endpoint | `https://cloud.example.com/remote.php/dav/principals/` | | **CalDAV Username** | Your CalDAV username | `john.doe` or `john.doe@example.com` | | **CalDAV Token** | Password or app-specific token | `your-secure-token` | **NextCloud Tip**: Generate app-specific passwords from Settings → Security → Devices & Sessions ### Step 2: Enable Sync for Events When creating or editing events: 1. Set **Event Type** = "Public" (required) 2. Check ✅ **"Sync with CalDAV"** 3. Select your calendar from **"CalDAV ID Calendar"** dropdown 4. Fill in event details (subject, date, time, etc.) 5. **Save** - Event syncs immediately to CalDAV ### Step 3: Verify Sync is Running ```bash # Check scheduler status cd ~/frappe-bench bench --site your-site-name doctor # Should show: # ✓ Scheduler Active: Yes ``` ### Configuration Options #### Sync Frequency **Default**: Every 3 minutes **To Change**: Edit `pibical/hooks.py` ```python scheduler_events = { "cron": { "*/3 * * * *": [ # Modify this cron expression "pibical.pibical.custom.sync_outside_caldav" ] } } ``` **Options**: - `*/1 * * * *` - Every 1 minute (high load) - `*/5 * * * *` - Every 5 minutes (balanced) - `*/10 * * * *` - Every 10 minutes (low load) **After changing**: `bench restart` #### Date Range for Sync **Default**: Yesterday to +30 days **To Change**: Edit `pibical/pibical/custom.py` (line ~627) ```python sel_events = c.date_search( datetime.now().date() - timedelta(days=1), # Start datetime.now().date() + timedelta(days=30) # End ) ``` **Recommendations**: - Small calendars (<100 events): -7 to +60 days - Medium calendars (100-500 events): -1 to +30 days (default) - Large calendars (>500 events): 0 to +14 days --- ## 📖 Usage ### Creating a Synchronized Event **In Frappe UI**: 1. Navigate to **Event** → **New Event** 2. Fill in: - **Subject**: "Team Meeting" - **Event Type**: "Public" *(required)* - **Sync with CalDAV**: ✅ Checked - **CalDAV ID Calendar**: Select your calendar - **Starts On / Ends On**: Set date/time - **Description**: Optional 3. **Save** **Expected**: Message "Event created on CalDAV server" **Result**: Event appears in NextCloud within 30 seconds ### Synchronization Behavior #### 📅 Event Created in Frappe/ERPNext **Actions**: - UID Generated: `frappe[hash]@pibico.es` - Immediate sync to CalDAV - Fields set: `event_uid`, `caldav_id_url`, `event_stamp` **Message**: "Event created on CalDAV server" #### 📅 Event Created in NextCloud/CalDAV **Actions**: - Background sync detects new event (every 3 minutes) - UID preserved from CalDAV (no "frappe" prefix) - Event Type set to "Public" in Frappe - Timezone converted to user's local time **Result**: Event appears in Frappe within 3 minutes (silent sync) #### ✏️ Event Modified in Frappe/ERPNext **Actions**: - If calendar changed: Deletes from old, creates in new - Uses smart update mechanism (no_create=True) - Original UID maintained **Message**: "Event updated on CalDAV server" #### ✏️ Event Modified in NextCloud/CalDAV **Actions**: - Timestamp comparison (±1 second tolerance) - Updates: subject, times, description, status, recurrence - Frappe-specific fields preserved **Result**: Event updated in Frappe within 3 minutes (silent sync) #### 🗑️ Event Deleted in Frappe/ERPNext **Actions**: - Immediate deletion from CalDAV - Three methods attempted (URL, UID search, date scan) **Messages**: - ✅ "Deleted Event in CalDav Calendar [name]" - ⚠️ "Event not found in CalDAV calendar" #### 🗑️ Event Deleted in NextCloud/CalDAV **Current Limitation**: ⚠️ Not automatically synced to Frappe **Workaround**: Manually delete event in Frappe ### Sending Event Invitations 1. Open event in Frappe 2. Add participants (Contacts with email addresses) 3. Save event 4. Click **"Send Invitations"** button 5. Select recipients 6. Click **Send** Recipients receive email with .ics attachment they can import to their calendar. --- ## 🏗️ Architecture ### System Overview ``` ┌─────────────────────────────────────────────────────────┐ │ BIDIRECTIONAL SYNC │ ├─────────────────────────────────────────────────────────┤ │ │ │ ERPNext/Frappe ←──────────────────→ CalDAV Server │ │ │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ Event.save() │ │ CalDAV Server │ │ │ │ │──────────────→│ (NextCloud) │ │ │ │ before_save │ Instant │ │ │ │ │ hook │ │ PUT/POST event │ │ │ └──────────────┘ └──────────────────┘ │ │ ↑ ↓ │ │ │ │ │ │ │ Flag: ignore_sync │ │ │ │ │ │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ Background │ │ CalDAV Server │ │ │ │ Job │←─────────────│ │ │ │ │ (every 3min) │ Poll events │ GET events │ │ │ │ │ │ (date range) │ │ │ └──────────────┘ └──────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ ``` ### Sync Flow (ERPNext → CalDAV) 1. **User saves Event** in Frappe UI 2. **`before_save` hook** triggers `sync_caldav_event_by_user()` 3. **Check flag**: If `ignore_caldav_sync` set, skip (prevents loops) 4. **Validate**: CalDAV credentials exist 5. **Build iCalendar**: Convert Frappe Event → iCal format 6. **Sync**: - **New event**: `c.save_event(ical_data)` - **Update**: `c.save_event(ical_data, no_create=True)` 7. **Success**: Show message to user ### Sync Flow (CalDAV → ERPNext) 1. **Cron job** runs `sync_outside_caldav()` every 3 minutes 2. **Get users** with CalDAV credentials 3. **For each user**: - Connect to CalDAV server - **For each calendar**: - Fetch events (yesterday to +30 days) - **For each event**: - Check if already processed (dedupe) - Check if exists in Frappe by UID - **If exists**: Compare timestamps, update if modified - **If new**: Create in Frappe - **Set flag**: `ignore_caldav_sync = True` (prevents loop) 4. **Commit**: Save all changes ### Loop Prevention Mechanism **Problem**: Sync from CalDAV would trigger `before_save` hook, syncing back to CalDAV (infinite loop) **Solution**: Document flags ```python # When syncing FROM CalDAV: event.flags.ignore_caldav_sync = True event.save() # Hook checks this flag and skips # In sync hook: if doc.flags.get('ignore_caldav_sync'): return # Skip sync back to CalDAV ``` **Flags are**: - In-memory only (not persisted to DB) - Temporary (cleared after save) - Frappe-native functionality --- ## 📂 Code Structure ### Directory Layout ``` pibical/ ├── pibical/ │ ├── __init__.py │ ├── hooks.py # Frappe integration hooks │ └── pibical/ │ ├── __init__.py │ ├── custom.py # Main sync logic (800+ lines) │ ├── doctype/ # Custom do

Related Integrations apps for Frappe & ERPNext

  • Insights — Open Source Business Intelligence Tool
  • Raven — Simple, open source team messaging platform
  • Frappe Whatsapp — WhatsApp cloud integration for frappe
  • Frappe Assistant Core — Infrastructure that connects LLMs to ERPNext. Frappe Assistant Core works with the Model Context Protocol (MCP) to expose ERPNext functionality to any compatible Language Model
  • Biometric Attendance Sync Tool — A simple tool for syncing Biometric Attendance data with your ERPNext server
  • Frappe React Sdk — React hooks for Frappe
  • Frappe Js Sdk — TypeScript/JavaScript library for Frappe REST API
  • Mcp — Frappe MCP allows Frappe apps to function as MCP servers