Aller au contenu

3. Compléter un écran

Les deux champs existent et l’API les renvoie. Ils n’apparaissent nulle part, parce qu’une vue liste les champs qu’elle montre, et celle de pipeline ne connaît pas les nôtres.

La tentation est de déclarer notre propre pipeline_lead.form.json. C’est le piège. Une vue complète remplace celle du module, donc :

  • vous recopiez ses trente champs pour en ajouter deux ;
  • la prochaine version de pipeline ajoute un champ, et votre copie ne l’a pas ;
  • une deuxième extension fait pareil, et vous vous battez pour le même emplacement.

La rustine

Un fichier <modèle>.<genre>.patch.json se pose par-dessus la vue existante au lieu de la remplacer.

modules/pipeline_rivals/views/pipeline_lead.form.patch.json :

{
"model": "pipeline_lead",
"kind": "form",
"module": "pipeline_rivals",
"patch": [
{
"op": "add",
"field": { "name": "rivals_competitor_id" },
"after": "source_id",
"section": "section.general"
},
{
"op": "add",
"field": { "name": "rivals_won_by_id" },
"after": "lost_reason_id",
"section": "section.general"
}
]
}

Rien à appeler : register_views_from_dir, que votre module appelle déjà pour ses propres écrans, reconnaît le suffixe et route le fichier.

module est porteur. Il devient la clé de portée de l’enregistrement, ce qui permet à deux extensions de compléter le même écran sans s’écraser. Une rustine anonyme serait indiscernable d’une autre : le noyau la refuse.

Les opérations disponibles sont add, hide, replace et move. Ancrage par before ou after sur un champ existant, et section pour dire dans quel bloc du formulaire poser le champ.

Le même geste sur la liste

modules/pipeline_rivals/views/pipeline_lead.list.patch.json :

{
"model": "pipeline_lead",
"kind": "list",
"module": "pipeline_rivals",
"patch": [
{
"op": "add",
"field": { "name": "rivals_competitor_id" },
"before": "amount_weighted_amount"
}
]
}

Les libellés

Un champ posé sur un écran affiche la clé <modèle>.<champ>. Elle est à vous, même si le modèle ne l’est pas :

{
"module.pipeline_rivals": "Competitors",
"model.rivals_competitor": "Competitors",
"rivals_competitor.name": "Name",
"rivals_competitor.tier": "Kind",
"pipeline_lead.rivals_competitor_id": "Up against",
"pipeline_lead.rivals_won_by_id": "Lost to"
}

pipeline ne traduira jamais vos champs : ils n’existent pas pour lui.

Ce que ça donne

La liste de pipeline, avec une colonne de plus exactement là où l’ancre l’a mise :

La liste des affaires, avec la colonne « Up against » entre « Close date » et « Weighted amount »

Et la fiche, où « Up against » suit « Source » et « Lost to » suit « Lost reason », dans la section « General » de pipeline :

Le formulaire d&#x27;une affaire, avec les deux champs de l&#x27;extension posés dans la section General

Le reste de l’écran est intact. Ce n’est pas une copie ressemblante : c’est la vue de pipeline, à laquelle deux opérations ont été appliquées à la lecture.

Et ensuite

Le module ajoute des données et les montre. Reste à le faire réagir, toujours sans importer ce qu’il étend : Réagir, et s’en aller.