Modularisation / dé-duplication : - lib/util/text.js : `norm` canonique partagé (remplace 2 ré-implémentations : address-db, legacy-dispatch-sync). - lib/util/legacy-parse.js : parseurs/mapping PURS du pont (DEPT_JOBTYPE, DUR, jobType, prio, tzDate, startTime, coord) extraits hors I/O → testables en isolation, sans pg/mysql/erp. - legacy-dispatch-sync + address-db importent ces utils (pont vérifié en prod : preview OK, 0 erreur). Observabilité (sûr, additif, 1 seul point) : - erp.js create/update/remove : log de l'échec à la SOURCE quand HTTP≥400 → toutes les écritures ERPNext silencieuses des 50+ appelants sont désormais tracées, SANS changer aucun flux de contrôle. Tests (fondation) : - vitest + npm test ; test/util.test.js : 19 tests verts sur norm + coord(bornes QC)/prio/startTime/jobType/tzDate. Tournent sans installer les deps lourdes du hub (modules purs). Aligné docs/architecture/VISION.md (P0 hygiène). Suite : audit r.ok des appelants financiers (payments/contracts) en revue supervisée ; CI/CD minimal (Gitea Actions lint+test) ; décomposition des god-files (Phase 2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
37 lines
1.9 KiB
JavaScript
37 lines
1.9 KiB
JavaScript
'use strict'
|
|
/**
|
|
* util/legacy-parse.js — parseurs/mapping PURS du pont legacy (osTicket → Dispatch Job).
|
|
* Aucune dépendance I/O (pg/mysql/erp) → testable en isolation (Phase 1 : logique pure séparée des I/O).
|
|
*/
|
|
|
|
// dept_id legacy → job_type Dispatch Job (valeurs valides : Installation/Réparation/Retrait/Dépannage/Autre)
|
|
const DEPT_JOBTYPE = {
|
|
27: 'Installation', 12: 'Installation', 7: 'Installation', // Installation Fibre / Installation / Monteur
|
|
26: 'Réparation', 10: 'Réparation', 33: 'Réparation', // Réparation Fibre / Réparation / Fusionneur
|
|
15: 'Retrait', // Désinstallation
|
|
}
|
|
const DUR = { Installation: 2, 'Réparation': 1.5, Retrait: 1, 'Dépannage': 1, Autre: 1 } // durée par défaut (le legacy n'en a pas)
|
|
const jobType = (deptId) => DEPT_JOBTYPE[deptId] || 'Autre'
|
|
const prio = (p) => { p = Number(p) || 0; return p >= 3 ? 'high' : p === 2 ? 'medium' : 'low' }
|
|
// due_date legacy = epoch à minuit LOCAL → date America/Toronto (évite le décalage UTC)
|
|
const tzDate = (unix) => (unix ? new Date(Number(unix) * 1000).toLocaleDateString('en-CA', { timeZone: 'America/Toronto' }) : null)
|
|
function startTime (dueTime) {
|
|
if (!dueTime) return null
|
|
const m = String(dueTime).match(/^(\d{1,2}):(\d{2})/)
|
|
if (m) return m[1].padStart(2, '0') + ':' + m[2] + ':00'
|
|
const t = String(dueTime).toLowerCase()
|
|
if (t === 'am') return '08:00:00'
|
|
if (t === 'pm') return '13:00:00'
|
|
return null // 'day' / inconnu → pas d'heure précise
|
|
}
|
|
// Coords legacy = chaînes ("-73.5599440" / "45.2528570"). Parse + valide les bornes Québec
|
|
// (lat 44→63, lon -80→-57) pour rejeter 0/0, placeholders et valeurs aberrantes → routage fiable.
|
|
function coord (lat, lon) {
|
|
const la = parseFloat(lat), lo = parseFloat(lon)
|
|
if (!isFinite(la) || !isFinite(lo)) return null
|
|
if (la < 44 || la > 63 || lo < -80 || lo > -57) return null
|
|
return { lat: la, lon: lo }
|
|
}
|
|
|
|
module.exports = { DEPT_JOBTYPE, DUR, jobType, prio, tzDate, startTime, coord }
|