Pibical
CalDav Integration between Frappe/ERPNExt and NextCloud
- Author: pibico
- Repository: https://github.com/pibico/pibical
- GitHub stars: 21
- Forks: 13
- License: NOASSERTION
- Category: Integrations
- Maintenance: Minimally Maintained
- Frappe versions: develop, v13
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:
[](https://frappegems.com/gems/apps/pibico/pibical)
About Pibical
# PibiCal
**Bidirectional Calendar Synchronization for Frappe/ERPNext and CalDAV Servers**
[](https://github.com/pibico/pibical)
[](https://frappeframework.com)
[](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