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(_("Invoice {0} submitted").format(self.name))
frappe.msgprint(__("Invoice {0} created", [doc.name]));

Écrivez le texte source en anglais. La traduction française se fait dans le fichier fr.po (voir Traductions).

Longueur des fonctions

Une fonction fait une seule chose. Au-delà d'une quarantaine de lignes, elle en fait généralement plusieurs : découpez-la en fonctions nommées selon ce qu'elles font.

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 tabulations, pas des espaces. Le formateur ruff format du dépôt applique cette règle (indent-style = "tab"), avec des guillemets doubles et des lignes de 110 caractères au plus.

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

Un commentaire explique pourquoi, jamais ce que fait le code. Gardez un commentaire court pour un contournement qui n'est pas évident.

N'écrivez pas l'historique de la correction dans le code (« correction du ticket 123 », « on vérifie maintenant… ») : il va dans la description de la merge request. Une docstring est utile quand elle donne à l'appelant une information que la signature ne donne pas, par exemple une limite ou un effet de bord.

Le marqueur @dokos est la seule exception (voir la section « Spécificités Dokos » plus bas).

APIs dépréciées

Évitez ces APIs obsolètes:

  • cur_frm - Utilisez le paramètre frm des handlers
  • 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

N'écrivez pas de nouvel appel à frappe.db.sql(). Utilisez le Query Builder (frappe.qb) ou les méthodes ORM. Du code ancien utilise encore frappe.db.sql() : remplacez-le quand vous le modifiez.
# 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.db.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 Manager')
    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

Annotez les arguments des méthodes whitelistées. Dodock vérifie leur type à chaque appel HTTP et refuse une valeur du mauvais type:

@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

  1. Créez votre branche depuis la dernière version de develop du projet Dokos (dokos/dokos, dokos/dodock…), et non depuis frappe/erpnext ou frappe/frappe.
  2. Ouvrez la merge request vers develop. Ne ciblez jamais v5 : c'est la branche des versions publiées.
  3. Gardez uniquement vos commits sur la branche. Rebasez-la sur develop au lieu d'y fusionner une autre branche.

Pour qu'une correction arrive dans une version publiée, indiquez-le dans la description. Un mainteneur pose le libellé de rétroportage :

LibelléUsage
backport-v5-fixCorrection de bug pour la version stable actuelle.
backport-v5-devÉvolution pour la prochaine version mineure.

Un contributeur externe ne peut pas poser de libellé : écrivez celui que vous souhaitez dans la description.

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
  • Hooks pre-commit installés (pre-commit install et pre-commit install --hook-type commit-msg) : ruff, ruff-format, prettier, eslint, commitlint
  • Documentation mise à jour si nécessaire
  • Screenshots ou GIF pour les changements d'interface
  • Laissez cochée l'option GitLab qui autorise les commits des membres pouvant fusionner

Description de la merge request

Un relecteur doit comprendre la modification en vingt secondes :

<une ligne : ce qui ne fonctionnait pas, vu par l'utilisateur>

- <ce qui change, vu par l'utilisateur>
- <comment vous l'avez testé : étapes ou test lancé, ou « non testé »>
- Rétroportage : <backport-v5-fix | backport-v5-dev | aucun>

Closes #<ticket>

Une merge request contient une seule correction ou une seule fonctionnalité.

Contenu à éviter

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


Spécificités Dokos

Dokos et Dodock sont des copies (forks) d'ERPNext et de Frappe. Un robot reporte chaque jour les pull requests de Frappe et d'ERPNext dans ces dépôts, sur des branches nommées ft-pr-<numéro>. Un report peut écraser une modification Dokos sans le signaler. Les règles ci-dessous permettent aux mainteneurs de repérer et de garder les modifications Dokos.

Marqueur @dokos

Un fichier qui existe aussi dans frappe/erpnext ou frappe/frappe est du code hérité. C'est aussi le cas de l'interface banking/. Marquez chaque ligne ajoutée ou modifiée dans ces fichiers :

LangageMarqueur
Python# @dokos en fin de ligne, ou # @dokos: <raison> au-dessus d'un bloc
JavaScript, TypeScript// @dokos ou // @dokos: <raison>
JSX{/* @dokos: <raison> */}
Vue (template), HTML<!-- @dokos: <raison> -->

Ne marquez pas le code des modules ou des applications qui n'existent que dans Dokos.

Données client

Tous les dépôts Dokos sont publics. Ne mettez aucune donnée client dans un commit, une merge request, un ticket, un commentaire de code ou un test : nom de site ou de domaine, nom de société, de fournisseur ou de client, nom de personne, adresse, numéro SIREN, SIRET ou de TVA, identifiant de document réel. Utilisez des noms inventés dans les tests et les exemples.

Assistants IA

Le fichier AGENTS.md du dépôt Dokos reprend ces règles en anglais. Dans Dodock, elles sont dans DOKOS_AGENTS.md. Donnez ce fichier à votre assistant de code. Relisez toujours ce qu'il produit avant d'ouvrir la merge request.


Références

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

Pour contribuer à Dokos: GitLab Dokos