64 lines
3.8 KiB
Python
64 lines
3.8 KiB
Python
"""
|
|
Custom Fields pour la planification (Roster AI) + prise de RDV — idempotent.
|
|
==========================================================================
|
|
Consolide tous les champs ajoutés à Dispatch Technician et Dispatch Job pour
|
|
le module Roster/Planification et le booking. Remplace les scripts épars.
|
|
|
|
Exécution :
|
|
docker cp setup_dispatch_custom_fields.py erpnext-backend-1:/tmp/
|
|
docker exec erpnext-backend-1 sh -lc \
|
|
"cd /home/frappe/frappe-bench && env/bin/python /tmp/setup_dispatch_custom_fields.py"
|
|
(créer d'abord /home/frappe/logs si frappe.init s'en plaint — cf.
|
|
reference_frappe_hr_postgres.md §Pièges scripts)
|
|
"""
|
|
import frappe
|
|
|
|
frappe.init(site="erp.gigafibre.ca", sites_path="sites")
|
|
frappe.connect()
|
|
frappe.flags.ignore_permissions = True
|
|
|
|
# (dt, fieldname, label, fieldtype, options, default, insert_after, extra)
|
|
FIELDS = [
|
|
# ── Dispatch Technician : cadence, coût chargé, compétences ──
|
|
("Dispatch Technician", "efficiency", "Cadence (facteur temps)", "Float", None, "1.0", "tech_group",
|
|
{"description": "1.0 = normal · 1.10 = +10% (plus lent) · 0.90 = -10% (plus rapide)"}),
|
|
("Dispatch Technician", "skills", "Compétences", "Data", None, None, "tech_group",
|
|
{"description": "Séparées par des virgules, ex: fibre,cuivre,aerien"}),
|
|
("Dispatch Technician", "cost_salary_h", "Salaire horaire ($/h)", "Float", None, "0", "efficiency", {}),
|
|
("Dispatch Technician", "cost_charges_pct", "Charges sociales (%)", "Float", None, "0", "cost_salary_h", {}),
|
|
("Dispatch Technician", "cost_other_h", "Autres coûts/h ($ véhicule, outils, frais)", "Float", None, "0", "cost_charges_pct", {}),
|
|
# ── Shift Template : quart de garde (sur appel) ──
|
|
("Shift Template", "on_call", "Garde (sur appel — non offert au booking)", "Check", None, "0", "color",
|
|
{"description": "Quart de garde/urgence : capacité de réserve. NON offert au booking client et exclu du calcul de capacité offrable. S'affiche en bande hachurée sur la timeline."}),
|
|
# ── Dispatch Job : prise de RDV ──
|
|
("Dispatch Job", "booking_prefs", "Préférences RDV (JSON)", "Small Text", None, None, "status", {}),
|
|
("Dispatch Job", "booking_status", "Statut RDV", "Select", "À planifier\nProposé\nConfirmé\nAnnulé\nÀ reporter", "À planifier", "booking_prefs", {}),
|
|
("Dispatch Job", "booking_token", "Jeton RDV client", "Data", None, None, "booking_status",
|
|
{"read_only": 1, "no_copy": 1}),
|
|
# ── Service Contract : installation financée (conformité CRTC 2026-43, pas de clawback) ──
|
|
("Service Contract", "monthly_regular", "Forfait — prix original (barré)", "Currency", None, None, "monthly_rate",
|
|
{"description": "Prix mensuel de référence barré (marketing). Le montant facturé reste monthly_rate. Vide/≤ = aucun barré."}),
|
|
("Service Contract", "install_fee", "Installation financée ($)", "Currency", None, None, "monthly_regular",
|
|
{"description": "Install financée sur la durée (vraie créance, pas une promo). Ex: 240 standard / 120 simple."}),
|
|
("Service Contract", "install_regular", "Installation — valeur affichée (barrée)", "Currency", None, None, "install_fee",
|
|
{"description": "Prix de référence barré (marketing), ex. 360. Le montant réellement financé/dû reste install_fee (ex. 240)."}),
|
|
]
|
|
|
|
for dt, fn, label, ft, opts, default, after, extra in FIELDS:
|
|
cf = f"{dt}-{fn}"
|
|
if frappe.db.exists("Custom Field", cf):
|
|
print("EXISTS", cf)
|
|
continue
|
|
doc = {"doctype": "Custom Field", "dt": dt, "fieldname": fn, "label": label,
|
|
"fieldtype": ft, "insert_after": after}
|
|
if opts:
|
|
doc["options"] = opts
|
|
if default is not None:
|
|
doc["default"] = default
|
|
doc.update(extra or {})
|
|
frappe.get_doc(doc).insert()
|
|
print("CREATED", cf)
|
|
|
|
frappe.db.commit()
|
|
print("DONE")
|