'Prêt personnel / Tous motifs / Achats divers', 'frais_notaire' => 'Financement frais de notaire', 'but_immo' => 'Crédit travaux / Rénovation / Energie', 'fin_neuve' => 'Financement véhicule NEUF', 'fin_occ_m3a' => 'Financement véhicule d\'occasion MOINS de 3 ans', 'fin_occ_p3a' => 'Financement véhicule d\'occasion PLUS de 3 ans', 'mobil_carav' => 'Financement Mobil-home et caravane', 'reno_energie' => 'Rénovation énergétique', 'regrouping' => 'Regroupement de crédit / Rachat de crédit', 'regroup_cred' => 'Regroupement de crédit / Rachat de crédit' ]; protected $houseCreditTypes = [ 'purchasehouse' => 'Achat bien immobilier', 'construction' => 'Nouvelle construction', 'regrouping_immo' => 'Regroupement de crédits', 'refinancing' => 'Refinancement crédit(s) hypothécaire(s)', 'purchaseabroad' => 'Achat d\'une 2éme résidence (à l\'étranger, en belgique)', 'but_immo_hypo' => 'Travaux de rénovation', 'achat_maison_de_rapport' => 'Achat maison de rapport', 'credit_pont' => 'Crédit pont', 'independants_et_entreprises_en_difficultes' => 'Indépendants et entreprises en difficultés', 'regroupement_de_credit__rachats_de_credits' => 'Rachats de crédits', 'financement_frais_de_notaire' => 'Financement frais de notaire', 'fonds_roulement_independants' => 'Fonds de roulement' ]; public function __construct() { // Le hook admin_menu est maintenant géré par le factory } public function init() { } /** * Ajouter le menu d'administration simple */ public function add_admin_menu() { add_menu_page( 'Gestion des Crédits', // Titre de la page 'Crédits', // Titre du menu 'edit_posts', // Capacité requise 'credit-manager', // Slug du menu array($this, 'credit_manager_interfaces'), // Fonction de callback 'dashicons-money-alt', // Icône 8 // Position dans le menu ); // Sous-menu: Import de crédits add_submenu_page( 'credit-manager', // Slug du menu parent 'Import de crédits', // Titre de la page 'Import de crédits', // Titre du sous-menu 'edit_posts', // Capacité requise 'credit-import', // Slug du sous-menu array($this, 'credit_import_interface') // Callback ); } public function credit_manager_interfaces() { // Récupérer les crédits $credits = $this->get_credits(); // Récupérer les sociétés de crédit pour le select $societes_credit = $this->get_societes_credit(); // Passer les types de crédit au template $creditTypes = $this->creditTypes; $houseCreditTypes = $this->houseCreditTypes; // Calculer les compteurs par statut avec array_reduce $status_counts = array_reduce($credits, function($counts, $credit) { $status = isset($credit->status) ? (string)$credit->status : '0'; if (!isset($counts[$status])) { $counts[$status] = 0; } $counts[$status]++; return $counts; }, array()); // S'assurer que tous les statuts ont une valeur (même 0) $status_counts = array_merge( array('0' => 0, '1' => 0, '2' => 0, '-1' => 0), $status_counts ); // Total de tous les crédits $total_credits = count($credits); // Inclure le template include plugin_dir_path(__FILE__) . '../../templates/admin/credit_manager_table.php'; } /** * Interface d'import CSV (non-AJAX) - Fonction d'aiguillage */ public function credit_import_interface() { if (!current_user_can('edit_posts')) { wp_die('Permissions insuffisantes'); } $import_summary = null; $import_errors = array(); // Soumission du formulaire if (!empty($_POST['credit_import_submit'])) { // Vérifier le nonce if (!isset($_POST['credit_import_nonce']) || !wp_verify_nonce($_POST['credit_import_nonce'], 'credit_import_action')) { $import_errors[] = 'Sécurité: Nonce invalide'; } else { // Valider le fichier if (!isset($_FILES['csv_file']) || !is_uploaded_file($_FILES['csv_file']['tmp_name'])) { $import_errors[] = 'Aucun fichier CSV envoyé.'; } else { $file_tmp = $_FILES['csv_file']['tmp_name']; $handle = fopen($file_tmp, 'r'); if ($handle === false) { $import_errors[] = 'Impossible d\'ouvrir le fichier CSV.'; } else { // Lire l'entête pour déterminer le nombre de colonnes $header = fgetcsv($handle, 0, ';'); $column_count = count($header); // Fermer et rouvrir le fichier pour réinitialiser le pointeur fclose($handle); // Aiguiller vers la fonction appropriée selon le nombre de colonnes if ($column_count >= 18) { // Format PH VERVIERS (20 colonnes) list($import_summary, $import_errors) = $this->import_ph_credits($file_tmp, $_POST); } else { // Format standard (13 colonnes) list($import_summary, $import_errors) = $this->import_standard_credits($file_tmp, $_POST); } } } } } include plugin_dir_path(__FILE__) . '../../templates/admin/credit_import.php'; } /** * Import standard (13 colonnes) */ private function import_standard_credits($file_tmp, $post_data) { $import_errors = array(); $import_summary = null; $status_input = isset($post_data['status']) ? intval($post_data['status']) : 1; // 1 par défaut $statut_client = isset($post_data['client_status']) ? sanitize_text_field($post_data['client_status']) : ''; if($statut_client !== '' && $statut_client !== 'habitation_locataire' && $statut_client !== 'habitation_proprietaire') { $import_errors[] = 'Statut du client invalide.'; return array($import_summary, $import_errors); } // Normaliser statut attendu par l'UI: 1=validé non signé, 2=validé signé, -1=refusé $allowed_status = array(1, 2, -1); if (!in_array($status_input, $allowed_status, true)) { $status_input = 1; } $handle = fopen($file_tmp, 'r'); if ($handle === false) { $import_errors[] = 'Impossible d\'ouvrir le fichier CSV.'; return array($import_summary, $import_errors); } // Lire l'entête et ignorer $header = fgetcsv($handle, 0, ';'); $row_num = 1; $inserted = 0; $skipped = 0; global $wpdb; $table_name = 'cdf_Credits_listing'; // Vérifier la table if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { $import_errors[] = 'Table de base de données non trouvée: ' . esc_html($table_name); fclose($handle); return array($import_summary, $import_errors); } while (($row = fgetcsv($handle, 0, ';')) !== false) { $row_num++; // Sauter les lignes vides if (count($row) === 0 || (count($row) === 1 && trim((string)$row[0]) === '')) { continue; } // Sécuriser les index attendus (jusqu'à 13 colonnes d'après le modèle) for ($i = 0; $i <= 12; $i++) { if (!isset($row[$i])) $row[$i] = ''; } // Mapping CSV -> champs $titre = trim((string)$row[0]); $nomPrenom = trim((string)$row[1]); $adresse = trim((string)$row[2]); $localiteField = trim((string)$row[3]); // ex: "4333 SOUMAGNE" $email = trim((string)$row[4]); $telephone = trim((string)$row[5]); $gsm = trim((string)$row[6]); $societe_credit = trim((string)$row[7]); $montantStr = trim((string)$row[8]); $dateSignatureStr = trim((string)$row[9]); $numero_dossier = trim((string)$row[10]); $code = trim((string)$row[11]); $remarque = trim((string)$row[12]); // Extraire nom/prénom (naïf: premier mot = nom, reste = prénom) $nom = ''; $prenom = ''; if ($nomPrenom !== '') { // Essayer "NOM Prénom" $parts = preg_split('/\s+/', $nomPrenom); if (count($parts) >= 2) { $nom = trim((string)$parts[0]); $prenom = trim(implode(' ', array_slice($parts, 1))); } else { $nom = $nomPrenom; } } // Extraire code postal et localité $code_postal = 0; $localite = $localiteField; if ($localiteField !== '') { if (preg_match('/^(\d{4,5})\s*(.+)$/u', $localiteField, $m)) { $code_postal = intval($m[1]); $localite = trim($m[2]); } } // Normaliser montant (ex: "4 000,00 €") $montantClean = str_replace(array("\xC2\xA0", '€', ' '), '', $montantStr); $montantClean = str_replace('.', '', $montantClean); // enlever séparateurs de milliers $montantClean = str_replace(',', '.', $montantClean); // virgule -> point $montant = is_numeric($montantClean) ? (float)$montantClean : 0.0; // Parser date de signature -> Y-m-d $date_signature = ''; $date_signature_candidates = array('d/m/Y', 'd-m-Y', 'Y-m-d', 'd.m.Y'); foreach ($date_signature_candidates as $fmt) { $dt = DateTime::createFromFormat($fmt, $dateSignatureStr); if ($dt instanceof DateTime) { $date_signature = $dt->format('Y-m-d'); break; } } if ($date_signature === '') { // Si échec, laisser vide $date_signature = ''; } // Champs requis minimaux: nom, prenom ou email if ($nom === '' && $prenom === '' && $email === '') { $skipped++; $import_errors[] = 'Ligne ' . $row_num . ' ignorée (nom/prénom/email manquants).'; continue; } $data = array( 'credit_id' => 0, 'emprunteur_id' => 0, 'credit_code_select' => '', 'code_credit' => '', 'type_credit' => '', 'nom' => $nom !== '' ? $nom : '', 'prenom' => $prenom !== '' ? $prenom : '', 'adresse' => $adresse !== '' ? $adresse : '', 'localite' => $localite !== '' ? $localite : '', 'code_postal' => $code_postal, 'pays' => 0, 'email' => $email !== '' ? $email : '', 'telephone' => $telephone !== '' ? $telephone : '', 'gsm' => $gsm !== '' ? $gsm : '', 'societe_credit' => $societe_credit !== '' ? $societe_credit : '', 'montant' => $montant, 'duree' => 0, 'date' => current_time('Y-m-d'), 'date_signature' => $date_signature !== '' ? $date_signature : current_time('Y-m-d'), 'numero_dossier' => $numero_dossier !== '' ? $numero_dossier : '', 'code' => $code !== '' ? $code : '', 'remarque' => $remarque !== '' ? $remarque : '', 'status' => $status_input, 'type_habitation' => $statut_client !== '' ? $statut_client : '', 'date_acte' => '0000-00-00', 'but_credit' => '', 'taux_credit' => 0.0, 'type_taux' => '', 'valeur_bien' => 0.0, 'notaire' => '' ); $formats = array('%d','%d','%s','%s','%s','%s','%s','%s','%s','%d','%d','%s','%s','%s','%s','%f','%d','%s','%s','%s','%s','%d','%s','%s','%f','%s','%f','%s'); $result = $wpdb->insert($table_name, $data, $formats); if ($result === false) { $skipped++; $import_errors[] = 'Erreur d\'insertion ligne ' . $row_num . ' : ' . esc_html($wpdb->last_error); } else { $inserted++; // Si l'import positionne directement le statut à 2, abonner l'email à la liste principale if (intval($status_input) === 2 && $email !== '') { $mailchimpController = $this->getCreditMailchimp(); if ($mailchimpController && method_exists($mailchimpController, 'subscribe_from_credit')) { $creditObj = (object) array( 'id' => isset($data['credit_id']) && !empty($data['credit_id']) ? intval($data['credit_id']) : intval($wpdb->insert_id), 'email' => $email, 'prenom' => $prenom, 'nom' => $nom ); try { $mailchimpController->subscribe_from_credit($creditObj); } catch (\Throwable $e) { error_log('Mailchimp subscribe during import (row ' . $row_num . ') failed: ' . $e->getMessage()); } } } } } if (is_resource($handle)) { fclose($handle); } $import_summary = array( 'inserted' => $inserted, 'skipped' => $skipped, ); return array($import_summary, $import_errors); } /** * Import format PH VERVIERS (20 colonnes) */ private function import_ph_credits($file_tmp, $post_data) { $import_errors = array(); $import_summary = null; $status_input = isset($post_data['status']) ? intval($post_data['status']) : 1; // 1 par défaut $statut_client = isset($post_data['client_status']) ? sanitize_text_field($post_data['client_status']) : ''; if($statut_client !== '' && $statut_client !== 'habitation_locataire' && $statut_client !== 'habitation_proprietaire') { $import_errors[] = 'Statut du client invalide.'; return array($import_summary, $import_errors); } // Normaliser statut attendu par l'UI: 1=validé non signé, 2=validé signé, -1=refusé $allowed_status = array(1, 2, -1); if (!in_array($status_input, $allowed_status, true)) { $status_input = 1; } $handle = fopen($file_tmp, 'r'); if ($handle === false) { $import_errors[] = 'Impossible d\'ouvrir le fichier CSV.'; return array($import_summary, $import_errors); } // Lire l'entête et ignorer $header = fgetcsv($handle, 0, ';'); $row_num = 1; $inserted = 0; $skipped = 0; global $wpdb; $table_name = 'cdf_Credits_listing'; // Vérifier la table if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { $import_errors[] = 'Table de base de données non trouvée: ' . esc_html($table_name); fclose($handle); return array($import_summary, $import_errors); } while (($row = fgetcsv($handle, 0, ';')) !== false) { $row_num++; // Sauter les lignes vides if (count($row) === 0 || (count($row) === 1 && trim((string)$row[0]) === '')) { continue; } // Sécuriser les index attendus (jusqu'à 20 colonnes pour le format PH) for ($i = 0; $i <= 19; $i++) { if (!isset($row[$i])) $row[$i] = ''; } // Mapping CSV -> champs (format PH VERVIERS) $titre = trim((string)$row[0]); $nomPrenom = trim((string)$row[1]); $adresse = trim((string)$row[2]); $localiteField = trim((string)$row[3]); // ex: "4800 VERVIERS" $email = trim((string)$row[4]); // $row[5] = Classeur (ignoré) $gsm = trim((string)$row[6]); // Premier GSM // $row[7] = Deuxième GSM (ignoré, doublon) $societe_credit = trim((string)$row[8]); $numero_dossier = trim((string)$row[9]); $montantStr = trim((string)$row[10]); $dureeStr = trim((string)$row[11]); $dateActeStr = trim((string)$row[12]); $but_credit = trim((string)$row[13]); $tauxStr = trim((string)$row[14]); $type_taux = trim((string)$row[15]); $valeur_bienStr = trim((string)$row[16]); $notaire = trim((string)$row[17]); $code = trim((string)$row[18]); $remarque = trim((string)$row[19]); // Extraire nom/prénom (format: "NOM - Prénom" ou "NOM Prénom") $nom = ''; $prenom = ''; if ($nomPrenom !== '') { // Gérer le format "NOM - Prénom" ou "NOM Prénom" if (strpos($nomPrenom, ' - ') !== false) { $parts = explode(' - ', $nomPrenom, 2); $nom = trim((string)$parts[0]); $prenom = trim((string)$parts[1]); } else { $parts = preg_split('/\s+/', $nomPrenom); if (count($parts) >= 2) { $nom = trim((string)$parts[0]); $prenom = trim(implode(' ', array_slice($parts, 1))); } else { $nom = $nomPrenom; } } } // Extraire code postal et localité $code_postal = 0; $localite = $localiteField; if ($localiteField !== '') { if (preg_match('/^(\d{4,5})\s*(.+)$/u', $localiteField, $m)) { $code_postal = intval($m[1]); $localite = trim($m[2]); } } // Normaliser montant (ex: "135.000 €") $montantClean = str_replace(array("\xC2\xA0", '€', ' '), '', $montantStr); $montantClean = str_replace('.', '', $montantClean); // enlever séparateurs de milliers $montantClean = str_replace(',', '.', $montantClean); // virgule -> point $montant = is_numeric($montantClean) ? (float)$montantClean : 0.0; // Normaliser durée $duree = is_numeric($dureeStr) ? intval($dureeStr) : 0; // Parser date acte -> Y-m-d $date_acte = ''; $date_acte_candidates = array('d/m/Y', 'd-m-Y', 'Y-m-d', 'd.m.Y'); foreach ($date_acte_candidates as $fmt) { $dt = DateTime::createFromFormat($fmt, $dateActeStr); if ($dt instanceof DateTime) { $date_acte = $dt->format('Y-m-d'); break; } } if ($date_acte === '') { $date_acte = '0000-00-00'; } // Normaliser taux (ex: "2,90%" ou "1,95%") $tauxClean = str_replace('%', '', $tauxStr); $tauxClean = str_replace(',', '.', $tauxClean); $taux_credit = is_numeric($tauxClean) ? (float)$tauxClean : 0.0; // Normaliser valeur bien (ex: "140.000 €") $valeur_bienClean = str_replace(array("\xC2\xA0", '€', ' ', ','), '', $valeur_bienStr); $valeur_bienClean = str_replace('.', '', $valeur_bienClean); // enlever séparateurs de milliers $valeur_bien = is_numeric($valeur_bienClean) ? (float)$valeur_bienClean : 0.0; // Champs requis minimaux: nom, prenom ou email if ($nom === '' && $prenom === '' && $email === '') { $skipped++; $import_errors[] = 'Ligne ' . $row_num . ' ignorée (nom/prénom/email manquants).'; continue; } $data = array( 'credit_id' => 0, 'emprunteur_id' => 0, 'credit_code_select' => '', 'code_credit' => '', 'type_credit' => '', 'nom' => $nom !== '' ? $nom : '', 'prenom' => $prenom !== '' ? $prenom : '', 'adresse' => $adresse !== '' ? $adresse : '', 'localite' => $localite !== '' ? $localite : '', 'code_postal' => $code_postal, 'pays' => 0, 'email' => $email !== '' ? $email : '', 'telephone' => '', 'gsm' => $gsm !== '' ? $gsm : '', 'societe_credit' => $societe_credit !== '' ? $societe_credit : '', 'montant' => $montant, 'duree' => $duree, 'date' => current_time('Y-m-d'), 'date_signature' => $date_acte !== '0000-00-00' ? $date_acte : current_time('Y-m-d'), 'numero_dossier' => $numero_dossier !== '' ? $numero_dossier : '', 'code' => $code !== '' ? $code : '', 'remarque' => $remarque !== '' ? $remarque : '', 'status' => $status_input, 'type_habitation' => $statut_client !== '' ? $statut_client : '', 'date_acte' => $date_acte !== '' ? $date_acte : '0000-00-00', 'but_credit' => $but_credit !== '' ? $but_credit : '', 'taux_credit' => $taux_credit, 'type_taux' => $type_taux !== '' ? $type_taux : '', 'valeur_bien' => $valeur_bien, 'notaire' => $notaire !== '' ? $notaire : '' ); $formats = array('%d','%d','%s','%s','%s','%s','%s','%s','%s','%d','%d','%s','%s','%s','%s','%f','%d','%s','%s','%s','%s','%d','%s','%s','%f','%s','%f','%s'); $result = $wpdb->insert($table_name, $data, $formats); if ($result === false) { $skipped++; $import_errors[] = 'Erreur d\'insertion ligne ' . $row_num . ' : ' . esc_html($wpdb->last_error); } else { $inserted++; // Si l'import positionne directement le statut à 2, abonner l'email à la liste principale if (intval($status_input) === 2 && $email !== '') { $mailchimpController = $this->getCreditMailchimp(); if ($mailchimpController && method_exists($mailchimpController, 'subscribe_from_credit')) { $creditObj = (object) array( 'id' => isset($data['credit_id']) && !empty($data['credit_id']) ? intval($data['credit_id']) : intval($wpdb->insert_id), 'email' => $email, 'prenom' => $prenom, 'nom' => $nom ); try { $mailchimpController->subscribe_from_credit($creditObj); } catch (\Throwable $e) { error_log('Mailchimp subscribe during import (row ' . $row_num . ') failed: ' . $e->getMessage()); } } } } } if (is_resource($handle)) { fclose($handle); } $import_summary = array( 'inserted' => $inserted, 'skipped' => $skipped, ); return array($import_summary, $import_errors); } public function get_credits($filters = array()) { global $wpdb; // Utiliser la table cdf_Credits_listing qui contient déjà toutes les informations $table_name = 'cdf_Credits_listing'; // Vérifier si la table existe if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { // Retourner des données de test si la table n'existe pas error_log('Credit Manager: Table ' . $table_name . ' n\'existe pas, retour de données de test'); return $this->get_test_credits(); } // Construire la clause WHERE dynamiquement $where_clauses = array(); $where_values = array(); // Configuration des filtres par type avec leur opérateur et placeholder $filter_config = array( // Filtres de type string avec égalité 'string_equal' => array( 'fields' => array('localite', 'societe_credit', 'code_postal', 'type_credit', 'credit_code_select'), 'operator' => '=', 'placeholder' => '%s' ), // Filtres de type integer avec égalité 'int_equal' => array( 'fields' => array('status'), 'operator' => '=', 'placeholder' => '%d', 'allow_zero' => true // Permet la valeur 0 pour status ), // Filtres de type float avec comparaison 'float_range' => array( 'montant_min' => array('field' => 'montant', 'operator' => '>=', 'placeholder' => '%f'), 'montant_max' => array('field' => 'montant', 'operator' => '<=', 'placeholder' => '%f') ), // Filtres de type date avec comparaison 'date_range' => array( 'date_signature_debut' => array('field' => 'date_signature', 'operator' => '>=', 'placeholder' => '%s'), 'date_signature_fin' => array('field' => 'date_signature', 'operator' => '<=', 'placeholder' => '%s') ) ); // Traiter les filtres string et int simples foreach (array('string_equal', 'int_equal') as $type) { $config = $filter_config[$type]; foreach ($config['fields'] as $field) { $allow_zero = isset($config['allow_zero']) && $config['allow_zero']; if ($allow_zero) { // Pour status, vérifier avec isset car 0 est une valeur valide if (isset($filters[$field]) && $filters[$field] !== '') { $where_clauses[] = "{$field} {$config['operator']} {$config['placeholder']}"; $where_values[] = ($type === 'int_equal') ? intval($filters[$field]) : $filters[$field]; } } else { // Pour les autres champs, utiliser !empty if (!empty($filters[$field])) { $where_clauses[] = "{$field} {$config['operator']} {$config['placeholder']}"; $where_values[] = ($type === 'int_equal') ? intval($filters[$field]) : $filters[$field]; } } } } // Traiter les filtres de fourchette (float et date) foreach (array('float_range', 'date_range') as $range_type) { foreach ($filter_config[$range_type] as $filter_key => $range_config) { if (!empty($filters[$filter_key])) { $where_clauses[] = "{$range_config['field']} {$range_config['operator']} {$range_config['placeholder']}"; $where_values[] = ($range_type === 'float_range') ? floatval($filters[$filter_key]) : $filters[$filter_key]; } } } // Construire la requête SQL $sql = "SELECT * FROM {$table_name}"; if (!empty($where_clauses)) { $sql .= " WHERE " . implode(" AND ", $where_clauses); } $sql .= " ORDER BY date DESC"; // Préparer et exécuter la requête if (!empty($where_values)) { $sql = $wpdb->prepare($sql, $where_values); } // Debug: Log SQL query error_log('Credit Manager SQL: ' . $sql); error_log('Credit Manager Filters: ' . print_r($filters, true)); $results = $wpdb->get_results($sql); // Debug: Log results count error_log('Credit Manager Results count: ' . count($results)); // Si pas de résultats ET pas de filtres appliqués, retourner des données de test // Si des filtres sont appliqués et qu'il n'y a pas de résultats, retourner un tableau vide if (empty($results)) { if (empty($filters)) { // Pas de filtres, table vide = données de test error_log('Credit Manager: Aucun crédit dans la base, retour de données de test'); return $this->get_test_credits(); } else { // Filtres appliqués, pas de résultats = tableau vide error_log('Credit Manager: Aucun crédit ne correspond aux filtres'); return array(); } } return $results; } /** * Récupérer un crédit spécifique par ID */ public function get_credit_by_id($credit_id) { global $wpdb; $table_name = 'cdf_Credits_listing'; // Vérifier si la table existe if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { return null; } $sql = $wpdb->prepare(" SELECT id, type_credit, nom, prenom, adresse, localite, email, telephone, gsm, societe_credit, montant, date, date_signature, numero_dossier, code, remarque as remarques FROM {$table_name} WHERE id = %d ", $credit_id); return $wpdb->get_row($sql); } /** * Récupérer les professions pour le select */ public function get_professions() { global $wpdb; $table_name = 'cdf_Profession'; if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { return array(); } $sql = "SELECT idprofession as id, nom_profession as nom FROM {$table_name} ORDER BY nom_profession"; return $wpdb->get_results($sql); } /** * Récupérer les agences pour le select */ public function get_agences() { global $wpdb; $table_name = 'cdf_Agences'; if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { return array(); } $sql = "SELECT idAgences as id, Nom_agence as nom FROM {$table_name} ORDER BY Nom_agence"; return $wpdb->get_results($sql); } /** * Récupérer les sociétés de crédit pour le select */ public function get_societes_credit() { global $wpdb; $table_name = 'cdf_societes_credit'; if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { return array(); } // Récupérer seulement les sociétés actives (status = 1) $sql = "SELECT id, nom FROM {$table_name} WHERE status = 1 ORDER BY nom"; return $wpdb->get_results($sql); } /** * Récupérer les localités distinctes */ public function get_distinct_localites() { global $wpdb; $table_name = 'cdf_Credits_listing'; if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { return array(); } $sql = "SELECT DISTINCT localite FROM {$table_name} WHERE localite != '' ORDER BY localite"; return $wpdb->get_col($sql); } /** * Récupérer les sociétés de crédit distinctes */ public function get_distinct_societes() { global $wpdb; $table_name = 'cdf_Credits_listing'; if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { return array(); } $sql = "SELECT DISTINCT societe_credit FROM {$table_name} WHERE societe_credit != '' ORDER BY societe_credit"; return $wpdb->get_col($sql); } /** * Récupérer les types de crédit distincts */ public function get_distinct_credit_types() { global $wpdb; $table_name = 'cdf_Credits_listing'; if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { return array(); } $sql = "SELECT DISTINCT type_credit FROM {$table_name} WHERE type_credit != '' ORDER BY type_credit"; return $wpdb->get_col($sql); } /** * Récupérer les types de crédit */ public function get_credit_types() { return array( 'credit_immobilier' => 'Crédit Immobilier', 'credit_consommation' => 'Crédit Consommation', 'credit_auto' => 'Crédit Automobile', 'credit_personnel' => 'Crédit Personnel', 'credit_travaux' => 'Crédit Travaux' ); } /** * Données de test pour le développement */ private function get_test_credits() { return array( (object) array( 'id' => 1, 'type_credit' => 'CH', 'nom' => 'Dupont', 'prenom' => 'Jean', 'adresse' => '123 Rue de la Paix', 'localite' => 'Bruxelles', 'email' => 'jean.dupont@example.com', 'telephone' => '02/123.45.67', 'gsm' => '0470/12.34.56', 'societe_credit' => 'Banque 1', 'montant' => 250000.00, 'date' => '2024-01-15', 'date_signature' => '2024-01-20', 'numero_dossier' => 'CD-2024-001', 'code' => 'ABC123', 'remarques' => 'Premier crédit test' ), (object) array( 'id' => 2, 'type_credit' => 'PAT', 'nom' => 'Martin', 'prenom' => 'Marie', 'adresse' => '456 Avenue des Champs', 'localite' => 'Anvers', 'email' => 'marie.martin@example.com', 'telephone' => '03/987.65.43', 'gsm' => '0471/98.76.54', 'societe_credit' => 'Banque 2', 'montant' => 15000.00, 'date' => '2024-01-20', 'date_signature' => '2024-01-25', 'numero_dossier' => 'CD-2024-002', 'code' => 'DEF456', 'remarques' => 'Crédit pour voiture' ) ); } /** * ======================================== * FONCTIONS CRUD POUR CREDIT MANAGER * ======================================== */ /** * Créer un nouveau crédit */ public function ajax_create_credit() { // Vérifier le nonce if (!wp_verify_nonce($_POST['nonce'], 'credit_manager_action')) { wp_die('Sécurité: Nonce invalide'); } // Vérifier les permissions if (!current_user_can('edit_posts')) { wp_die('Permissions insuffisantes'); } global $wpdb; $table_name = 'cdf_Credits_listing'; // Récupérer et valider les données $data = array( 'type_credit' => sanitize_text_field($_POST['type_credit']), 'nom' => sanitize_text_field($_POST['nom']), 'prenom' => sanitize_text_field($_POST['prenom']), 'adresse' => sanitize_textarea_field($_POST['adresse']), 'localite' => sanitize_text_field($_POST['localite']), 'email' => sanitize_email($_POST['email']), 'telephone' => sanitize_text_field($_POST['telephone']), 'gsm' => sanitize_text_field($_POST['gsm']), 'societe_credit' => sanitize_text_field($_POST['societe_credit']), 'montant' => floatval($_POST['montant']), 'date' => sanitize_text_field($_POST['date']), 'date_signature' => sanitize_text_field($_POST['signature']), 'numero_dossier' => sanitize_text_field($_POST['numero_dossier']), 'code' => sanitize_text_field($_POST['code']), 'remarque' => sanitize_textarea_field($_POST['remarques']) ); // Validation des champs obligatoires if (empty($data['nom']) || empty($data['prenom']) || empty($data['email'])) { wp_send_json_error('Les champs nom, prénom et email sont obligatoires'); } // Vérifier si la table existe if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { wp_send_json_error('Table de base de données non trouvée'); } // Insérer le nouveau crédit $result = $wpdb->insert($table_name, $data); if ($result === false) { wp_send_json_error('Erreur lors de la création du crédit'); } $credit_id = $wpdb->insert_id; $new_credit = $this->get_credit_by_id($credit_id); wp_send_json_success(array( 'message' => 'Crédit créé avec succès', 'credit' => $new_credit )); } /** * Lire un crédit spécifique */ public function ajax_get_credit() { // Vérifier le nonce if (!wp_verify_nonce($_POST['nonce'], 'credit_manager_action')) { wp_die('Sécurité: Nonce invalide'); } // Vérifier les permissions if (!current_user_can('edit_posts')) { wp_die('Permissions insuffisantes'); } $credit_id = intval($_POST['credit_id']); if ($credit_id <= 0) { wp_send_json_error('ID de crédit invalide'); } $credit = $this->get_credit_by_id($credit_id); if (!$credit) { wp_send_json_error('Crédit non trouvé'); } wp_send_json_success(array( 'credit' => $credit )); } /** * Mettre à jour un crédit existant */ public function ajax_update_credit() { // Vérifier le nonce if (!wp_verify_nonce($_POST['nonce'], 'credit_manager_action')) { wp_die('Sécurité: Nonce invalide'); } // Vérifier les permissions if (!current_user_can('edit_posts')) { wp_die('Permissions insuffisantes'); } global $wpdb; $table_name = 'cdf_Credits_listing'; $credit_id = intval($_POST['credit_id']); if ($credit_id <= 0) { wp_send_json_error('ID de crédit invalide'); } // Vérifier si le crédit existe $existing_credit = $this->get_credit_by_id($credit_id); if (!$existing_credit) { wp_send_json_error('Crédit non trouvé'); } // Récupérer et valider les données $data = array( 'type_credit' => sanitize_text_field($_POST['type_credit']), 'nom' => sanitize_text_field($_POST['nom']), 'prenom' => sanitize_text_field($_POST['prenom']), 'adresse' => sanitize_textarea_field($_POST['adresse']), 'localite' => sanitize_text_field($_POST['localite']), 'email' => sanitize_email($_POST['email']), 'telephone' => sanitize_text_field($_POST['telephone']), 'gsm' => sanitize_text_field($_POST['gsm']), 'societe_credit' => sanitize_text_field($_POST['societe_credit']), 'montant' => floatval($_POST['montant']), 'date' => sanitize_text_field($_POST['date']), 'date_signature' => sanitize_text_field($_POST['signature']), 'numero_dossier' => sanitize_text_field($_POST['numero_dossier']), 'code' => sanitize_text_field($_POST['code']), 'remarque' => sanitize_textarea_field($_POST['remarques']) ); // Validation des champs obligatoires if (empty($data['nom']) || empty($data['prenom']) || empty($data['email'])) { wp_send_json_error('Les champs nom, prénom et email sont obligatoires'); } // Mettre à jour le crédit $result = $wpdb->update( $table_name, $data, array('id' => $credit_id), array('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%f', '%s', '%s', '%s', '%s', '%s'), array('%d') ); if ($result === false) { wp_send_json_error('Erreur lors de la mise à jour du crédit'); } $updated_credit = $this->get_credit_by_id($credit_id); wp_send_json_success(array( 'message' => 'Crédit mis à jour avec succès', 'credit' => $updated_credit )); } /** * Supprimer un crédit */ public function ajax_delete_credit() { // Vérifier le nonce if (!wp_verify_nonce($_POST['nonce'], 'credit_manager_action')) { wp_die('Sécurité: Nonce invalide'); } // Vérifier les permissions if (!current_user_can('edit_posts')) { wp_die('Permissions insuffisantes'); } global $wpdb; $table_name = 'cdf_Credits_listing'; $credit_id = intval($_POST['credit_id']); if ($credit_id <= 0) { wp_send_json_error('ID de crédit invalide'); } // Vérifier si le crédit existe $existing_credit = $this->get_credit_by_id($credit_id); if (!$existing_credit) { wp_send_json_error('Crédit non trouvé'); } // Supprimer le crédit $result = $wpdb->delete( $table_name, array('id' => $credit_id), array('%d') ); if ($result === false) { wp_send_json_error('Erreur lors de la suppression du crédit'); } wp_send_json_success(array( 'message' => 'Crédit supprimé avec succès', 'credit_id' => $credit_id )); } /** * Lister tous les crédits avec pagination et filtres */ public function ajax_list_credits() { // Debug: Log des données POST reçues error_log('Credit Manager AJAX - POST data: ' . print_r($_POST, true)); // Vérifier que le nonce est présent if (!isset($_POST['nonce']) || empty($_POST['nonce'])) { error_log('Credit Manager AJAX - Nonce manquant dans POST'); wp_send_json_error('Nonce manquant'); return; } // Vérifier le nonce if (!wp_verify_nonce($_POST['nonce'], 'credit_manager_action')) { error_log('Credit Manager AJAX - Nonce invalide: ' . $_POST['nonce']); wp_send_json_error('Sécurité: Nonce invalide'); return; } // Vérifier les permissions if (!current_user_can('edit_posts')) { error_log('Credit Manager AJAX - Permissions insuffisantes'); wp_send_json_error('Permissions insuffisantes'); return; } // Récupérer les filtres $filters = array(); if (!empty($_POST['localite'])) { $filters['localite'] = sanitize_text_field($_POST['localite']); } if (!empty($_POST['societe_credit'])) { $filters['societe_credit'] = sanitize_text_field($_POST['societe_credit']); } if (!empty($_POST['code_postal'])) { $filters['code_postal'] = sanitize_text_field($_POST['code_postal']); } if (isset($_POST['status']) && $_POST['status'] !== '') { $filters['status'] = intval($_POST['status']); } if (!empty($_POST['type_credit'])) { $filters['type_credit'] = sanitize_text_field($_POST['type_credit']); } if (!empty($_POST['credit_code_select'])) { $filters['credit_code_select'] = sanitize_text_field($_POST['credit_code_select']); } if (!empty($_POST['montant_min'])) { $filters['montant_min'] = floatval($_POST['montant_min']); } if (!empty($_POST['montant_max'])) { $filters['montant_max'] = floatval($_POST['montant_max']); } if (!empty($_POST['date_signature_debut'])) { $filters['date_signature_debut'] = sanitize_text_field($_POST['date_signature_debut']); } if (!empty($_POST['date_signature_fin'])) { $filters['date_signature_fin'] = sanitize_text_field($_POST['date_signature_fin']); } // Utiliser la méthode get_credits avec les filtres $credits = $this->get_credits($filters); // Récupérer TOUS les crédits pour les compteurs (sans filtre) $all_credits = $this->get_credits(array()); // Calculer les compteurs par statut avec array_reduce $status_counts = array_reduce($all_credits, function($counts, $credit) { $status = isset($credit->status) ? (string)$credit->status : '0'; if (!isset($counts[$status])) { $counts[$status] = 0; } $counts[$status]++; return $counts; }, array()); // S'assurer que tous les statuts ont une valeur (même 0) $status_counts = array_merge( array('0' => 0, '1' => 0, '2' => 0, '-1' => 0), $status_counts ); wp_send_json_success(array( 'credits' => $credits, 'total' => count($credits), 'status_counts' => $status_counts, 'total_all' => count($all_credits) )); } /** * Récupérer les options de filtres */ public function ajax_get_filter_options() { // Vérifier le nonce if (!wp_verify_nonce($_POST['nonce'], 'credit_manager_action')) { wp_die('Sécurité: Nonce invalide'); } // Vérifier les permissions if (!current_user_can('edit_posts')) { wp_die('Permissions insuffisantes'); } wp_send_json_success(array( 'localites' => $this->get_distinct_localites(), 'societes' => $this->get_distinct_societes(), 'types_credit' => $this->get_distinct_credit_types() )); } /** * change credit status */ public function ajax_change_credit_status() { // Vérifier le nonce if (!wp_verify_nonce($_POST['nonce'], 'credit_manager_action')) { wp_die('Sécurité: Nonce invalide'); } global $wpdb; $table_name = 'cdf_Credits_listing'; $credit_id = isset($_POST['credit_id']) ? absint($_POST['credit_id']) : 0; $new_status = isset($_POST['new_status']) ? intval($_POST['new_status']) : 0; if ($credit_id <= 0) { wp_send_json_error('ID de crédit invalide'); } // Vérifier si le crédit existe $existing_credit = $this->get_credit_by_id($credit_id); if (!$existing_credit) { wp_send_json_error('Crédit non trouvé'); } // Changer le statut du crédit error_log('Credit Manager: change_status id=' . $credit_id . ' new_status=' . $new_status); $result = $wpdb->update( $table_name, array('status' => $new_status), array('id' => $credit_id), array('%d'), array('%d') ); if ($result === false) { wp_send_json_error('Erreur lors du changement de statut du crédit'); } // Si statut = 2 (accepté classé), tenter d'ajouter l'email à la liste Mailchimp principale if (intval($new_status) === 2 && !empty($existing_credit->email)) { $mailchimpController = $this->getCreditMailchimp(); if ($mailchimpController && method_exists($mailchimpController, 'subscribe_from_credit')) { $mailchimpController->subscribe_from_credit($existing_credit); } } wp_send_json_success(array( 'message' => 'Statut du crédit changé avec succès', 'credit_id' => $credit_id, 'status' => $new_status )); } /** * Mettre à jour le statut du crédit (accepté non signé, accepté classé, refusé) */ public function ajax_update_credit_status() { // Vérifier le nonce if (!wp_verify_nonce($_POST['nonce'], 'credit_manager_action')) { wp_die('Sécurité: Nonce invalide'); } // Vérifier les permissions if (!current_user_can('edit_posts')) { wp_die('Permissions insuffisantes'); } global $wpdb; $table_name = 'cdf_Credits_listing'; $credit_id = isset($_POST['credit_id']) ? absint($_POST['credit_id']) : 0; $status = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : ''; if ($credit_id <= 0) { wp_send_json_error('ID de crédit invalide'); } // Vérifier si le crédit existe $existing_credit = $this->get_credit_by_id($credit_id); if (!$existing_credit) { wp_send_json_error('Crédit non trouvé'); } // Mapper les statuts aux valeurs numériques $status_map = array( 'accepte_non_signe' => 1, 'accepte_classe' => 2, 'refuse' => -1 ); if (!isset($status_map[$status])) { wp_send_json_error('Statut invalide'); } $status_value = $status_map[$status]; // Mettre à jour le statut error_log('Credit Manager: update_status id=' . $credit_id . ' status=' . $status . ' value=' . $status_value); $result = $wpdb->update( $table_name, array('status' => $status_value), array('id' => $credit_id), array('%d'), array('%d') ); if ($result === false) { wp_send_json_error('Erreur lors de la mise à jour du statut'); } // Si statut = 2 (accepté classé), tenter d'ajouter l'email à la liste Mailchimp principale if (intval($status_value) === 2 && !empty($existing_credit->email)) { $mailchimpController = $this->getCreditMailchimp(); if ($mailchimpController && method_exists($mailchimpController, 'subscribe_from_credit')) { $mailchimpController->subscribe_from_credit($existing_credit); } } wp_send_json_success(array( 'message' => 'Statut mis à jour avec succès', 'credit_id' => $credit_id, 'status' => $status, 'status_value' => $status_value )); } /** * Si la configuration Mailchimp est présente, abonner le contact à l'audience principale. * @param object $credit Objet crédit contenant au moins email, prenom, nom */ // méthode supprimée: la logique d'abonnement est dans CRED_credit_mailchimp }