Contribuer

Conventions de Code

Standards et bonnes pratiques pour contribuer au code de Dokos

Conventions de Code

Ces conventions sont adaptées des Contribution Guidelines du framework Frappe. Dokos étant basé sur Frappe/ERPNext, nous suivons les mêmes standards pour assurer la cohérence et faciliter les contributions croisées.

Le code source de Dokos est disponible sur GitLab.


Standards de Code

Messages utilisateur

Tous les textes affichés à l'utilisateur doivent être traduisibles:

  • En Python: _("texte")
  • En JavaScript: __("texte")
frappe.msgprint(_("Facture {0} soumise").format(self.name))
frappe.msgprint(__("Facture {0} créée", [doc.name]));

Longueur des fonctions

Les fonctions ne devraient pas dépasser 10 lignes de code. Une fonction plus longue indique généralement qu'elle fait trop de choses.

Signes qu'une fonction est trop complexe:
  • Plus d'un niveau d'indentation
  • Des "paragraphes" séparés par des lignes vides

Indentation

Utilisez des tabs (caractères de tabulation), pas des espaces. C'est la convention historique de Frappe.

Les chaînes multi-lignes et expressions doivent être indentées de façon cohérente:

D = frappe.qb.DocType("Accounting Dimension")
query = frappe.qb.from_(D).select(D.label, D.fieldname, D.document_type).where(D.disabled == 0).run(as_dict=1)
Dimension = frappe.qb.DocType("Accounting Dimension")
query = (
    frappe.qb.from_(Dimension)
    .select(
        Dimension.label,
        Dimension.fieldname,
        Dimension.document_type,
    )
    .where(Dimension.disabled == 0)
)
results = query.run(as_dict=1)

Ordre des fonctions

La fonction appelante doit être placée en haut, les fonctions appelées en dessous:

def fa():
    fb()
    fc()

def fb():
    pass

def fc():
    pass

Conditions multiples

Évitez de regrouper plusieurs conditions sur une seule ligne. Séparez-les pour améliorer la lisibilité.

Logique métier

Toute la logique métier doit être implémentée côté serveur (Python). Lors des appels REST API, seul le code Python s'exécute. Le JavaScript est réservé à l'amélioration de l'expérience utilisateur.

Commentaires

Le code doit être auto-explicatif, mais les commentaires expliquant le pourquoi sont essentiels. Chaque méthode devrait avoir une brève explication de son but.

APIs dépréciées

Évitez ces APIs obsolètes:

  • cur_frm - Utilisez le paramètre frm des handlers
  • $c_obj() - Utilisez frappe.call()
  • get_query - Utilisez set_query
  • add_fetch - Utilisez les Link fields avec fetch_from

Sécurité du Code

Adapté de Code Security Guidelines - Frappe/ERPNext

Injections SQL

frappe.db.sql() est proscrit sur Dokos. Utilisez le Query Builder (frappe.qb) ou les méthodes ORM.
# RECOMMANDÉ - Query Builder
from frappe.query_builder import DocType

User = DocType('User')
query = (
    frappe.qb.from_(User)
    .select(User.name, User.email)
    .where(User.name == user)
)
result = query.run(as_dict=True)

# RECOMMANDÉ - Méthodes ORM (cas simples)
frappe.get_all('User',
    fields=['name', 'email'],
    filters={'name': user}
)

frappe.get_value('User', user, ['name', 'email'])

Avantages du Query Builder :

  • Protection contre les injections SQL : paramétrage automatique des valeurs
  • Compatibilité cross-database : même syntaxe pour MariaDB et PostgreSQL
  • API Python typée : objets Field et Function au lieu de chaînes de caractères

Création de documents

Les méthodes whitelistées acceptant des dictionnaires peuvent contourner les permissions. Ajoutez des vérifications explicites:

@frappe.whitelist()
def create_document(values: dict):
    frappe.only_for('System User')
    if values['doctype'] not in ('ToDo', 'Note', 'Task'):
        frappe.throw(_('Type de document invalide'))
    doc = frappe.get_doc(values).insert()
    return doc

Injection de code

  • Évitez eval() et exec()
  • Utilisez uniquement frappe.safe_eval ou frappe.utils.safe_exec.safe_exec

Traversée de répertoires

Ne permettez jamais aux utilisateurs de contrôler les chemins de fichiers. Utilisez l'API du DocType "File".

Permissions par défaut

  • frappe.get_doc() ne vérifie PAS les permissions par défaut
  • Préférez frappe.get_list() à frappe.get_all() pour appliquer les permissions
  • Vérifiez explicitement les permissions dans les méthodes whitelistées:
@frappe.whitelist()
def get_document(doctype: str, name: str):
    doc = frappe.get_doc(doctype, name)
    doc.check_permission("read")
    return doc

Validation des types

Utilisez les annotations de type pour validation automatique:

@frappe.whitelist()
def get_user(name: str):
    doc = frappe.get_doc("User", name)
    doc.check_permission("read")
    return doc

Checklist Merge Request

Adapté de Pull Request Checklist - Frappe/ERPNext

Branches cibles

  • develop - À utiliser par défaut. Les corrections seront backportés sur la branche principale au cas par cas.
  • vX-dev- À utiliser pour une publication dans une version mineur.
  • vX-fix - Uniquement si le correctif n'est pas applicable en develop (e.g. rupture de rétro-comptabilité).

Messages de commit

Format Conventional Commits: type(scope): description

TypeDescription
featNouvelle fonctionnalité
fixCorrection de bug
docsDocumentation uniquement
refactorRefactoring sans changement fonctionnel
testAjout ou modification de tests

Vérifications avant soumission

  • Tous les tests passent
  • Pre-commit hooks installés (ruff, prettier, eslint)
  • Documentation mise à jour si nécessaire
  • Screenshots ou GIF pour les changements d'interface
  • Activez l'option "Allow edits from maintainers"

Contenu à éviter

N'incluez pas de changements automatisés (linters de masse, corrections IA non revues) sans validation manuelle.


Références

Ces conventions sont dérivées de la documentation officielle Frappe/ERPNext:

Pour contribuer à Dokos: GitLab Dokos