diff --git a/.gitignore b/.gitignore index cd30472..7f54ae6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,43 +1,44 @@ -# Logs -*.log -logs/ -log/ - -# IDE -.vscode/ -.idea/ -*.swp -*.swo -*~ -.DS_Store - -# Composer -/vendor/ - -# Configuration sensible -config/conf.local.php -.env -.env.local - -# Fichiers temporaires -*.tmp -tmp/ -temp/ -cache/ - -# Uploads et fichiers utilisateurs -pub/files/upload/* -!pub/files/upload/.gitkeep - -# Sauvegardes -*.bak -*.backup -# *.sql -backup/ -backups/ - -# Sessions PHP -sessions/ - -# Fichiers système -Thumbs.db*.swp +# Logs +*.log +logs/ +log/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# Composer +/vendor/ + +# Configuration sensible +config/conf.local.php +.env +.env.local + +# Fichiers temporaires +*.tmp +tmp/ +temp/ +cache/ + +# Uploads et fichiers utilisateurs +pub/files/upload/* +!pub/files/upload/.gitkeep + +# Sauvegardes +*.bak +*.backup +# *.sql +backup/ +backups/ + +# Sessions PHP +sessions/ + +# Fichiers système +Thumbs.db*.swp +.aider* diff --git a/config/conf.php b/config/conf.php index bebaea8..001dccd 100644 --- a/config/conf.php +++ b/config/conf.php @@ -10,7 +10,7 @@ class Conf { public $_appname = "cleo"; public $_appscript = "login"; - public $_appversion = "2.0.3"; + public $_appversion = "2.0.4"; public $_appenv; public $_apptitle = "CLEO - Gestion de devis"; diff --git a/controllers/cjxdevis.php b/controllers/cjxdevis.php index 515aeed..1240b2d 100644 --- a/controllers/cjxdevis.php +++ b/controllers/cjxdevis.php @@ -1368,5 +1368,280 @@ switch ($Route->_action) { } } break; + + case "search_devis": + eLog("=== search_devis case appelé ==="); + $rawData = file_get_contents("php://input"); + eLog("Raw data: " . $rawData); + $data = json_decode($rawData); + eLog("Data decoded: " . print_r($data, true)); + eLog("isset term: " . (isset($data->term) ? 'YES' : 'NO')); + + if (isset($data->term)) { + $term = nettoie_input($data->term); + $context = nettoie_input($data->context); + + if (strlen($term) < 3) { + echo json_encode(array("success" => false, "message" => "Le terme de recherche doit contenir au moins 3 caractères")); + break; + } + + $termSafe = '%' . $term . '%'; + + $whereParams = []; + switch ($fk_role) { + case 1: + $whereRole = 'd.fk_user = :fkUser OR d.fk_statut_devis >= 2'; + $whereParams[':fkUser'] = $fk_user; + break; + case 2: + try { + $db = Database::getInstance(); + $sql = 'SELECT rowid FROM users WHERE fk_parent = :fkParent'; + $aRR = $db->fetchAll($sql, [':fkParent' => $fk_user]); + + $rrIds = array_column($aRR, 'rowid'); + if (!empty($rrIds)) { + $placeholders = []; + foreach ($rrIds as $index => $id) { + $placeholder = ':rr' . $index; + $placeholders[] = $placeholder; + $whereParams[$placeholder] = $id; + } + $whereRole = 'd.fk_user = :fkUser OR (d.fk_statut_devis >= 3 AND d.fk_user IN (' . implode(',', $placeholders) . '))'; + $whereParams[':fkUser'] = $fk_user; + } else { + $whereRole = 'd.fk_user = :fkUser'; + $whereParams[':fkUser'] = $fk_user; + } + } catch (Exception $e) { + error_log("Erreur récupération RR : " . $e->getMessage()); + $whereRole = 'd.fk_user = :fkUser'; + $whereParams[':fkUser'] = $fk_user; + } + break; + default: + $whereRole = 'd.fk_user = :fkUser'; + $whereParams[':fkUser'] = $fk_user; + break; + } + + if ($context === "archives") { + $whereStatut = ' AND d.fk_statut_devis = 20'; + } else { + $whereStatut = ' AND d.fk_statut_devis != 20'; + } + + $whereParams[':term1'] = $termSafe; + $whereParams[':term2'] = $termSafe; + $whereParams[':term3'] = $termSafe; + $whereParams[':term4'] = $termSafe; + $whereParams[':term5'] = $termSafe; + $whereParams[':term6'] = $termSafe; + $whereParams[':term7'] = $termSafe; + $whereParams[':term8'] = $termSafe; + $whereParams[':term9'] = $termSafe; + $whereParams[':term10'] = $termSafe; + $whereParams[':term11'] = $termSafe; + $whereParams[':term12'] = $termSafe; + $whereParams[':term13'] = $termSafe; + $whereParams[':term14'] = $termSafe; + $whereParams[':term15'] = $termSafe; + $whereParams[':term16'] = $termSafe; + $whereParams[':term17'] = $termSafe; + + try { + $db = Database::getInstance(); + $sql = 'SELECT DISTINCT d.rowid, d.dossier, d.date_demande, d.date_remise, d.num_opportunite, d.fk_client, d.montant_total_ht_remise, d.marge_totale, d.commentaire, d.chk_speciaux, c.libelle, c.ville, c.cp, d.fk_statut_devis, '; + $sql .= 'd.lib_new_client, d.type_new_client, d.adresse1_new_client, d.adresse2_new_client, d.adresse3_new_client, d.cp_new_client, d.ville_new_client, d.comment_devis, d.comment_geste_comm, '; + $sql .= 'd.contact_new_nom, d.contact_new_prenom, d.new_telephone, d.new_mobile, d.new_email, d.contact_new_fonction, LEFT(u.prenom,1) AS prenom, u.libelle as nom, '; + $sql .= 'xs.libelle as lib_statut, d.chk_new_statut, m.libelle as lib_marche, d.chk_validat, d.fk_user_validat, d.date_validat '; + $sql .= 'FROM devis d '; + $sql .= 'LEFT JOIN clients c ON c.rowid=d.fk_client '; + $sql .= 'LEFT JOIN x_statuts_devis xs ON xs.rowid=d.fk_statut_devis '; + $sql .= 'LEFT JOIN marches m ON m.rowid=d.fk_marche '; + $sql .= 'LEFT JOIN users u ON u.rowid=d.fk_user '; + $sql .= 'LEFT JOIN clients_contacts ct ON ct.fk_client=d.fk_client '; + $sql .= 'WHERE (' . $whereRole . ')' . $whereStatut . ' AND ('; + $sql .= 'd.rowid LIKE :term1 OR '; + $sql .= 'c.libelle LIKE :term2 OR '; + $sql .= 'c.adresse1 LIKE :term3 OR '; + $sql .= 'c.adresse2 LIKE :term4 OR '; + $sql .= 'c.adresse3 LIKE :term5 OR '; + $sql .= 'c.cp LIKE :term6 OR '; + $sql .= 'c.ville LIKE :term7 OR '; + $sql .= 'm.libelle LIKE :term8 OR '; + $sql .= 'd.num_opportunite LIKE :term9 OR '; + $sql .= 'd.lib_new_client LIKE :term10 OR '; + $sql .= 'd.cp_new_client LIKE :term11 OR '; + $sql .= 'd.ville_new_client LIKE :term12 OR '; + $sql .= 'ct.nom LIKE :term13 OR '; + $sql .= 'ct.prenom LIKE :term14 OR '; + $sql .= 'ct.fonction LIKE :term15 OR '; + $sql .= 'ct.email LIKE :term16 OR '; + $sql .= 'd.commentaire LIKE :term17) '; + $sql .= 'ORDER BY d.dossier, d.date_remise DESC'; + + eLog("=== SEARCH DEVIS DEBUG ==="); + eLog("Terme recherché: " . $term); + eLog("Context: " . $context); + eLog("SQL: " . $sql); + eLog("Params: " . print_r($whereParams, true)); + + $pdo = $db->getPDO(); + $stmt = $pdo->prepare($sql); + $stmt->execute($whereParams); + $devis = $stmt->fetchAll(PDO::FETCH_ASSOC); + + eLog("Nombre de devis trouvés: " . count($devis)); + + $nb_devis = array(); + foreach ($devis as $dev) { + if (!isset($nb_devis[$dev["fk_statut_devis"]])) { + $nb_devis[$dev["fk_statut_devis"]] = 1; + } else { + $nb_devis[$dev["fk_statut_devis"]]++; + } + } + + $dossiers = array(); + foreach ($devis as $dev) { + if (!in_array($dev["dossier"], array_column($dossiers, 'dossier'))) { + $dossiers[] = array("dossier" => $dev["dossier"]); + } + } + + echo json_encode(array( + "success" => true, + "devis" => $devis, + "nb_devis" => $nb_devis, + "dossiers" => $dossiers + )); + } catch (Exception $e) { + error_log("Erreur recherche devis : " . $e->getMessage()); + echo json_encode(array("success" => false, "message" => "Erreur lors de la recherche : " . $e->getMessage())); + } + } + break; + + case "search_devis_sap": + eLog("=== search_devis_sap case appelé ==="); + $rawData = file_get_contents("php://input"); + eLog("Raw data: " . $rawData); + $data = json_decode($rawData); + eLog("Data decoded: " . print_r($data, true)); + eLog("isset term: " . (isset($data->term) ? 'YES' : 'NO')); + + if (isset($data->term)) { + $term = nettoie_input($data->term); + $context = nettoie_input($data->context); + + if (strlen($term) < 3) { + echo json_encode(array("success" => false, "message" => "Le terme de recherche doit contenir au moins 3 caractères")); + break; + } + + $termSafe = '%' . $term . '%'; + + $whereParams = []; + $whereRole = '1=1'; + + if ($context === "archives") { + $whereStatut = ' AND d.fk_statut_devis = 20'; + } else { + $whereStatut = ' AND d.fk_statut_devis != 20'; + } + + $whereParams[':term1'] = $termSafe; + $whereParams[':term2'] = $termSafe; + $whereParams[':term3'] = $termSafe; + $whereParams[':term4'] = $termSafe; + $whereParams[':term5'] = $termSafe; + $whereParams[':term6'] = $termSafe; + $whereParams[':term7'] = $termSafe; + $whereParams[':term8'] = $termSafe; + $whereParams[':term9'] = $termSafe; + $whereParams[':term10'] = $termSafe; + $whereParams[':term11'] = $termSafe; + $whereParams[':term12'] = $termSafe; + $whereParams[':term13'] = $termSafe; + $whereParams[':term14'] = $termSafe; + $whereParams[':term15'] = $termSafe; + $whereParams[':term16'] = $termSafe; + $whereParams[':term17'] = $termSafe; + + try { + $db = Database::getInstance(); + $sql = 'SELECT DISTINCT d.rowid, d.dossier, d.date_demande, d.date_remise, d.num_opportunite, d.fk_client, d.montant_total_ht_remise, d.marge_totale, d.commentaire, d.chk_speciaux, c.libelle, c.ville, c.cp, d.fk_statut_devis, '; + $sql .= 'd.lib_new_client, d.type_new_client, d.adresse1_new_client, d.adresse2_new_client, d.adresse3_new_client, d.cp_new_client, d.ville_new_client, d.comment_devis, d.comment_geste_comm, '; + $sql .= 'd.contact_new_nom, d.contact_new_prenom, d.new_telephone, d.new_mobile, d.new_email, d.contact_new_fonction, LEFT(u.prenom,1) AS prenom, u.libelle as nom, '; + $sql .= 'xs.libelle as lib_statut, d.chk_new_statut, m.libelle as lib_marche, d.chk_validat, d.fk_user_validat, d.date_validat, c.code '; + $sql .= 'FROM devis d '; + $sql .= 'LEFT JOIN clients c ON c.rowid=d.fk_client '; + $sql .= 'LEFT JOIN x_statuts_devis xs ON xs.rowid=d.fk_statut_devis '; + $sql .= 'LEFT JOIN marches m ON m.rowid=d.fk_marche '; + $sql .= 'LEFT JOIN users u ON u.rowid=d.fk_user '; + $sql .= 'LEFT JOIN clients_contacts ct ON ct.fk_client=d.fk_client '; + $sql .= 'WHERE (' . $whereRole . ')' . $whereStatut . ' AND ('; + $sql .= 'd.rowid LIKE :term1 OR '; + $sql .= 'c.libelle LIKE :term2 OR '; + $sql .= 'c.adresse1 LIKE :term3 OR '; + $sql .= 'c.adresse2 LIKE :term4 OR '; + $sql .= 'c.adresse3 LIKE :term5 OR '; + $sql .= 'c.cp LIKE :term6 OR '; + $sql .= 'c.ville LIKE :term7 OR '; + $sql .= 'c.code LIKE :term8 OR '; + $sql .= 'm.libelle LIKE :term9 OR '; + $sql .= 'd.num_opportunite LIKE :term10 OR '; + $sql .= 'd.lib_new_client LIKE :term11 OR '; + $sql .= 'd.cp_new_client LIKE :term12 OR '; + $sql .= 'd.ville_new_client LIKE :term13 OR '; + $sql .= 'ct.nom LIKE :term14 OR '; + $sql .= 'ct.prenom LIKE :term15 OR '; + $sql .= 'ct.fonction LIKE :term16 OR '; + $sql .= 'ct.email LIKE :term17) '; + $sql .= 'ORDER BY d.dossier, d.date_remise DESC'; + + eLog("=== SEARCH DEVIS SAP DEBUG ==="); + eLog("Terme recherché: " . $term); + eLog("Context: " . $context); + eLog("SQL: " . $sql); + eLog("Params: " . print_r($whereParams, true)); + + $pdo = $db->getPDO(); + $stmt = $pdo->prepare($sql); + $stmt->execute($whereParams); + $devis = $stmt->fetchAll(PDO::FETCH_ASSOC); + + eLog("Nombre de devis trouvés: " . count($devis)); + + $nb_devis = array(); + foreach ($devis as $dev) { + if (!isset($nb_devis[$dev["fk_statut_devis"]])) { + $nb_devis[$dev["fk_statut_devis"]] = 1; + } else { + $nb_devis[$dev["fk_statut_devis"]]++; + } + } + + $dossiers = array(); + foreach ($devis as $dev) { + if (!in_array($dev["dossier"], array_column($dossiers, 'dossier'))) { + $dossiers[] = array("dossier" => $dev["dossier"]); + } + } + + echo json_encode(array( + "success" => true, + "devis" => $devis, + "nb_devis" => $nb_devis, + "dossiers" => $dossiers + )); + } catch (Exception $e) { + error_log("Erreur recherche devis SAP : " . $e->getMessage()); + echo json_encode(array("success" => false, "message" => "Erreur lors de la recherche : " . $e->getMessage())); + } + } + break; } exit(); diff --git a/controllers/cjximport.php b/controllers/cjximport.php index 97a6bc2..001fa58 100644 --- a/controllers/cjximport.php +++ b/controllers/cjximport.php @@ -49,6 +49,64 @@ function formate_date($sdate) return $ladate; } +function syncContactClient($code, $contactNom, $contactPrenom, $contactFonction, $telephone, $mobile, $email, $fkUser) +{ + try { + $db = Database::getInstance(); + + // 1. Compter les contacts actifs pour ce client + $sql = 'SELECT COUNT(*) as nb FROM clients_contacts WHERE fk_client = :code AND active = 1'; + $countResult = $db->fetchAll($sql, [':code' => $code]); + $nbContacts = $countResult[0]['nb']; + + if ($nbContacts == 0) { + // Aucun contact : créer directement avec principal=1 + $principal = 1; + } else { + // Des contacts existent : vérifier si ce nom+prénom existe (en MAJUSCULES) + $sql = 'SELECT rowid FROM clients_contacts + WHERE fk_client = :code + AND UPPER(nom) = UPPER(:nom) + AND UPPER(prenom) = UPPER(:prenom) + AND active = 1'; + $existingContact = $db->fetchAll($sql, [ + ':code' => $code, + ':nom' => $contactNom, + ':prenom' => $contactPrenom + ]); + + if (count($existingContact) > 0) { + // Contact déjà présent : ne rien faire + eLog("syncContactClient : Contact existe déjà pour client " . $code); + return; + } + + // Contact pas trouvé : créer avec principal=0 + $principal = 0; + } + + // Créer le contact + $sql = 'INSERT INTO clients_contacts SET fk_client = :code, nom = :nom, prenom = :prenom, fonction = :fonction, telephone = :telephone, mobile = :mobile, email = :email, principal = :principal, active = 1, date_creat = NOW(), fk_user_creat = :fk_user'; + $db->query($sql, [ + ':code' => $code, + ':nom' => $contactNom, + ':prenom' => $contactPrenom, + ':fonction' => $contactFonction, + ':telephone' => $telephone, + ':mobile' => $mobile, + ':email' => $email, + ':principal' => $principal, + ':fk_user' => $fkUser + ]); + + eLog("syncContactClient : Contact créé pour client " . $code . " (principal=" . $principal . ")"); + + } catch (Exception $e) { + error_log("Erreur syncContactClient : " . $e->getMessage()); + eLog("Erreur syncContactClient pour client " . $code . " : " . $e->getMessage()); + } +} + switch ($Route->_action) { case "upload_clients": @@ -171,6 +229,10 @@ switch ($Route->_action) { ]); $fkClient = $db->lastInsertId(); fwrite($fhlog, $row . "--- Ajout client avec requête préparée\r\n"); + + // Synchroniser le contact dans clients_contacts + syncContactClient($code, $contactNom, $contactPrenom, $contactFonction, $telephone, $mobile, $email, $fkUser); + } catch (Exception $e) { error_log("Erreur insertion client : " . $e->getMessage()); fwrite($fhlog, "Erreur insertion : " . $e->getMessage() . "\r\n"); @@ -212,6 +274,10 @@ switch ($Route->_action) { ':code' => $code ]); fwrite($fhlog, $row . "--- MàJ client avec requête préparée\r\n"); + + // Synchroniser le contact dans clients_contacts + syncContactClient($code, $contactNom, $contactPrenom, $contactFonction, $telephone, $mobile, $email, $fkUser); + } catch (Exception $e) { error_log("Erreur mise à jour client : " . $e->getMessage()); fwrite($fhlog, "Erreur MàJ : " . $e->getMessage() . "\r\n"); diff --git a/docs/TODO.md b/docs/TODO.md index 6253a4a..3111e4f 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -195,12 +195,90 @@ DB_PASSWORD= # À sécuriser - [ ] Scripts de backup automatisés à mettre en place - [ ] Réplication master-slave pour haute disponibilité (optionnel) +## ⚠️ CRITIQUE - Risque de collision de codes clients (EN ATTENTE CLIENT) + +### Problématique identifiée +**Date**: 26 novembre 2025 +**Statut**: 🔴 EN ATTENTE RÉPONSE CLIENT + +#### Situation actuelle +Lorsqu'un commercial crée un nouveau client manuellement dans CLEO (client non présent dans SAP), le système génère automatiquement un code via : +```php +$newCode = MAX(code) + 1; // cjxdevis.php ligne 1326 +``` + +#### Risque de collision +**Scénario catastrophe** : +1. Commercial crée un client manuel → code auto = `12345` +2. Commercial ajoute des contacts, fait des devis +3. **Import SAP suivant** : un nouveau client SAP arrive avec le code `12345` +4. L'import trouve le client existant (même code) et **écrase toutes les données** du client manuel +5. Les contacts du client manuel deviennent incohérents (pointent vers le mauvais client SAP) +6. Les devis du client manuel sont rattachés au mauvais client SAP + +#### Question posée au client +**"Que se passe-t-il lorsqu'un devis avec un nouveau client (code = MAX+1) est intégré dans SAP ?"** +- Le client manuel reçoit-il un vrai code SAP ? +- Le code est-il synchronisé dans CLEO après intégration ? +- Existe-t-il un processus de réconciliation ? + +### Solutions techniques envisagées + +#### Option A : Plage réservée pour clients manuels +```php +// Codes 9000000+ réservés aux créations manuelles +$newCode = 9000000 + $compteur; +``` +**Avantages** : Simple, pas de collision possible +**Inconvénients** : Nécessite coordination avec SAP + +#### Option B : Codes négatifs pour clients manuels +```php +// Codes négatifs = clients manuels non SAP +$newCode = -1 * (MAX(ABS(code)) + 1); +``` +**Avantages** : Distinction claire SAP/Manuel +**Inconvénients** : Peut poser problème avec certains systèmes + +#### Option C : Flag `chk_manual` + protection +```sql +ALTER TABLE clients ADD COLUMN chk_manual TINYINT DEFAULT 0; +``` +- `chk_manual = 1` → Client créé manuellement, jamais écrasé par import SAP +- Lors de l'import SAP, ignorer les clients avec `chk_manual = 1` +- Processus manuel de réconciliation si le client est créé dans SAP + +**Avantages** : Protection garantie, traçabilité +**Inconvénients** : Nécessite gestion manuelle de la réconciliation + +#### Option D : Code temporaire + synchronisation +- Client manuel créé avec code `TEMP_XXXXX` +- Lors de l'intégration SAP, récupération du vrai code SAP +- Mise à jour du code client + tous les contacts/devis associés + +**Avantages** : Cohérence totale avec SAP +**Inconvénients** : Complexe, nécessite API ou process de sync + +### Actions en attente +- [ ] **Réponse client** sur le processus actuel d'intégration SAP +- [ ] Choix de la solution technique selon la réponse +- [ ] Implémentation de la solution retenue +- [ ] Tests de non-régression sur imports SAP +- [ ] Documentation du processus de gestion des clients manuels + +### Impact sur le code existant +**Fichiers concernés** : +- `controllers/cjxdevis.php` : fonction `save_new_client` (ligne 1308) +- `controllers/cjximport.php` : fonction `upload_clients` (ligne 112) +- Documentation utilisateur à mettre à jour + +--- + ## Modification Contacts Clients - Migration vers clients.code ### Contexte -La relation entre `clients_contacts` et `clients` utilise actuellement `clients.rowid` comme clé étrangère. -Cela pose problème lors des imports SAP qui peuvent écraser ou modifier les `rowid`. -Il faut migrer vers `clients.code` (identifiant SAP immuable) pour garantir l'intégrité des relations. +La relation entre `clients_contacts` et `clients` utilise `clients.code` comme clé de référence. +Le système a été conçu pour utiliser le `code` SAP (clé métier immuable) plutôt que le `rowid` (clé technique auto-incrémentée). ### Plan de correction diff --git a/docs/cleo.sql b/docs/cleo-202512021157.sql similarity index 85% rename from docs/cleo.sql rename to docs/cleo-202512021157.sql index 3f818d7..9c54b70 100644 --- a/docs/cleo.sql +++ b/docs/cleo-202512021157.sql @@ -1,9 +1,9 @@ /*M!999999\- enable the sandbox mode */ --- MariaDB dump 10.19 Distrib 10.11.9-MariaDB, for debian-linux-gnu (x86_64) +-- MariaDB dump 10.19-11.8.3-MariaDB, for debian-linux-gnu (x86_64) -- --- Host: localhost Database: uof_linet +-- Host: localhost Database: cleo -- ------------------------------------------------------ --- Server version 10.11.9-MariaDB-deb12 +-- Server version 11.4.8-MariaDB-log /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -14,7 +14,7 @@ /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */; -- -- Table structure for table `clients` @@ -22,7 +22,7 @@ DROP TABLE IF EXISTS `clients`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `clients` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `code` int(11) NOT NULL, @@ -51,9 +51,38 @@ CREATE TABLE `clients` ( UNIQUE KEY `code_UNIQUE` (`code`), KEY `libelle` (`libelle`), KEY `cp` (`cp`) -) ENGINE=InnoDB AUTO_INCREMENT=5307 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; +) ENGINE=InnoDB AUTO_INCREMENT=5309 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `clients_contacts` +-- + +DROP TABLE IF EXISTS `clients_contacts`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `clients_contacts` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `fk_client` int(11) NOT NULL, + `nom` varchar(50) DEFAULT NULL, + `prenom` varchar(50) DEFAULT NULL, + `fonction` varchar(50) DEFAULT NULL, + `telephone` varchar(20) DEFAULT NULL, + `mobile` varchar(20) DEFAULT NULL, + `email` varchar(75) DEFAULT NULL, + `principal` tinyint(1) DEFAULT 0 COMMENT 'Contact principal du client', + `active` tinyint(1) DEFAULT 1, + `date_creat` datetime DEFAULT NULL, + `fk_user_creat` int(11) DEFAULT NULL, + `date_modif` datetime DEFAULT NULL, + `fk_user_modif` int(11) DEFAULT NULL, + PRIMARY KEY (`rowid`), + UNIQUE KEY `rowid_UNIQUE` (`rowid`), + KEY `fk_client` (`fk_client`), + KEY `principal` (`fk_client`,`principal`), + KEY `email` (`email`) +) ENGINE=InnoDB AUTO_INCREMENT=8199 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Contacts multiples par client' `PAGE_COMPRESSED`='ON'; +/*!40101 SET character_set_client = @saved_cs_client */; -- -- Table structure for table `clients_sites` @@ -61,7 +90,7 @@ CREATE TABLE `clients` ( DROP TABLE IF EXISTS `clients_sites`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `clients_sites` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_client` int(11) NOT NULL, @@ -77,22 +106,13 @@ CREATE TABLE `clients_sites` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `clients_sites` --- - -LOCK TABLES `clients_sites` WRITE; -/*!40000 ALTER TABLE `clients_sites` DISABLE KEYS */; -/*!40000 ALTER TABLE `clients_sites` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `commerciaux` -- DROP TABLE IF EXISTS `commerciaux`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `commerciaux` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_entite` int(11) DEFAULT 0, @@ -133,14 +153,13 @@ CREATE TABLE `commerciaux` ( ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `commerciaux_entites` -- DROP TABLE IF EXISTS `commerciaux_entites`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `commerciaux_entites` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `libelle` varchar(45) DEFAULT NULL, @@ -157,26 +176,13 @@ CREATE TABLE `commerciaux_entites` ( ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `commerciaux_entites` --- - -LOCK TABLES `commerciaux_entites` WRITE; -/*!40000 ALTER TABLE `commerciaux_entites` DISABLE KEYS */; -INSERT INTO `commerciaux_entites` VALUES -(1,'LINET',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1), -(2,'WISSNER-BOSSERHOFF',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1), -(3,'LINET & WI-BO',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1); -/*!40000 ALTER TABLE `commerciaux_entites` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `commerciaux_params` -- DROP TABLE IF EXISTS `commerciaux_params`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `commerciaux_params` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_commercial` int(11) DEFAULT NULL, @@ -269,14 +275,13 @@ CREATE TABLE `commerciaux_params` ( ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `devis` -- DROP TABLE IF EXISTS `devis`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `devis` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_user` int(11) NOT NULL DEFAULT 0, @@ -285,6 +290,7 @@ CREATE TABLE `devis` ( `date_remise` date DEFAULT NULL, `num_opportunite` varchar(8) NOT NULL DEFAULT '', `fk_client` int(11) NOT NULL DEFAULT 0, + `fk_contact` int(11) DEFAULT NULL, `fk_marche` int(11) NOT NULL DEFAULT 0, `fk_statut_devis` int(11) NOT NULL DEFAULT 0, `chk_clients_secteur` tinyint(1) NOT NULL DEFAULT 1, @@ -327,18 +333,18 @@ CREATE TABLE `devis` ( KEY `fk_client` (`fk_client`), KEY `fk_statut_devis` (`fk_statut_devis`), KEY `date_demande` (`date_demande`), - KEY `dossier` (`fk_user`,`dossier`) -) ENGINE=InnoDB AUTO_INCREMENT=4611 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; + KEY `dossier` (`fk_user`,`dossier`), + KEY `fk_contact` (`fk_contact`) +) ENGINE=InnoDB AUTO_INCREMENT=4624 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `devis_histo` -- DROP TABLE IF EXISTS `devis_histo`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `devis_histo` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_devis` int(11) DEFAULT NULL, @@ -350,7 +356,7 @@ CREATE TABLE `devis_histo` ( `fk_statut_devis` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `devis_histo_fk_devis_index` (`fk_devis`) -) ENGINE=InnoDB AUTO_INCREMENT=22331 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; +) ENGINE=InnoDB AUTO_INCREMENT=22388 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -359,7 +365,7 @@ CREATE TABLE `devis_histo` ( DROP TABLE IF EXISTS `devis_produits`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `devis_produits` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_devis` int(11) NOT NULL, @@ -392,17 +398,16 @@ CREATE TABLE `devis_produits` ( PRIMARY KEY (`rowid`), KEY `devis_produits__devis` (`fk_devis`), KEY `devis_produits__produit` (`fk_produit`) -) ENGINE=InnoDB AUTO_INCREMENT=29277 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; +) ENGINE=InnoDB AUTO_INCREMENT=29314 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `devis_speciaux` -- DROP TABLE IF EXISTS `devis_speciaux`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `devis_speciaux` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_devis` int(11) NOT NULL DEFAULT 0, @@ -461,14 +466,13 @@ CREATE TABLE `devis_speciaux` ( ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `entites` -- DROP TABLE IF EXISTS `entites`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `entites` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `libelle` varchar(45) DEFAULT '', @@ -513,14 +517,13 @@ CREATE TABLE `entites` ( ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `import_ventes` -- DROP TABLE IF EXISTS `import_ventes`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `import_ventes` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `source` varchar(15) DEFAULT '', @@ -570,7 +573,7 @@ CREATE TABLE `import_ventes` ( DROP TABLE IF EXISTS `infos`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `infos` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `date_infos` date DEFAULT NULL, @@ -586,14 +589,13 @@ CREATE TABLE `infos` ( ) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `marches` -- DROP TABLE IF EXISTS `marches`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `marches` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `numero` varchar(20) NOT NULL DEFAULT '', @@ -642,7 +644,7 @@ CREATE TABLE `marches` ( DROP TABLE IF EXISTS `marches_listes`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `marches_listes` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_marche` int(11) DEFAULT NULL, @@ -660,7 +662,7 @@ CREATE TABLE `marches_listes` ( DROP TABLE IF EXISTS `marches_produits`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `marches_produits` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_marche` int(11) DEFAULT 0, @@ -702,7 +704,7 @@ CREATE TABLE `marches_produits` ( DROP TABLE IF EXISTS `marches_versions`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `marches_versions` ( `rowid` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Id', `libelle` varchar(75) DEFAULT NULL COMMENT 'Libellé', @@ -713,24 +715,13 @@ CREATE TABLE `marches_versions` ( ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Version des marchés' `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `marches_versions` --- - -LOCK TABLES `marches_versions` WRITE; -/*!40000 ALTER TABLE `marches_versions` DISABLE KEYS */; -INSERT INTO `marches_versions` VALUES -(1,'Version Avril 2022','2022-04-01','0000-00-00',1); -/*!40000 ALTER TABLE `marches_versions` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `medias` -- DROP TABLE IF EXISTS `medias`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `medias` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `dir0` varchar(150) DEFAULT NULL, @@ -746,7 +737,7 @@ CREATE TABLE `medias` ( PRIMARY KEY (`rowid`), UNIQUE KEY `rowid_UNIQUE` (`rowid`), KEY `support` (`support`,`support_rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3866 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; +) ENGINE=InnoDB AUTO_INCREMENT=3878 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -755,7 +746,7 @@ CREATE TABLE `medias` ( DROP TABLE IF EXISTS `notifications`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `notifications` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `dateheure` datetime DEFAULT NULL, @@ -775,7 +766,7 @@ CREATE TABLE `notifications` ( DROP TABLE IF EXISTS `produits`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `produits` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_marche` int(11) NOT NULL DEFAULT 0, @@ -821,7 +812,7 @@ CREATE TABLE `produits` ( DROP TABLE IF EXISTS `produits_familles`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `produits_familles` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `groupe` varchar(30) NOT NULL, @@ -841,7 +832,7 @@ CREATE TABLE `produits_familles` ( DROP TABLE IF EXISTS `regions`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `regions` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `libelle` varchar(75) DEFAULT NULL, @@ -851,22 +842,13 @@ CREATE TABLE `regions` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `regions` --- - -LOCK TABLES `regions` WRITE; -/*!40000 ALTER TABLE `regions` DISABLE KEYS */; -/*!40000 ALTER TABLE `regions` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `simul` -- DROP TABLE IF EXISTS `simul`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `simul` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_import_vente` int(11) DEFAULT NULL, @@ -885,14 +867,13 @@ CREATE TABLE `simul` ( ) ENGINE=InnoDB AUTO_INCREMENT=1057 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `users` -- DROP TABLE IF EXISTS `users`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `users` ( `rowid` int(10) unsigned NOT NULL AUTO_INCREMENT, `fk_entite` int(11) DEFAULT NULL, @@ -940,13 +921,64 @@ CREATE TABLE `users` ( ) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `users_entites` +-- + +DROP TABLE IF EXISTS `users_entites`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `users_entites` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `libelle` varchar(45) DEFAULT '', + `http_host` varchar(150) DEFAULT '', + `adresse1` varchar(45) DEFAULT '', + `adresse2` varchar(45) DEFAULT '', + `cp` varchar(5) DEFAULT '', + `ville` varchar(45) DEFAULT '', + `type_entite` varchar(5) DEFAULT 'form', + `tva_intra` varchar(15) DEFAULT '', + `rcs` varchar(45) DEFAULT '', + `siret` varchar(17) DEFAULT NULL, + `ape` varchar(5) DEFAULT '', + `num_opca` varchar(15) DEFAULT '', + `logo` varchar(45) DEFAULT '', + `tel1` varchar(20) DEFAULT '', + `tel2` varchar(20) DEFAULT '', + `couleur` varchar(7) DEFAULT '#FFFAF0', + `prefecture` varchar(45) DEFAULT 'Bretagne', + `fk_titre_gerant` int(11) DEFAULT 1, + `gerant_prenom` varchar(45) DEFAULT '', + `gerant_nom` varchar(45) DEFAULT '', + `email` varchar(45) DEFAULT '', + `site_url` varchar(45) DEFAULT '', + `gerant_signature` varchar(45) DEFAULT '', + `tampon_signature` varchar(45) DEFAULT '', + `rib_banque` varchar(5) DEFAULT '', + `rib_guichet` varchar(5) DEFAULT '', + `rib_compte` varchar(11) DEFAULT '', + `rib_cle` varchar(2) DEFAULT '', + `rib_domiciliation` varchar(45) DEFAULT '', + `iban` varchar(33) DEFAULT '', + `bic` varchar(15) DEFAULT '', + `demo` tinyint(1) DEFAULT 0, + `genbase` varchar(45) DEFAULT '0', + `groupebase` varchar(45) DEFAULT '0', + `table_users_gen` varchar(50) DEFAULT '', + `appname` varchar(45) DEFAULT '', + `raz_num_devis` tinyint(1) DEFAULT 0, + `active` tinyint(1) DEFAULT 1, + PRIMARY KEY (`rowid`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci `PAGE_COMPRESSED`='ON'; +/*!40101 SET character_set_client = @saved_cs_client */; + -- -- Table structure for table `ventes` -- DROP TABLE IF EXISTS `ventes`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `ventes` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `source` varchar(45) DEFAULT NULL, @@ -977,22 +1009,13 @@ CREATE TABLE `ventes` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `ventes` --- - -LOCK TABLES `ventes` WRITE; -/*!40000 ALTER TABLE `ventes` DISABLE KEYS */; -/*!40000 ALTER TABLE `ventes` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `x_clients_types` -- DROP TABLE IF EXISTS `x_clients_types`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `x_clients_types` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `code` char(3) DEFAULT NULL, @@ -1004,28 +1027,13 @@ CREATE TABLE `x_clients_types` ( ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `x_clients_types` --- - -LOCK TABLES `x_clients_types` WRITE; -/*!40000 ALTER TABLE `x_clients_types` DISABLE KEYS */; -INSERT INTO `x_clients_types` VALUES -(1,'PUB','Public',1), -(2,'PRA','Privé Associatif',1), -(3,'PRD','Privé Distributeur',1), -(4,'PRC','Privé Commercial',1), -(5,'ESP','ESPIC',1); -/*!40000 ALTER TABLE `x_clients_types` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `x_familles` -- DROP TABLE IF EXISTS `x_familles`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `x_familles` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `libelle` varchar(20) NOT NULL DEFAULT '', @@ -1037,34 +1045,13 @@ CREATE TABLE `x_familles` ( ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `x_familles` --- - -LOCK TABLES `x_familles` WRITE; -/*!40000 ALTER TABLE `x_familles` DISABLE KEYS */; -INSERT INTO `x_familles` VALUES -(3,'Lits SBU1',1,1), -(4,'Lits SBU2',2,1), -(5,'Accessoires SBU1',3,1), -(6,'Accessoires SBU2',4,1), -(7,'Services',5,1), -(8,'Matelas mousse',6,1), -(9,'Matelas à air',7,1), -(10,'Mobilier',8,1), -(11,'Assises',9,1), -(12,'Autres',11,1), -(13,'Domalys',10,1); -/*!40000 ALTER TABLE `x_familles` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `x_regions` -- DROP TABLE IF EXISTS `x_regions`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `x_regions` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_entite` int(11) DEFAULT 0, @@ -1075,39 +1062,13 @@ CREATE TABLE `x_regions` ( ) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `x_regions` --- - -LOCK TABLES `x_regions` WRITE; -/*!40000 ALTER TABLE `x_regions` DISABLE KEYS */; -INSERT INTO `x_regions` VALUES -(1,1,'SUD-OUEST',1), -(2,1,'RHONE-ALPES / AUVERGNE',1), -(3,1,'PACA',1), -(4,1,'EST',1), -(5,1,'NORD',1), -(6,1,'GRAND-OUEST',1), -(7,1,'IDF',1), -(8,1,'DOM-TOM',1), -(9,2,'WB-NORD',1), -(13,2,'WB-SUD OUEST',1), -(14,2,'WB-EST',1), -(15,2,'WB-ILE DE FRANCE',1), -(16,2,'WB-SUD-EST',1), -(17,2,'WB-CENTRE-EST ET DOM',1), -(18,1,'DIRECTION',1), -(19,2,'WB-NORD OUEST',1); -/*!40000 ALTER TABLE `x_regions` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `x_roles` -- DROP TABLE IF EXISTS `x_roles`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `x_roles` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `libelle` varchar(45) DEFAULT '', @@ -1118,30 +1079,13 @@ CREATE TABLE `x_roles` ( ) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Les différents rôles des utilisateurs' `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `x_roles` --- - -LOCK TABLES `x_roles` WRITE; -/*!40000 ALTER TABLE `x_roles` DISABLE KEYS */; -INSERT INTO `x_roles` VALUES -(1,'Direction Commerciale','DC',1), -(2,'Direction des Ventes','DV',1), -(3,'Commercial(e)','RR',1), -(4,'Clinicien(ne)','CL',1), -(5,'Direction Grands Comptes','GC',1), -(20,'Administration des ventes','ADV',1), -(90,'Administrateur','ADM',1); -/*!40000 ALTER TABLE `x_roles` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `x_statuts_devis` -- DROP TABLE IF EXISTS `x_statuts_devis`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `x_statuts_devis` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `libelle` varchar(30) DEFAULT NULL, @@ -1152,22 +1096,47 @@ CREATE TABLE `x_statuts_devis` ( /*!40101 SET character_set_client = @saved_cs_client */; -- --- Dumping data for table `x_statuts_devis` +-- Table structure for table `y_pages` -- -LOCK TABLES `x_statuts_devis` WRITE; -/*!40000 ALTER TABLE `x_statuts_devis` DISABLE KEYS */; -INSERT INTO `x_statuts_devis` VALUES -(1,'En cours de création',1), -(2,'En cours de validation DIR-CO',1), -(3,'En cours de validation DV/DGC',1), -(4,'A traiter sur SAP',1), -(6,'A vérifier par le RR',1), -(7,'A envoyer au client',1), -(10,'Envoyé au client',0), -(20,'Archivé',1); -/*!40000 ALTER TABLE `x_statuts_devis` ENABLE KEYS */; -UNLOCK TABLES; +DROP TABLE IF EXISTS `y_pages`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `y_pages` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `fk_parent` int(11) DEFAULT 0, + `link` varchar(75) DEFAULT NULL, + `libelle` varchar(45) DEFAULT NULL, + `titre` varchar(75) DEFAULT NULL, + `tooltip` varchar(45) DEFAULT NULL, + `description` varchar(200) DEFAULT NULL, + `keywords` varchar(200) DEFAULT NULL, + `script` varchar(45) DEFAULT NULL, + `enmaintenance` tinyint(1) DEFAULT 0 COMMENT '0 libre d''accès, 1 en maintenance mais accès aux données, 2 en maintenance sans accès aux données', + `admin` tinyint(1) DEFAULT 0, + `mail` tinyint(1) DEFAULT 0, + `admtools` tinyint(1) DEFAULT 0, + `magazine` tinyint(1) DEFAULT 0, + `files` tinyint(1) DEFAULT 0, + `editor` tinyint(1) DEFAULT 0, + `autocomplete` tinyint(1) DEFAULT 0, + `print` tinyint(1) DEFAULT 0, + `form` tinyint(1) DEFAULT 0, + `sidebar` tinyint(1) DEFAULT 0, + `chart` tinyint(1) DEFAULT 0, + `agenda` tinyint(1) DEFAULT 0, + `scheduler` tinyint(1) DEFAULT 0, + `osm` tinyint(1) DEFAULT 0, + `layout` varchar(45) DEFAULT 'default.php', + `in_menu` tinyint(1) DEFAULT 1, + `ordre_menu` int(11) DEFAULT 0, + `active` tinyint(1) DEFAULT 1, + PRIMARY KEY (`rowid`), + UNIQUE KEY `rowid_UNIQUE` (`rowid`), + KEY `script` (`script`), + KEY `admin` (`admin`) +) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci `PAGE_COMPRESSED`='ON'; +/*!40101 SET character_set_client = @saved_cs_client */; -- -- Table structure for table `z_history` @@ -1175,7 +1144,7 @@ UNLOCK TABLES; DROP TABLE IF EXISTS `z_history`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `z_history` ( `fk_user` int(11) NOT NULL, `libelle` varchar(20) NOT NULL DEFAULT 'tiers', @@ -1185,22 +1154,13 @@ CREATE TABLE `z_history` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Dumping data for table `z_history` --- - -LOCK TABLES `z_history` WRITE; -/*!40000 ALTER TABLE `z_history` DISABLE KEYS */; -/*!40000 ALTER TABLE `z_history` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `z_logs` -- DROP TABLE IF EXISTS `z_logs`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `z_logs` ( `date` datetime NOT NULL, `ip` varchar(15) NOT NULL, @@ -1220,7 +1180,7 @@ CREATE TABLE `z_logs` ( DROP TABLE IF EXISTS `z_sessions`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `z_sessions` ( `sid` text NOT NULL, `fk_user` int(11) NOT NULL, @@ -1238,7 +1198,7 @@ CREATE TABLE `z_sessions` ( DROP TABLE IF EXISTS `z_stats`; /*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `z_stats` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `libelle` varchar(75) DEFAULT NULL, @@ -1253,6 +1213,10 @@ CREATE TABLE `z_stats` ( ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci `PAGE_COMPRESSED`='ON'; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Dumping routines for database 'cleo' +-- +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; @@ -1260,6 +1224,6 @@ CREATE TABLE `z_stats` ( /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */; --- Dump completed on 2025-09-11 14:44:41 +-- Dump completed on 2025-12-02 11:57:09 diff --git a/models/mexpxls.php b/models/mexpxls.php index 9ff6555..fd639bc 100644 --- a/models/mexpxls.php +++ b/models/mexpxls.php @@ -1,8 +1,7 @@ _action) { $excelData .= implode("\t", array_values($fields)) . "\n"; array_walk($contact, 'filterData'); $excelData .= implode("\t", array_values($contact)) . "\n"; - } else { // Client existant : données depuis la table clients $sql = 'SELECT c.code, c.libelle, c.adresse1, c.adresse2, c.adresse3, c.cp, c.ville FROM clients c WHERE c.rowid = :client_id'; @@ -112,7 +109,7 @@ switch ($Route->_action) { $contact = $db->fetchOne($sql, [':contact_id' => $fkContactSafe]); } else { // Fallback : contact principal du client - $sql = 'SELECT cc.nom, cc.prenom, cc.fonction, cc.telephone, cc.mobile, cc.email FROM clients_contacts cc WHERE cc.fk_client = :client_id AND cc.chk_principal = 1 AND cc.active = 1 LIMIT 1'; + $sql = 'SELECT cc.nom, cc.prenom, cc.fonction, cc.telephone, cc.mobile, cc.email FROM clients_contacts cc WHERE cc.fk_client = :client_id AND cc.principal = 1 AND cc.active = 1 LIMIT 1'; $contact = $db->fetchOne($sql, [':client_id' => $fkClientSafe]); } @@ -217,12 +214,10 @@ switch ($Route->_action) { header('Content-Disposition: attachment; filename="' . $fileName . '"'); header('Cache-Control: max-age=0'); echo $excelData; - } catch (Exception $e) { error_log("Erreur export Excel : " . $e->getMessage()); http_response_code(500); echo "Erreur lors de l'export du devis"; } exit(); - -} \ No newline at end of file +} diff --git a/models/msap.php b/models/msap.php index 03e570c..0ba261e 100644 --- a/models/msap.php +++ b/models/msap.php @@ -5,9 +5,10 @@ $aModel = array(); $sql = 'SELECT m.* FROM medias m WHERE m.support="devis_pdf_sap" ORDER BY m.support_rowid;'; $aModel["medias"] = getinfos($sql, "gen"); -$sql = 'SELECT d.*, c.libelle, c.adresse1, c.adresse2, c.adresse3, c.code, c.cp, c.ville, c.email, u.libelle as nom, u.prenom, s.libelle as lib_statut, m.libelle as lib_marche '; +$sql = 'SELECT d.*, c.libelle, c.adresse1, c.adresse2, c.adresse3, c.code, c.cp, c.ville, cc.email, u.libelle as nom, u.prenom, s.libelle as lib_statut, m.libelle as lib_marche '; $sql .= 'FROM devis d '; $sql .= 'LEFT JOIN clients c on d.fk_client = c.rowid '; +$sql .= 'LEFT JOIN clients_contacts cc ON d.fk_contact = cc.rowid '; $sql .= 'LEFT JOIN users u ON d.fk_user = u.rowid '; $sql .= 'LEFT JOIN marches m ON d.fk_marche = m.rowid '; $sql .= 'LEFT JOIN x_statuts_devis s ON d.fk_statut_devis = s.rowid '; @@ -24,9 +25,10 @@ foreach ($aModel["devisEnCours"] as $devis) { } } -$sql = 'SELECT d.*, c.libelle, c.adresse1, c.adresse2, c.adresse3, c.code, c.cp, c.ville, u.libelle as nom, u.prenom, s.libelle as lib_statut, m.libelle as lib_marche '; +$sql = 'SELECT d.*, c.libelle, c.adresse1, c.adresse2, c.adresse3, c.code, c.cp, c.ville, cc.email, u.libelle as nom, u.prenom, s.libelle as lib_statut, m.libelle as lib_marche '; $sql .= 'FROM devis d '; $sql .= 'LEFT JOIN clients c on d.fk_client = c.rowid '; +$sql .= 'LEFT JOIN clients_contacts cc ON d.fk_contact = cc.rowid '; $sql .= 'LEFT JOIN users u ON d.fk_user = u.rowid '; $sql .= 'LEFT JOIN marches m ON d.fk_marche = m.rowid '; $sql .= 'LEFT JOIN x_statuts_devis s ON d.fk_statut_devis = s.rowid '; diff --git a/pub/res/js/jdevis.js b/pub/res/js/jdevis.js index 1bf2647..421d378 100644 --- a/pub/res/js/jdevis.js +++ b/pub/res/js/jdevis.js @@ -37,14 +37,107 @@ let devisTotalRemHT = 0 let devisTotalMarge = 0 let chkRegleSeuilsMarge = false // indique si le marché sélectionné prend en compte les seuils de marge fixés dans les familles de produits -let seuilMargeRR = 30 // le seuil de marge du RR sur ce devis, par défaut à 30 % -let seuilMargeDV = 20 // le seuil de marge du DV sur ce devis, par défaut à 20 % +let seuilMargeRR = 40 // le seuil de marge du RR sur ce devis, par défaut à 40 % (MAJ 05/11/2025) +let seuilMargeDV = 30 // le seuil de marge du DV sur ce devis, par défaut à 30 % (MAJ 05/11/2025) let intervalRefresh let nbCommentChat = 0 let draggedElement = null // l'élément qui est en train d'être déplacé (la ligne du produit du devis lors d'un drag and drop) +const tableSortStates = new Map() + +function initTableSort() { + const allTables = document.querySelectorAll('[id^="tblDos"], [id^="tblDosArch"]') + + allTables.forEach(table => { + const tableId = table.id + const headers = table.querySelectorAll('th[data-sortable="true"]') + const tbody = table.querySelector('tbody') + + if (!tbody) return + + if (!tableSortStates.has(tableId)) { + tableSortStates.set(tableId, { + originalOrder: null, + currentSort: { column: null, direction: null } + }) + } + + headers.forEach(header => { + header.addEventListener('click', function() { + const columnIndex = parseInt(this.getAttribute('data-column-index')) + const sortType = this.getAttribute('data-sort-type') + sortTable(tableId, columnIndex, sortType, this) + }) + }) + }) +} + +function sortTable(tableId, columnIndex, sortType, headerElement) { + const table = document.getElementById(tableId) + const tbody = table.querySelector('tbody') + const rows = Array.from(tbody.querySelectorAll('tr')) + const state = tableSortStates.get(tableId) + + if (!state.originalOrder) { + state.originalOrder = rows.slice() + } + + const allHeaders = table.querySelectorAll('th[data-sortable="true"]') + allHeaders.forEach(h => h.style.fontWeight = 'normal') + + let sortedRows + + if (state.currentSort.column === columnIndex && state.currentSort.direction === 'desc') { + sortedRows = state.originalOrder.slice() + state.currentSort = { column: null, direction: null } + } else { + const direction = (state.currentSort.column === columnIndex && state.currentSort.direction === 'asc') ? 'desc' : 'asc' + + sortedRows = rows.slice().sort((a, b) => { + const aCell = a.cells[columnIndex] + const bCell = b.cells[columnIndex] + + if (!aCell || !bCell) return 0 + + let aValue = aCell.textContent.trim() + let bValue = bCell.textContent.trim() + + if (sortType === 'number') { + aValue = aValue.replace(/[^\d,.-]/g, '').replace(',', '.') + bValue = bValue.replace(/[^\d,.-]/g, '').replace(',', '.') + aValue = parseFloat(aValue) || 0 + bValue = parseFloat(bValue) || 0 + return direction === 'asc' ? aValue - bValue : bValue - aValue + } else if (sortType === 'date') { + aValue = parseDateFromText(aValue) + bValue = parseDateFromText(bValue) + if (!aValue && !bValue) return 0 + if (!aValue) return direction === 'asc' ? 1 : -1 + if (!bValue) return direction === 'asc' ? -1 : 1 + return direction === 'asc' ? aValue - bValue : bValue - aValue + } else { + const comparison = aValue.localeCompare(bValue, 'fr') + return direction === 'asc' ? comparison : -comparison + } + }) + + state.currentSort = { column: columnIndex, direction } + headerElement.style.fontWeight = 'bold' + } + + tbody.innerHTML = '' + sortedRows.forEach(row => tbody.appendChild(row)) +} + +function parseDateFromText(dateText) { + const match = dateText.match(/(\d{2})\/(\d{2})[\/\s](\d{4})/) + if (!match) return null + const [, day, month, year] = match + return new Date(year, month - 1, day) +} + window.addEventListener('DOMContentLoaded', (event) => { console.log('#') @@ -3790,6 +3883,148 @@ window.addEventListener('DOMContentLoaded', (event) => { chkVariante.addEventListener('change', calculDevis) }) + let elSearchDevis = document.getElementById('searchDevis') + let elBtnResetSearch = document.getElementById('btnResetSearch') + let searchTimeout = null + + function restoreSearch() { + const savedTerm = sessionStorage.getItem('devisSearchTerm') + if (savedTerm && savedTerm.length >= 3) { + elSearchDevis.value = savedTerm + elBtnResetSearch.style.display = 'inline-block' + performSearch(savedTerm) + } + } + + function performSearch(term) { + if (term.length < 3) { + return + } + + const context = chkShowDevisArchives ? 'archives' : 'encours' + + fetch('/jxdevis/search_devis', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + term: term, + context: context, + }), + }) + .then((response) => response.json()) + .then((data) => { + if (data.success) { + const devisIds = data.devis.map((d) => d.rowid) + filterDevisTables(devisIds, context) + updateBadges(data.nb_devis) + } else { + console.error('Erreur recherche:', data.message) + } + }) + .catch((error) => { + console.error('Erreur AJAX:', error) + }) + } + + function filterDevisTables(devisIds, context) { + if (context === 'encours') { + const statuts = document.querySelectorAll('[id^="tblBodyDos"]') + statuts.forEach((tbody) => { + const rows = tbody.querySelectorAll('tr') + rows.forEach((row) => { + if (row.id && row.id.startsWith('tr_')) { + const rowId = parseInt(row.id.replace('tr_', '')) + if (devisIds.includes(rowId)) { + row.style.display = '' + } else { + row.style.display = 'none' + } + } + }) + }) + } else { + const archives = document.querySelectorAll('[id^="tblBodyDosArch"]') + archives.forEach((tbody) => { + const rows = tbody.querySelectorAll('tr') + rows.forEach((row) => { + if (row.id && row.id.startsWith('trArch_')) { + const rowId = parseInt(row.id.replace('trArch_', '')) + if (devisIds.includes(rowId)) { + row.style.display = '' + } else { + row.style.display = 'none' + } + } + }) + }) + } + } + + function updateBadges(nbDevis) { + Object.keys(nbDevis).forEach((statutId) => { + const badge = document.querySelector('[id^="liStat"]') + if (badge) { + badge.setAttribute('data-after-text', nbDevis[statutId]) + badge.setAttribute('data-after-type', 'orange badge top left') + } + }) + } + + function resetSearch() { + elSearchDevis.value = '' + elBtnResetSearch.style.display = 'none' + sessionStorage.removeItem('devisSearchTerm') + + const context = chkShowDevisArchives ? 'archives' : 'encours' + if (context === 'encours') { + const statuts = document.querySelectorAll('[id^="tblBodyDos"]') + statuts.forEach((tbody) => { + const rows = tbody.querySelectorAll('tr') + rows.forEach((row) => { + row.style.display = '' + }) + }) + } else { + const archives = document.querySelectorAll('[id^="tblBodyDosArch"]') + archives.forEach((tbody) => { + const rows = tbody.querySelectorAll('tr') + rows.forEach((row) => { + row.style.display = '' + }) + }) + } + } + + elSearchDevis.addEventListener('input', function () { + const term = this.value.trim() + + if (searchTimeout) { + clearTimeout(searchTimeout) + } + + if (term.length >= 3) { + elBtnResetSearch.style.display = 'inline-block' + sessionStorage.setItem('devisSearchTerm', term) + searchTimeout = setTimeout(() => { + performSearch(term) + }, 300) + } else if (term.length === 0) { + resetSearch() + } else { + elBtnResetSearch.style.display = 'none' + } + }) + + elBtnResetSearch.addEventListener('click', function () { + resetSearch() + }) + + restoreSearch() + + initTableSort() + elBtnSideBarDevis.addEventListener('click', function () { if (elVerticalBar.style.width == '10px') { elVerticalBar.style.width = '1100px' // Largeur de la barre lorsqu'elle est ouverte diff --git a/pub/res/js/jsap.js b/pub/res/js/jsap.js index 860fafd..801d06d 100644 --- a/pub/res/js/jsap.js +++ b/pub/res/js/jsap.js @@ -11,6 +11,100 @@ let oldIdLnEnCours; let oldIdLnArchives; let nbCommentChat = 0; let selectedXmlDevis = new Set(); +let searchSapTimeout = null; + +const tableSortStates = new Map(); + +function initTableSort() { + const allTables = document.querySelectorAll('[id^="tblDos"], [id^="tblDosArch"]'); + + allTables.forEach(table => { + const tableId = table.id; + const headers = table.querySelectorAll('th[data-sortable="true"]'); + const tbody = table.querySelector('tbody'); + + if (!tbody) return; + + if (!tableSortStates.has(tableId)) { + tableSortStates.set(tableId, { + originalOrder: null, + currentSort: { column: null, direction: null } + }); + } + + headers.forEach(header => { + header.addEventListener('click', function() { + const columnIndex = parseInt(this.getAttribute('data-column-index')); + const sortType = this.getAttribute('data-sort-type'); + sortTable(tableId, columnIndex, sortType, this); + }); + }); + }); +} + +function sortTable(tableId, columnIndex, sortType, headerElement) { + const table = document.getElementById(tableId); + const tbody = table.querySelector('tbody'); + const rows = Array.from(tbody.querySelectorAll('tr')); + const state = tableSortStates.get(tableId); + + if (!state.originalOrder) { + state.originalOrder = rows.slice(); + } + + const allHeaders = table.querySelectorAll('th[data-sortable="true"]'); + allHeaders.forEach(h => h.style.fontWeight = 'normal'); + + let sortedRows; + + if (state.currentSort.column === columnIndex && state.currentSort.direction === 'desc') { + sortedRows = state.originalOrder.slice(); + state.currentSort = { column: null, direction: null }; + } else { + const direction = (state.currentSort.column === columnIndex && state.currentSort.direction === 'asc') ? 'desc' : 'asc'; + + sortedRows = rows.slice().sort((a, b) => { + const aCell = a.cells[columnIndex]; + const bCell = b.cells[columnIndex]; + + if (!aCell || !bCell) return 0; + + let aValue = aCell.textContent.trim(); + let bValue = bCell.textContent.trim(); + + if (sortType === 'number') { + aValue = aValue.replace(/[^\d,.-]/g, '').replace(',', '.'); + bValue = bValue.replace(/[^\d,.-]/g, '').replace(',', '.'); + aValue = parseFloat(aValue) || 0; + bValue = parseFloat(bValue) || 0; + return direction === 'asc' ? aValue - bValue : bValue - aValue; + } else if (sortType === 'date') { + aValue = parseDateFromText(aValue); + bValue = parseDateFromText(bValue); + if (!aValue && !bValue) return 0; + if (!aValue) return direction === 'asc' ? 1 : -1; + if (!bValue) return direction === 'asc' ? -1 : 1; + return direction === 'asc' ? aValue - bValue : bValue - aValue; + } else { + const comparison = aValue.localeCompare(bValue, 'fr'); + return direction === 'asc' ? comparison : -comparison; + } + }); + + state.currentSort = { column: columnIndex, direction }; + headerElement.style.fontWeight = 'bold'; + } + + tbody.innerHTML = ''; + sortedRows.forEach(row => tbody.appendChild(row)); +} + +function parseDateFromText(dateText) { + const match = dateText.match(/(\d{2})\/(\d{2})[\/\s](\d{4})/); + if (!match) return null; + const [, day, month, year] = match; + return new Date(year, month - 1, day); +} window.addEventListener('DOMContentLoaded', (event) => { console.log('#'); @@ -786,6 +880,201 @@ window.addEventListener('DOMContentLoaded', (event) => { return false; }; + // Fonctions de recherche SAP + let elSearchSAP = document.getElementById('searchSAP'); + let elBtnResetSearchSAP = document.getElementById('btnResetSearchSAP'); + + function restoreSearchSAP() { + const storageKey = panel === 'enCours' ? 'sapSearchTermEnCours' : 'sapSearchTermArchives'; + const savedTerm = sessionStorage.getItem(storageKey); + if (savedTerm && savedTerm.length >= 3) { + elSearchSAP.value = savedTerm; + elBtnResetSearchSAP.style.display = 'inline-block'; + performSearchSAP(savedTerm); + } else { + elSearchSAP.value = ''; + elBtnResetSearchSAP.style.display = 'none'; + } + } + + function performSearchSAP(term) { + if (term.length < 3) { + return; + } + + const context = panel === 'archives' ? 'archives' : 'encours'; + + fetch('/jxdevis/search_devis_sap', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + term: term, + context: context, + }), + }) + .then((response) => response.json()) + .then((data) => { + if (data.success) { + const devisIds = data.devis.map((d) => d.rowid); + filterDevisTablesSAP(devisIds, context); + updateBadgesSAP(data.nb_devis); + } else { + console.error('Erreur recherche:', data.message); + } + }) + .catch((error) => { + console.error('Erreur AJAX:', error); + }); + } + + function filterDevisTablesSAP(devisIds, context) { + if (context === 'encours') { + const statuts = document.querySelectorAll('[id^="tblBodyDos"]'); + statuts.forEach((tbody) => { + const rows = tbody.querySelectorAll('tr.ligEnCours'); + rows.forEach((row) => { + const cells = row.querySelectorAll('.celEnCours'); + if (cells.length > 0) { + const rowId = parseInt(cells[0].getAttribute('data-rid')); + if (devisIds.includes(rowId)) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + }); + }); + } else { + const archives = document.querySelectorAll('[id^="tblBodyDosArch"]'); + archives.forEach((tbody) => { + const rows = tbody.querySelectorAll('tr'); + rows.forEach((row) => { + if (row.id && (row.id.startsWith('trArch_') || row.id.startsWith('trArchTous_'))) { + const rowId = parseInt(row.id.replace('trArch_', '').replace('trArchTous_', '')); + if (devisIds.includes(rowId)) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + }); + }); + } + } + + function updateBadgesSAP(nbDevis) { + Object.keys(nbDevis).forEach((statutId) => { + const liElements = document.querySelectorAll('[id^="liStat"]'); + liElements.forEach((li) => { + li.setAttribute('data-after-text', nbDevis[statutId] || '0'); + li.setAttribute('data-after-type', 'orange badge top left'); + }); + }); + } + + function resetSearchSAP() { + elSearchSAP.value = ''; + elBtnResetSearchSAP.style.display = 'none'; + const storageKey = panel === 'enCours' ? 'sapSearchTermEnCours' : 'sapSearchTermArchives'; + sessionStorage.removeItem(storageKey); + + const context = panel === 'archives' ? 'archives' : 'encours'; + if (context === 'encours') { + const statuts = document.querySelectorAll('[id^="tblBodyDos"]'); + statuts.forEach((tbody) => { + const rows = tbody.querySelectorAll('tr'); + rows.forEach((row) => { + row.style.display = ''; + }); + }); + } else { + const archives = document.querySelectorAll('[id^="tblBodyDosArch"]'); + archives.forEach((tbody) => { + const rows = tbody.querySelectorAll('tr'); + rows.forEach((row) => { + row.style.display = ''; + }); + }); + const tbodyTous = document.getElementById('tblBodyDosArchTous'); + if (tbodyTous) { + const rowsTous = tbodyTous.querySelectorAll('tr'); + rowsTous.forEach((row) => { + row.style.display = ''; + }); + } + } + } + + elSearchSAP.addEventListener('input', function () { + const term = this.value.trim(); + + if (searchSapTimeout) { + clearTimeout(searchSapTimeout); + } + + if (term.length >= 3) { + elBtnResetSearchSAP.style.display = 'inline-block'; + const storageKey = panel === 'enCours' ? 'sapSearchTermEnCours' : 'sapSearchTermArchives'; + sessionStorage.setItem(storageKey, term); + searchSapTimeout = setTimeout(() => { + performSearchSAP(term); + }, 300); + } else if (term.length === 0) { + resetSearchSAP(); + } else { + elBtnResetSearchSAP.style.display = 'none'; + } + }); + + elBtnResetSearchSAP.addEventListener('click', function () { + resetSearchSAP(); + }); + + // Hook sur changement d'onglet pour restaurer la recherche appropriée + $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { + if ($(this).attr('href') == '#tabEnCours') { + panel = 'enCours'; + restoreSearchSAP(); + } else if ($(this).attr('href') == '#tabArchives') { + panel = 'archives'; + restoreSearchSAP(); + } + }); + + restoreSearchSAP(); + + initTableSort(); + + // Gestion des états actifs des onglets départements (multiples