Crvi/app/models/Beneficiaire_Model.php
2026-01-20 07:54:37 +01:00

654 lines
24 KiB
PHP

<?php
declare(strict_types=1);
namespace ESI_CRVI_AGENDA\models;
use Carbon\Carbon;
use ESI_CRVI_AGENDA\helpers\Api_Helper;
class CRVI_Beneficiaire_Model extends Main_Model {
public $id;
public $nom;
public $prenom;
public $email;
public $telephone;
public $langues_parlees;
public $commentaire;
public $statut;
/**
* Carte des codes de langue vers le nom de la langue (Europe continentale + Afrique du Nord)
*/
public static $lg_map = [
'fr' => '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;
}
}