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

542 lines
20 KiB
PHP

<?php
declare(strict_types=1);
namespace ESI_CRVI_AGENDA\models;
use WP_Error;
class CRVI_Intervenant_Model extends Main_Model {
public $id;
public $nom;
public $prenom;
public $email;
public $departements_ids; // array d'IDs CPT departement
public $types_intervention_ids; // array d'IDs CPT type_intervention
public $jours_de_disponibilite;
public $indisponibilites;
public $commentaires;
/**
* Schéma des champs ACF : nom => type (ex : group, repeater, taxonomy, text...)
*/
public static $acf_schema = [
'telephone' => 'text',
'departements' => 'taxonomy',
'specialisations' => 'taxonomy',
'commentaires' => 'textarea',
'jours_de_disponibilite' => 'checkbox',
'indisponibilitee_ponctuelle' => 'repeater',
];
public function __construct($data = []) {
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}
public static function load($user_id, $fields = []) {
$user = get_user_by('id', $user_id);
if (!$user || !in_array('intervenant', (array) $user->roles, true)) {
return null;
}
// Fonction helper pour récupérer les champs ACF ou meta
$get_field_value = function($user_id, $field_name) {
if (function_exists('get_field')) {
return get_field($field_name, 'user_' . $user_id) ?: [];
} else {
return get_user_meta($user_id, $field_name, true) ?: [];
}
};
// 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'] = $user->ID;
} elseif ($field === 'nom') {
$data['nom'] = $user->last_name;
} elseif ($field === 'prenom') {
$data['prenom'] = $user->first_name;
} elseif ($field === 'email') {
$data['email'] = $user->user_email;
} elseif (property_exists(self::class, $field)) {
$data[$field] = $get_field_value($user->ID, $field);
}
}
return new self($data);
}
// Sinon, charger tous les champs par défaut
return new self([
'id' => $user->ID,
'nom' => $user->last_name,
'prenom' => $user->first_name,
'email' => $user->user_email,
'departements_ids' => $get_field_value($user->ID, 'departements_ids'),
'types_intervention_ids' => $get_field_value($user->ID, 'types_intervention_ids'),
'jours_de_disponibilite' => $get_field_value($user->ID, 'jours_de_disponibilite'),
'indisponibilites' => $get_field_value($user->ID, 'indisponibilites'),
'commentaires' => $get_field_value($user->ID, 'commentaires'),
]);
}
public static function get_intervenants($filters = [],$simple_list = false) {
$users = get_users([
'role' => 'intervenant',
'number' => -1,
'meta_query' => $filters,
]);
if ($simple_list) {
$users = array_map(function($user) {
return [
'id' => $user->ID,
'nom' => $user->last_name,
'prenom' => $user->first_name,
];
}, $users);
}
return $users;
}
/**
* Retourne les intervenants disponibles à une date donnée, avec filtres optionnels.
* @param string $date au format Y-m-d
* @param int|null $departement_id (ID CPT departement)
* @param int|null $type_intervention_id (ID CPT type_intervention)
* @param int|null $event_id ID de l'événement en cours d'édition (pour inclure l'intervenant actuel)
* @return array Liste des intervenants disponibles (CRVI_Intervenant_Model)
*/
public static function filtrer_disponibles($date, $departement_id = null, $type_intervention_id = null, $event_id = null) {
if (empty($date)) {
return [];
}
$meta_query = [
[
'key' => 'jours_de_disponibilite',
'compare' => 'EXISTS',
],
];
if ($departement_id) {
$meta_query[] = [
'key' => 'departements_ids',
'value' => $departement_id,
'compare' => 'LIKE',
];
}
if ($type_intervention_id) {
$meta_query[] = [
'key' => 'types_intervention_ids',
'value' => $type_intervention_id,
'compare' => 'LIKE',
];
}
$args = [
'role' => 'intervenant',
'number' => -1,
'meta_query' => $meta_query,
];
$users = get_users($args);
$disponibles = [];
foreach ($users as $user) {
$intervenant = self::load($user->ID);
if ($intervenant) {
$is_disponible = $intervenant->is_disponible($user->ID ,$date, $departement_id, $type_intervention_id);
// Vérifier les conflits d'événements existants
if ($is_disponible) {
$conflits = self::verifier_conflits_intervenant($intervenant->id, $date, $event_id);
if (!empty($conflits)) {
$is_disponible = false;
}
}
// Si c'est l'événement en cours d'édition, inclure l'intervenant même s'il est "pris"
if ($event_id && self::is_intervenant_of_event($intervenant->id, $event_id)) {
$is_disponible = true;
}
if ($is_disponible) {
$disponibles[] = $intervenant;
}
}
}
// error_log("CRVI_Intervenant_Model::filtrer_disponibles - Intervenants disponibles: " . count($disponibles));
return $disponibles;
}
/**
* Vérifie si un intervenant est associé à un événement donné
* @param int $intervenant_id
* @param int $event_id
* @return bool
*/
private static function is_intervenant_of_event($intervenant_id, $event_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'crvi_agenda';
$result = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM $table_name WHERE id = %d AND id_intervenant = %d",
$event_id,
$intervenant_id
));
return !empty($result);
}
/**
* Vérifie si cet intervenant est disponible à une date donnée, avec filtres optionnels.
* @param string $date au format Y-m-d
* @param int|null $departement_id
* @param int|null $type_intervention_id
* @return bool
*/
public function is_disponible($intervenant_id, $date, $departement_id = null, $type_intervention_id = null) {
// Vérifier que la date n'est pas null ou vide
if (empty($date)) {
return false;
}
$timestamp = strtotime($date);
if ($timestamp === false) {
return false;
}
$jour = strtolower(date('l', $timestamp));
$jours_fr = [
'monday' => 'lundi',
'tuesday' => 'mardi',
'wednesday' => 'mercredi',
'thursday' => 'jeudi',
'friday' => 'vendredi',
'saturday' => 'samedi',
'sunday' => 'dimanche',
];
$jour_semaine = $jours_fr[$jour] ?? '';
$jours_disponibles = get_field('jours_de_disponibilite', 'user_' . $intervenant_id);
if (!is_array($jours_disponibles) || !in_array($jour_semaine, $jours_disponibles, true)) {
return false;
}
$indisponibilites = get_field('indisponibilitee_ponctuelle', 'user_' . $intervenant_id);
if (is_array($indisponibilites)) {
foreach ($indisponibilites as $absence) {
$debut = isset($absence['debut']) ? strtotime($absence['debut']) : null;
$fin = isset($absence['fin']) ? strtotime($absence['fin']) : null;
if ($debut && $fin && $timestamp >= $debut && $timestamp <= $fin) {
return false;
}
}
}
if ($departement_id && (!is_array($this->departements_ids) || !in_array($departement_id, $this->departements_ids))) {
return false;
}
if ($type_intervention_id && (!is_array($this->types_intervention_ids) || !in_array($type_intervention_id, $this->types_intervention_ids))) {
return false;
}
return true;
}
public function get_relations() {
// À adapter selon la nouvelle structure (ex : récupérer les rendez-vous liés via user_id)
return [];
}
/**
* Créer un intervenant (user avec rôle intervenant).
* @param array $data
* @return array|WP_Error
*/
public static function create(array $data, bool $as_rest = false) {
// Suppression du debug
// echo '<pre>';
// print_r($data);
// echo '</pre>';
// die();
if (!is_user_logged_in() || !get_current_user_id()) {
if ($as_rest) {
return [
'success' => false,
'code' => 401,
'message' => 'Authentification requise',
'data' => null
];
}
return new WP_Error('auth_required', 'Authentification requise', ['status' => 401]);
}
if (!current_user_can('edit_users')) {
if ($as_rest) {
return [
'success' => false,
'code' => 403,
'message' => 'Non autorisé',
'data' => null
];
}
return new WP_Error('not_allowed', 'Non autorisé', ['status' => 403]);
}
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 new WP_Error('missing_fields', 'Champs obligatoires manquants', ['status' => 400]);
}
$user = get_user_by('email', $data['email']);
/* echo '<pre>';
print_r($user);
echo '</pre>';
die(); */
if ($user && in_array('intervenant', (array)$user->roles, true)) {
// Mise à jour
$user_id = $user->ID;
$user_data = [
'ID' => $user_id,
'user_email' => $data['email'],
'first_name' => $data['prenom'],
'last_name' => $data['nom'],
];
$result = wp_update_user($user_data);
if (is_wp_error($result)) {
if ($as_rest) {
return [
'success' => false,
'code' => 500,
'message' => 'Erreur lors de la mise à jour',
'data' => json_encode($result)
];
}
return $result;
}
// Mettre à jour les champs ACF
foreach ($data as $key => $value) {
// Pour departements et types_intervention, on attend des IDs de posts CPT (séparés par pipe si string)
if ($key === 'departements') {
$ids = is_array($value) ? $value : explode('|', $value);
$ids = array_filter(array_map('intval', $ids));
\update_field('departements', $ids, 'user_' . $user_id);
} else if ($key === 'types_intervention' || $key === 'specialisations') {
$ids = is_array($value) ? $value : explode('|', $value);
$ids = array_filter(array_map('intval', $ids));
\update_field('specialisations', $ids, 'user_' . $user_id);
} else if (isset(self::$acf_schema[$key])) {
self::set_acf_field('user_' . $user_id, $key, $value, self::$acf_schema);
} else {
// Pour les autres champs non ACF, on ignore ou on peut loguer
}
}
if ($as_rest) {
return [
'success' => true,
'code' => 200,
'message' => 'Intervenant mis à jour',
'data' => [ 'user_id' => $user_id ]
];
}
return ['user_id' => $user_id, 'updated' => true];
} else if($user) {
// L'utilisateur existe mais n'a pas le rôle intervenant
if ($as_rest) {
return [
'success' => false,
'code' => 409,
'message' => 'Email déjà utilisé par un autre rôle',
'data' => null
];
}
return new WP_Error('email_exists', 'Email déjà utilisé par un autre rôle', ['status' => 409]);
}
echo 'vhvh';
die();
// Création
$user_id = wp_insert_user([
'user_login' => $data['email'],
'user_email' => $data['email'],
'first_name' => $data['prenom'],
'last_name' => $data['nom'],
'role' => 'intervenant',
'user_pass' => wp_generate_password(12, true),
]);
if (is_wp_error($user_id)) {
if ($as_rest) {
return [
'success' => false,
'code' => 500,
'message' => 'Erreur lors de la création',
'data' => null
];
}
return $user_id;
}
// Générer un mot de passe applicatif (Application Password)
if (!class_exists('WP_Application_Passwords') && file_exists(ABSPATH . 'wp-includes/class-wp-application-passwords.php')) {
require_once ABSPATH . 'wp-includes/class-wp-application-passwords.php';
}
if (class_exists('WP_Application_Passwords')) {
$app_pass_result = \WP_Application_Passwords::create_new_application_password($user_id, [
'name' => 'CRVI API',
]);
if (is_array($app_pass_result) && isset($app_pass_result[0], $app_pass_result[1]['new_password'])) {
$application_password = $app_pass_result[1]['new_password'];
} else {
$application_password = null;
}
} else {
$application_password = null;
}
// Stocker les champs ACF
foreach ($data as $key => $value) {
if ($key === 'departements') {
$ids = is_array($value) ? $value : explode('|', $value);
$ids = array_filter(array_map('intval', $ids));
\update_field('departements', $ids, 'user_' . $user_id);
} else if ($key === 'types_intervention' || $key === 'specialisations') {
$ids = is_array($value) ? $value : explode('|', $value);
$ids = array_filter(array_map('intval', $ids));
\update_field('specialisations', $ids, 'user_' . $user_id);
} else if (isset(self::$acf_schema[$key])) {
self::set_acf_field('user_' . $user_id, $key, $value, self::$acf_schema);
} else {
// Pour les autres champs non ACF, on ignore ou on peut loguer
}
}
if ($as_rest) {
return [
'success' => true,
'code' => 201,
'message' => 'Intervenant créé',
'data' => [ 'user_id' => $user_id, 'application_password' => $application_password ]
];
}
return ['user_id' => $user_id, 'created' => true, 'application_password' => $application_password];
}
/**
* Mettre à jour un intervenant (user).
* @param int $user_id
* @param array $data
* @return bool|WP_Error
*/
public static function update(int $user_id, array $data) {
if (!current_user_can('edit_users')) {
return new WP_Error('not_allowed', 'Non autorisé', ['status' => 403]);
}
$user = get_user_by('id', $user_id);
if (!$user || !in_array('intervenant', (array) $user->roles, true)) {
return new WP_Error('not_found', 'Intervenant introuvable', ['status' => 404]);
}
$user_data = ['ID' => $user_id];
if (!empty($data['email'])) {
if (email_exists($data['email']) && $data['email'] !== $user->user_email) {
return new WP_Error('email_exists', 'Email déjà utilisé', ['status' => 409]);
}
$user_data['user_email'] = $data['email'];
}
if (!empty($data['prenom'])) {
$user_data['first_name'] = $data['prenom'];
}
if (!empty($data['nom'])) {
$user_data['last_name'] = $data['nom'];
}
$user_update = wp_update_user($user_data);
if (is_wp_error($user_update)) {
return $user_update;
}
// Mettre à jour les meta
foreach (['departements_ids', 'types_intervention_ids', 'jours_de_disponibilite', 'indisponibilites', 'commentaires'] as $meta_key) {
if (isset($data[$meta_key])) {
\update_field($meta_key, $data[$meta_key], 'user_' . $user_id);
}
}
return true;
}
/**
* Supprimer un intervenant (user).
* @param int $user_id
* @return bool|WP_Error
*/
public function delete(int $user_id) {
if (!current_user_can('delete_users')) {
return new WP_Error('not_allowed', 'Non autorisé', ['status' => 403]);
}
$user = get_user_by('id', $user_id);
if (!$user || !in_array('intervenant', (array) $user->roles, true)) {
return new WP_Error('not_found', 'Intervenant introuvable', ['status' => 404]);
}
require_once ABSPATH . 'wp-admin/includes/user.php';
$result = wp_delete_user($user_id);
if (!$result) {
return new WP_Error('delete_failed', 'Erreur lors de la suppression', ['status' => 500]);
}
return true;
}
public static function all() {
$users = get_users([
'role' => 'intervenant',
'number' => -1,
]);
return $users;
}
/**
* Vérifie s'il y a des conflits d'événements pour un intervenant donné
* @param int $intervenant_id
* @param string $date
* @param int|null $event_id - ID de l'événement à exclure (pour l'édition)
* @return array
*/
private static function verifier_conflits_intervenant($intervenant_id, $date, $event_id = null) {
global $wpdb;
$table_events = $wpdb->prefix . 'crvi_agenda';
$where_conditions = ['id_intervenant = %d'];
$where_values = [$intervenant_id];
// Exclure l'événement en cours d'édition si spécifié
if ($event_id) {
$where_conditions[] = 'id != %d';
$where_values[] = $event_id;
}
// Ajouter la condition de date
$where_conditions[] = 'date_rdv = %s';
$where_values[] = $date;
// Exclure les événements annulés ou terminés
$where_conditions[] = 'statut NOT IN (%s, %s)';
$where_values[] = 'annule';
$where_values[] = 'termine';
$where_clause = implode(' AND ', $where_conditions);
$query = $wpdb->prepare(
"SELECT * FROM {$table_events} WHERE {$where_clause}",
$where_values
);
return $wpdb->get_results($query, ARRAY_A);
}
}