Applications personnalisées

Champs additionnels

Créer des champs personnalisés et des propriétés depuis une application, pour synchroniser plusieurs sites sans l'interface.

Créer des champs additionnels à l'aide d'une application personnalisée

Une application personnalisée peut créer des champs personnalisés et des « Property Setter » par le code. Les mêmes champs arrivent alors sur tous les sites où l'application est installée (développement, recette, production), sans passer par Personnaliser un formulaire.

Principe

  1. Le hook after_install crée les champs à l'installation de l'application.
  2. Le hook after_migrate les met à jour à chaque bench migrate.
  3. La fonction create_custom_fields crée un champ absent et met à jour un champ existant.
  4. La liste OLD_FIELDS supprime les champs que vous ne voulez plus.

Déclarer les hooks

my_app/hooks.py
after_install = "my_app.install.after_install"
after_migrate = "my_app.install.after_migrate"

Écrire le module d'installation

L'exemple ajoute le champ my_app_field après le champ customer_name du type de document Client (Customer).

my_app/install.py
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields

# The existing OLD_FIELDS will always be deleted first.
# Make sure to include all fields that you create in this list.
# When removing a field from the CUSTOM_FIELDS list, keep its entry in the OLD_FIELDS.
# This will ensure that future migrations will correctly remove obsolete fields.
# The fields will be created in module MODULE.
# Each property setter is deleted and inserted again at every migrate.
# Keep it in the PROPERTY_SETTERS list until it becomes obsolete.

MODULE = "My App"

OLD_FIELDS = [
    ("Customer", "my_field"),
]

CUSTOM_FIELDS = {
    "Customer": [
        {
            "fieldname": "my_app_field",
            "label": "My Field",
            "fieldtype": "Data",
            "insert_after": "customer_name",
        },
    ],
}

PROPERTY_SETTERS = [
    # {
    #   "name": "DOCTYPE-FIELD-PROPERTY",
    #   "doc_type": "DOCTYPE",
    #   "doctype_or_field": "DocField",
    #   "field_name": "FIELD",
    #   "property": "PROPERTY",
    #   "value": "VALUE",
    # },
]


def after_install():
    after_migrate()


def after_migrate():
    for dt, fieldname in OLD_FIELDS:
        if name := frappe.db.exists("Custom Field", {"dt": dt, "fieldname": fieldname}):
            frappe.delete_doc("Custom Field", name)

    # Add `module` property and `insert_after` if missing
    for fields in CUSTOM_FIELDS.values():
        prev_field = None
        for field in fields:
            field["module"] = MODULE
            if prev_field and not field.get("insert_after"):
                field["insert_after"] = prev_field["fieldname"]
            prev_field = field

    create_custom_fields(CUSTOM_FIELDS)

    # Insert property setters
    for ps in PROPERTY_SETTERS:
        assert ps["name"], "Property Setter name is required"
        frappe.delete_doc_if_exists("Property Setter", ps["name"])

        doc = frappe.new_doc("Property Setter")  # type: ignore
        doc.is_system_generated = True
        doc.module = MODULE
        doc.update(ps)
        doc.insert()

Points d'attention

  • Gardez dans OLD_FIELDS chaque champ retiré de CUSTOM_FIELDS. Sans cela, le champ reste sur les sites déjà installés.
  • Le nom d'un « Property Setter » suit le format <type de document>-<champ>-<propriété>, par exemple Customer-customer_group-reqd. Dodock recalcule ce nom à l'insertion : utilisez ce format dans PROPERTY_SETTERS.
  • Pour un seul « Property Setter », frappe.make_property_setter() fait le même travail en un appel.
  • Préfixez le nom de vos champs (my_app_…) pour éviter un conflit avec un champ standard ou une autre application.