Documentation Frappe Framework
Frappe est le framework web Python/JavaScript open source sur lequel ERPNext est construit. Il fournit le moteur de base : ORM, API REST, système de formulaires, authentification, permissions, scheduler et bien plus.
Qu'est-ce que Frappe ?
Frappe (anciennement Web Notes) est un framework full-stack low-code développé en Python (backend) et JavaScript (frontend). Il est la base d'ERPNext et de nombreuses autres applications.
ORM intégré
Modèles de données définis en JSON, automatiquement convertis en tables MariaDB
API REST auto
Chaque DocType expose automatiquement des endpoints CRUD sans code supplémentaire
UI générative
Les formulaires et listes sont générés automatiquement depuis la définition du DocType
Permissions granulaires
Contrôle d'accès par rôle, utilisateur, et même par champ de données
Scheduler
Tâches planifiées (cron-like) exécutées par des workers Redis Queue
Multi-site
Un seul serveur peut héberger plusieurs sites Frappe indépendants
Architecture
Frappe suit une architecture multi-couche :
| Couche | Technologie | Rôle |
|---|---|---|
| Base de données | MariaDB | Persistance des données |
| Cache | Redis | Sessions, cache applicatif, queues de tâches |
| Backend | Python / Gunicorn | Logique métier, API REST, ORM |
| Realtime | Socket.io / Node.js | Notifications en temps réel, progression |
| Frontend | Jinja2 + JavaScript | Templates HTML, formulaires dynamiques |
| Web | Nginx | Reverse proxy, fichiers statiques, SSL |
DocType — Le concept central
Un DocType est le modèle de base dans Frappe. C'est à la fois :
- La définition de la structure de données (les champs)
- La table en base de données
- Le formulaire d'interface utilisateur
- L'endpoint API REST
Structure d'un DocType (JSON)
{
"doctype": "DocType",
"name": "Client",
"module": "MonApp",
"autoname": "field:nom_client",
"fields": [
{
"fieldname": "nom_client",
"fieldtype": "Data",
"label": "Nom du Client",
"reqd": 1
},
{
"fieldname": "email",
"fieldtype": "Data",
"label": "Email",
"options": "Email"
},
{
"fieldname": "telephone",
"fieldtype": "Phone",
"label": "Téléphone"
},
{
"fieldname": "actif",
"fieldtype": "Check",
"label": "Actif",
"default": "1"
}
],
"permissions": [
{
"role": "System Manager",
"read": 1, "write": 1, "create": 1, "delete": 1
}
]
}
Types de champs courants
| fieldtype | Description | Exemple |
|---|---|---|
| Data | Texte court (255 chars) | Nom, code |
| Text | Texte long | Description, notes |
| Int | Entier | Quantité, durée |
| Float / Currency | Décimal / Montant | Prix, montant |
| Date / Datetime | Date ou date+heure | Date facture |
| Select | Liste de choix fixes | Statut (Brouillon/Validé) |
| Link | Référence vers un autre DocType | Client → Facture |
| Table | Table enfant (child table) | Lignes de facture |
| Attach / Attach Image | Fichier joint | Photo, PDF |
| Password | Champ chiffré | Clé API, token |
| Check | Booléen (0/1) | Actif, validé |
Document — Instance de DocType
Un Document est une instance d'un DocType (une ligne en base). Chaque document a un nom unique (name) qui sert d'identifiant et de clé primaire.
Cycle de vie d'un document
import frappe
from frappe.model.document import Document
class Client(Document):
def before_save(self):
# Appelé avant chaque sauvegarde
self.nom_client = self.nom_client.strip().title()
def after_insert(self):
# Appelé après la création (INSERT)
frappe.sendmail(
recipients=[self.email],
subject="Bienvenue",
message=f"Bonjour {self.nom_client}!"
)
def validate(self):
# Validation avant sauvegarde
if not self.email and not self.telephone:
frappe.throw("Email ou téléphone obligatoire")
Hooks de cycle de vie disponibles
| Hook | Déclencheur |
|---|---|
| validate | Avant sauvegarde — pour validation |
| before_save | Juste avant l'écriture en base |
| after_insert | Après création uniquement |
| on_update | Après chaque sauvegarde (insert + update) |
| before_submit | Avant soumission du document |
| on_submit | Après soumission |
| on_cancel | Après annulation |
| on_trash | Avant suppression |
Hooks — Personnalisation sans modifier le core
Le fichier hooks.py est le point central de configuration d'une app Frappe. Il permet d'étendre les comportements sans toucher au code source d'ERPNext.
app_name = "monapp"
app_title = "Mon Application"
# Injecter du CSS/JS dans toutes les pages
app_include_css = "/assets/monapp/css/monapp.css"
app_include_js = "/assets/monapp/js/monapp.js"
# Scripts JS associés à des DocTypes
doctype_js = {
"Facture de vente": "public/js/facture_vente.js",
"Client": "public/js/client.js",
}
# Tâches planifiées
scheduler_events = {
"daily": ["monapp.tasks.rapport_quotidien"],
"hourly": ["monapp.tasks.sync_externe"],
"weekly": ["monapp.tasks.nettoyage_logs"],
}
# Surcharger des permissions globales
permission_query_conditions = {
"Client": "monapp.utils.get_client_conditions",
}
# Fixtures (données exportées avec l'app)
fixtures = ["Custom Field", "Property Setter"]
Permissions & Rôles
Frappe gère un système de permissions à plusieurs niveaux :
Niveaux de permission
| Niveau | Description |
|---|---|
| Rôle (Role) | Groupe d'utilisateurs avec des accès définis par DocType |
| Utilisateur | Permissions supplémentaires ou restrictions par utilisateur |
| Champ | Cacher ou rendre un champ en lecture seule selon le rôle |
| Document | Restreindre l'accès à certains documents (ex: seulement ses propres fiches) |
Permissions standard par rôle
- System Manager — accès complet à tous les modules
- Administrator — accès système total (base de données, paramètres serveur)
- Accounts User — accès module comptabilité en lecture/écriture
- HR Manager — accès complet module RH
- Employee — accès limité (fiche personnelle, congés, feuilles de temps)
- Rôles personnalisés créables via Frappe Desk → Rôle
API REST
Frappe expose automatiquement une API REST pour tous les DocTypes. Authentication par token API ou session.
Endpoints CRUD automatiques
| Méthode | URL | Action |
|---|---|---|
| GET | /api/resource/{DocType} | Lister les documents |
| GET | /api/resource/{DocType}/{name} | Lire un document |
| POST | /api/resource/{DocType} | Créer un document |
| PUT | /api/resource/{DocType}/{name} | Modifier un document |
| DELETE | /api/resource/{DocType}/{name} | Supprimer un document |
Exemples avec curl
# Récupérer la liste des clients
curl -H "Authorization: token api_key:api_secret" \
"https://monsite.erpsys.ovh/api/resource/Customer?limit=10"
# Créer un client
curl -X POST \
-H "Authorization: token api_key:api_secret" \
-H "Content-Type: application/json" \
-d '{"customer_name": "ACME Corp", "customer_type": "Company"}' \
"https://monsite.erpsys.ovh/api/resource/Customer"
# Appeler une méthode personnalisée
curl -X POST \
-H "Authorization: token api_key:api_secret" \
-H "Content-Type: application/json" \
-d '{"post_name": "POST-001"}' \
"https://monsite.erpsys.ovh/api/method/monapp.api.publish_post"
API Python (frappe.*)
Le module frappe expose de nombreuses fonctions utilitaires utilisables dans vos scripts Python.
import frappe
# ── Lire / écrire des documents ──────────────────────────────────
doc = frappe.get_doc("Client", "ACME Corp")
doc.email = "[email protected]"
doc.save()
# Créer un nouveau document
new_doc = frappe.get_doc({
"doctype": "Client",
"nom_client": "Nouvelle Entreprise",
"email": "[email protected]"
})
new_doc.insert()
# ── Requêtes base de données ──────────────────────────────────────
# Récupérer une valeur unique
email = frappe.db.get_value("Client", "ACME Corp", "email")
# Récupérer plusieurs documents
clients = frappe.db.get_all("Client",
filters={"actif": 1},
fields=["name", "nom_client", "email"],
limit=50
)
# SQL brut (à éviter sauf nécessité)
results = frappe.db.sql("SELECT name FROM `tabClient` WHERE actif=1", as_dict=True)
# ── Utilitaires ────────────────────────────────────────────────────
frappe.throw("Message d'erreur — arrête l'exécution")
frappe.msgprint("Message informatif")
frappe.log_error("Détail erreur", title="Titre erreur")
# Vérifier la permission de l'utilisateur courant
frappe.only_for("System Manager") # lève une exception si pas ce rôle
# Mettre en cache
frappe.cache().set_value("ma_cle", {"data": 123}, expires_in_sec=300)
val = frappe.cache().get_value("ma_cle")
Méthodes whitelistées (API publique)
import frappe
@frappe.whitelist()
def get_dashboard_data(filters=None):
"""Méthode accessible via /api/method/monapp.api.get_dashboard_data"""
# Accessible uniquement aux utilisateurs connectés
data = frappe.db.get_all("Vente", filters=filters, fields=["*"])
return data
@frappe.whitelist(allow_guest=True)
def ping():
"""Accessible sans authentification"""
return {"status": "ok", "site": frappe.local.site}
Scripts Client (JavaScript)
Les scripts client permettent de personnaliser le comportement des formulaires dans le navigateur.
frappe.ui.form.on('Client', {
// Déclenché à l'ouverture du formulaire
refresh(frm) {
// Ajouter un bouton personnalisé
if (!frm.is_new()) {
frm.add_custom_button('Envoyer Email', () => {
frappe.call({
method: 'monapp.api.envoyer_email_bienvenue',
args: { client_name: frm.doc.name },
callback(r) {
frappe.show_alert({ message: 'Email envoyé !', indicator: 'green' });
}
});
}, 'Actions');
}
},
// Déclenché quand le champ "email" change
email(frm) {
if (frm.doc.email && !frm.doc.email.includes('@')) {
frappe.msgprint('Email invalide');
frm.set_value('email', '');
}
},
// Avant sauvegarde (côté client)
before_save(frm) {
frm.set_value('nom_client', frm.doc.nom_client.toUpperCase());
}
});
Rapports personnalisés
Frappe propose plusieurs types de rapports :
| Type | Description | Usage |
|---|---|---|
| Query Report | Rapport SQL ou Python avec filtres | Analyses complexes |
| Script Report | Python qui retourne des colonnes et lignes | Rapports dynamiques |
| Report Builder | Interface glisser-déposer sans code | Rapports simples |
| Dashboard Chart | Graphiques auto depuis des données | Tableaux de bord |
def execute(filters=None):
columns = [
{"label": "Client", "fieldname": "client", "fieldtype": "Link", "options": "Customer", "width": 200},
{"label": "Montant Total", "fieldname": "total", "fieldtype": "Currency", "width": 150},
]
data = frappe.db.sql("""
SELECT customer_name as client, SUM(grand_total) as total
FROM `tabSales Invoice`
WHERE docstatus=1
GROUP BY customer_name
ORDER BY total DESC
""", as_dict=True)
return columns, data
Créer une application Frappe
Une app Frappe est un module Python standard avec une structure conventionnelle.
# Depuis le répertoire bench cd /home/frappe/frappe-bench # Créer l'app (génère la structure de base) bench new-app mon_app # Installer l'app sur un site bench --site monsite.local install-app mon_app # Structure générée mon_app/ ├── mon_app/ │ ├── __init__.py │ ├── hooks.py ← Configuration principale │ ├── modules.txt ← Liste des modules │ ├── api.py ← Méthodes whitelistées │ ├── tasks.py ← Tâches planifiées │ └── mon_module/ │ └── doctype/ │ └── mon_doctype/ │ ├── mon_doctype.json ← Définition du DocType │ └── mon_doctype.py ← Logique Python ├── requirements.txt └── setup.py
Commandes Bench essentielles
| Commande | Description |
|---|---|
| bench start | Démarrer le serveur de développement |
| bench --site X migrate | Appliquer les migrations (nouveaux DocTypes/champs) |
| bench --site X install-app APP | Installer une app sur un site |
| bench --site X clear-cache | Vider le cache Redis |
| bench --site X console | Console Python interactive (frappe shell) |
| bench --site X backup | Créer une sauvegarde complète |
| bench update | Mettre à jour toutes les apps depuis git |
| bench build | Compiler les assets JS/CSS |
| bench restart | Redémarrer tous les processus (gunicorn, workers) |
| bench setup supervisor | Regénérer la config supervisord |
Scheduler — Tâches automatiques
Le scheduler Frappe utilise Redis Queue (RQ) pour exécuter des tâches planifiées.
import frappe
def rapport_quotidien():
"""Exécutée chaque jour à minuit"""
# frappe.local.site est disponible
clients_actifs = frappe.db.count("Client", {"actif": 1})
frappe.log_error(f"Clients actifs : {clients_actifs}", title="Rapport Quotidien")
# Dans hooks.py :
# scheduler_events = {
# "daily": ["monapp.tasks.rapport_quotidien"],
# "hourly": ["monapp.tasks.sync_stocks"],
# "weekly": ["monapp.tasks.nettoyage"],
# "cron": {
# "0 9 * * 1-5": ["monapp.tasks.email_matin_semaine"]
# }
# }
Emails & Notifications
Frappe intègre un système d'envoi d'emails et de notifications.
frappe.sendmail(
recipients=["[email protected]"],
cc=["[email protected]"],
subject="Confirmation de commande",
message=frappe.render_template("monapp/templates/emails/confirmation.html", {
"doc": commande,
"company": frappe.defaults.get_defaults().get("company")
}),
attachments=[{"fname": "facture.pdf", "fcontent": pdf_content}]
)
Liens utiles
- Documentation officielle Frappe : frappeframework.com/docs
- Documentation ERPNext : docs.erpnext.com
- Forum communauté : discuss.frappe.io
- Code source Frappe : github.com/frappe/frappe
- Code source ERPNext : github.com/frappe/erpnext
- Guide installation : Guide LSI — Ubuntu 22.04