🚚 VROOM ! — Spécifications Techniques
Documentation complète du système de gestion des livraisons Voyey — v3.1 — 18/05/2026
1. Architecture générale
VROOM est une application web de gestion logistique pour les livraisons de colis en Guadeloupe. Elle gère le cycle complet : réception en stock → planification des tournées → livraison → historique.
┌─────────────────┐ FDW (next_server) ┌─────────────────────┐
│ DB Next (prod) │◄────────────────────────►│ Supabase (Voyey) │
│ DigitalOcean │ next_data schema │ PostgreSQL │
└─────────────────┘ │ │
│ vr_parcels_cache │
│ vr_tours / v2 │
│ vr_tours_meta │
│ vr_deliveries │
│ vr_comments │
│ vr_client_labels │
│ vr_gps_corrections │
│ vr_trucks │
│ vr_truck_history │
│ vr_agents │
└──────────┬──────────┘
│ Supabase JS SDK
┌──────────▼──────────┐
│ Frontend (Web) │
│ index.html │
│ vr-app.js │
│ vr-database.js │
│ vr-history.html/js │
└─────────────────────┘
2. Stack technique
Frontend
| Language | Vanilla JavaScript ES6 |
| Framework | Aucun — modules IIFE |
| HTML/CSS | HTML5, CSS3 Grid/Flexbox |
| Carte | Leaflet.js 1.9.4 (OpenStreetMap) |
| Export PDF | jsPDF (via window.open + print) |
| Build | Aucun — imports directs |
Backend
| Base de données | Supabase (PostgreSQL 15) |
| Source données | FDW next_server → DB Next |
| Auth | Custom (email + PIN 6 chiffres) |
| Hébergement | vtt.xo.je — InfinityFree (Apache mutualisé, dépôt FTP). ⚠️ Migration vers voyey.com envisagée mais NON encore effectuée au 18/05/2026. |
| Sync | Cron pg_cron multi-paliers : full /15 min, light /5 min, images /15 min, purge /30 min (détail §19) |
| API | Supabase REST (PostgREST) |
3. Fichiers du projet
| Fichier | Taille | Rôle |
index.html | 280 KB | UI principale — tous les onglets, modals, styles CSS |
vr-app.js | 523 KB | Logique métier — 12 000+ lignes, IIFE, 120+ fonctions publiques |
vr-database.js | 29 KB | Couche d'abstraction Supabase — 730 lignes, module IIFE VR_Database |
vr-history.html | 19 KB | UI historique — page séparée |
vr-history.js | 58 KB | Logique historique — 1 275 lignes, module IIFE VRHistory |
sql/update_sync_parcels_cache.sql | 6 KB | Fonction RPC sync_parcels_cache() — synchronisation FDW |
sql/vr_trucks.sql | 6.5 KB | Schéma camions — tables + fonctions + vue |
4. Base de données (Supabase)
4.1 vr_parcels_cache TABLE SYNC 30min
Cache local des colis synchronisé depuis la DB Next via FDW. Source de vérité pour l'app VR.
| Colonne | Type | Description |
id | BIGINT PK | ID colis dans Next (logistics_parcel.id) |
sticker | VARCHAR | Numéro de sticker (numérique, ex: 46831) |
type | VARCHAR | Type de colis (L1, L2, XL, Pneu, etc.) |
weight | FLOAT | Poids en kg |
client_email | VARCHAR | Email du client (clé de regroupement) |
client_name | VARCHAR | Prénom + Nom |
client_phone | VARCHAR | Téléphone (+590...) |
address | TEXT | Adresse de livraison spécifique ou par défaut |
ville | VARCHAR | Ville (depuis logistics_city ou adresse) |
zip_code | INT | Code postal (971XX) |
lieu_dit | TEXT | Lieu-dit / complément d'adresse |
instruction | TEXT | Instructions de livraison |
latitude | FLOAT | GPS latitude (nullable) |
longitude | FLOAT | GPS longitude (nullable) |
gps_certified | BOOLEAN | GPS vérifié manuellement |
zone_id | INT | ID zone de livraison (FK logistics_zone) |
zone_name | VARCHAR | Nom de zone (B/T-Z1, G/T-Z2, Point relais Jarry...) |
transaction_id | BIGINT | ID transaction/container Next |
transaction_name | VARCHAR | Nom transaction (T0320, T0321...) |
etagere | VARCHAR | Numéro d'étagère (1-799 = stock, 800-899 = camions) |
status | VARCHAR | Statut colis (voir §4.1.1) |
order_value | DECIMAL | Valeur déclarée de la commande |
payment_status | VARCHAR | Statut paiement (PAID, PENDING...) |
images | JSONB | Photos du colis [{id, url, date}] |
anomaly_flags | TEXT[] | Flags d'anomalies détectées (voir §20) |
parcel_created_at | TIMESTAMPTZ | Date de création dans Next |
synced_at | TIMESTAMPTZ | Dernière synchronisation |
4.1.1 Statuts des colis (whitelist sync)
| Status | Signification | Dans le cache ? |
PRET_RETRAIT | En stock, prêt à retirer en point relais | ✅ Oui |
PRET_POUR_PLANIFICATION | En stock, à planifier en livraison | ✅ Oui |
PRET_LIVRAISON | Planifié, prêt à livrer | ✅ Oui |
PLANIFICATION_LIVRAISON | En cours de planification | ✅ Oui |
PRET_RECUPERATION | Prêt à récupérer | ✅ Oui |
STOCKE | En stock (pas encore prêt) | ✅ Oui |
TRANSIT | En transit France → Guadeloupe | ❌ Exclu |
RECEPTIONNE_BOISSY | Reçu au dépôt France | ❌ Exclu |
ENREGISTRE_VOYEY | Enregistré mais pas prêt | ❌ Exclu |
LIVRE | Livré | ❌ Exclu |
PERDU | Perdu | ❌ Exclu |
ANNULE | Annulé | ❌ Exclu |
4.2 Autres tables
| Table | PK / Unique | Rôle |
vr_tours | (tour_number, tour_date) | Tournées legacy — liste d'emails par tournée/date |
vr_tours_v2 | id (UUID) | Tournées v2 — clients_data (JSONB), status, planned_date |
vr_tours_meta | (tour_num, date) | Métadonnées tournée — agent, véhicule, schedule, report, tracking |
vr_deliveries | (tour_number, tour_date, client_email) | Statut livraison par client — delivered/absent/postponed/cancelled |
vr_comments | id (auto) | Commentaires clients — contenu, auteur, priorité, mentions |
vr_client_labels | client_email | Labels priorité — normal/high/urgent |
vr_gps_corrections | client_email | Corrections GPS manuelles — lat, lng |
vr_trucks | id | Config camions — driver, shelf_start..shelf_end, bags_count |
vr_truck_history | id | Historique mouvements camion — loaded/unloaded/delivered |
vr_agents | id | Liste des agents/livreurs |
vr_notifications | id | Notifications mentions @agent |
vr_clients_geo_capture | id | GPS capturés pendant livraison |
5. FDW & Synchronisation
Serveur FDW
| Param | Valeur |
| Nom | next_server |
| Wrapper | postgres_fdw |
| Host | db-postgresql-voyey-production-db-do-user-4749825-0.m.db.ondigitalocean.com |
| Port | 25060 |
| Database | voyeyproduction |
| Remote user | alexandre |
| Schéma local | next_data |
Tables distantes (next_data.*)
logistics_parcel, logistics_shelf, logistics_zone, logistics_city, logistics_transaction, logistics_orderrequest, logistics_parcelimage, userzone_customer, userzone_customeraddress, userzone_user, payment_payment
Fonction sync_parcels_cache() RPC CRON multi-paliers
Appelée par sync_parcels_full_safe() via pg_cron job sync-parcels-full-15min (5,20,35,50 * * * *, timeout 600s). Sync incrémentale légère en complément via sync_parcels_light() (*/5). Voir §19.
1
Crée table temporaire _protected_stickers depuis les tournées actives (draft, ready, in_progress, paused) + completed < 24h
2
DELETE du cache : supprime les colis non protégés qui n'existent plus dans Next avec un statut éligible
3
UPSERT : insère/met à jour tous les colis éligibles depuis next_data avec jointures adresse (spécifique + fallback default), zone, ville, transaction, étagère
4
Nettoyage table temporaire
Filtres critiques dans le WHERE :
• p.is_deleted = false
• p.sticker IS NOT NULL (exclut colis sans sticker)
• p.status IN ('PRET_LIVRAISON', 'PLANIFICATION_LIVRAISON', 'PRET_POUR_PLANIFICATION', 'PRET_RETRAIT', 'PRET_RECUPERATION', 'STOCKE')
• p.is_delivered = false OR sticker protégé
Fonction sync_parcel_images() RPC
Met à jour la colonne images (JSONB) depuis next_data.logistics_parcelimage. Appel manuel.
6. vr-database.js — Module DB
Module IIFE VR_Database. Couche d'abstraction Supabase. Toutes les fonctions sont asynchrones sauf init(), isTruckShelf() et convertToClients().
Connexion
| Fonction | Retour | Description |
init() | boolean | Initialise le client Supabase. Doit être appelé avant toute opération. |
getClient() | SupabaseClient | Retourne l'instance Supabase brute |
syncCache() | string|null | Appelle RPC sync_parcels_cache() |
getLastSyncDate() | string|null | Retourne le synced_at le plus récent |
Colis & Clients
| Fonction | Retour | Description |
getAllParcels(excludeTrucks=false) | Parcel[] | Charge tous les colis. Exclut automatiquement les anomalies (sans étagère sauf PRET_LIVRAISON/PLANIFICATION_LIVRAISON). Option pour exclure les camions (800-899). |
getCacheStats() | Stats{} | Statistiques agrégées : total, par zone, par ville, par transaction, livraison vs retrait |
convertToClients(parcels) | Client[] | Regroupe les colis par email client. Détermine le type (livraison/retrait), corrige zones et villes via code postal, marque les colis en camion. |
Tournées
| Fonction | Retour | Description |
saveTour(n, emails) | boolean | UPSERT vr_tours (legacy) — tour_number + tour_date du jour |
loadTours() | Tour[] | Charge les tournées à partir d'aujourd'hui |
Commentaires
| Fonction | Retour | Description |
loadComments(email?) | Comment[] | Charge les commentaires. Si email fourni, filtre par client. |
addComment(email, content, author, priority) | boolean | Ajoute un commentaire. Priority : normal | high | urgent |
loadCommentsStats() | {email: {count, maxPriority}} | Stats pour badges dans la liste clients |
Labels, GPS, Livraisons, Agents
| Fonction | Retour | Description |
loadLabels() | Label[] | Charge les labels de priorité client |
saveLabel(email, priority) | boolean | UPSERT ou DELETE si priority=normal |
loadGPSCorrections() | GPS[] | Charge les corrections GPS manuelles |
saveGPSCorrection(email, lat, lng) | boolean | UPSERT correction GPS |
loadDeliveries(tourNum, date) | Delivery[] | Charge les livraisons pour une tournée/date |
saveDelivery(tourNum, date, email, status, comment) | boolean | UPSERT statut de livraison |
loadAgents() | Agent[] | Charge la liste des agents/livreurs |
Camions
| Fonction | Retour | Description |
isTruckShelf(shelf) | boolean | Vérifie si étagère entre 800-899 |
loadTrucks() | Truck[] | Config camions actifs (driver, shelves, bags) |
getTruckParcels() | {trucks, totalParcels} | Colis groupés par camion avec main/bags |
updateTruck(truckId, updates) | Truck | Met à jour la config d'un camion |
logTruckAction(truckId, parcelId, sticker, action, from, to, userId, userName, notes) | History | Enregistre un mouvement (loaded, unloaded, moved_to_bag, delivered) |
Constantes exportées
ZONES_LIVRAISON = ['B/T-Z1', 'B/T-Z2', 'B/T-Z3', 'G/T-Z1', 'G/T-Z2', 'G/T-Z3']
ZONES_RETRAIT = ['Point relais Jarry']
TRUCK_SHELF_MIN = 800
TRUCK_SHELF_MAX = 899
7. vr-app.js — Application principale
Module IIFE. 12 000+ lignes. Gère toutes les vues, interactions, filtres et persistance.
Fonctions utilitaires critiques
| Fonction | Retour | Description |
getLocalDateStr(date?) | string YYYY-MM-DD | Timezone-safe. Retourne la date locale. Accepte Date, string ISO, YYYY-MM-DD ou vide. Remplace toISOString().split('T')[0]. |
formatDateFr(dateStr) | string | Timezone-safe. "mer. 27 mars". Utilise new Date(y, m-1, d, 12) en heure locale. |
getTourDisplayNum(tourNum) | number | Retourne le vrai numéro de tournée (identité). Les couleurs sont gérées séparément par getTourColor(). |
getTourColor(tourNum) | string CSS | Couleur basée sur l'index dans activeTourNumbers. Fallback : hsl((num*137)%360, 70%, 50%) |
getTourKey(tourNum, date) | string | Clé composite ${date}-T${tourNum} pour indexer toursMeta |
Onglets principaux (10 vues)
| Vue | data-view | Description |
| 📊 Dashboard | dashboard | KPIs : colis total, clients, GPS %, zones, transactions, colis lourds/valeur |
| 📦 Transactions | transactions | Vue par transaction/container. Filtrage, assignation par lot. |
| 👥 Clients | clients | Liste complète. Filtres zone/transaction/GPS. Sidebar livraison/retrait/mixte. |
| 🗺️ Carte | map | Carte Leaflet interactive. Markers par zone/tournée. Popups client. |
| 🚚 Tournées | tours | Grille tournées par ville/zone. Cards avec statut, clients, GPS. Planification. |
| 🚛 Camions | trucks | Vue camions. Étagères 800-899. Main + bags (grille 3x3). Colis par compartiment. |
| 📋 Livraison | delivery | Interface livraison active. Client courant, GPS, timer, pause, commentaire. |
| 👔 Manager | manager | Supervision temps réel. Stats par tournée. Suivi agents. Config camions. |
| 📜 Historique | history | Lien vers vr-history.html (page séparée) |
| ⚠️ Anomalies | anomalies | Lien vers vr-anomalies.html (page séparée, à créer) |
État interne principal
parcels[]
clients[]
stats{}
tours{}
toursMeta{}
tourIdCache{}
agents[]
currentTour
currentUser
map
markers[]
clientComments{}
clientLabels{}
8. Zones de livraison
| Zone | Couleur | Type | Communes |
B/T-Z1 | Rouge | Livraison | Lamentin (97129), Sainte-Rose (97115), Deshaies (97126), Pointe-Noire (97116) |
B/T-Z2 | Jaune | Livraison | Baie-Mahault (97122), Petit-Bourg (97170) |
B/T-Z3 | Vert | Livraison | Capesterre-BE (97130), Trois-Rivières (97114), Basse-Terre (97100), +9 communes |
G/T-Z1 | Bleu | Livraison | Morne-à-l'Eau (97111), Petit-Canal (97131), Port-Louis (97117), Anse-Bertrand (97121), Le Moule (97160) |
G/T-Z2 | Noir | Livraison | Les Abymes (97139/42/83), Pointe-à-Pitre (97110) |
G/T-Z3 | Orange | Livraison | Le Gosier (97190), Sainte-Anne (97180), Saint-François (97118), La Désirade, Marie-Galante |
Point relais Jarry | Violet | Retrait | Retrait en point relais — pas de GPS, pas d'adresse |
Correction automatique : Si un colis livraison n'a pas de zone valide, le code JS détermine la zone à partir du code postal (CP_TO_ZONE). Idem pour la ville (CP_TO_CITY).
9. Gestion des tournées
Cycle de vie d'une tournée
draft ──► planned ──► in_progress ──► completed ──► archived
│ │
│ ▼
│ paused ──► in_progress (reprise)
│
└── deleted (si vide)
Structure d'une tournée (vr_tours_meta)
{
tour_num: 55,
date: '2026-03-24',
status: 'planned',
agent: { id, name, email },
driver_name: 'Melinda',
vehicle: { type, plate },
schedule: {
journeyStart, journeyEnd,
tourStart, tourEnd,
pauseDuration
},
tracking: {
startTime, endTime,
deliveryLogs: [{ clientId, timestamp, status, gps }],
pauses: [{ start, end, duration }],
totalPauseTime
},
report: {
fuelLevel, coolantOk,
expenses: [],
issues, urgentReschedule,
cancelledClients: [],
validated: true/false
}
}
Assignation des clients
Les clients sont assignés par zone, par ville, individuellement ou par transfert depuis une autre tournée. L'ordre peut être modifié par drag & drop. Les données sont persistées dans vr_tours_v2.clients_data (JSONB).
Numérotation
Tournées numérotées de 55 à 99+. Jusqu'à 12 tournées ont des couleurs prédéfinies (voir CSS). Au-delà, couleur calculée par hsl((num * 137) % 360, 70%, 50%).
10. Système de livraison
Statuts de livraison par client
| Statut | Icône | Signification |
pending | ⏳ | Pas encore traité |
delivered | ✅ | Livré avec succès |
absent | 🚫 | Client absent |
postponed | 📅 | Reporté à une date ultérieure |
cancelled | ❌ | Annulé |
Persistance offline-first
L'état de livraison est sauvé en localStorage à chaque action. En cas de perte réseau, le livreur peut continuer. La synchronisation Supabase est throttled à 2 secondes.
Tracking temps réel
Timer de livraison avec gestion des pauses. Chaque livraison enregistre un log avec timestamp, coordonnées GPS, et durée depuis le client précédent.
11. Gestion des camions
Convention étagères
| Range | Usage |
1 — 799 | Stock entrepôt (étagères physiques) |
800 — 899 | Réservé camions |
Structure camion
Exemple : Camion de Melinda
shelf_start: 800, shelf_end: 809
truck_shelf: 800 (compartiment principal)
bags_count: 9 (bags 801-809)
Visualisation grille 3×3 :
┌─────┬─────┬─────┐
│ 801 │ 802 │ 803 │
├─────┼─────┼─────┤
│ 804 │ 805 │ 806 │
├─────┼─────┼─────┤
│ 807 │ 808 │ 809 │
└─────┴─────┴─────┘
+ Compartiment principal : 800
Les colis sur étagère 800-899 sont exclus du stock actif (sauf si excludeTrucks=false) mais visibles dans l'onglet Camions.
12. GPS & Carte
Sources GPS (par ordre de priorité)
1
vr_gps_corrections — corrections manuelles (priorité max)
2
delivery_address.latitude/longitude — adresse de livraison spécifique
3
default_address.latitude/longitude — adresse par défaut du client
4
captureClientGeolocation() — capture GPS navigateur pendant livraison
Configuration carte
| Param | Valeur |
| Centre | [16.265, -61.551] (Guadeloupe) |
| Zoom | 11 |
| Bounds Guadeloupe | 15.8°N — 16.6°N / -62° — -60.8°W |
| Timeout géoloc | 15 secondes |
| Provider | OpenStreetMap via Leaflet.js 1.9.4 |
Les clients retrait n'ont PAS de GPS (ils viennent chercher au point relais). Les markers sont colorés par tournée ou par zone selon le mode.
13. Commentaires & Mentions
Système de commentaires
Chaque client peut avoir des commentaires avec 3 niveaux de priorité :
| Priorité | Affichage |
normal | Texte standard |
high | ⚠️ Badge orange |
urgent | 🚨 Badge rouge |
Mentions @agent
Taper @ dans un commentaire active l'autocomplétion des noms d'agents (vr_agents). Les mentions créent des notifications (vr_notifications).
14. Labels & Priorités clients
Table vr_client_labels. Un label par client (UPSERT sur client_email). Affichage visuel dans la liste clients et la carte.
| Priorité | Couleur | Effet |
normal | — | Pas de label (DELETE de la table) |
high | Orange/Jaune | ⚠️ Highlight dans la liste |
urgent | Rouge | 🚨 Highlight + priorité visuelle haute |
15. vr-history.js — Module historique
Page séparée (vr-history.html). Module IIFE VRHistory. Affiche les tournées terminées/archivées.
Sources de données
1
vr_tours_meta — 200 dernières entrées (agent, tracking, schedule, report)
2
vr_deliveries — statuts livraison par client (batchés par 10 dates)
3
vr_tours_v2 — statuts et comptages clients
Filtres disponibles
Période (aujourd'hui / semaine / mois), numéro de tournée, nom d'agent.
Vue détail
Chaque tournée affiche : agent, véhicule, horaires (prévus vs réels), résultats (livrés/absents/reportés/annulés), pauses, chronologie des livraisons, dépenses, notes, export PDF.
Export PDF
Génère un document HTML imprimable (window.open + print) avec tableau complet des livraisons, statistiques et notes.
16. Authentification & Rôles
Flux de connexion
1
L'utilisateur entre son email + PIN à 6 chiffres
2
Vérification contre la table users (email + pin_code)
3
Session sauvée en localStorage['vr_user'] + localStorage['vttUser']
Rôles
| Rôle | Accès |
agent (livreur) | Dashboard, Clients, Carte, Tournées, Livraison, Camions, Historique |
manager | Tout + onglet Manager (supervision, config camions) |
admin | Tout |
Pas de Supabase Auth. L'authentification est custom (table users + PIN). Pas de JWT Supabase. La clé anon_key est utilisée directement.
17. Flux de données
Pipeline de chargement principal
init()
└─► VR_Database.init()
└─► loadData()
├─► getAllParcels()
├─► convertToClients(parcels)
├─► getCacheStats()
├─► applyGPSCorrections()
├─► loadSavedTours()
├─► loadCommentsIndicators()
├─► loadClientLabels()
└─► Render : Dashboard + Filtres + Grille tournées + Carte
Détermination type client (livraison vs retrait)
Pour chaque colis :
1. STATUS prioritaire :
- PRET_RETRAIT / PRET_RECUPERATION → retrait
- PRET_LIVRAISON / PLANIFICATION_LIVRAISON / PRET_POUR_PLANIFICATION → livraison
2. Sinon, ZONE :
- B/T-Z* ou G/T-Z* → livraison
- "relais" ou "jarry" ou vide → retrait
3. Fallback : retrait
Pour le client (après regroupement) :
- Si AU MOINS UN colis livraison → clientType = 'livraison'
- Sinon → clientType = 'retrait'
- Retrait : zone = 'Point relais Jarry', GPS = null, adresse = null
18. Constantes & Configuration
| Constante | Valeur | Usage |
| SUPABASE_URL | https://qkxggtrmpgqjbwlaoztn.supabase.co | Endpoint Supabase |
| TRUCK_SHELF_MIN/MAX | 800 / 899 | Range étagères camions |
| DEFAULT_MAP_CENTER | [16.265, -61.551] | Centre carte Guadeloupe |
| DEFAULT_ZOOM | 11 | Zoom initial carte |
| GEOLOC_TIMEOUT | 15 000 ms | Timeout capture GPS navigateur |
| SYNC_THROTTLE | 2 000 ms | Délai sync Supabase pendant livraison |
| HEAVY_PARCEL | 20 kg | Seuil colis lourd |
| HIGH_VALUE | 500 € | Seuil haute valeur |
| TOUR_NUM_RANGE | 55 — 99+ | Numéros de tournée |
| WORKING_DAYS | Lundi — Samedi | Pas de tournée le dimanche |
19. Cron Jobs (pg_cron)
| Job ID | Schedule | Fonction | Description |
| sync-parcels-full-15min | 5,20,35,50 * * * * | sync_parcels_full_safe() | Sync complète cache colis depuis Next → vr_parcels_cache (timeout 600s) |
| sync-parcels-light-5min | */5 * * * * | sync_parcels_light() | Sync incrémentale légère (changements récents, timeout 300s) |
| sync-parcels-images-15min | 13,28,43,58 * * * * | sync_parcel_images_new() | Sync nouvelles photos colis (timeout 300s) |
| sync-parcels-purge-30min | 12,42 * * * * | sync_parcels_purge() | Purge colis disparus de Next (timeout 300s) |
| 3 | 0 6 * * * | cron_wrapper_close_forgotten() | Ferme les sessions VTT oubliées |
| 4 | 0 5 * * * | cron_wrapper_cleanup_tokens() | Nettoyage tokens expirés |
| 5 | 0 7 * * * | cron_wrapper_integrity_check() | Vérification intégrité VTT |
| 6 | 0 22 * * * | cron_wrapper_anomalies_check() | Détection anomalies VTT |
20. Anomalies & Maintenance
Types d'anomalies détectées
| Code | Détection | Gravité |
PRET_SANS_ETAGERE | status PRET_* mais etagere vide/null | Haute |
CLIENT_NON_IDENTIFIE | client_email et client_name vides ou "NON IDENTIFIÉ" | Haute |
SANS_STICKER | sticker NULL (filtré à la sync, ne devrait plus arriver) | Haute |
ANCIEN_STOCK | parcel_created_at > 6 mois | Moyenne |
LIVRAISON_SANS_ZONE | status livraison mais zone_name vide | Basse |
Filtrage dans l'app principale
Les colis anomalies (sans étagère, hors PRET_LIVRAISON/PLANIFICATION_LIVRAISON) sont automatiquement exclus de getAllParcels() et getCacheStats(). Ils restent dans vr_parcels_cache et sont accessibles via la page Anomalies.
Colonne anomaly_flags (TEXT[]) : Ajoutée le 19/03/2026. Remplie par la sync (step post-upsert). Indexée GIN pour requêtes rapides.
Historique des corrections (19/03/2026)
| Problème | Correction |
FDW voyey_next_server cassé (hostname introuvable) | Migration vers next_server + suppression ancien FDW |
| Filtre blacklist (laissait passer ENREGISTRE_VOYEY, PRET_VWAZINAJ) | Remplacé par whitelist stricte |
| Zones vides (zone_id/zone_name toujours NULL) | Restauration jointures zone dans la sync |
| Adresses fausses (uniquement default_address) | Restauration delivery_address_id + fallback default |
| 984 colis sticker NULL dans le cache | Ajout filtre sticker IS NOT NULL |
| TRANSIT + RECEPTIONNE_BOISSY dans le cache (pas en stock physique) | Retirés de la whitelist de sync |
| sync_parcel_images() cassée | Migrée de voyey_next vers next_data |
Corrections planification tournées (26/03/2026)
| Problème | Cause racine | Correction |
| Dates décalées d'un jour (27 mars affiché 26 mars) |
Bug timezone : new Date().toISOString().split('T')[0] retourne UTC (en Guadeloupe UTC-4, minuit local = veille UTC). formatDateFr() utilisait new Date(str + 'T00:00:00') interprété en UTC. |
Ajout de getLocalDateStr(date) : utilitaire timezone-safe basé sur getFullYear()/getMonth()/getDate(). Remplacement de ~30 occurrences. formatDateFr() utilise new Date(y, m-1, d, 12, 0, 0) (heure locale midi). |
| Numérotation incohérente (T3 à côté de T188, T203) |
getTourDisplayNum() convertissait le numéro réel en index dans activeTourNumbers (T188 → T1 si position 0) |
getTourDisplayNum() retourne maintenant le vrai numéro. Les couleurs restent basées sur l'index pour la cohérence visuelle. |
| Tournées affichées avec 0 clients alors qu'elles en ont |
Clé composite ${date}-T${tourNum} ne matchait pas entre vr_tours_meta et vr_tours_v2 car meta.date contenait un timestamp complet au lieu de YYYY-MM-DD |
meta.date est maintenant toujours normalisé en YYYY-MM-DD dans loadToursMeta() |
| Données différentes selon le clic (carte "À venir" vs "Prochaines tournées") |
openTourDetailModal ne chargeait que depuis toursMeta en mémoire (vide si le match échouait). openTourPlanModal avait des fallbacks multiples. |
openTourDetailModal est maintenant async avec fallback direct vers vr_tours_v2 quand clientsData est vide. Le résultat est mis en cache dans meta. |
| Numéros de tournées bloqués entre jours (T1 du jour bloquait T1 de demain → création de T188, T203) |
getAvailableTourNumbersForDate() excluait les numéros utilisés aujourd'hui (blockedByToday) même pour les dates futures |
Les numéros sont maintenant indépendants par jour. T1 du 26/03 ≠ T1 du 27/03. Propose T1 à T20 en excluant seulement ceux déjà utilisés pour CE jour. |
create_tour_v2 RPC appelé sans p_tour_number → auto-incrément global (T205, T206...) |
3 call sites sur 6 ne passaient pas p_tour_number à la RPC. get_next_tour_number() générait des numéros séquentiels globaux. |
Tous les appels à create_tour_v2 passent maintenant p_tour_number: tourNum. Plus d'auto-incrément orphelin. |
| Données orphelines dans vr_tours_v2 (T205-T212 sans correspondance meta) |
Conséquence des 2 bugs ci-dessus : meta avait T3/T188/T203, v2 avait T205-T212 |
Nettoyage manuel : orphelins supprimés, T208→T1, T209→T2, T206→T3. Meta réaligné avec tour_key correct. |
Fonctions utilitaires ajoutées / modifiées (26/03/2026)
getLocalDateStr(date?)
Input: Date object, string ISO, string YYYY-MM-DD, ou vide (= maintenant)
Output: string 'YYYY-MM-DD' en heure LOCALE (pas UTC)
Usage: Remplace new Date().toISOString().split('T')[0] partout (~30 occurrences)
formatDateFr(dateStr)
Input: string date (tout format)
Output: 'mer. 27 mars' (fr-FR, weekday short + day + month short)
Logique: Parse YYYY-MM-DD → new Date(y, m-1, d, 12, 0, 0) en LOCAL
getAvailableTourNumbersForDate(dateStr, excludeNum?)
Propose T1-T20, exclut seulement les numéros déjà utilisés POUR CE JOUR
Plus de blocage cross-jours (T1 du 26 n'empêche plus T1 du 27)
getTourCacheKey(tourNum, date?)
Output: "tourNum-YYYY-MM-DD" (ex: "1-2026-03-31")
getCachedTourId(tourNum, date?)
Lit tourIdCache[getTourCacheKey(tourNum, date)]
setCachedTourId(tourNum, date, tourId)
Écrit tourIdCache[getTourCacheKey(tourNum, date)] = tourId
Règles de gestion des numéros de tournée
Numéros par jour : Les tournées sont numérotées T1, T2, T3... par date. Chaque jour repart de T1. Deux jours différents peuvent avoir chacun une T1 — elles sont distinguées par la clé composite ${date}-T${tourNum}.
| Table | Clé d'unicité | Exemple |
vr_tours_meta | tour_key = ${date}-T${tourNum} | 2026-03-27-T1 |
vr_tours_v2 | (tour_number, planned_date) | tour_number=1, planned_date=2026-03-27 |
tourIdCache (JS) | ${tourNum}-${date} | 1-2026-03-27 |
La RPC create_tour_v2 accepte p_tour_number (obligatoire côté JS). Si le numéro existe déjà pour cette date, elle lève une exception. Le fallback get_next_tour_number() (auto-incrément global) ne doit jamais être utilisé — tous les appels JS passent maintenant le numéro explicitement.
tourIdCache — Piège corrigé le 30/03/2026 : Le cache était indexé par tourNum seul (ex: tourIdCache[1]). Quand T1 du 30/03 était en cache, T1 du 31/03 réutilisait le même UUID → clients sauvés dans la mauvaise tournée + création d'orphelins auto-incrémentés (T218-T221). Fix : 21 occurrences migrées vers getCachedTourId(tourNum, date) / setCachedTourId(tourNum, date, tourId).
21. Historique complet des corrections
18/05/2026 — Mise à jour doc (transmission Dannick) + intégration dashboard VIE
| Point | État réel (vérifié PROD) |
| Hébergement frontend (doc disait « voyey.com ») | FAUX : hébergé sur vtt.xo.je (InfinityFree, Apache mutualisé, dépôt FTP). Migration voyey.com non effectuée. |
| Cron sync colis (doc disait « toutes les 30 min ») | OBSOLÈTE : multi-paliers — full /15 min (sync_parcels_full_safe), light /5 min (sync_parcels_light), images /15 min, purge /30 min. Voir §19. |
| Intégration VIE Dashboard (mig 248) | VROOM alimente le dashboard VIE : métrique vr.planned_tours_today (tournées du jour : agent, clients, colis, villes) + vr.parcels_by_status (bucket PRET_RETRAIT ajouté) + global.deliveries_today (planned_tours/clients). Source : vr_tours_meta ⨝ vr_tours_v2. |
Piège data vr_tours_meta.agent / .zones | jsonb double-encodé (chaîne JSON, jsonb_typeof='string'). agent->>'name' = NULL. Décoder : (agent #>> '{}')::jsonb ->> 'name'. |
19/03/2026 — Chantier FDW & Cache colis
| Problème | Correction |
FDW voyey_next_server cassé (hostname introuvable après changement credentials) | Migration de toutes les fonctions vers next_server + suppression du FDW cassé et du schéma voyey_next |
Filtre blacklist dans sync_parcels_cache() (laissait passer ENREGISTRE_VOYEY, PRET_VWAZINAJ = 3 947 fantômes) | Remplacement par whitelist stricte : PRET_LIVRAISON, PLANIFICATION_LIVRAISON, PRET_POUR_PLANIFICATION, PRET_RETRAIT, PRET_RECUPERATION, STOCKE |
| Zones vides (zone_id/zone_name toujours NULL dans le cache) | Restauration des jointures zone dans la sync via delivery_zone_id + fallback customer.delivery_zone_id |
| Adresses fausses (uniquement default_address au lieu de delivery_address) | Restauration delivery_address_id + fallback default. ALTER FOREIGN TABLE pour ajouter la colonne manquante. |
| 984 colis sticker NULL dans le cache (non actionnables) | Ajout filtre p.sticker IS NOT NULL dans le WHERE de la sync |
| TRANSIT + RECEPTIONNE_BOISSY dans le cache (pas en stock physique GP) | Retirés de la whitelist. Le cache ne contient que les colis physiquement en stock. |
sync_parcel_images() cassée (utilisait voyey_next) | Migrée vers next_data.logistics_parcelimage |
| 224 colis PRET_* sans étagère (anomalies source Next) | Exclus du stock actif dans getAllParcels(). Restent dans le cache pour la page Anomalies. |
26/03/2026 — Chantier Tournées (dates, numérotation, modals)
| Problème | Correction |
| Décalage de date -1 jour (tournée du 27 affichée comme 26) | getLocalDateStr() : timezone-safe, remplace ~30 occurrences de toISOString().split('T')[0] |
| Numérotation incohérente (T3 à côté de T188, T203) | getTourDisplayNum() retourne le vrai numéro (plus de conversion en index) |
| Numéros bloqués entre jours (T1 du 26 empêchait T1 du 27) | getAvailableTourNumbersForDate() : propose T1-T20 par jour, indépendant |
create_tour_v2 appelé sans p_tour_number → auto-incrément (T205-T212) | 5 call sites corrigés : tous passent p_tour_number: tourNum |
| Tournées 0 clients (meta↔v2 non liées) | meta.date normalisé YYYY-MM-DD, openTourDetailModal async + fallback DB |
| Données différentes selon le modal cliqué | Unification : même source de données (v2 avec fallback) quel que soit le point d'entrée |
30/03/2026 — Chantier tourIdCache (doublons cross-jours)
| Problème | Correction |
tourIdCache[tourNum] indexé par numéro seul → T1 du 30/03 réutilisait l'UUID de T1 du 29/03 | Cache migré vers tourIdCache["tourNum-date"]. 21 occurrences remplacées par getCachedTourId() / setCachedTourId(). |
| Orphelins v2 créés par le bug (T218-T221) | Nettoyage manuel : orphelins supprimés, tour_number réalignés avec meta |
| Agent/livreur perdu au changement de jour | Conséquence du match meta↔v2 cassé. Résolu par les fixes date + cache. |
22. Page livraison autonome (vr-delivery)
30/03/2026 — Création et déploiement
| Aspect | Ancien (onglet dans vr-app.js) | Nouveau (page autonome) |
| Architecture | Onglet dans index.html, dépend de vr-app.js (12K lignes) | Page séparée vr-delivery.html + vr-delivery.js (1 600 lignes). Utilise vr-database.js pour Supabase. |
| Taille chargée | ~550 KB (vr-app.js complet) | ~90 KB (vr-delivery.js + vr-database.js) |
| Persistance | localStorage throttled 2s, sync silencieuse | Save immédiat à chaque action + sync queue avec retry exponentiel (1s→60s, max 5 retries) |
| beforeunload | Aucun | Alerte si tournée en cours |
| Indicateur réseau | Aucun | Badge vert (sync OK) / orange (pending) / rouge (offline) |
| Timer save | Toutes les 30s | Toutes les 5s |
| GPS capture | DB seulement | localStorage + queue sync (double sécurité) |
| Rapport | Dépend de clients[] en mémoire | Autonome, flush sync avant archivage, fallback queue si DB fail |
| Auth | Partagée avec vr-app.js | Autonome (même table users, même localStorage vr_user) |
| Page refresh | Restauration partielle via vr-app.js | Restauration complète depuis localStorage (clients, statuts, timer, tracking) |
Fichiers créés
| Fichier | Taille | Rôle |
vr-delivery.html | ~15 KB | Page mobile-first pour livreurs terrain. CSS inline, bottom-sheet modals, safe-area iPhone. |
vr-delivery.js | ~60 KB | Module IIFE VR_Delivery. Auth, tours, delivery, timer, GPS, rapport, sync engine. |
Architecture vr-delivery.js
VR_Delivery (IIFE)
├── Auth: checkSession(), login(), logout(), _doLogin()
├── Date helpers: getLocalDateStr(), formatDateFr(), formatTime(), formatDuration()
├── Tour loading: loadMyTours() → vr_tours_meta + vr_tours_v2
├── Persistence: saveStateLocal(), loadStateLocal(), clearStateLocal()
│ └── Key: 'vr_delivery_state_v2' (date-validated, full client data)
├── Sync engine: addToSyncQueue(), processSyncQueue(), updateSyncIndicator()
│ ├── Types: delivery, tracking, meta, gps
│ ├── Queue key: 'vr_sync_queue' (localStorage)
│ ├── Retry: exponential backoff [1s, 3s, 10s, 30s, 60s], max 5
│ └── Network: online/offline listeners auto-flush
├── Delivery: selectTour(), markClientStatus(), openDeliveryModal()
├── Timer: toggleTimer(), startTimerTick(), updateTimerDisplay(), updateTimerUI()
│ └── States: not started → running → paused → running → ...
├── GPS: captureGPS() — Guadeloupe bounds, localStorage + sync queue
├── Report: openReportModal(), collectReportData(), saveReportDraft(), validateReport()
├── UI: renderTourSelector(), renderDeliveryList(), updateProgress()
└── beforeunload: force save on exit
Sync Engine — détail
Offline-first : Chaque action de livraison est sauvée en localStorage immédiatement (synchrone), puis ajoutée à la sync queue pour Supabase. La queue est persistée en localStorage et traitée toutes les 3 secondes. Si une opération échoue, elle est retentée avec backoff exponentiel.
| Opération | Table Supabase | Méthode |
delivery | vr_deliveries | UPSERT (onConflict: tour_number, tour_date, client_email) |
tracking | vr_tours_meta | UPSERT tracking JSONB |
meta | vr_tours_meta | UPSERT status + report |
gps | vr_clients_geo_capture | INSERT |
Bugs corrigés pendant le déploiement
| Bug | Cause | Fix |
| Login bouton inactif | HTML statique avait onclick="VR_Delivery.login()" mais le JS expose _doLogin() | Retiré le HTML statique login, le JS génère son propre overlay |
| Login "Email non trouvé" | JS sélectait pin_code (n'existe pas), colonne réelle = pin | pin_code → pin dans select + comparaison |
| Login "Email non trouvé" (2) | JS ne filtrait pas is_active = true | Ajout .eq('is_active', true) + .select('*') |
| Tournées non chargées | JS sélectait agent_name (n'existe pas dans vr_tours_v2) | Colonne retirée du select |
| Liste clients vide au clic | HTML avait id="clientList", JS cherchait id="deliveryList" | Aligné HTML sur JS |
| Bouton Démarrer/Terminer absent | updateTimerUI() ne gérait pas la visibilité de timerBar ni finishBtn | Ajout toggle visible class + gestion finishBtn |
| IDs progrès manquants | HTML manquait progressBar, progressPct, progressText, deliveryStats | IDs ajoutés dans le HTML |
23. Gestion des colis par client (livraison partielle)
Ajouté le 31/03/2026. Permet au livreur de signaler quels colis ont été remis et lesquels ne l'ont pas été chez un même client.
Flux utilisateur
1
Le livreur ouvre la fiche d'un client — les colis s'affichent avec des checkboxes (tous cochés par défaut)
2
S'il ne peut pas remettre un colis → il le décoche
3
Il clique "Livré" → les stickers cochés vont dans parcels_delivered, les décochés dans parcels_not_delivered
4
Le commentaire de livraison inclut automatiquement "Colis non livrés: 129280, 123735"
Persistance (3 niveaux)
| Niveau | Stockage | Données |
| localStorage | vr_delivery_state_v2 | deliveryStatuses[id].parcels_delivered + parcels_not_delivered (arrays de stickers) |
| Supabase | vr_deliveries | Colonnes JSONB parcels_delivered + parcels_not_delivered |
| Tracking | tourTracking.deliveryLogs[] | parcelsDelivered + parcelsNotDelivered arrays par log |
Schema DB
ALTER TABLE vr_deliveries
ADD COLUMN parcels_delivered JSONB DEFAULT '[]'::jsonb,
ADD COLUMN parcels_not_delivered JSONB DEFAULT '[]'::jsonb;
CREATE INDEX idx_deliveries_not_delivered
ON vr_deliveries USING gin(parcels_not_delivered)
WHERE parcels_not_delivered != '[]'::jsonb;
Rapport de fin de tournée
La section "Colis retour" du rapport affiche 2 catégories :
| Catégorie | Badge | Source |
| Colis de clients non livrés (absent/reporté/annulé) | Absent / Reporté / Annulé | Tous les colis du client sont retournés |
| Colis non remis chez un client livré (partiel) | Non remis | parcels_not_delivered du deliveryStatuses |
UI — Modal livraison (stickers)
Les stickers sont affichés en gros, monospace, couleur or (#f0b009) avec checkbox. Bouton "Tout cocher/décocher" en haut de la liste. Les colis non cochés sont automatiquement ajoutés au commentaire ET aux champs JSONB dédiés.
Enrichissement des téléphones
Les clients_data de vr_tours_v2 ne contiennent pas le champ phone. Au chargement d'une tournée dans vr-delivery.js, les téléphones sont enrichis depuis vr_parcels_cache.client_phone par batch (requête .in('client_email', emails)).
Drag & Drop (réordonnement clients)
Les clients de la tournée peuvent être réordonnés par drag & drop (desktop + touch mobile). La technique utilisée est la même que dans vr-app.js (modale détail tournée) : déplacement DOM direct pendant le dragover/touchmove via insertBefore + getDragAfterElement(). Le nouvel ordre est sauvé en localStorage + sync vers vr_tours_v2.clients_data.
24. Panneau clients spéciaux (Tournées)
Ajouté le 31/03/2026. Panneau en haut de l'onglet Tournées affichant les clients nécessitant un traitement particulier. Tout fermé par défaut (toggle ▶/▼).
4 catégories
| Section | Couleur | Critère | Comportement |
| 🚨 Urgent | #ef4444 | client.label === 'urgent' | Visible dans les villes/zones + ici. Boutons assignation par tournée. |
| ⚠️ Important | #f59e0b | client.label === 'important' | Idem urgent. |
| 🏋️ Hors format (+40kg) | #a855f7 | Au moins 1 colis individuel ≥ 40kg | Exclus des villes/zones. Modal de sélection + impression BAL individuel. Alerte si assigné à une tournée. |
| 🏝️ Dépendances (îles) | #06b6d4 | Client dans une île dépendante | Exclus des villes/zones. Groupé par île. Modal de sélection + impression BAL individuel. |
Hors format — logique métier
Règle : Si un client a AU MOINS 1 colis pesant ≥ 40kg, tout le client (tous ses colis) est classé hors format. Un client avec 1 colis de 50kg + 3 colis de 2kg = hors format. Un client avec 10 colis de 10kg = normal (100kg total mais aucun colis individuel ≥ 40kg).
- Exclus de
getStatsByCity() → pas dans les cartes villes/zones
- Alerte
confirm() si on tente de les assigner à une tournée via assignToTour()
- Bon de livraison individuel (1 page A4 par client) avec infos VOYEY + client + colis + signatures
Dépendances — îles concernées
| Groupe | Villes | Codes postaux |
| La Désirade | La Désirade | 97127 |
| Marie-Galante | Grand-Bourg, Capesterre-de-Marie-Galante, Saint-Louis | 97112, 97140, 97134 |
| Les Saintes | Les Saintes, Terre-de-Haut, Terre-de-Bas | 97136, 97137 |
- Détection par ville OU code postal (avec variantes d'orthographe : "Grand Bourg" / "Grand-Bourg")
- Exclus de
getStatsByCity() → pas dans les cartes villes/zones normales
- Groupés par île dans le panneau (La Désirade, Marie-Galante, Les Saintes)
- Même système de BAL que hors format (modal sélection + impression individuelle)
Bon de Livraison (BAL) — format
1 page A4 par client. Utilisé pour hors format ET dépendances. Fonction generateHorsFormatPDF().
┌─────────────────────────────────────────────────┐
│ VOYEY.COM │ DESTINATAIRE │
│ 372 impasse palétuviers │ Nom du client │
│ 97122 Baie-Mahault │ Adresse complète │
│ [email protected] │ Tél + Email │
├─────────────────────────────────────────────────┤
│ 🏋️ BON DE LIVRAISON N° BL-HF-YYYYMMDD-XX │
├─────────────────────────────────────────────────┤
│ Zone │ Étagères │ Poids total │ Nb colis │
├─────────────────────────────────────────────────┤
│ # │ Sticker │ Type │ Poids │ Étagère │ Obs │
│ 1 │ 129431 │ B3 │ 45.2kg│ ETG923 │ ⚠️ HF │
│ 2 │ 127102 │ B3 │ 1.7kg │ ETG708 │ — │
├─────────────────────────────────────────────────┤
│ Signature livreur │ Signature client │ Date │
└─────────────────────────────────────────────────┘
Normalisation status v2
Mapping status : vr_tours_v2 a une contrainte valid_status qui accepte : draft, scheduled, in_progress, completed, cancelled. Le code JS normalise scheduled → planned partout dans loadToursMeta() et renderToursGrid() pour cohérence avec vr_tours_meta.
Performances (optimisation 31/03/2026)
initDatabase() : loadData(), loadAgents(), loadToursMeta() en parallèle (était séquentiel)
loadData() : GPS corrections, tournées, agents, commentaires, labels, mentions en parallèle (était séquentiel)
getCacheStats() : calcul local depuis les données déjà chargées (suppression de la 2e requête SELECT * sur vr_parcels_cache)
- Labels : propagation vers
client.label au chargement + mise à jour immédiate sans refresh
25. Cohérence écriture 3 tables tournées
Audit et correction du 01/04/2026. Le système de tournées utilise 3 tables qui doivent rester synchronisées.
Les 3 tables
| Table | Rôle | Clé |
vr_tours | Legacy — stocke clients_data, status, compteurs | (tour_number, tour_date) |
vr_tours_v2 | V2 — UUID, clients_data JSONB, status avec contrainte | id (UUID), unique (tour_number, planned_date) |
vr_tours_meta | Métadonnées — agent, véhicule, schedule, tracking, report | (tour_num, date), tour_key |
Cause racine des orphelins T228/T229 : savePlanningTourClients() écrivait dans vr_tours (legacy) mais PAS dans vr_tours_v2. Quand assignClientsToTourByKey() cherchait ensuite dans v2, il ne trouvait rien et appelait create_tour_v2 sans p_tour_number → auto-incrément global (T228, T229...).
Matrice d'écriture (après correction)
| Fonction | vr_tours | vr_tours_v2 | vr_tours_meta |
savePlanningTourClients() | ✅ INSERT/UPDATE | ✅ CREATE/UPDATE | — |
updateTourStatus() | ✅ UPDATE | ✅ UPDATE | ✅ UPDATE |
saveTourMeta() | — | — | ✅ UPSERT |
saveTourMetaSimple() | 🗑️ DELETE old | — | ✅ UPSERT |
saveTourMetaWithKey() | — | — | ✅ UPSERT |
startTourDelivery() | — | ✅ UPDATE status | ✅ via saveTourMeta |
deleteTourByNumber() | ✅ DELETE | ✅ DELETE | ✅ DELETE |
deleteTourByKey() | ✅ DELETE | ✅ DELETE | ✅ DELETE |
clearAllTodayTours() | ✅ DELETE | ✅ DELETE | ✅ DELETE |
Mapping des status entre tables
| Concept | vr_tours (français) | vr_tours_v2 (contrainte) | vr_tours_meta |
| Brouillon | brouillon | draft | draft |
| Planifiée | planifiee | scheduled | planned |
| En cours | en_cours | in_progress | in_progress |
| Terminée | terminee | completed | completed |
| Annulée | annulee | cancelled | cancelled |
Le JS normalise scheduled → planned dans loadToursMeta() pour l'affichage. La contrainte v2 valid_status n'accepte PAS planned — utiliser scheduled pour les écritures v2.
Corrections appliquées (01/04/2026)
| Fonction | Avant | Après |
savePlanningTourClients() | Écrit uniquement vr_tours | Écrit vr_tours + vr_tours_v2 (avec p_tour_number) + remplit tourIdCache |
updateTourStatus() | Écrit uniquement vr_tours | Écrit vr_tours + vr_tours_v2 (status mappé) + vr_tours_meta |
savePlanningTourClients() clients_data | Seulement id, email, name, address, city, phone | + zone, lat, lng, postalCode, parcels (données complètes pour vr-delivery.js) |
Règle pour les futurs développements
TOUTE modification de tournée doit écrire dans les 3 tables. Si une fonction écrit dans vr_tours sans écrire dans vr_tours_v2, les données seront désynchronisées et des orphelins auto-incrémentés seront créés. Vérifier la matrice ci-dessus avant tout nouveau développement.
VROOM ! — Documentation technique v3.7 — Mise à jour 01/04/2026 — Voyey