v2.0 to v2.0

316 added, 7 removed. Audit A to A.

---
name: frappe-ops-upgrades
description: >
Use when upgrading Frappe/ERPNext between major versions (v14 to v15, v15 to v16), troubleshooting failed migrations, or planning rollback.
Prevents broken upgrades from skipped patches, incompatible customizations, and missing pre-upgrade checks.
Covers version upgrade paths, bench update, migrate command, patch troubleshooting, rollback procedures, breaking changes per version.
Keywords: upgrade, migration, v14, v15, v16, bench update, bench migrate, rollback, patches, breaking changes.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
author: OpenAEC-Foundation
version: "2.0"
---
# Version Upgrades
- > **STATUS**: Stub - content pending V2.5 development phase.
+ Complete guide for upgrading Frappe/ERPNext between major versions, handling failed migrations, and rolling back safely.
- ## Quick Reference
+ **Versions**: v14 → v15 → v16
- _Content to be developed._
+ ---
- ## Decision Tree
+ ## Quick Reference — Upgrade Commands
- _Content to be developed._
+ | Task | Command |
+ |------|---------|
+ | Full update | `bench update` |
+ | Update specific app | `bench update --pull --app erpnext` |
+ | Switch branch | `bench switch-to-branch version-15 frappe erpnext` |
+ | Run migrations only | `bench --site mysite migrate` |
+ | Check migration readiness | `bench --site mysite ready-for-migration` |
+ | Backup before upgrade | `bench --site mysite backup` |
+ | Restore from backup | `bench --site mysite restore /path/to/backup.sql.gz` |
+ | Re-run failed patch | Add `#YYYY-MM-DD` suffix in patches.txt |
- ## See Also
+ ---
- _Cross-references to be added._
+ ## Decision Tree: Upgrade Strategy
+
+ ```
+ Need to upgrade?
+ ├── Single minor version bump (e.g., v15.10 → v15.20)?
+ │ └── YES → Run `bench update` directly
+ ├── Major version jump (e.g., v14 → v15)?
+ │ ├── Have custom apps?
+ │ │ ├── YES → Test on staging FIRST, check breaking changes
+ │ │ └── NO → Follow standard upgrade path
+ │ └── Multiple major versions (v14 → v16)?
+ │ └── ALWAYS upgrade one version at a time: v14 → v15 → v16
+ └── Production environment?
+ ├── YES → ALWAYS test on staging clone first
+ └── NO → Proceed with standard upgrade
+ ```
+
+ ---
+
+ ## Pre-Upgrade Checklist
+
+ **ALWAYS** complete these steps before ANY major version upgrade:
+
+ 1. **Full backup** — `bench --site mysite backup --with-files`
+ 2. **Test on staging** — Clone production to a staging bench and test there first
+ 3. **Check breaking changes** — Review the breaking changes section below
+ 4. **Audit custom apps** — Run custom apps against new version's API changes
+ 5. **Check Python/Node versions** — v15 requires Node 18+; v16 requires Node 24+, Python 3.14+
+ 6. **Disable scheduler** — `bench --site mysite scheduler disable`
+ 7. **Check pending jobs** — `bench --site mysite ready-for-migration`
+ 8. **Read release notes** — Check GitHub release notes for each version
+
+ ---
+
+ ## Standard Upgrade Process
+
+ ### Step-by-Step
+
+ ```bash
+ # 1. Backup all sites
+ bench backup-all-sites
+
+ # 2. Switch to target version branch
+ bench switch-to-branch version-15 frappe erpnext
+
+ # 3. Update (pulls code, installs deps, builds, migrates)
+ bench update
+
+ # 4. Verify
+ bench --site mysite migrate # if not done by update
+ bench version # confirm versions
+ ```
+
+ ### What `bench update` Executes (In Order)
+
+ 1. Backup all sites
+ 2. Pull latest code for all apps (`git pull`)
+ 3. Install Python requirements (`pip install`)
+ 4. Install Node requirements (`yarn install`)
+ 5. Build static assets (`bench build`)
+ 6. Run migrations on all sites (`bench migrate`)
+ 7. Restart bench processes
+
+ ---
+
+ ## v14 → v15 Breaking Changes
+
+ ### Environment Requirements
+
+ | Requirement | v14 | v15 |
+ |-------------|-----|-----|
+ | Node.js | v14+ | **v18+** |
+ | Python packaging | setup.py | **pyproject.toml** |
+
+ ### Backend Breaking Changes
+
+ - **`db.set()` removed** — Use `doc.db_set()` instead
+ - **`db.sql()` parameters removed** — `as_utf8` and `formatted` no longer accepted
+ - **`db.set_value()` for Singles** — Use `frappe.db.set_single_value()` instead
+ - **`job_name` deprecated** — Use `job_id` parameter in `enqueue()`
+ - **`frappe.new_doc()` arguments** — `parent_doc`, `parentfield`, `as_dict` MUST be keyword args
+ - **`frappe.get_installed_apps()`** — No longer accepts `sort` or `frappe_last` args
+ - **Method override order reversed** — Last override now takes precedence
+ - **Timezone functions renamed** — `convert_utc_to_user_timezone` → `convert_utc_to_system_timezone`
+
+ ### Frontend Breaking Changes
+
+ - **Vue 2 → Vue 3** — All Vue components MUST be migrated
+ - **Window globals removed** — `get_today` → `frappe.datetime.get_today`, `user` → `frappe.session.user`
+ - **`this` in Client Scripts** — Local scope access no longer supported
+ - **Image lazy loading** — Replace `website-image-lazy` class with native `loading="lazy"`
+
+ ### Security Changes
+
+ - **Server Scripts disabled by default** — Enable: `bench set-config -g server_script_enabled 1`
+ - **"Desk User" role added** — Replaces "All" role for desk user permissions
+ - **`currentsite.txt` removed** — Use `bench use sitename` or `FRAPPE_SITE` env var
+
+ ### Removed Features
+
+ - Event Streaming moved to separate app
+ - Cordova support removed
+ - `setup.py` removed (use `pyproject.toml`)
+ - `--make_copy` and `--restore` build flags removed (use `--hard-link`)
+
+ See [breaking-changes.md](references/breaking-changes.md) for the complete list.
+
+ ---
+
+ ## v15 → v16 Breaking Changes
+
+ ### Environment Requirements
+
+ | Requirement | v15 | v16 |
+ |-------------|-----|-----|
+ | Node.js | v18+ | **v24+** |
+ | Python | 3.10+ | **3.14+** |
+
+ ### Backend Breaking Changes
+
+ - **Default sort order changed** — `creation` instead of `modified` for all list queries
+ - **`has_permission` hooks** — MUST return explicit `True`; `None` no longer accepted
+ - **`frappe.get_doc(doctype, name, field=value)`** — No longer updates values
+ - **DB commits in document hooks** — No longer allowed to prevent data integrity issues
+ - **`frappe.sendmail(now=True)`** — No longer commits transactions implicitly
+ - **`db.get_value()` for Singles** — Now returns proper types instead of strings
+ - **State-changing methods require POST** — `/api/method/logout`, `/api/method/upload_file`, etc.
+
+ ### Separated Modules (Install Separately)
+
+ - Energy Points → `frappe/eps`
+ - Newsletter → `frappe/newsletter`
+ - Backup Integrations → `frappe/offsite_backups`
+ - Blog → `frappe/blog`
+
+ ### Frontend Breaking Changes
+
+ - Report/Dashboard/Page JS evaluated as IIFEs (no global scope pollution)
+ - Awesome Bar redesigned, moved to sidebar (`Cmd+K`)
+ - List view right sidebar removed
+ - `/apps` endpoint deprecated; `/app` reroutes to `/desk`
+
+ ### Configuration Changes
+
+ - Site config cached for up to one minute (changes not immediate)
+ - Country field requires valid ISO 3166 ALPHA-2 code
+ - `bench version` output format changed to "plain" (use `-f legacy` for old format)
+ - `override_doctype` hook classes MUST inherit from the overridden class
+
+ See [breaking-changes.md](references/breaking-changes.md) for the complete list.
+
+ ---
+
+ ## Patch System
+
+ ### How Patches Work
+
+ Patches are one-off data migration scripts that run during `bench migrate`. They are defined in each app's `patches.txt` file.
+
+ ### patches.txt Format [v14+]
+
+ ```ini
+ [pre_model_sync]
+ # Runs BEFORE schema sync — use for data prep
+ myapp.patches.v15_0.prepare_data_for_migration
+
+ [post_model_sync]
+ # Runs AFTER schema sync — use for data that needs new schema
+ myapp.patches.v15_0.migrate_data_to_new_fields
+ ```
+
+ ### Patch Execution Rules
+
+ - Patches run in the order defined in `patches.txt`
+ - Each patch runs exactly ONCE — tracked in the `__patches` table
+ - To re-run a patch, append a date comment: `myapp.patches.v15_0.fix #2025-03-20`
+ - One-off statements: `execute:frappe.delete_doc('Page', 'old_page', ignore_missing=True)`
+
+ ### Writing a Patch
+
+ ```python
+ # myapp/patches/v15_0/migrate_field_data.py
+ import frappe
+
+ def execute():
+ # ALWAYS reload if you need the NEW schema
+ frappe.reload_doc("module_name", "doctype", "doctype_name")
+
+ # Perform data migration
+ frappe.db.sql("""
+ UPDATE `tabSales Invoice`
+ SET new_field = old_field
+ WHERE old_field IS NOT NULL
+ """)
+ ```
+
+ ### Debugging Stuck Patches
+
+ ```bash
+ # Check which patches have run
+ bench --site mysite console
+ >>> frappe.db.sql("SELECT * FROM __patches WHERE patch LIKE '%stuck_patch%'")
+
+ # Remove a patch record to force re-run
+ >>> frappe.db.sql("DELETE FROM __patches WHERE patch = 'myapp.patches.v15_0.broken_patch'")
+ >>> frappe.db.commit()
+
+ # Then re-run migrate
+ bench --site mysite migrate
+ ```
+
+ ---
+
+ ## Rollback Procedure
+
+ ### Immediate Rollback (Within Hours)
+
+ ```bash
+ # 1. Stop all processes
+ bench stop
+
+ # 2. Restore database from pre-upgrade backup
+ bench --site mysite restore /path/to/pre-upgrade-backup.sql.gz \
+ --with-public-files /path/to/files.tar \
+ --with-private-files /path/to/private-files.tar
+
+ # 3. Switch back to previous version branch
+ bench switch-to-branch version-14 frappe erpnext
+
+ # 4. Install old dependencies
+ bench setup requirements
+
+ # 5. Build old assets
+ bench build
+
+ # 6. Start bench
+ bench start # or: sudo bench restart (production)
+ ```
+
+ ### Critical Rules for Rollback
+
+ - **ALWAYS** keep pre-upgrade backups for at least 7 days
+ - **NEVER** run `bench migrate` after restoring to old branch — schema is already correct
+ - **ALWAYS** restore files alongside database — file references may break otherwise
+ - **NEVER** attempt rollback after users have created new data on the upgraded version
+
+ ---
+
+ ## Custom App Compatibility Checks
+
+ Before upgrading, audit each custom app:
+
+ 1. **Check deprecated APIs** — Search for removed functions listed in breaking changes
+ 2. **Check `setup.py`** — Must migrate to `pyproject.toml` for v15+
+ 3. **Check Vue components** — Must be Vue 3 compatible for v15+
+ 4. **Check `patches.txt`** — Ensure patches use `[pre_model_sync]`/`[post_model_sync]` sections [v14+]
+ 5. **Check hooks.py** — Verify no removed hooks are used
+ 6. **Run tests** — `bench --site test_site run-tests --app myapp`
+
+ ---
+
+ ## Decision Tree: In-Place vs Fresh Install
+
+ ```
+ Choosing upgrade strategy:
+ ├── Small site (< 10 GB database)?
+ │ └── In-place upgrade is usually fine
+ ├── Large site (> 50 GB database)?
+ │ ├── Many custom apps? → Fresh install + data migration
+ │ └── Standard apps only? → In-place with extended downtime window
+ ├── Skipping multiple versions (v13 → v15)?
+ │ └── ALWAYS fresh install — sequential upgrades are too risky
+ └── Critical production with zero-downtime requirement?
+ └── Fresh install on parallel server + DNS switch
+ ```
+
+ ---
+
+ ## Version Differences Summary
+
+ | Feature | v14 | v15 | v16 |
+ |---------|:---:|:---:|:---:|
+ | Python packaging | setup.py | pyproject.toml | pyproject.toml |
+ | Vue version | Vue 2 | **Vue 3** | Vue 3 |
+ | Node.js minimum | v14 | **v18** | **v24** |
+ | Python minimum | 3.8 | 3.10 | **3.14** |
+ | Server Scripts | Enabled | **Disabled default** | Disabled default |
+ | Default sort | modified | modified | **creation** |
+ | patches.txt sections | Yes | Yes | Yes |
+ | Workspace sidebar | No | No | **Yes** |
+ | Separated modules | — | Event Streaming | Blog, Newsletter, EPS |
+
+ ---
+
+ ## Reference Files
+
+ | File | Contents |
+ |------|----------|
+ | [examples.md](references/examples.md) | Complete upgrade workflow examples |
+ | [anti-patterns.md](references/anti-patterns.md) | Common upgrade mistakes and fixes |
+ | [breaking-changes.md](references/breaking-changes.md) | Detailed breaking changes per version |