84 lines
6.2 KiB
Python
84 lines
6.2 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", "skill_levels", "Niveaux de compétence (JSON)", "Small Text", None, None, "skills",
|
|
{"description": "Maîtrise 1-5 par compétence (JSON, ex: {\"installation\":4}). Distinct de l'efficacité (=vitesse). Édité dans la grille Planification."}),
|
|
("Dispatch Technician", "skill_eff", "Efficacité par compétence (JSON)", "Small Text", None, None, "skill_levels",
|
|
{"description": "Facteur de vitesse PAR compétence (JSON, ex: {\"installation\":0.9}). Défaut = efficacité globale. Édité dans la grille Planification."}),
|
|
("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."}),
|
|
# ── Tech Availability : absence longue durée (à remplacer vs vacances) ──
|
|
("Tech Availability", "long_term", "Longue durée (à remplacer)", "Check", None, "0", "availability_type",
|
|
{"description": "Absence longue durée (maternité, invalidité…) : à REMPLACER lors de la réapplication d'un modèle, pas juste à sauter comme des vacances."}),
|
|
# ── 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}),
|
|
# ── Dispatch Job : pont de synchro avec la DB legacy (osTicket) ──
|
|
("Dispatch Job", "legacy_ticket_id", "ID ticket legacy (pont)", "Data", None, None, "ticket_id",
|
|
{"read_only": 1, "no_copy": 1, "search_index": 1,
|
|
"description": "ID du ticket osTicket legacy (table `ticket`). Renseigné par le pont legacy→dispatch (lib/legacy-dispatch-sync.js) → idempotence : 1 ticket legacy = 1 Dispatch Job."}),
|
|
("Dispatch Job", "legacy_dept", "Département legacy (pont)", "Data", None, None, "legacy_ticket_id",
|
|
{"read_only": 1, "no_copy": 1,
|
|
"description": "Département osTicket legacy (Installation Fibre / Réparation Fibre / Install-Réparation Télé / Téléphonie / Désinstallation…). Sert au coloriage des cartes dispatch « comme legacy »."}),
|
|
("Dispatch Job", "legacy_activation_url", "Lien activation TV legacy (pont)", "Small Text", None, None, "legacy_dept",
|
|
{"read_only": 1, "no_copy": 1,
|
|
"description": "Lien connect_ministra.php (activation STB/Ministra) extrait du fil du ticket legacy par le pont. Affiché tel quel dans le dispatch — MÊME lien que le tech reçoit (aucune reconstruction)."}),
|
|
("Dispatch Job", "legacy_detail", "Détails du ticket legacy (pont)", "Text", None, None, "legacy_activation_url",
|
|
{"read_only": 1, "no_copy": 1,
|
|
"description": "Description/contenu du ticket legacy (1er message du fil osTicket, HTML nettoyé) extrait par le pont → visible dans Ops sans ouvrir le legacy."}),
|
|
# ── 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")
|