'Français', 'de' => 'Allemand', 'it' => 'Italien', 'es' => 'Espagnol', 'pt' => 'Portugais', 'nl' => 'Néerlandais', 'en' => 'Anglais', 'ru' => 'Russe', 'pl' => 'Polonais', 'ro' => 'Roumain', 'el' => 'Grec', 'tr' => 'Turc', 'ar' => 'Arabe', 'kab' => 'Kabyle', 'ber' => 'Berbère', 'tzm' => 'Tamazight', 'da' => 'Danois', 'sv' => 'Suédois', 'no' => 'Norvégien', 'fi' => 'Finnois', 'cs' => 'Tchèque', 'sk' => 'Slovaque', 'hu' => 'Hongrois', 'bg' => 'Bulgare', 'hr' => 'Croate', 'sr' => 'Serbe', 'bs' => 'Bosnien', 'sq' => 'Albanais', 'mk' => 'Macédonien', 'sl' => 'Slovène', 'he' => 'Hébreu', 'lt' => 'Lituanien', 'lv' => 'Letton', 'et' => 'Estonien', ]; /** * Schéma des champs ACF : nom => type (ex : group, repeater, taxonomy, text...) */ public static $acf_schema = [ 'nom' => 'text', 'prenom' => 'text', 'email' => 'email', 'telephone' => 'text', 'code_postal' => 'number', 'date_de_naissance' => 'date_picker', 'langues_parlees' => 'taxonomy', 'genre' => 'radio', 'commentaire' => 'textarea', 'statut' => 'select', ]; public function __construct($data = []) { foreach ($data as $key => $value) { if (property_exists($this, $key)) { $this->$key = $value; } } } public function load_previous_benef_events($id) { global $wpdb; $table_name = $wpdb->prefix . 'crvi_agenda'; $events = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_name WHERE id_beneficiaire = %d", $id)); return $events; } public function benef_previous_events($events) { $previous_events = []; if(!empty($events)) { foreach($events as $event) { $event = CRVI_Event_Model::load($event->id); $event->get_details($event->id); $traducteur = CRVI_Traducteur_Model::load($event->id_traducteur); $local = CRVI_Local_Model::load($event->id_local); $previous_events['date'] = Carbon::parse($event->date)->format('d/m/Y'); $previous_events['heure'] = Carbon::parse($event->heure)->format('H:i'); $previous_events['local'] = $local->nom; $previous_events['traducteur'] = $traducteur->nom.' '.$traducteur->prenom; $previous_events['statut'] = $event->statut; $previous_events['commentaire'] = $event->commentaire; } } } public function benef_previous_events_list($events) { $previous_events = []; $event_count = 0; if(!empty($events)) { foreach($events as $event) { $previous_events[$event_count]['date'] = Carbon::parse($event->date)->format('d/m/Y'); $previous_events[$event_count]['heure'] = Carbon::parse($event->heure)->format('H:i'); $previous_events[$event_count]['local'] = $event->local; $previous_events[$event_count]['traducteur'] = $event->traducteur; $previous_events[$event_count]['statut'] = $event->statut; $previous_events[$event_count]['commentaire'] = $event->commentaire; } $event_count++; } return $previous_events; } public function benef_previous_non_attended_events($events, $count = true) { $previous_events = []; $event_count = 0; if(!empty($events)) { foreach($events as $event) { $statut = $event->statut; if($statut == 'non_tenu') { $previous_events[$event_count]['date'] = Carbon::parse($event->date)->format('d/m/Y'); $previous_events[$event_count]['heure'] = Carbon::parse($event->heure)->format('H:i'); $previous_events[$event_count]['local'] = $event->local; $previous_events[$event_count]['traducteur'] = $event->traducteur; $previous_events[$event_count]['statut'] = $event->statut; } } $event_count++; } return $previous_events; } /** * Récupère les absences d'un bénéficiaire * @param int $beneficiaire_id * @return array|WP_Error */ public function get_absences(int $beneficiaire_id) { global $wpdb; $table_name = $wpdb->prefix . 'crvi_agenda'; // Vérifier si la table existe $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'"); if (!$table_exists) { return new \WP_Error('table_not_found', 'Table agenda non trouvée'); } // Récupérer les événements où le bénéficiaire était absent $query = $wpdb->prepare( "SELECT id, date_rdv, heure_rdv, statut, commentaire FROM $table_name WHERE id_beneficiaire = %d AND statut IN ('non_tenu', 'absent', 'annule') ORDER BY date_rdv DESC, heure_rdv DESC LIMIT 10", $beneficiaire_id ); $absences = $wpdb->get_results($query, ARRAY_A); if ($wpdb->last_error) { return new \WP_Error('db_error', 'Erreur lors de la récupération des absences: ' . $wpdb->last_error); } return $absences; } public static function load($id, $fields = []) { // Charger depuis CPT/meta $beneficiaire = get_post($id); if (!$beneficiaire) { return null; } // Si des champs spécifiques sont demandés, ne charger que ceux-ci if (!empty($fields)) { $data = []; foreach ($fields as $field) { if ($field === 'id') { $data['id'] = $beneficiaire->ID; } elseif (property_exists(self::class, $field)) { $data[$field] = get_field($field, $beneficiaire->ID); } } return new self($data); } // Sinon, charger tous les champs par défaut return new self([ 'id' => $beneficiaire->ID, 'nom' => get_field('nom', $beneficiaire->ID), 'prenom' => get_field('prenom', $beneficiaire->ID), 'email' => get_field('email', $beneficiaire->ID), 'telephone' => get_field('telephone', $beneficiaire->ID), 'langues_parlees' => get_field('langues_parlees', $beneficiaire->ID), 'commentaire' => get_field('commentaire', $beneficiaire->ID), 'statut' => get_field('statut', $beneficiaire->ID), ]); } /** * Récupère tous les rendez-vous liés à un bénéficiaire * @param int $beneficiaire_id * @return array */ public static function get_rendezvous_by_beneficiaire(int $beneficiaire_id): array { $args = [ 'post_type' => 'crvi_agenda', 'numberposts' => -1, 'meta_query' => [ [ 'key' => 'id_beneficiaire', 'value' => $beneficiaire_id, 'compare' => '=', ], ], ]; $rdvs = get_posts($args); $result = []; foreach ($rdvs as $rdv) { $date = get_field('date', $rdv->ID); $heure = get_field('heure', $rdv->ID); $departements = get_field('departements', $rdv->ID); // à adapter selon structure $departement_nom =get_term_by('id', $departements, 'departement'); $departement_nom = $departement_nom->name; $type_intervention = get_field('type_intervention', $rdv->ID); $type_intervention_nom =get_term_by('id', $type_intervention, 'type_intervention'); $type_intervention_nom = $type_intervention_nom->name; $intervenant_id = get_field('id_intervenant', $rdv->ID); $langue = get_field('langue', $rdv->ID); $result[] = [ 'id' => $rdv->ID, 'date' => $date, 'heure' => $heure, 'departements' => $departement_nom, 'type_intervention' => $type_intervention_nom, 'intervenant_id' => $intervenant_id, 'langue' => $langue, ]; } return $result; } /** * Récupère les intervenants liés à un bénéficiaire, avec le nombre de rdv * @param int $beneficiaire_id * @return array */ public static function get_intervenants_by_beneficiaire(int $beneficiaire_id): array { $rdvs = self::get_rendezvous_by_beneficiaire($beneficiaire_id); $intervenants = []; foreach ($rdvs as $rdv) { $intervenant_id = $rdv['intervenant_id']; if (!$intervenant_id) continue; if (!isset($intervenants[$intervenant_id])) { $nom = get_field('nom', $intervenant_id); $prenom = get_field('prenom', $intervenant_id); $intervenants[$intervenant_id] = [ 'id' => $intervenant_id, 'nom' => $nom, 'prenom' => $prenom, 'nb_rdv' => 1, ]; } else { $intervenants[$intervenant_id]['nb_rdv']++; } } return array_values($intervenants); } public function get_relations() { $benef_id = $this->id; $rdvs = self::get_rendezvous_by_beneficiaire($benef_id); $futurs = []; $passe = []; $now = Carbon::now(); foreach ($rdvs as $rdv) { // On suppose que la date est au format d/m/Y ou Y-m-d $date = $rdv['date']; $heure = $rdv['heure'] ?? '00:00'; $dt = null; if ($date) { $dt = Carbon::createFromFormat('d/m/Y H:i', $date.' '.$heure); if (!$dt) { $dt = Carbon::createFromFormat('Y-m-d H:i', $date.' '.$heure); } } $rdv_info = [ 'date' => $date, 'heure' => $heure, 'departements' => $rdv['departements'], 'type_intervention' => $rdv['type_intervention'], 'intervenant' => $rdv['intervenant_id'] ? [ 'id' => $rdv['intervenant_id'], 'nom' => get_field('nom', $rdv['intervenant_id']), 'prenom' => get_field('prenom', $rdv['intervenant_id']), ] : null, 'langue' => $rdv['langue'], ]; if ($dt && $dt->greaterThanOrEqualTo($now)) { $futurs[] = $rdv_info; } else { $passe[] = $rdv_info; } } $intervenants = self::get_intervenants_by_beneficiaire($benef_id); return [ 'rendezvous' => [ 'futurs' => $futurs, 'passe' => $passe, ], 'intervenants' => $intervenants, ]; } public function get_all_beneficiaires($attributes = []) { $args = [ 'post_type' => 'beneficiaire', 'numberposts' => -1, ]; // Si des critères sont fournis, construire le meta_query if (!empty($attributes)) { $meta_query = ['relation' => 'AND']; foreach ($attributes as $key => $value) { $meta_query[] = [ 'key' => $key, 'value' => $value, 'compare' => '=', ]; } $args['meta_query'] = $meta_query; } $beneficiaires = get_posts($args); $result = []; foreach ($beneficiaires as $beneficiaire) { $result[] = new self([ 'id' => $beneficiaire->ID, 'nom' => get_field('nom', $beneficiaire->ID), 'prenom' => get_field('prenom', $beneficiaire->ID), 'email' => get_field('email', $beneficiaire->ID), 'telephone' => get_field('telephone', $beneficiaire->ID), 'langues_parlees' => get_field('langues_parlees', $beneficiaire->ID), 'commentaire' => get_field('commentaire', $beneficiaire->ID), 'statut' => get_field('statut', $beneficiaire->ID), ]); } return $result; } /** * Créer ou mettre à jour un bénéficiaire (upsert par email). * Utilisable en REST (retour structuré) ou import CSV (retour simple). * @param array $data * @param bool $as_rest * @return array|string|false */ public static function create(array $data, bool $as_rest = false) { // 1. Vérifier les droits (admin ou opérateur) if (!current_user_can('edit_posts')) { if ($as_rest) { return [ 'success' => false, 'code' => 403, 'message' => 'Non autorisé', 'data' => null ]; } return false; } // 2. Valider les données (ex : nom, prénom, email obligatoires) if (empty($data['nom']) || empty($data['prenom']) || empty($data['email'])) { if ($as_rest) { return [ 'success' => false, 'code' => 400, 'message' => 'Champs obligatoires manquants', 'data' => null ]; } return false; } // 3. Vérifier si le bénéficiaire existe déjà (unicité email) $existing = get_posts([ 'post_type' => 'beneficiaire', 'meta_key' => 'email', 'meta_value' => $data['email'], 'post_status'=> 'any', 'numberposts'=> 1, ]); if ($existing) { // Mise à jour $post_id = $existing[0]->ID ?? $existing[0]; wp_update_post([ 'ID' => $post_id, 'post_title' => ($data['nom'] ?? get_post_meta($post_id, 'nom', true)) . ' ' . ($data['prenom'] ?? get_post_meta($post_id, 'prenom', true)), ]); foreach ($data as $key => $value) { if ($key === 'langues_parlees') { // Logique spécifique : créer le terme avec le bon nom (lg_map) et slug if (!taxonomy_exists('langue_beneficiaire')) { register_taxonomy( 'langue_beneficiaire', 'beneficiaire', [ 'label' => 'Langues bénéficiaire', 'public' => false, 'hierarchical' => false, 'show_ui' => true, 'show_in_rest' => false, ] ); } $langue_ids = []; $langues = is_array($value) ? $value : explode('|', $value); foreach ($langues as $slug) { $slug = trim($slug); if (empty($slug)) continue; $term = get_term_by('slug', $slug, 'langue_beneficiaire'); if (!$term) { $nom = self::$lg_map[$slug] ?? $slug; $result = wp_insert_term($nom, 'langue_beneficiaire', ['slug' => $slug]); if (!is_wp_error($result) && isset($result['term_id'])) { $langue_ids[] = $result['term_id']; } } else { $langue_ids[] = $term->term_id; } } if (!empty($langue_ids)) { update_field('langues_parlees', $langue_ids, $post_id); } } elseif (isset(self::$acf_schema[$key])) { self::set_acf_field($post_id, $key, $value, self::$acf_schema); } else { update_post_meta($post_id, $key, $value); } } if ($as_rest) { return [ 'success' => true, 'code' => 200, 'message' => 'Bénéficiaire mis à jour', 'data' => [ 'id' => $post_id ] ]; } return 'updated'; } else { // Création $post_id = wp_insert_post([ 'post_type' => 'beneficiaire', 'post_title' => $data['nom'] . ' ' . $data['prenom'], 'post_status' => 'publish', ]); if (is_wp_error($post_id)) { if ($as_rest) { return [ 'success' => false, 'code' => 500, 'message' => 'Erreur lors de la création', 'data' => null ]; } return false; } foreach ($data as $key => $value) { if ($key === 'langues_parlees') { // Logique spécifique : créer le terme avec le bon nom (lg_map) et slug if (!taxonomy_exists('langue_beneficiaire')) { register_taxonomy( 'langue_beneficiaire', 'beneficiaire', [ 'label' => 'Langues bénéficiaire', 'public' => false, 'hierarchical' => false, 'show_ui' => true, 'show_in_rest' => false, ] ); } $langue_ids = []; $langues = is_array($value) ? $value : explode('|', $value); foreach ($langues as $slug) { $slug = trim($slug); if (empty($slug)) continue; $term = get_term_by('slug', $slug, 'langue_beneficiaire'); if (!$term) { $nom = self::$lg_map[$slug] ?? $slug; $result = wp_insert_term($nom, 'langue_beneficiaire', ['slug' => $slug]); if (!is_wp_error($result) && isset($result['term_id'])) { $langue_ids[] = $result['term_id']; } } else { $langue_ids[] = $term->term_id; } } if (!empty($langue_ids)) { update_field('langue_beneficiaire', $langue_ids, $post_id); } } elseif (isset(self::$acf_schema[$key])) { self::set_acf_field($post_id, $key, $value, self::$acf_schema); } else { update_post_meta($post_id, $key, $value); } } if ($as_rest) { return [ 'success' => true, 'code' => 201, 'message' => 'Bénéficiaire créé', 'data' => [ 'id' => $post_id ] ]; } return 'created'; } } /** * Mettre à jour un bénéficiaire. * @param int $id * @param array $data * @return bool|WP_Error */ public function update(int $id, array $data) { // 1. Vérifier les droits if (!current_user_can('edit_posts')) { return Api_Helper::json_error('Non autorisé', 403); } // 2. Vérifier l'existence du post $post = get_post($id); if (!$post || $post->post_type !== 'beneficiaire') { return Api_Helper::json_error('Bénéficiaire introuvable', 404); } // 3. Valider les données (ex : email unique si modifié) if (!empty($data['email'])) { $existing = get_posts([ 'post_type' => 'beneficiaire', 'meta_key' => 'email', 'meta_value' => $data['email'], 'post_status'=> 'any', 'exclude' => [$id], 'numberposts'=> 1, ]); if ($existing) { return Api_Helper::json_error('Email déjà utilisé', 409); } } // 4. Mettre à jour le post $result = wp_update_post([ 'ID' => $id, 'post_title' => ($data['nom'] ?? get_post_meta($id, 'nom', true)) . ' ' . ($data['prenom'] ?? get_post_meta($id, 'prenom', true)), ], true); if (is_wp_error($result)) { return $result; } // 5. Mettre à jour les champs personnalisés foreach ($data as $key => $value) { update_post_meta($id, $key, $value); } return true; } /** * Supprimer un bénéficiaire (corbeille WordPress) * @param int $id * @return bool|WP_Error */ public function delete(int $id) { // 1. Vérifier les droits (admin uniquement) if (!current_user_can('delete_posts')) { return Api_Helper::json_error('Non autorisé', 403); } // 2. Vérifier l'existence du post $post = get_post($id); if (!$post || $post->post_type !== 'beneficiaire') { return Api_Helper::json_error('Bénéficiaire introuvable', 404); } // 3. Mettre à la corbeille $result = wp_trash_post($id); if (!$result) { return Api_Helper::json_error('Erreur lors de la suppression', 500); } return true; } /** * Calcule l'âge à partir d'une date de naissance (format d/m/Y ou Y-m-d) ou d'un ID de bénéficiaire * @param string|int|null $date_naissance_ou_id * @param string $champ_acf Nom du champ ACF pour la date de naissance (par défaut 'date_de_naissance') * @return int|null */ public static function calculer_age($date_naissance_ou_id, string $champ_acf = 'date_de_naissance'): ?int { $date_naissance = null; // Si c'est un entier, on considère que c'est un ID de bénéficiaire if (is_int($date_naissance_ou_id)) { $date_naissance = get_field($champ_acf, $date_naissance_ou_id); } elseif (is_string($date_naissance_ou_id)) { $date_naissance = $date_naissance_ou_id; } if (empty($date_naissance)) { return null; } // Gérer le format d/m/Y (ACF) ou Y-m-d $formats = ['d/m/Y', 'Y-m-d']; foreach ($formats as $format) { $date = \DateTime::createFromFormat($format, $date_naissance); if ($date !== false) { $now = new \DateTime(); $age = $now->diff($date)->y; return $age; } } // Si aucun format ne correspond return null; } }