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.

Version couverte : Frappe v15 (Python 3.11+, Node.js 18+). Code source sur github.com/frappe/frappe

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 :

CoucheTechnologieRôle
Base de donnéesMariaDBPersistance des données
CacheRedisSessions, cache applicatif, queues de tâches
BackendPython / GunicornLogique métier, API REST, ORM
RealtimeSocket.io / Node.jsNotifications en temps réel, progression
FrontendJinja2 + JavaScriptTemplates HTML, formulaires dynamiques
WebNginxReverse 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)

monapp/monapp/doctype/client/client.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

fieldtypeDescriptionExemple
DataTexte court (255 chars)Nom, code
TextTexte longDescription, notes
IntEntierQuantité, durée
Float / CurrencyDécimal / MontantPrix, montant
Date / DatetimeDate ou date+heureDate facture
SelectListe de choix fixesStatut (Brouillon/Validé)
LinkRéférence vers un autre DocTypeClient → Facture
TableTable enfant (child table)Lignes de facture
Attach / Attach ImageFichier jointPhoto, PDF
PasswordChamp chiffréClé API, token
CheckBoolé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

Python — client.py
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

HookDéclencheur
validateAvant sauvegarde — pour validation
before_saveJuste avant l'écriture en base
after_insertAprès création uniquement
on_updateAprès chaque sauvegarde (insert + update)
before_submitAvant soumission du document
on_submitAprès soumission
on_cancelAprès annulation
on_trashAvant 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.

monapp/hooks.py
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

NiveauDescription
Rôle (Role)Groupe d'utilisateurs avec des accès définis par DocType
UtilisateurPermissions supplémentaires ou restrictions par utilisateur
ChampCacher ou rendre un champ en lecture seule selon le rôle
DocumentRestreindre 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éthodeURLAction
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

bash — Authentification par token API
# 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.

Python — Fonctions frappe.* courantes
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)

Python — Exposer une méthode via API
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.

JavaScript — doctype_js / Client Script
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 :

TypeDescriptionUsage
Query ReportRapport SQL ou Python avec filtresAnalyses complexes
Script ReportPython qui retourne des colonnes et lignesRapports dynamiques
Report BuilderInterface glisser-déposer sans codeRapports simples
Dashboard ChartGraphiques auto depuis des donnéesTableaux de bord
Python — Script Report simple
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.

bash — Créer et installer une app
# 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

CommandeDescription
bench startDémarrer le serveur de développement
bench --site X migrateAppliquer les migrations (nouveaux DocTypes/champs)
bench --site X install-app APPInstaller une app sur un site
bench --site X clear-cacheVider le cache Redis
bench --site X consoleConsole Python interactive (frappe shell)
bench --site X backupCréer une sauvegarde complète
bench updateMettre à jour toutes les apps depuis git
bench buildCompiler les assets JS/CSS
bench restartRedémarrer tous les processus (gunicorn, workers)
bench setup supervisorRegénérer la config supervisord

Scheduler — Tâches automatiques

Le scheduler Frappe utilise Redis Queue (RQ) pour exécuter des tâches planifiées.

Python — tasks.py
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.

Python — Envoi d'email
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
Besoin d'aide ? L'équipe LSI développe des applications Frappe personnalisées. Contactez-nous à [email protected] pour vos projets de développement.

Contacter l'équipe dev →