Version de développement
Cette documentation décrit la version « next » (develop), non encore publiée. Pour la version stable, basculez sur « v5 » dans l'en-tête.
Interface

Utiliser les points d'extension client

Guide pour étendre les fonctionnalités client de Dodock via des hooks JavaScript

Utiliser les points d'extension client

Dodock offre plusieurs points d'extension JavaScript pour personnaliser le comportement de l'interface utilisateur sans modifier le code source principal. Ces hooks permettent d'intervenir à des moments clés du cycle de vie des documents et des actions.

Points d'extension disponibles

1. Guards pour les documents mappés (open_mapped_doc)

Les guards permettent de contrôler l'ouverture des documents créés via un mapping (ex: créer une facture à partir d'une commande).

Fonctions disponibles

  • frappe.model.add_mapped_doc_guard(fn) Enregistre une fonction de garde qui sera exécutée avant l'ouverture d'un document mappé.
    frappe.model.add_mapped_doc_guard((mapped_doc, opts) => {
        // mapped_doc: objet du document en mémoire (non sauvegardé)
        // opts: options passées à open_mapped_doc
        return true; // true = ouvrir le document, false = annuler
    });
    
  • frappe.model.remove_mapped_doc_guard(fn) Supprime un guard précédemment enregistré.
    const myGuard = (mapped_doc) => { /* ... */ };
    frappe.model.add_mapped_doc_guard(myGuard);
    frappe.model.remove_mapped_doc_guard(myGuard); // Désenregistre
    
  • frappe.model.should_open_mapped_doc(mapped_doc, opts) Exécute tous les guards enregistrés et retourne true si tous autorisent l'ouverture.
    const canOpen = await frappe.model.should_open_mapped_doc(mapped_doc, opts);
    

Cas d'usage

  1. Validation métier Vérifier que le document mappé respecte des règles spécifiques avant création.
    frappe.model.add_mapped_doc_guard((mapped_doc) => {
        if (mapped_doc.doctype === "Facture" && mapped_doc.total < 0) {
            frappe.msgprint("Une facture ne peut pas avoir un total négatif.");
            return false;
        }
        return true;
    });
    
  2. Confirmation utilisateur Demander une confirmation avant d'ouvrir le document.
    frappe.model.add_mapped_doc_guard((mapped_doc) => {
        return new Promise((resolve) => {
            frappe.confirm(
                `Voulez-vous vraiment créer un ${mapped_doc.doctype} ?`,
                () => resolve(true),
                () => resolve(false)
            );
        });
    });
    
  3. Vérification de doublons Empêcher la création de documents en double.
    frappe.model.add_mapped_doc_guard(async (mapped_doc) => {
        const exists = await frappe.db.exists(mapped_doc.doctype, {
            name: mapped_doc.name,
            docstatus: ["!=", 2]
        });
        if (exists) {
            frappe.msgprint("Un document similaire existe déjà.");
            return false;
        }
        return true;
    });
    

Comportement technique

  • Les guards s'exécutent après la création du document en mémoire (make_mapped_doc) mais avant sa sauvegarde et son ouverture.
  • Un guard peut retourner une Promise pour des opérations asynchrones (ex: requêtes API).
  • Si un guard retourne false, le document n'est pas créé et l'interface reste sur le document source.
  • Une erreur dans un guard est loguée mais n'empêche pas la création du document (pour éviter des blocages).
  • L'interface reste gelée pendant l'exécution des guards pour éviter les interactions utilisateur.

Exemple complet

// Enregistrer un guard pour les bons de livraison
frappe.model.add_mapped_doc_guard(async (mapped_doc) => {
    if (mapped_doc.doctype !== "Bon de Livraison") return true;
    
    // Vérifier si un bon de livraison existe déjà pour la même commande
    const existing = await frappe.db.get_value(
        "Bon de Livraison", 
        {
            commande: mapped_doc.commande,
            docstatus: ["!=", 2]
        },
        "name"
    );
    
    if (existing) {
        const confirm = await frappe.confirm(
            `Un bon de livraison (${existing.name}) existe déjà pour cette commande. Voulez-vous continuer ?`
        );
        return confirm;
    }
    
    return true;
});

Bonnes pratiques

  1. Idempotence Un guard doit pouvoir être enregistré plusieurs fois sans effet de bord (la fonction gère déjà cela).
  2. Gestion des erreurs Toujours encapsuler les opérations asynchrones dans un try/catch pour éviter des blocages.
  3. Performance Éviter les requêtes lourdes dans les guards pour ne pas ralentir l'interface.
  4. Désenregistrement Utiliser remove_mapped_doc_guard pour nettoyer les guards inutiles (ex: dans un script de désinstallation).

Limitations

  • Les guards ne peuvent pas modifier le document mappé (utiliser before_save ou validate pour cela).
  • Les guards ne s'appliquent qu'aux documents créés via open_mapped_doc (pas aux créations manuelles).

Dépannage

  • Problème : Le document ne s'ouvre pas après un mapping. Solution : Vérifier les logs de la console pour des erreurs dans les guards.
  • Problème : L'interface reste gelée. Solution : Un guard asynchrone ne résout pas sa Promise. Vérifier les opérations asynchrones.

::: tip Pour tester un guard dans la console du navigateur :

frappe.model.add_mapped_doc_guard((mapped_doc) => {
    console.log("Guard exécuté pour:", mapped_doc.doctype, mapped_doc.name);
    return true;
});

:::