2187 lines
92 KiB
PHP
2187 lines
92 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace ESI_CRVI_AGENDA\controllers;
|
|
|
|
use ESI_CRVI_AGENDA\models\Event_Model;
|
|
use ESI_CRVI_AGENDA\helpers\Api_Helper;
|
|
use ESI_CRVI_AGENDA\models\CRVI_Event_Model;
|
|
use ESI_CRVI_AGENDA\models\CRVI_Traducteur_Model;
|
|
use ESI_CRVI_AGENDA\models\CRVI_Intervenant_Model;
|
|
use ESI_CRVI_AGENDA\models\CRVI_Presence_Model;
|
|
use ESI_CRVI_AGENDA\controllers\Intervenant_Controller;
|
|
|
|
class CRVI_Event_Controller {
|
|
public static function register_routes() {
|
|
|
|
register_rest_route('crvi/v1', '/agenda/permissions', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_user_permissions'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
]);
|
|
|
|
register_rest_route('crvi/v1', '/agenda/disponibilites', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_disponibilites'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'get_disponibilites'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
]);
|
|
|
|
// Endpoint alternatif pour les filtres de disponibilités
|
|
register_rest_route('crvi/v1', '/filters/disponibilites', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_disponibilites'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'get_disponibilites'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
]);
|
|
|
|
\register_rest_route('crvi/v1', '/events/(?P<id>\\d+)/conflits', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'conflits_item'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
|
|
\register_rest_route('crvi/v1', '/events/export', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'export_items'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
|
|
// --- Filtres dynamiques ---
|
|
\register_rest_route('crvi/v1', '/filters/departements', [
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_departements'],
|
|
'permission_callback' => '__return_true',
|
|
]);
|
|
\register_rest_route('crvi/v1', '/filters/types-intervention', [
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_types_intervention'],
|
|
'permission_callback' => '__return_true',
|
|
]);
|
|
\register_rest_route('crvi/v1', '/filters/langues', [
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_langues'],
|
|
'permission_callback' => '__return_true',
|
|
]);
|
|
\register_rest_route('crvi/v1', '/filters/langues-beneficiaire', [
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_langues_beneficiaire'],
|
|
'permission_callback' => '__return_true',
|
|
]);
|
|
\register_rest_route('crvi/v1', '/filters/statuts', [
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_statuts'],
|
|
'permission_callback' => '__return_true',
|
|
]);
|
|
|
|
// --- Historique bénéficiaire ---
|
|
\register_rest_route('crvi/v1', '/beneficiaires/(?P<id>\\d+)/historique', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_beneficiaire_historique'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
]);
|
|
|
|
// --- CRUD Events ---
|
|
\register_rest_route('crvi/v1', '/events', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_events'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'create_event'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
|
|
// Endpoint pour le tableau de stats avec pagination
|
|
\register_rest_route('crvi/v1', '/events/table', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_events_table'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
]);
|
|
|
|
// --- Validation des présences pour les rendez-vous de groupe ---
|
|
\register_rest_route('crvi/v1', '/events/(?P<id>\\d+)/presences', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_event_presences'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'save_group_presences'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
// Création de permanences (intervenant)
|
|
\register_rest_route('crvi/v1', '/intervenant/permanences', [
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'create_permanences'],
|
|
'permission_callback' => [\ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller::class, 'check_intervenant_permission'],
|
|
],
|
|
]);
|
|
|
|
// Import CSV de permanences (intervenant)
|
|
\register_rest_route('crvi/v1', '/intervenant/permanences/import-csv', [
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'import_permanences_csv_intervenant'],
|
|
'permission_callback' => [\ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller::class, 'check_intervenant_permission'],
|
|
],
|
|
]);
|
|
|
|
// Création de permanences (admin)
|
|
\register_rest_route('crvi/v1', '/admin/permanences', [
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'create_permanences_admin'],
|
|
'permission_callback' => [\ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller::class, 'check_admin_permission'],
|
|
],
|
|
]);
|
|
|
|
// Import CSV de permanences (admin)
|
|
\register_rest_route('crvi/v1', '/admin/permanences/import-csv', [
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'import_permanences_csv'],
|
|
'permission_callback' => [\ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller::class, 'check_admin_permission'],
|
|
],
|
|
]);
|
|
|
|
\register_rest_route('crvi/v1', '/events/(?P<id>\\d+)', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_event'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
[
|
|
'methods' => 'PUT,PATCH',
|
|
'callback' => [self::class, 'update_event'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
[
|
|
'methods' => 'DELETE',
|
|
'callback' => [self::class, 'delete_event'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
|
|
// Endpoints pour la gestion des événements supprimés
|
|
\register_rest_route('crvi/v1', '/events/deleted', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_deleted_events'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
|
|
\register_rest_route('crvi/v1', '/events/(?P<id>\\d+)/restore', [
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'restore_event'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
|
|
\register_rest_route('crvi/v1', '/events/(?P<id>\\d+)/hard-delete', [
|
|
[
|
|
'methods' => 'DELETE',
|
|
'callback' => [self::class, 'hard_delete_event'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
|
|
// Route pour changer le statut d'un événement
|
|
\register_rest_route('crvi/v1', '/events/(?P<id>\\d+)/statut', [
|
|
[
|
|
'methods' => 'PUT',
|
|
'callback' => [self::class, 'change_statut'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Endpoint global pour récupérer les entités disponibles à une date/créneau donné.
|
|
* @param \WP_REST_Request $request
|
|
* @return \WP_REST_Response
|
|
*/
|
|
public static function get_disponibilites($request) {
|
|
// Récupérer les paramètres depuis query string ou JSON body
|
|
$params = $request->get_params();
|
|
$json_params = $request->get_json_params() ?: [];
|
|
|
|
// Fusionner les paramètres (JSON body a priorité sur query string)
|
|
$all_params = array_merge($params, $json_params);
|
|
|
|
// DEBUG: Afficher les paramètres reçus
|
|
|
|
// Vérifier si on demande les dates indisponibles
|
|
$id_intervenant = $all_params['id_intervenant'] ?? null;
|
|
$id_traducteur = $all_params['id_traducteur'] ?? null;
|
|
$id_local = $all_params['id_local'] ?? null;
|
|
$date_debut = $all_params['date_debut'] ?? null;
|
|
$date_fin = $all_params['date_fin'] ?? null;
|
|
|
|
// Si on a tous les paramètres pour les dates indisponibles
|
|
if ($id_intervenant && $id_traducteur && $id_local && $date_debut && $date_fin) {
|
|
return self::get_available_dates($id_intervenant, $id_traducteur, $id_local, $date_debut, $date_fin);
|
|
}
|
|
|
|
// Sinon, logique existante pour les entités disponibles
|
|
$date = $all_params['date'] ?? $all_params['date_rdv'] ?? null;
|
|
$heure = $all_params['heure'] ?? $all_params['heure_rdv'] ?? null;
|
|
$langue = $all_params['langue'] ?? null;
|
|
$departement = $all_params['departement'] ?? null;
|
|
$specialisation = $all_params['specialisation'] ?? null;
|
|
$type = $all_params['type'] ?? null;
|
|
|
|
// Normaliser event_id (convertir string en int si nécessaire)
|
|
$event_id = isset($all_params['event_id']) ? (int) $all_params['event_id'] : null;
|
|
if ($event_id === 0) {
|
|
$event_id = null;
|
|
}
|
|
|
|
// Normaliser id_traducteur (convertir string en int, "0" signifie pas de traducteur)
|
|
$id_traducteur_param = isset($all_params['id_traducteur']) ? (int) $all_params['id_traducteur'] : null;
|
|
if ($id_traducteur_param === 0) {
|
|
$id_traducteur_param = null;
|
|
}
|
|
|
|
// Normaliser id_intervenant et id_local
|
|
$id_intervenant_param = isset($all_params['id_intervenant']) ? (int) $all_params['id_intervenant'] : null;
|
|
$id_local_param = isset($all_params['id_local']) ? (int) $all_params['id_local'] : null;
|
|
|
|
// Appel aux méthodes de chaque controller
|
|
// Ne vérifier les traducteurs que s'il y en a un d'assigné à l'événement (id_traducteur > 0) OU si une langue est spécifiée
|
|
$traducteurs = [];
|
|
// Vérifier que id_traducteur est valide (supérieur à 0, pas null, pas 0)
|
|
$hasValidTraducteurId = $id_traducteur_param !== null && ($id_traducteur_param > 0 || $id_traducteur_param != '0');
|
|
if ($hasValidTraducteurId || !empty($langue)) {
|
|
try {
|
|
$traducteurs_result = \ESI_CRVI_AGENDA\models\CRVI_Traducteur_Model::filtrer_disponibles($date, $langue, $event_id);
|
|
$traducteurs = is_array($traducteurs_result) ? $traducteurs_result : [];
|
|
} catch (\Exception $e) {
|
|
error_log('[CRVI] Erreur lors de la récupération des traducteurs disponibles: ' . $e->getMessage());
|
|
$traducteurs = [];
|
|
}
|
|
}
|
|
|
|
// Récupérer les intervenants disponibles
|
|
$intervenants = [];
|
|
try {
|
|
$intervenants_result = \ESI_CRVI_AGENDA\models\CRVI_Intervenant_Model::filtrer_disponibles($date, $departement, $specialisation, $event_id);
|
|
$intervenants = is_array($intervenants_result) ? $intervenants_result : [];
|
|
} catch (\Exception $e) {
|
|
error_log('[CRVI] Erreur lors de la récupération des intervenants disponibles: ' . $e->getMessage());
|
|
$intervenants = [];
|
|
}
|
|
|
|
// Formater les intervenants pour le JavaScript
|
|
$intervenants_formatted = [];
|
|
foreach ($intervenants as $intervenant) {
|
|
// Vérifier que l'intervenant est un objet valide
|
|
if (!is_object($intervenant) || !isset($intervenant->id)) {
|
|
continue;
|
|
}
|
|
$intervenants_formatted[] = [
|
|
'id' => $intervenant->id,
|
|
'nom' => ($intervenant->nom ?? '') . ' ' . ($intervenant->prenom ?? ''),
|
|
];
|
|
}
|
|
|
|
// Formater les traducteurs pour le JavaScript
|
|
$traducteurs_formatted = [];
|
|
foreach ($traducteurs as $traducteur) {
|
|
// Vérifier que le traducteur est un objet valide
|
|
if (!is_object($traducteur) || !isset($traducteur->id)) {
|
|
continue;
|
|
}
|
|
$traducteurs_formatted[] = [
|
|
'id' => $traducteur->id,
|
|
'nom' => ($traducteur->nom ?? '') . ' ' . ($traducteur->prenom ?? ''),
|
|
];
|
|
}
|
|
|
|
|
|
// Récupérer les locaux disponibles selon le type de RDV et la disponibilité
|
|
$type_rdv = $all_params['type_rdv'] ?? null;
|
|
$locals_posts = [];
|
|
try {
|
|
$locals_result = \ESI_CRVI_AGENDA\models\CRVI_Local_Model::all();
|
|
$locals_posts = is_array($locals_result) ? $locals_result : [];
|
|
} catch (\Exception $e) {
|
|
error_log('[CRVI] Erreur lors de la récupération des locaux: ' . $e->getMessage());
|
|
$locals_posts = [];
|
|
}
|
|
|
|
$locaux = [];
|
|
foreach ($locals_posts as $local_post) {
|
|
// Vérifier que le post est valide
|
|
if (!is_object($local_post) || !isset($local_post->ID)) {
|
|
continue;
|
|
}
|
|
|
|
$type_local = get_post_meta($local_post->ID, 'type_de_local', true);
|
|
$capacite = get_post_meta($local_post->ID, 'capacite', true);
|
|
|
|
// Normaliser le type_local (peut être vide ou null)
|
|
$type_local = !empty($type_local) ? $type_local : '';
|
|
|
|
// Construire le nom du local avec le type si disponible
|
|
$nom_local = $local_post->post_title ?? 'Local sans nom';
|
|
if (!empty($type_local)) {
|
|
$nom_local .= ' (' . $type_local . ')';
|
|
}
|
|
|
|
// Vérifier la disponibilité du local pour la date/heure spécifiée
|
|
$disponible = true;
|
|
if ($date && $heure) {
|
|
// Calculer la date/heure de fin (par défaut +1h si pas spécifiée)
|
|
$date_fin = $all_params['date_fin'] ?? $date;
|
|
$heure_fin = $all_params['heure_fin'] ?? null;
|
|
|
|
if (!$heure_fin) {
|
|
// Si pas d'heure de fin spécifiée, ajouter 1h par défaut
|
|
$heure_obj = \DateTime::createFromFormat('H:i', $heure);
|
|
if ($heure_obj) {
|
|
$heure_obj->add(new \DateInterval('PT1H'));
|
|
$heure_fin = $heure_obj->format('H:i');
|
|
} else {
|
|
$heure_fin = '10:00'; // Fallback
|
|
}
|
|
}
|
|
|
|
// Vérifier les conflits d'événements pour ce local
|
|
try {
|
|
$conflits = self::verifier_conflits_local($local_post->ID, $date, $date_fin, $heure, $heure_fin, $event_id);
|
|
if (!empty($conflits) && is_array($conflits)) {
|
|
$disponible = false;
|
|
}
|
|
} catch (\Exception $e) {
|
|
error_log('[CRVI] Erreur lors de la vérification des conflits pour le local ' . $local_post->ID . ': ' . $e->getMessage());
|
|
// En cas d'erreur, considérer le local comme disponible pour ne pas bloquer l'utilisateur
|
|
$disponible = true;
|
|
}
|
|
}
|
|
|
|
// Filtrer par type de RDV si spécifié
|
|
// Si le type_local est vide, on considère le local comme compatible avec tous les types
|
|
$type_compatible = true;
|
|
if ($type_rdv && in_array($type_rdv, ['individuel', 'groupe']) && !empty($type_local)) {
|
|
if ($type_rdv === 'individuel' && $type_local !== 'individuel') {
|
|
$type_compatible = false;
|
|
} elseif ($type_rdv === 'groupe' && $type_local !== 'groupe') {
|
|
$type_compatible = false;
|
|
}
|
|
}
|
|
|
|
// Ajouter le local seulement s'il est disponible et compatible
|
|
if ($disponible && $type_compatible) {
|
|
$locaux[] = [
|
|
'id' => $local_post->ID,
|
|
'nom' => $nom_local,
|
|
'type_de_local' => $type_local,
|
|
'capacite' => $capacite ? $capacite : '',
|
|
];
|
|
}
|
|
}
|
|
|
|
// Récupérer tous les bénéficiaires
|
|
$beneficiaires = [];
|
|
try {
|
|
$beneficiaire_model = new \ESI_CRVI_AGENDA\models\CRVI_Beneficiaire_Model();
|
|
$beneficiaires_objects = $beneficiaire_model->get_all_beneficiaires();
|
|
if (is_array($beneficiaires_objects)) {
|
|
foreach ($beneficiaires_objects as $beneficiaire) {
|
|
// Vérifier que le bénéficiaire est un objet valide
|
|
if (!is_object($beneficiaire) || !isset($beneficiaire->id)) {
|
|
continue;
|
|
}
|
|
$beneficiaires[] = [
|
|
'id' => $beneficiaire->id,
|
|
'nom' => ($beneficiaire->nom ?? '') . ' ' . ($beneficiaire->prenom ?? ''),
|
|
];
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
error_log('[CRVI] Erreur lors de la récupération des bénéficiaires: ' . $e->getMessage());
|
|
$beneficiaires = [];
|
|
}
|
|
|
|
// Récupérer toutes les langues disponibles depuis la taxonomie
|
|
$langues_terms = get_terms([
|
|
'taxonomy' => 'langue',
|
|
'hide_empty' => false,
|
|
'orderby' => 'name',
|
|
'order' => 'ASC',
|
|
]);
|
|
|
|
$langues = [];
|
|
if (!is_wp_error($langues_terms) && !empty($langues_terms)) {
|
|
foreach ($langues_terms as $term) {
|
|
// Ignorer les termes invalides (slug vide ou seulement numérique suspect)
|
|
if (empty($term->slug) || empty($term->name)) {
|
|
continue;
|
|
}
|
|
|
|
// Utiliser le slug comme ID (standard WordPress)
|
|
// Si le slug est numérique, c'est peut-être un terme mal configuré, mais on le garde
|
|
$langues[] = [
|
|
'id' => $term->slug,
|
|
'nom' => $term->name,
|
|
];
|
|
}
|
|
}
|
|
|
|
// Récupérer tous les départements actifs
|
|
$departements = CRVI_Departement_Model::all(true, true);
|
|
|
|
// Récupérer tous les types d'intervention disponibles depuis les CPT
|
|
$types_intervention_posts = get_posts([
|
|
'post_type' => 'type_intervention',
|
|
'numberposts' => -1,
|
|
'post_status' => 'publish',
|
|
]);
|
|
|
|
$types_intervention = [];
|
|
foreach ($types_intervention_posts as $post) {
|
|
$types_intervention[] = [
|
|
'id' => $post->ID,
|
|
'nom' => $post->post_title,
|
|
];
|
|
}
|
|
|
|
$response = [
|
|
'intervenants' => $intervenants_formatted,
|
|
'locaux' => $locaux,
|
|
'beneficiaires' => $beneficiaires,
|
|
'langues' => $langues,
|
|
'departements' => $departements,
|
|
'types_intervention' => $types_intervention,
|
|
];
|
|
|
|
// Inclure les traducteurs seulement si la vérification a été faite
|
|
if ($traducteurs !== null) {
|
|
$response['traducteurs'] = $traducteurs_formatted;
|
|
}
|
|
|
|
return Api_Helper::json_success($response);
|
|
}
|
|
|
|
/**
|
|
* Endpoint pour récupérer les permissions de l'utilisateur courant.
|
|
* @param WP_REST_Request $request
|
|
* @return WP_REST_Response
|
|
*/
|
|
public static function get_user_permissions($request) {
|
|
$permissions = \ESI_CRVI_AGENDA\helpers\Api_Helper::get_user_permissions();
|
|
return Api_Helper::json_success($permissions);
|
|
}
|
|
|
|
public static function conflits_item($request) {
|
|
$id = (int) $request['id'];
|
|
$date = $request->get_param('date');
|
|
$heure = $request->get_param('heure');
|
|
$id_intervenant = $request->get_param('id_intervenant');
|
|
$id_traducteur = $request->get_param('id_traducteur');
|
|
$id_local = $request->get_param('id_local');
|
|
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->get_conflits($date, $heure, $id_intervenant, $id_traducteur, $id_local);
|
|
return Api_Helper::json_success($result);
|
|
}
|
|
|
|
public static function export_items($request) {
|
|
$model = new CRVI_Event_Model();
|
|
$params = $request->get_params();
|
|
|
|
// Support de la pagination pour l'export
|
|
$date_debut = $params['start'] ?? $params['date_debut'] ?? null;
|
|
$date_fin = $params['end'] ?? $params['date_fin'] ?? null;
|
|
|
|
// Filtrer les paramètres pour éviter les conflits
|
|
$filters = array_diff_key($params, array_flip(['start', 'end', 'date_debut', 'date_fin']));
|
|
|
|
$result = $model->get_events_by('date_rdv', null, $filters, $date_debut, $date_fin);
|
|
return Api_Helper::json_success($result);
|
|
}
|
|
|
|
// --- CRUD ---
|
|
public static function get_events($request) {
|
|
$params = $request->get_params();
|
|
$model = new CRVI_Event_Model();
|
|
$events = $model->get_events_by_filters($params);
|
|
|
|
// Convertir les langues, départements et types d'intervention (IDs vers noms)
|
|
foreach ($events as &$event) {
|
|
// Convertir la langue - ajouter langue_label pour l'affichage
|
|
if (!empty($event['langue'])) {
|
|
$langue_original = $event['langue'];
|
|
$langue_label = '';
|
|
|
|
// Essayer d'abord par ID numérique
|
|
$langue_term = get_term((int)$langue_original, 'langue');
|
|
if ($langue_term && !is_wp_error($langue_term)) {
|
|
$langue_label = $langue_term->name;
|
|
} else {
|
|
// Essayer par slug si l'ID ne fonctionne pas
|
|
$langue_term = get_term_by('slug', $langue_original, 'langue');
|
|
if ($langue_term && !is_wp_error($langue_term)) {
|
|
$langue_label = $langue_term->name;
|
|
} else {
|
|
// Debug: comprendre pourquoi on ne trouve pas le terme
|
|
error_log('CRVI Debug - Langue non trouvée: ID=' . $langue_original);
|
|
if (is_wp_error($langue_term)) {
|
|
error_log('CRVI Debug - Erreur WP: ' . $langue_term->get_error_message());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ajouter le label de la langue pour l'affichage dans la modal (seulement si on a trouvé le nom)
|
|
if (!empty($langue_label)) {
|
|
$event['langue_label'] = $langue_label;
|
|
} else {
|
|
// Ne pas assigner l'ID comme fallback - laisser le champ vide
|
|
$event['langue_label'] = '';
|
|
}
|
|
}
|
|
|
|
// Convertir le département - ajouter departement_label pour l'affichage
|
|
if (!empty($event['id_departement']) && $event['id_departement'] != '0' && $event['id_departement'] != 0) {
|
|
$departement_post = get_post((int)$event['id_departement']);
|
|
if ($departement_post) {
|
|
$event['departement_nom'] = $departement_post->post_title;
|
|
$event['departement_label'] = $departement_post->post_title;
|
|
}
|
|
}
|
|
|
|
// Convertir le type d'intervention - ajouter type_intervention_label pour l'affichage
|
|
if (!empty($event['id_type_intervention']) && $event['id_type_intervention'] != '0' && $event['id_type_intervention'] != 0) {
|
|
$type_post = get_post((int)$event['id_type_intervention']);
|
|
if ($type_post) {
|
|
$event['type_intervention_nom'] = $type_post->post_title;
|
|
$event['type_intervention_label'] = $type_post->post_title;
|
|
}
|
|
}
|
|
|
|
// Ajouter nom_traducteur si id_traducteur est 0 ou null
|
|
if (empty($event['id_traducteur']) || $event['id_traducteur'] == '0' || $event['id_traducteur'] == 0) {
|
|
if (!empty($event['nom_traducteur'])) {
|
|
// nom_traducteur est déjà dans les données de l'événement
|
|
}
|
|
}
|
|
}
|
|
|
|
return Api_Helper::json_success($events);
|
|
}
|
|
public static function get_event($request) {
|
|
$id = (int) $request['id'];
|
|
$model = new CRVI_Event_Model();
|
|
$event = $model->get_event_enriched($id);
|
|
if (!$event) {
|
|
return Api_Helper::json_error('Événement introuvable', 404);
|
|
}
|
|
|
|
// Convertir la langue - ajouter langue_label pour l'affichage
|
|
if (!empty($event['langue'])) {
|
|
$langue_original = $event['langue'];
|
|
$langue_label = '';
|
|
|
|
// Essayer d'abord par ID numérique
|
|
$langue_term = get_term((int)$langue_original, 'langue');
|
|
if ($langue_term && !is_wp_error($langue_term)) {
|
|
$langue_label = $langue_term->name;
|
|
} else {
|
|
// Essayer par slug si l'ID ne fonctionne pas
|
|
$langue_term = get_term_by('slug', $langue_original, 'langue');
|
|
if ($langue_term && !is_wp_error($langue_term)) {
|
|
$langue_label = $langue_term->name;
|
|
} else {
|
|
// Debug: comprendre pourquoi on ne trouve pas le terme
|
|
error_log('CRVI Debug get_event - Langue non trouvée: ID=' . $langue_original);
|
|
if (is_wp_error($langue_term)) {
|
|
error_log('CRVI Debug get_event - Erreur WP: ' . $langue_term->get_error_message());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ajouter le label de la langue pour l'affichage dans la modal (seulement si on a trouvé le nom)
|
|
if (!empty($langue_label)) {
|
|
$event['langue_label'] = $langue_label;
|
|
} else {
|
|
// Ne pas assigner l'ID comme fallback - laisser le champ vide
|
|
$event['langue_label'] = '';
|
|
}
|
|
}
|
|
|
|
// Convertir le département
|
|
if (!empty($event['id_departement'])) {
|
|
$departement_post = get_post((int)$event['id_departement']);
|
|
if ($departement_post) {
|
|
$event['departement_nom'] = $departement_post->post_title;
|
|
}
|
|
}
|
|
|
|
// Convertir le type d'intervention
|
|
if (!empty($event['id_type_intervention'])) {
|
|
$type_post = get_post((int)$event['id_type_intervention']);
|
|
if ($type_post) {
|
|
$event['type_intervention_nom'] = $type_post->post_title;
|
|
}
|
|
}
|
|
|
|
return Api_Helper::json_success($event);
|
|
}
|
|
public static function create_event($request) {
|
|
$data = $request->get_json_params();
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->create_event($data);
|
|
if (is_wp_error($result)) {
|
|
return Api_Helper::json_error($result->get_error_message(), 400);
|
|
}
|
|
return Api_Helper::json_success(['id' => $result, 'message' => 'Événement créé avec succès']);
|
|
}
|
|
public static function update_event($request) {
|
|
$id = (int) $request['id'];
|
|
$data = $request->get_json_params();
|
|
|
|
// Vérifier que les données JSON sont présentes
|
|
if ($data === null) {
|
|
// Essayer de récupérer depuis le body brut
|
|
$body = $request->get_body();
|
|
if (!empty($body)) {
|
|
$data = json_decode($body, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
return Api_Helper::json_error('Données JSON invalides: ' . json_last_error_msg(), 400);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si toujours null, utiliser un tableau vide
|
|
if ($data === null) {
|
|
$data = [];
|
|
}
|
|
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->update_event($id, $data);
|
|
if (is_wp_error($result)) {
|
|
$error_code = $result->get_error_code();
|
|
$error_data = $result->get_error_data();
|
|
$status_code = isset($error_data['status']) ? $error_data['status'] : 400;
|
|
return Api_Helper::json_error($result->get_error_message(), $status_code);
|
|
}
|
|
return Api_Helper::json_success(['id' => $id, 'message' => 'Événement modifié avec succès']);
|
|
}
|
|
public static function delete_event($request) {
|
|
$id = (int) $request['id'];
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->delete_event($id);
|
|
if (is_wp_error($result)) {
|
|
return Api_Helper::json_error($result->get_error_message(), 400);
|
|
}
|
|
return Api_Helper::json_success(['id' => $id, 'message' => 'Événement supprimé avec succès']);
|
|
}
|
|
|
|
/**
|
|
* Crée des permanences pour l'intervenant connecté
|
|
* POST /wp-json/crvi/v1/intervenant/permanences
|
|
*
|
|
* Cette méthode crée des événements de type "permanence" pour l'intervenant connecté.
|
|
* Les permanences sont des créneaux horaires disponibles sans bénéficiaire assigné.
|
|
*/
|
|
public static function create_permanences($request) {
|
|
$data = $request->get_json_params();
|
|
$user_id = get_current_user_id();
|
|
$intervenant = CRVI_Intervenant_Model::load($user_id);
|
|
|
|
if (!$intervenant) {
|
|
return Api_Helper::json_error('Intervenant introuvable', 404);
|
|
}
|
|
|
|
// Validation des paramètres
|
|
$periode = isset($data['periode']) ? (int)$data['periode'] : 0;
|
|
$mois_debut = isset($data['mois_debut']) ? trim($data['mois_debut']) : '';
|
|
$jours = isset($data['jours']) && is_array($data['jours']) ? $data['jours'] : [];
|
|
$plage_horaire = isset($data['plage_horaire']) && is_array($data['plage_horaire']) ? $data['plage_horaire'] : [];
|
|
$duree_permanence = isset($data['duree_permanence']) ? sanitize_text_field($data['duree_permanence']) : '1h';
|
|
$nb_tranches = isset($data['nb_tranches']) && $duree_permanence === '15min' ? (int)$data['nb_tranches'] : null;
|
|
$langues = isset($data['langues']) && is_array($data['langues']) ? $data['langues'] : [];
|
|
$informations_complementaires = isset($data['informations_complementaires']) ? sanitize_textarea_field($data['informations_complementaires']) : '';
|
|
|
|
// Validation de la durée et du nombre de tranches
|
|
if (!in_array($duree_permanence, ['1h', '15min'], true)) {
|
|
return Api_Helper::json_error('Durée de permanence invalide (attendu: 1h ou 15min)', 400);
|
|
}
|
|
|
|
if ($duree_permanence === '15min') {
|
|
if ($nb_tranches === null || $nb_tranches < 1 || $nb_tranches > 4) {
|
|
return Api_Helper::json_error('Le nombre de tranches doit être entre 1 et 4 pour les permanences de 15 minutes', 400);
|
|
}
|
|
}
|
|
|
|
// Nettoyer et valider les langues (slugs de la taxonomie)
|
|
$langues_valides = [];
|
|
if (!empty($langues)) {
|
|
foreach ($langues as $langue_slug) {
|
|
$langue_slug = sanitize_text_field($langue_slug);
|
|
// Vérifier que la langue existe dans la taxonomie
|
|
$term = get_term_by('slug', $langue_slug, 'langue');
|
|
if ($term && !is_wp_error($term)) {
|
|
$langues_valides[] = $langue_slug;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validation
|
|
if (!in_array($periode, [3, 6], true)) {
|
|
return Api_Helper::json_error('La période doit être de 3 ou 6 mois', 400);
|
|
}
|
|
|
|
if (empty($mois_debut)) {
|
|
return Api_Helper::json_error('Veuillez sélectionner un mois de début', 400);
|
|
}
|
|
|
|
// Validation du format du mois (YYYY-MM)
|
|
if (!preg_match('/^\d{4}-\d{2}$/', $mois_debut)) {
|
|
return Api_Helper::json_error('Format de mois invalide (attendu: YYYY-MM)', 400);
|
|
}
|
|
|
|
if (empty($jours)) {
|
|
return Api_Helper::json_error('Veuillez sélectionner au moins un jour de la semaine', 400);
|
|
}
|
|
|
|
// Récupérer les heures sélectionnées
|
|
$heures_selectionnees = isset($data['heures']) && is_array($data['heures']) ? $data['heures'] : [];
|
|
|
|
if (empty($heures_selectionnees)) {
|
|
return Api_Helper::json_error('Veuillez sélectionner au moins une heure de permanence', 400);
|
|
}
|
|
|
|
// Validation du format des heures (HH:mm)
|
|
foreach ($heures_selectionnees as $heure) {
|
|
if (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $heure)) {
|
|
return Api_Helper::json_error('Format d\'heure invalide (attendu: HH:mm)', 400);
|
|
}
|
|
}
|
|
|
|
// Calculer la plage de dates à partir du mois de début sélectionné
|
|
list($year, $month) = explode('-', $mois_debut);
|
|
$date_debut = new \DateTime("{$year}-{$month}-01"); // Premier jour du mois
|
|
$date_fin = clone $date_debut;
|
|
$date_fin->modify("+{$periode} months");
|
|
$date_fin->modify('-1 day'); // Dernier jour du dernier mois
|
|
|
|
// Générer les tranches horaires selon la durée choisie
|
|
$tranches_horaires = [];
|
|
|
|
if ($duree_permanence === '1h') {
|
|
// Mode 1 heure : créer une tranche d'1 heure pour chaque heure sélectionnée
|
|
foreach ($heures_selectionnees as $heure_debut) {
|
|
list($h, $m) = explode(':', $heure_debut);
|
|
$h = (int)$h;
|
|
$m = (int)$m;
|
|
$heure_fin = sprintf('%02d:%02d', ($h + 1) % 24, $m);
|
|
|
|
$tranches_horaires[] = [
|
|
'debut' => $heure_debut,
|
|
'fin' => $heure_fin,
|
|
];
|
|
}
|
|
} else {
|
|
// Mode 15 minutes : créer X tranches de 15 minutes pour chaque heure sélectionnée
|
|
foreach ($heures_selectionnees as $heure_debut) {
|
|
list($h, $m) = explode(':', $heure_debut);
|
|
$h = (int)$h;
|
|
$m = (int)$m;
|
|
|
|
// Créer le nombre de tranches demandé (1 à 4)
|
|
for ($i = 0; $i < $nb_tranches; $i++) {
|
|
$debut_minutes = $m + ($i * 15);
|
|
$fin_minutes = $debut_minutes + 15;
|
|
|
|
$tranche_h = $h + floor($debut_minutes / 60);
|
|
$tranche_m = $debut_minutes % 60;
|
|
$tranche_fin_h = $h + floor($fin_minutes / 60);
|
|
$tranche_fin_m = $fin_minutes % 60;
|
|
|
|
$tranche_debut = sprintf('%02d:%02d', $tranche_h % 24, $tranche_m);
|
|
$tranche_fin = sprintf('%02d:%02d', $tranche_fin_h % 24, $tranche_fin_m);
|
|
|
|
$tranches_horaires[] = [
|
|
'debut' => $tranche_debut,
|
|
'fin' => $tranche_fin,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mapping des jours de la semaine (français vers numéro)
|
|
$jours_mapping = [
|
|
'lundi' => 1,
|
|
'mardi' => 2,
|
|
'mercredi' => 3,
|
|
'jeudi' => 4,
|
|
'vendredi' => 5,
|
|
'samedi' => 6,
|
|
'dimanche' => 0,
|
|
];
|
|
|
|
$jours_numeriques = [];
|
|
foreach ($jours as $jour) {
|
|
$jour_lower = strtolower($jour);
|
|
if (isset($jours_mapping[$jour_lower])) {
|
|
$jours_numeriques[] = $jours_mapping[$jour_lower];
|
|
}
|
|
}
|
|
|
|
if (empty($jours_numeriques)) {
|
|
return Api_Helper::json_error('Jours de la semaine invalides', 400);
|
|
}
|
|
|
|
// Créer les événements pour chaque jour sélectionné dans la période
|
|
$permanences_crees = 0;
|
|
$current_date = clone $date_debut;
|
|
|
|
while ($current_date <= $date_fin) {
|
|
$jour_semaine = (int)$current_date->format('w'); // 0 = dimanche, 1 = lundi, etc.
|
|
|
|
// Ignorer les dates passées
|
|
$today = new \DateTime();
|
|
if ($current_date < $today) {
|
|
$current_date->modify('+1 day');
|
|
continue;
|
|
}
|
|
|
|
// Si ce jour de la semaine est sélectionné
|
|
if (in_array($jour_semaine, $jours_numeriques, true)) {
|
|
// Créer une permanence pour chaque tranche horaire
|
|
foreach ($tranches_horaires as $tranche) {
|
|
// Utiliser la méthode dédiée du modèle pour créer une permanence
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->create_permanence([
|
|
'date_rdv' => $current_date->format('Y-m-d'),
|
|
'heure_rdv' => $tranche['debut'],
|
|
'date_fin' => $current_date->format('Y-m-d'),
|
|
'heure_fin' => $tranche['fin'],
|
|
'id_intervenant' => $intervenant->id,
|
|
'commentaire' => !empty($informations_complementaires) ? $informations_complementaires : '',
|
|
'langues' => $langues_valides, // Langues sélectionnées (tableau de slugs)
|
|
// Passer les jours et heures sélectionnés dans le formulaire
|
|
'jours_permis' => $jours, // Jours sélectionnés (lundi, mardi, etc.)
|
|
'heures_selectionnees' => $heures_selectionnees, // Heures sélectionnées
|
|
]);
|
|
|
|
if (!is_wp_error($result)) {
|
|
$permanences_crees++;
|
|
}
|
|
}
|
|
}
|
|
|
|
$current_date->modify('+1 day');
|
|
}
|
|
|
|
if ($permanences_crees === 0) {
|
|
return Api_Helper::json_error('Aucune permanence n\'a pu être créée', 500);
|
|
}
|
|
|
|
return Api_Helper::json_success([
|
|
'message' => "Permanences enregistrées avec succès",
|
|
'permanences_crees' => $permanences_crees,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Crée des permanences pour un intervenant (version admin)
|
|
* POST /wp-json/crvi/v1/admin/permanences
|
|
*
|
|
* Différence avec create_permanences : accepte un intervenant_id dans les paramètres
|
|
*/
|
|
public static function create_permanences_admin($request) {
|
|
$data = $request->get_json_params();
|
|
|
|
// Récupérer l'ID de l'intervenant depuis les paramètres (au lieu de get_current_user_id())
|
|
$intervenant_user_id = isset($data['intervenant_id']) ? (int)$data['intervenant_id'] : 0;
|
|
|
|
if (!$intervenant_user_id) {
|
|
return Api_Helper::json_error('Veuillez sélectionner un intervenant', 400);
|
|
}
|
|
|
|
$intervenant = CRVI_Intervenant_Model::load($intervenant_user_id);
|
|
|
|
if (!$intervenant) {
|
|
return Api_Helper::json_error('Intervenant introuvable', 404);
|
|
}
|
|
|
|
// Validation des paramètres (identique à create_permanences)
|
|
$periode = isset($data['periode']) ? (int)$data['periode'] : 0;
|
|
$mois_debut = isset($data['mois_debut']) ? trim($data['mois_debut']) : '';
|
|
$jours = isset($data['jours']) && is_array($data['jours']) ? $data['jours'] : [];
|
|
$duree_permanence = isset($data['duree_permanence']) ? sanitize_text_field($data['duree_permanence']) : '1h';
|
|
$nb_tranches = isset($data['nb_tranches']) && $duree_permanence === '15min' ? (int)$data['nb_tranches'] : null;
|
|
$langues = isset($data['langues']) && is_array($data['langues']) ? $data['langues'] : [];
|
|
$informations_complementaires = isset($data['informations_complementaires']) ? sanitize_textarea_field($data['informations_complementaires']) : '';
|
|
|
|
// Validation de la durée et du nombre de tranches
|
|
if (!in_array($duree_permanence, ['1h', '15min'], true)) {
|
|
return Api_Helper::json_error('Durée de permanence invalide (attendu: 1h ou 15min)', 400);
|
|
}
|
|
|
|
if ($duree_permanence === '15min') {
|
|
if ($nb_tranches === null || $nb_tranches < 1 || $nb_tranches > 4) {
|
|
return Api_Helper::json_error('Le nombre de tranches doit être entre 1 et 4 pour les permanences de 15 minutes', 400);
|
|
}
|
|
}
|
|
|
|
// Nettoyer et valider les langues (slugs de la taxonomie)
|
|
$langues_valides = [];
|
|
if (!empty($langues)) {
|
|
foreach ($langues as $langue_slug) {
|
|
$langue_slug = sanitize_text_field($langue_slug);
|
|
// Vérifier que la langue existe dans la taxonomie
|
|
$term = get_term_by('slug', $langue_slug, 'langue');
|
|
if ($term && !is_wp_error($term)) {
|
|
$langues_valides[] = $langue_slug;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validation
|
|
if (!in_array($periode, [3, 6], true)) {
|
|
return Api_Helper::json_error('La période doit être de 3 ou 6 mois', 400);
|
|
}
|
|
|
|
if (empty($mois_debut)) {
|
|
return Api_Helper::json_error('Veuillez sélectionner un mois de début', 400);
|
|
}
|
|
|
|
// Validation du format du mois (YYYY-MM)
|
|
if (!preg_match('/^\d{4}-\d{2}$/', $mois_debut)) {
|
|
return Api_Helper::json_error('Format de mois invalide (attendu: YYYY-MM)', 400);
|
|
}
|
|
|
|
if (empty($jours)) {
|
|
return Api_Helper::json_error('Veuillez sélectionner au moins un jour de la semaine', 400);
|
|
}
|
|
|
|
// Récupérer les heures sélectionnées
|
|
$heures_selectionnees = isset($data['heures']) && is_array($data['heures']) ? $data['heures'] : [];
|
|
|
|
if (empty($heures_selectionnees)) {
|
|
return Api_Helper::json_error('Veuillez sélectionner au moins une heure de permanence', 400);
|
|
}
|
|
|
|
// Validation du format des heures (HH:mm)
|
|
foreach ($heures_selectionnees as $heure) {
|
|
if (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $heure)) {
|
|
return Api_Helper::json_error('Format d\'heure invalide (attendu: HH:mm)', 400);
|
|
}
|
|
}
|
|
|
|
// Calculer la plage de dates à partir du mois de début sélectionné
|
|
list($year, $month) = explode('-', $mois_debut);
|
|
$date_debut = new \DateTime("{$year}-{$month}-01"); // Premier jour du mois
|
|
$date_fin = clone $date_debut;
|
|
$date_fin->modify("+{$periode} months");
|
|
$date_fin->modify('-1 day'); // Dernier jour du dernier mois
|
|
|
|
// Générer les tranches horaires selon la durée choisie
|
|
$tranches_horaires = [];
|
|
|
|
if ($duree_permanence === '1h') {
|
|
// Mode 1 heure : créer une tranche d'1 heure pour chaque heure sélectionnée
|
|
foreach ($heures_selectionnees as $heure_debut) {
|
|
list($h, $m) = explode(':', $heure_debut);
|
|
$h = (int)$h;
|
|
$m = (int)$m;
|
|
$heure_fin = sprintf('%02d:%02d', ($h + 1) % 24, $m);
|
|
|
|
$tranches_horaires[] = [
|
|
'debut' => $heure_debut,
|
|
'fin' => $heure_fin,
|
|
];
|
|
}
|
|
} else {
|
|
// Mode 15 minutes : créer X tranches de 15 minutes pour chaque heure sélectionnée
|
|
foreach ($heures_selectionnees as $heure_debut) {
|
|
list($h, $m) = explode(':', $heure_debut);
|
|
$h = (int)$h;
|
|
$m = (int)$m;
|
|
|
|
// Créer le nombre de tranches demandé (1 à 4)
|
|
for ($i = 0; $i < $nb_tranches; $i++) {
|
|
$debut_minutes = $m + ($i * 15);
|
|
$fin_minutes = $debut_minutes + 15;
|
|
|
|
$tranche_h = $h + floor($debut_minutes / 60);
|
|
$tranche_m = $debut_minutes % 60;
|
|
$tranche_fin_h = $h + floor($fin_minutes / 60);
|
|
$tranche_fin_m = $fin_minutes % 60;
|
|
|
|
$tranche_debut = sprintf('%02d:%02d', $tranche_h % 24, $tranche_m);
|
|
$tranche_fin = sprintf('%02d:%02d', $tranche_fin_h % 24, $tranche_fin_m);
|
|
|
|
$tranches_horaires[] = [
|
|
'debut' => $tranche_debut,
|
|
'fin' => $tranche_fin,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mapping des jours de la semaine (français vers numéro)
|
|
$jours_mapping = [
|
|
'lundi' => 1,
|
|
'mardi' => 2,
|
|
'mercredi' => 3,
|
|
'jeudi' => 4,
|
|
'vendredi' => 5,
|
|
'samedi' => 6,
|
|
'dimanche' => 0,
|
|
];
|
|
|
|
$jours_numeriques = [];
|
|
foreach ($jours as $jour) {
|
|
$jour_lower = strtolower($jour);
|
|
if (isset($jours_mapping[$jour_lower])) {
|
|
$jours_numeriques[] = $jours_mapping[$jour_lower];
|
|
}
|
|
}
|
|
|
|
if (empty($jours_numeriques)) {
|
|
return Api_Helper::json_error('Jours de la semaine invalides', 400);
|
|
}
|
|
|
|
// Créer les événements pour chaque jour sélectionné dans la période
|
|
$permanences_crees = 0;
|
|
$current_date = clone $date_debut;
|
|
|
|
while ($current_date <= $date_fin) {
|
|
$jour_semaine = (int)$current_date->format('w'); // 0 = dimanche, 1 = lundi, etc.
|
|
|
|
// Ignorer les dates passées
|
|
$today = new \DateTime();
|
|
if ($current_date < $today) {
|
|
$current_date->modify('+1 day');
|
|
continue;
|
|
}
|
|
|
|
// Si ce jour de la semaine est sélectionné
|
|
if (in_array($jour_semaine, $jours_numeriques, true)) {
|
|
// Créer une permanence pour chaque tranche horaire
|
|
foreach ($tranches_horaires as $tranche) {
|
|
// Utiliser la méthode dédiée du modèle pour créer une permanence
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->create_permanence([
|
|
'date_rdv' => $current_date->format('Y-m-d'),
|
|
'heure_rdv' => $tranche['debut'],
|
|
'date_fin' => $current_date->format('Y-m-d'),
|
|
'heure_fin' => $tranche['fin'],
|
|
'id_intervenant' => $intervenant->id,
|
|
'commentaire' => !empty($informations_complementaires) ? $informations_complementaires : '',
|
|
'langues' => $langues_valides, // Langues sélectionnées (tableau de slugs)
|
|
// Passer les jours et heures sélectionnés dans le formulaire
|
|
'jours_permis' => $jours, // Jours sélectionnés (lundi, mardi, etc.)
|
|
'heures_selectionnees' => $heures_selectionnees, // Heures sélectionnées
|
|
]);
|
|
|
|
if (!is_wp_error($result)) {
|
|
$permanences_crees++;
|
|
}
|
|
}
|
|
}
|
|
|
|
$current_date->modify('+1 day');
|
|
}
|
|
|
|
if ($permanences_crees === 0) {
|
|
return Api_Helper::json_error('Aucune permanence n\'a pu être créée', 500);
|
|
}
|
|
|
|
return Api_Helper::json_success([
|
|
'message' => "Permanences enregistrées avec succès pour " . $intervenant->nom . ' ' . $intervenant->prenom,
|
|
'permanences_crees' => $permanences_crees,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Import CSV de permanences (admin)
|
|
* POST /wp-json/crvi/v1/admin/permanences/import-csv
|
|
*
|
|
* Format CSV attendu :
|
|
* - intervenant_id : ID de l'intervenant (obligatoire)
|
|
* - date_debut : Date de début (YYYY-MM-DD) (obligatoire)
|
|
* - date_fin : Date de fin (YYYY-MM-DD) (obligatoire)
|
|
* - heure_debut : Heure de début (HH:MM) (obligatoire)
|
|
* - heure_fin : Heure de fin (HH:MM) (obligatoire)
|
|
* - informations_complementaires : Notes (optionnel)
|
|
*/
|
|
public static function import_permanences_csv($request) {
|
|
if (!current_user_can('manage_options')) {
|
|
return Api_Helper::json_error('Non autorisé', 403);
|
|
}
|
|
|
|
$file = $request->get_file_params()['file'] ?? null;
|
|
if (!$file || !is_uploaded_file($file['tmp_name'])) {
|
|
return Api_Helper::json_error('Fichier CSV manquant ou invalide', 400);
|
|
}
|
|
|
|
$handle = fopen($file['tmp_name'], 'r');
|
|
if (!$handle) {
|
|
return Api_Helper::json_error('Impossible d\'ouvrir le fichier', 400);
|
|
}
|
|
|
|
// Lire l'en-tête
|
|
$header = fgetcsv($handle, 0, ',');
|
|
if (!$header) {
|
|
fclose($handle);
|
|
return Api_Helper::json_error('Fichier CSV vide ou invalide', 400);
|
|
}
|
|
|
|
// Normaliser les noms de colonnes (minuscules avec underscores)
|
|
$header = array_map(function($k) {
|
|
return sanitize_title(str_replace(' ', '_', strtolower(trim($k))));
|
|
}, $header);
|
|
|
|
$created = 0;
|
|
$errors = [];
|
|
$row_num = 1;
|
|
|
|
// Lire chaque ligne
|
|
while (($row = fgetcsv($handle, 0, ',')) !== false) {
|
|
$row_num++;
|
|
|
|
// Combiner l'en-tête avec les valeurs
|
|
$data = array_combine($header, array_map('trim', $row));
|
|
|
|
// Validation des champs obligatoires
|
|
$intervenant_id = isset($data['intervenant_id']) ? (int)$data['intervenant_id'] : 0;
|
|
$date_debut = isset($data['date_debut']) ? trim($data['date_debut']) : '';
|
|
$date_fin = isset($data['date_fin']) ? trim($data['date_fin']) : '';
|
|
$heure_debut = isset($data['heure_debut']) ? trim($data['heure_debut']) : '';
|
|
$heure_fin = isset($data['heure_fin']) ? trim($data['heure_fin']) : '';
|
|
$informations_complementaires = isset($data['informations_complementaires']) ? trim($data['informations_complementaires']) : '';
|
|
|
|
// Validation
|
|
if (!$intervenant_id) {
|
|
$errors[] = ['line' => $row_num, 'message' => 'intervenant_id manquant ou invalide'];
|
|
continue;
|
|
}
|
|
|
|
if (empty($date_debut) || empty($date_fin)) {
|
|
$errors[] = ['line' => $row_num, 'message' => 'date_debut ou date_fin manquant'];
|
|
continue;
|
|
}
|
|
|
|
// Validation du format des dates (YYYY-MM-DD)
|
|
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_debut) ||
|
|
!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_fin)) {
|
|
$errors[] = ['line' => $row_num, 'message' => 'Format de date invalide (attendu: YYYY-MM-DD)'];
|
|
continue;
|
|
}
|
|
|
|
// Validation du format des heures (HH:MM)
|
|
if (empty($heure_debut) || empty($heure_fin)) {
|
|
$errors[] = ['line' => $row_num, 'message' => 'heure_debut ou heure_fin manquant'];
|
|
continue;
|
|
}
|
|
|
|
if (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $heure_debut) ||
|
|
!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $heure_fin)) {
|
|
$errors[] = ['line' => $row_num, 'message' => 'Format d\'heure invalide (attendu: HH:MM)'];
|
|
continue;
|
|
}
|
|
|
|
// Vérifier que l'intervenant existe
|
|
$intervenant = \ESI_CRVI_AGENDA\models\CRVI_Intervenant_Model::load($intervenant_id);
|
|
if (!$intervenant) {
|
|
$errors[] = ['line' => $row_num, 'message' => "Intervenant avec l'ID $intervenant_id introuvable"];
|
|
continue;
|
|
}
|
|
|
|
// Créer la permanence pour chaque date entre date_debut et date_fin
|
|
$date_start = new \DateTime($date_debut);
|
|
$date_end = new \DateTime($date_fin);
|
|
$date_end->modify('+1 day'); // Inclure le jour de fin
|
|
|
|
$current_date = clone $date_start;
|
|
$permanences_ligne = 0;
|
|
|
|
while ($current_date < $date_end) {
|
|
$date_str = $current_date->format('Y-m-d');
|
|
|
|
// Utiliser la méthode du modèle pour créer une permanence
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->create_permanence([
|
|
'date_rdv' => $date_str,
|
|
'heure_rdv' => $heure_debut,
|
|
'date_fin' => $date_str,
|
|
'heure_fin' => $heure_fin,
|
|
'id_intervenant' => $intervenant_id,
|
|
'commentaire' => $informations_complementaires,
|
|
]);
|
|
|
|
if (!is_wp_error($result)) {
|
|
$permanences_ligne++;
|
|
$created++;
|
|
} else {
|
|
$errors[] = [
|
|
'line' => $row_num,
|
|
'message' => "Erreur création permanence pour $date_str : " . $result->get_error_message()
|
|
];
|
|
}
|
|
|
|
$current_date->modify('+1 day');
|
|
}
|
|
}
|
|
|
|
fclose($handle);
|
|
|
|
return Api_Helper::json_success([
|
|
'message' => "Import terminé : $created permanence(s) créée(s)",
|
|
'created' => $created,
|
|
'errors' => $errors,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Import CSV de permanences (intervenant)
|
|
* POST /wp-json/crvi/v1/intervenant/permanences/import-csv
|
|
*
|
|
* Format CSV attendu (sans intervenant_id - utilise l'intervenant connecté) :
|
|
* - date_debut : Date de début (YYYY-MM-DD) (obligatoire)
|
|
* - date_fin : Date de fin (YYYY-MM-DD) (obligatoire)
|
|
* - heure_debut : Heure de début (HH:MM) (obligatoire)
|
|
* - heure_fin : Heure de fin (HH:MM) (obligatoire)
|
|
* - informations_complementaires : Notes (optionnel)
|
|
*/
|
|
public static function import_permanences_csv_intervenant($request) {
|
|
// Vérifier les permissions (intervenant connecté)
|
|
if (!\ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller::check_intervenant_permission()) {
|
|
return Api_Helper::json_error('Non autorisé', 403);
|
|
}
|
|
|
|
$user_id = get_current_user_id();
|
|
$intervenant = \ESI_CRVI_AGENDA\models\CRVI_Intervenant_Model::load($user_id);
|
|
|
|
if (!$intervenant) {
|
|
return Api_Helper::json_error('Intervenant introuvable pour cet utilisateur', 404);
|
|
}
|
|
|
|
$file = $request->get_file_params()['file'] ?? null;
|
|
if (!$file || !is_uploaded_file($file['tmp_name'])) {
|
|
return Api_Helper::json_error('Fichier CSV manquant ou invalide', 400);
|
|
}
|
|
|
|
$handle = fopen($file['tmp_name'], 'r');
|
|
if (!$handle) {
|
|
return Api_Helper::json_error('Impossible d\'ouvrir le fichier', 400);
|
|
}
|
|
|
|
// Lire l'en-tête
|
|
$header = fgetcsv($handle, 0, ',');
|
|
if (!$header) {
|
|
fclose($handle);
|
|
return Api_Helper::json_error('Fichier CSV vide ou invalide', 400);
|
|
}
|
|
|
|
// Normaliser les noms de colonnes (minuscules avec underscores)
|
|
$header = array_map(function($k) {
|
|
return sanitize_title(str_replace(' ', '_', strtolower(trim($k))));
|
|
}, $header);
|
|
|
|
$created = 0;
|
|
$errors = [];
|
|
$row_num = 1;
|
|
|
|
// Lire chaque ligne
|
|
while (($row = fgetcsv($handle, 0, ',')) !== false) {
|
|
$row_num++;
|
|
|
|
// Combiner l'en-tête avec les valeurs
|
|
$data = array_combine($header, array_map('trim', $row));
|
|
|
|
// Validation des champs obligatoires
|
|
$date_debut = isset($data['date_debut']) ? trim($data['date_debut']) : '';
|
|
$date_fin = isset($data['date_fin']) ? trim($data['date_fin']) : '';
|
|
$heure_debut = isset($data['heure_debut']) ? trim($data['heure_debut']) : '';
|
|
$heure_fin = isset($data['heure_fin']) ? trim($data['heure_fin']) : '';
|
|
$informations_complementaires = isset($data['informations_complementaires']) ? trim($data['informations_complementaires']) : '';
|
|
|
|
// Validation
|
|
if (empty($date_debut) || empty($date_fin)) {
|
|
$errors[] = ['line' => $row_num, 'message' => 'date_debut ou date_fin manquant'];
|
|
continue;
|
|
}
|
|
|
|
// Validation du format des dates (YYYY-MM-DD)
|
|
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_debut) ||
|
|
!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_fin)) {
|
|
$errors[] = ['line' => $row_num, 'message' => 'Format de date invalide (attendu: YYYY-MM-DD)'];
|
|
continue;
|
|
}
|
|
|
|
// Validation du format des heures (HH:MM)
|
|
if (empty($heure_debut) || empty($heure_fin)) {
|
|
$errors[] = ['line' => $row_num, 'message' => 'heure_debut ou heure_fin manquant'];
|
|
continue;
|
|
}
|
|
|
|
if (!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $heure_debut) ||
|
|
!preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $heure_fin)) {
|
|
$errors[] = ['line' => $row_num, 'message' => 'Format d\'heure invalide (attendu: HH:MM)'];
|
|
continue;
|
|
}
|
|
|
|
// Créer la permanence pour chaque date entre date_debut et date_fin
|
|
$date_start = new \DateTime($date_debut);
|
|
$date_end = new \DateTime($date_fin);
|
|
$date_end->modify('+1 day'); // Inclure le jour de fin
|
|
|
|
$current_date = clone $date_start;
|
|
$permanences_ligne = 0;
|
|
|
|
while ($current_date < $date_end) {
|
|
$date_str = $current_date->format('Y-m-d');
|
|
|
|
// Utiliser la méthode du modèle pour créer une permanence
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->create_permanence([
|
|
'date_rdv' => $date_str,
|
|
'heure_rdv' => $heure_debut,
|
|
'date_fin' => $date_str,
|
|
'heure_fin' => $heure_fin,
|
|
'id_intervenant' => $intervenant->id,
|
|
'commentaire' => $informations_complementaires,
|
|
]);
|
|
|
|
if (!is_wp_error($result)) {
|
|
$permanences_ligne++;
|
|
$created++;
|
|
} else {
|
|
$errors[] = [
|
|
'line' => $row_num,
|
|
'message' => "Erreur création permanence pour $date_str : " . $result->get_error_message()
|
|
];
|
|
}
|
|
|
|
$current_date->modify('+1 day');
|
|
}
|
|
}
|
|
|
|
fclose($handle);
|
|
|
|
return Api_Helper::json_success([
|
|
'message' => "Import terminé : $created permanence(s) créée(s)",
|
|
'created' => $created,
|
|
'errors' => $errors,
|
|
]);
|
|
}
|
|
|
|
public static function get_deleted_events($request) {
|
|
$params = $request->get_params();
|
|
$model = new CRVI_Event_Model();
|
|
$events = $model->get_deleted_events($params);
|
|
return Api_Helper::json_success($events);
|
|
}
|
|
|
|
public static function restore_event($request) {
|
|
$id = (int) $request['id'];
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->restore_event($id);
|
|
if (is_wp_error($result)) {
|
|
return Api_Helper::json_error($result->get_error_message(), 400);
|
|
}
|
|
return Api_Helper::json_success(['id' => $id, 'message' => 'Événement restauré avec succès']);
|
|
}
|
|
|
|
public static function hard_delete_event($request) {
|
|
$id = (int) $request['id'];
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->hard_delete_event($id);
|
|
if (is_wp_error($result)) {
|
|
return Api_Helper::json_error($result->get_error_message(), 400);
|
|
}
|
|
return Api_Helper::json_success(['id' => $id, 'message' => 'Événement supprimé définitivement']);
|
|
}
|
|
|
|
// --- Avancés ---
|
|
public static function cloture_event($request) {
|
|
$id = (int) $request['id'];
|
|
$data = $request->get_json_params();
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->cloture_event($id, $data['statut'] ?? '');
|
|
if (is_wp_error($result)) {
|
|
return Api_Helper::json_error($result->get_error_message(), 400);
|
|
}
|
|
return Api_Helper::json_success(['id' => $id, 'message' => 'Événement clôturé avec succès']);
|
|
}
|
|
public static function change_statut($request) {
|
|
$id = (int) $request['id'];
|
|
$data = $request->get_json_params();
|
|
$model = new CRVI_Event_Model();
|
|
$result = $model->change_statut($id, $data);
|
|
if (is_wp_error($result)) {
|
|
return Api_Helper::json_error($result->get_error_message(), 400);
|
|
}
|
|
return Api_Helper::json_success(['id' => $id, 'message' => 'Statut modifié avec succès']);
|
|
}
|
|
|
|
/**
|
|
* Retourne la couleur selon le statut
|
|
*/
|
|
private static function get_status_color($statut) {
|
|
switch ($statut) {
|
|
case 'prevu':
|
|
return '#28a745'; // Vert
|
|
case 'annule':
|
|
return '#dc3545'; // Rouge
|
|
case 'non_tenu':
|
|
return '#ffc107'; // Jaune
|
|
case 'cloture':
|
|
return '#6c757d'; // Gris
|
|
case 'absence':
|
|
return '#fd7e14'; // Orange
|
|
default:
|
|
return '#007bff'; // Bleu
|
|
}
|
|
}
|
|
|
|
public static function get_events_fullcalendar($request) {
|
|
$model = new CRVI_Event_Model();
|
|
$params = $request->get_params();
|
|
|
|
// Support de la pagination FullCalendar
|
|
$date_debut = $params['start'] ?? $params['date_debut'] ?? null;
|
|
$date_fin = $params['end'] ?? $params['date_fin'] ?? null;
|
|
|
|
// Filtrer les paramètres pour éviter les conflits
|
|
$filters = array_diff_key($params, array_flip(['start', 'end', 'date_debut', 'date_fin']));
|
|
|
|
// Ajouter les dates de début et fin aux filtres si elles existent
|
|
if ($date_debut) {
|
|
$filters['date_debut'] = $date_debut;
|
|
}
|
|
if ($date_fin) {
|
|
$filters['date_fin'] = $date_fin;
|
|
}
|
|
|
|
// Utiliser get_events_by_filters au lieu de get_events_by pour une meilleure compatibilité
|
|
$events = $model->get_events_by_filters($filters);
|
|
|
|
// Formater pour FullCalendar
|
|
$formatted_events = [];
|
|
foreach ($events as $event) {
|
|
// Récupérer les détails des entités liées
|
|
$details = $model->get_details($event['id']);
|
|
|
|
// Couleur basée sur le type d'intervention (ACF champ 'couleur' sur le CPT type_intervention)
|
|
$type_color = '#6c757d';
|
|
if (!empty($event['type_intervention'])) {
|
|
$color_field = function_exists('get_field') ? get_field('couleur', (int)$event['type_intervention']) : null;
|
|
if (!empty($color_field)) {
|
|
$type_color = $color_field;
|
|
}
|
|
}
|
|
|
|
// Convertir la langue pour obtenir le label
|
|
$langue_label = '';
|
|
if (!empty($event['langue'])) {
|
|
$langue_original = $event['langue'];
|
|
// Essayer d'abord par ID numérique
|
|
$langue_term = get_term((int)$langue_original, 'langue');
|
|
if ($langue_term && !is_wp_error($langue_term)) {
|
|
$langue_label = $langue_term->name;
|
|
} else {
|
|
// Essayer par slug si l'ID ne fonctionne pas
|
|
$langue_term = get_term_by('slug', $langue_original, 'langue');
|
|
if ($langue_term && !is_wp_error($langue_term)) {
|
|
$langue_label = $langue_term->name;
|
|
} else {
|
|
// Debug: comprendre pourquoi on ne trouve pas le terme
|
|
error_log('CRVI Debug FullCalendar - Langue non trouvée: ID=' . $langue_original);
|
|
if (is_wp_error($langue_term)) {
|
|
error_log('CRVI Debug FullCalendar - Erreur WP: ' . $langue_term->get_error_message());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convertir le département pour obtenir le label
|
|
$departement_label = '';
|
|
if (!empty($event['id_departement']) && $event['id_departement'] != '0' && $event['id_departement'] != 0) {
|
|
$departement_post = get_post((int)$event['id_departement']);
|
|
if ($departement_post) {
|
|
$departement_label = $departement_post->post_title;
|
|
}
|
|
}
|
|
|
|
// Convertir le type d'intervention pour obtenir le label
|
|
$type_intervention_label = '';
|
|
if (!empty($event['type_intervention']) && $event['type_intervention'] != '0' && $event['type_intervention'] != 0) {
|
|
$type_post = get_post((int)$event['type_intervention']);
|
|
if ($type_post) {
|
|
$type_intervention_label = $type_post->post_title;
|
|
}
|
|
}
|
|
|
|
// Récupérer nom_traducteur si id_traducteur est 0 ou null
|
|
$nom_traducteur = null;
|
|
if (empty($event['id_traducteur']) || $event['id_traducteur'] == '0' || $event['id_traducteur'] == 0) {
|
|
$nom_traducteur = $event['nom_traducteur'] ?? null;
|
|
}
|
|
|
|
$formatted_events[] = [
|
|
'id' => $event['id'],
|
|
'title' => ($details->beneficiaire->nom ?? '') . ' ' . ($details->beneficiaire->prenom ?? '') . ' - ' .
|
|
($details->intervenant->nom ?? '') . ' ' . ($details->intervenant->prenom ?? ''),
|
|
'start' => $event['date_rdv'] . 'T' . $event['heure_rdv'],
|
|
'end' => $event['date_fin'] . 'T' . $event['heure_fin'],
|
|
'backgroundColor' => $type_color,
|
|
'borderColor' => $type_color,
|
|
'textColor' => '#fff',
|
|
'extendedProps' => [
|
|
'type' => $event['type'],
|
|
'statut' => $event['statut'],
|
|
'langue' => $event['langue'],
|
|
'langue_label' => $langue_label,
|
|
'langues_disponibles' => $event['langues_disponibles'] ?? null,
|
|
'assign' => isset($event['assign']) ? (int)$event['assign'] : 0,
|
|
'id_type_intervention' => $event['type_intervention'] ?? null,
|
|
'type_intervention_label' => $type_intervention_label,
|
|
'id_departement' => $event['id_departement'] ?? null,
|
|
'departement_label' => $departement_label,
|
|
'beneficiaire' => $details->beneficiaire ?? null,
|
|
'intervenant' => $details->intervenant ?? null,
|
|
'traducteur' => $details->traducteur ?? null,
|
|
'local' => $details->local ?? null,
|
|
'commentaire' => $event['commentaire'],
|
|
'id_beneficiaire' => $event['id_beneficiaire'],
|
|
'id_intervenant' => $event['id_intervenant'],
|
|
'id_traducteur' => $event['id_traducteur'],
|
|
'nom_traducteur' => $nom_traducteur,
|
|
'id_local' => $event['id_local']
|
|
]
|
|
];
|
|
}
|
|
|
|
return Api_Helper::json_success($formatted_events);
|
|
}
|
|
|
|
/**
|
|
* Endpoint pour récupérer les événements en format tableau avec pagination
|
|
* @param \WP_REST_Request $request
|
|
* @return \WP_REST_Response
|
|
*/
|
|
public static function get_events_table($request) {
|
|
$model = new CRVI_Event_Model();
|
|
$params = $request->get_params();
|
|
|
|
// Pagination
|
|
$page = isset($params['page']) ? max(1, (int)$params['page']) : 1;
|
|
$per_page = isset($params['per_page']) ? max(1, min(100, (int)$params['per_page'])) : 20;
|
|
|
|
// Gestion du filtre par année
|
|
if (!empty($params['annee'])) {
|
|
$annee = (int)$params['annee'];
|
|
$params['date_debut'] = sprintf('%d-01-01', $annee);
|
|
$params['date_fin'] = sprintf('%d-12-31', $annee);
|
|
unset($params['annee']);
|
|
}
|
|
|
|
// Récupérer les événements avec pagination
|
|
$result = $model->get_events_table($params, $page, $per_page);
|
|
|
|
return Api_Helper::json_success($result);
|
|
}
|
|
|
|
public static function get_events_stats($request) {
|
|
$params = $request->get_params();
|
|
$model = new CRVI_Event_Model();
|
|
$stats = $model->get_events_stats($params);
|
|
return Api_Helper::json_success($stats);
|
|
}
|
|
public static function get_event_historique($request) {
|
|
$id = (int) $request['id'];
|
|
$model = new CRVI_Event_Model();
|
|
$historique = $model->get_historique();
|
|
return Api_Helper::json_success($historique);
|
|
}
|
|
|
|
// --- Filtres dynamiques ---
|
|
public static function get_departements($request) {
|
|
$departements = get_terms([
|
|
'taxonomy' => 'departement',
|
|
'hide_empty' => false,
|
|
]);
|
|
$result = [];
|
|
foreach ($departements as $departement) {
|
|
$result[] = [
|
|
'id' => $departement->term_id,
|
|
'name' => $departement->name,
|
|
'slug' => $departement->slug,
|
|
];
|
|
}
|
|
return Api_Helper::json_success($result);
|
|
}
|
|
public static function get_types_intervention($request) {
|
|
$types = get_terms([
|
|
'taxonomy' => 'type_intervention',
|
|
'hide_empty' => false,
|
|
]);
|
|
|
|
$result = [];
|
|
foreach ($types as $type) {
|
|
$result[] = [
|
|
'id' => $type->term_id,
|
|
'name' => $type->name,
|
|
'slug' => $type->slug,
|
|
];
|
|
}
|
|
|
|
return Api_Helper::json_success($result);
|
|
}
|
|
public static function get_langues($request) {
|
|
$langues = get_terms([
|
|
'taxonomy' => 'langue',
|
|
'hide_empty' => false,
|
|
]);
|
|
$result = [];
|
|
foreach ($langues as $langue) {
|
|
$result[] = [
|
|
'id' => $langue->term_id,
|
|
'name' => $langue->name,
|
|
'slug' => $langue->slug,
|
|
];
|
|
}
|
|
return Api_Helper::json_success($result);
|
|
}
|
|
|
|
public static function get_langues_beneficiaire($request) {
|
|
$langues = \ESI_CRVI_AGENDA\helpers\Api_Helper::get_languages(true);
|
|
return \ESI_CRVI_AGENDA\helpers\Api_Helper::json_success($langues);
|
|
}
|
|
public static function get_statuts($request) {
|
|
$statuts = [
|
|
['id' => 'prevu', 'label' => 'Prévu'],
|
|
['id' => 'annule', 'label' => 'Annulé'],
|
|
['id' => 'non_tenu', 'label' => 'Non tenu'],
|
|
['id' => 'cloture', 'label' => 'Clôturé'],
|
|
['id' => 'absence', 'label' => 'Absence'],
|
|
];
|
|
return Api_Helper::json_success($statuts);
|
|
}
|
|
|
|
/**
|
|
* Vérifie si l'utilisateur peut modifier un événement
|
|
* @param \WP_REST_Request $request
|
|
* @return bool
|
|
*/
|
|
public static function can_edit($request = null) {
|
|
// Si admin ou rôle ayant edit_posts : accès total
|
|
if (current_user_can('edit_posts')) {
|
|
return true;
|
|
}
|
|
|
|
// Récupérer l'id_intervenant depuis la requête ou l'événement
|
|
$id_intervenant = null;
|
|
if ($request) {
|
|
// Si on modifie un événement existant, récupérer l'ID depuis l'événement
|
|
if ($request->get_param('id')) {
|
|
$event = \ESI_CRVI_AGENDA\models\CRVI_Event_Model::load((int)$request->get_param('id'));
|
|
if ($event && isset($event->id_intervenant)) {
|
|
$id_intervenant = $event->id_intervenant;
|
|
}
|
|
}
|
|
// Sinon, essayer de le prendre dans les paramètres de la requête (création)
|
|
if (!$id_intervenant) {
|
|
$data = $request->get_json_params();
|
|
if ($data && isset($data['id_intervenant'])) {
|
|
$id_intervenant = $data['id_intervenant'];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Appel à la logique intervenant
|
|
return CRVI_Intervenant_Controller::can_edit_own_event($id_intervenant);
|
|
}
|
|
|
|
/**
|
|
* Fonction pour récupérer les dates et créneaux indisponibles pour une combinaison d'entités
|
|
*/
|
|
private static function get_available_dates($id_intervenant, $id_traducteur, $id_local, $date_debut, $date_fin) {
|
|
$unavailable_dates = [];
|
|
$unavailable_slots = [];
|
|
|
|
// Convertir les dates en objets DateTime
|
|
$debut = \DateTime::createFromFormat('Y-m-d', $date_debut);
|
|
$fin = \DateTime::createFromFormat('Y-m-d', $date_fin);
|
|
|
|
if (!$debut || !$fin) {
|
|
return Api_Helper::json_error('Format de date invalide');
|
|
}
|
|
|
|
// Définir les créneaux horaires possibles (8h-18h par exemple)
|
|
$creneaux_horaires = [
|
|
'08:00', '08:30', '09:00', '09:30', '10:00', '10:30', '11:00', '11:30',
|
|
'12:00', '12:30', '13:00', '13:30', '14:00', '14:30', '15:00', '15:30',
|
|
'16:00', '16:30', '17:00', '17:30', '18:00'
|
|
];
|
|
|
|
// Parcourir chaque jour dans la plage
|
|
$current_date = clone $debut;
|
|
while ($current_date <= $fin) {
|
|
$date_str = $current_date->format('Y-m-d');
|
|
|
|
// Vérifier la disponibilité pour cette date
|
|
$disponible = self::check_availability_for_date($date_str, $id_intervenant, $id_traducteur, $id_local);
|
|
|
|
if (!$disponible) {
|
|
$unavailable_dates[] = $date_str;
|
|
} else {
|
|
// Si la date est disponible, vérifier les créneaux horaires
|
|
foreach ($creneaux_horaires as $heure) {
|
|
$disponible_creneau = self::check_availability_for_slot($date_str, $heure, $id_intervenant, $id_traducteur, $id_local);
|
|
if (!$disponible_creneau) {
|
|
$unavailable_slots[] = $date_str . ' ' . $heure;
|
|
}
|
|
}
|
|
}
|
|
|
|
$current_date->add(new \DateInterval('P1D'));
|
|
}
|
|
|
|
return Api_Helper::json_success([
|
|
'unavailable_dates' => $unavailable_dates,
|
|
'unavailable_slots' => $unavailable_slots
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Vérifie la disponibilité pour une date donnée
|
|
*/
|
|
private static function check_availability_for_date($date, $id_intervenant, $id_traducteur, $id_local) {
|
|
// Vérifier les indisponibilités ponctuelles
|
|
|
|
// Vérifier les indisponibilités ponctuelles de l'intervenant
|
|
$intervenant = \ESI_CRVI_AGENDA\models\CRVI_Intervenant_Model::load($id_intervenant);
|
|
if ($intervenant) {
|
|
$indisponibilites = $intervenant->indisponibilitee_ponctuelle ?? null;
|
|
if (!empty($indisponibilites)) {
|
|
// Pour l'intervenant, le champ n'est pas défini dans le modèle actuel
|
|
// donc on ne traite pas les indisponibilités pour l'instant
|
|
}
|
|
}
|
|
|
|
// Vérifier les indisponibilités ponctuelles du traducteur
|
|
$traducteur = \ESI_CRVI_AGENDA\models\CRVI_Traducteur_Model::load($id_traducteur);
|
|
if ($traducteur) {
|
|
$indisponibilites = $traducteur->indisponibilitee_ponctuelle ?? null;
|
|
if (!empty($indisponibilites) && is_array($indisponibilites)) {
|
|
// Le champ indisponibilitee_ponctuelle est un repeater ACF (array)
|
|
foreach ($indisponibilites as $indisponibilite) {
|
|
if (isset($indisponibilite['date']) && $indisponibilite['date'] === $date) {
|
|
return false; // Indisponible
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vérifier les indisponibilités ponctuelles du local
|
|
$local = \ESI_CRVI_AGENDA\models\CRVI_Local_Model::load($id_local);
|
|
if ($local) {
|
|
$indisponibilites = $local->indisponibilitee_ponctuelle ?? null;
|
|
if (!empty($indisponibilites)) {
|
|
// Pour le local, le champ n'est pas défini dans le modèle actuel
|
|
// donc on ne traite pas les indisponibilités pour l'instant
|
|
}
|
|
}
|
|
|
|
// Vérifier les événements existants pour cette date
|
|
$event_model = new \ESI_CRVI_AGENDA\models\CRVI_Event_Model();
|
|
$events = $event_model->get_events_by_filters([
|
|
'date_rdv' => $date,
|
|
'id_intervenant' => $id_intervenant,
|
|
'id_traducteur' => $id_traducteur,
|
|
'id_local' => $id_local
|
|
]);
|
|
|
|
if (!empty($events)) {
|
|
return false; // Il y a déjà des événements pour cette combinaison
|
|
}
|
|
|
|
return true; // Disponible
|
|
}
|
|
|
|
/**
|
|
* Vérifie s'il y a des conflits d'événements pour un local donné
|
|
* @param int $local_id
|
|
* @param string $date_debut
|
|
* @param string $date_fin
|
|
* @param string $heure_debut
|
|
* @param string $heure_fin
|
|
* @param int|null $event_id - ID de l'événement à exclure (pour l'édition)
|
|
* @return array
|
|
*/
|
|
private static function verifier_conflits_local($local_id, $date_debut, $date_fin, $heure_debut = null, $heure_fin = null, $event_id = null) {
|
|
global $wpdb;
|
|
|
|
try {
|
|
$table_events = $wpdb->prefix . 'crvi_agenda';
|
|
|
|
// Vérifier que le local_id est valide
|
|
if (empty($local_id) || !is_numeric($local_id)) {
|
|
return [];
|
|
}
|
|
|
|
$where_conditions = ['id_local = %d', 'is_deleted = 0'];
|
|
$where_values = [$local_id];
|
|
|
|
// Exclure l'événement en cours d'édition si spécifié
|
|
if ($event_id && is_numeric($event_id)) {
|
|
$where_conditions[] = 'id != %d';
|
|
$where_values[] = $event_id;
|
|
}
|
|
|
|
// Ajouter les conditions de date
|
|
if ($date_debut && $date_fin) {
|
|
$where_conditions[] = '(
|
|
(date_rdv BETWEEN %s AND %s) OR
|
|
(date_fin BETWEEN %s AND %s) OR
|
|
(%s BETWEEN date_rdv AND date_fin) OR
|
|
(%s BETWEEN date_rdv AND date_fin)
|
|
)';
|
|
$where_values = array_merge($where_values, [$date_debut, $date_fin, $date_debut, $date_fin, $date_debut, $date_fin]);
|
|
}
|
|
|
|
// Ajouter les conditions d'heure si fournies
|
|
if ($heure_debut && $heure_fin) {
|
|
$where_conditions[] = '(
|
|
(heure_rdv BETWEEN %s AND %s) OR
|
|
(heure_fin BETWEEN %s AND %s) OR
|
|
(%s BETWEEN heure_rdv AND heure_fin) OR
|
|
(%s BETWEEN heure_rdv AND heure_fin)
|
|
)';
|
|
$where_values = array_merge($where_values, [$heure_debut, $heure_fin, $heure_debut, $heure_fin, $heure_debut, $heure_fin]);
|
|
}
|
|
|
|
$where_clause = implode(' AND ', $where_conditions);
|
|
$query = $wpdb->prepare(
|
|
"SELECT * FROM {$table_events} WHERE {$where_clause}",
|
|
$where_values
|
|
);
|
|
|
|
if ($wpdb->last_error) {
|
|
error_log('[CRVI] Erreur SQL lors de la vérification des conflits de local: ' . $wpdb->last_error);
|
|
return [];
|
|
}
|
|
|
|
$results = $wpdb->get_results($query, ARRAY_A);
|
|
return is_array($results) ? $results : [];
|
|
} catch (\Exception $e) {
|
|
error_log('[CRVI] Exception lors de la vérification des conflits de local: ' . $e->getMessage());
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vérifie la disponibilité pour un créneau horaire spécifique
|
|
*/
|
|
private static function check_availability_for_slot($date, $heure, $id_intervenant, $id_traducteur, $id_local) {
|
|
// Vérifier les indisponibilités ponctuelles avec heure
|
|
|
|
// Vérifier les indisponibilités ponctuelles de l'intervenant
|
|
$intervenant = \ESI_CRVI_AGENDA\models\CRVI_Intervenant_Model::load($id_intervenant);
|
|
if ($intervenant) {
|
|
$indisponibilites = $intervenant->indisponibilitee_ponctuelle ?? null;
|
|
if (!empty($indisponibilites)) {
|
|
// Pour l'intervenant, le champ n'est pas défini dans le modèle actuel
|
|
// donc on ne traite pas les indisponibilités pour l'instant
|
|
}
|
|
}
|
|
|
|
// Vérifier les indisponibilités ponctuelles du traducteur
|
|
$traducteur = \ESI_CRVI_AGENDA\models\CRVI_Traducteur_Model::load($id_traducteur);
|
|
if ($traducteur) {
|
|
$indisponibilites = $traducteur->indisponibilitee_ponctuelle ?? null;
|
|
if (!empty($indisponibilites) && is_array($indisponibilites)) {
|
|
// Le champ indisponibilitee_ponctuelle est un repeater ACF (array)
|
|
foreach ($indisponibilites as $indisponibilite) {
|
|
if (isset($indisponibilite['date']) && $indisponibilite['date'] === $date) {
|
|
// Si pas d'heures spécifiées, toute la journée est indisponible
|
|
if (empty($indisponibilite['heure_debut']) && empty($indisponibilite['heure_fin'])) {
|
|
return false;
|
|
}
|
|
|
|
// Si heures spécifiées, vérifier si le créneau est dans la plage
|
|
if (!empty($indisponibilite['heure_debut']) && !empty($indisponibilite['heure_fin'])) {
|
|
if ($heure >= $indisponibilite['heure_debut'] && $heure < $indisponibilite['heure_fin']) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vérifier les indisponibilités ponctuelles du local
|
|
$local = \ESI_CRVI_AGENDA\models\CRVI_Local_Model::load($id_local);
|
|
if ($local) {
|
|
$indisponibilites = $local->indisponibilitee_ponctuelle ?? null;
|
|
if (!empty($indisponibilites)) {
|
|
// Pour le local, le champ n'est pas défini dans le modèle actuel
|
|
// donc on ne traite pas les indisponibilités pour l'instant
|
|
}
|
|
}
|
|
|
|
// Vérifier les événements existants pour ce créneau
|
|
$event_model = new \ESI_CRVI_AGENDA\models\CRVI_Event_Model();
|
|
$events = $event_model->get_events_by_filters([
|
|
'date_rdv' => $date,
|
|
'heure_rdv' => $heure,
|
|
'id_intervenant' => $id_intervenant,
|
|
'id_traducteur' => $id_traducteur,
|
|
'id_local' => $id_local
|
|
]);
|
|
|
|
if (!empty($events)) {
|
|
return false; // Il y a déjà des événements pour cette combinaison
|
|
}
|
|
|
|
return true; // Disponible
|
|
}
|
|
|
|
/**
|
|
* Récupérer l'historique des 3 derniers rendez-vous d'un bénéficiaire
|
|
* @param \WP_REST_Request $request
|
|
* @return \WP_REST_Response|\WP_Error
|
|
*/
|
|
public static function get_beneficiaire_historique($request) {
|
|
$beneficiaire_id = (int) $request['id'];
|
|
|
|
if (!$beneficiaire_id) {
|
|
return Api_Helper::json_error('ID du bénéficiaire requis', 400);
|
|
}
|
|
|
|
// Vérifier que le bénéficiaire existe
|
|
$beneficiaire = \ESI_CRVI_AGENDA\models\CRVI_Beneficiaire_Model::load($beneficiaire_id);
|
|
if (!$beneficiaire) {
|
|
return Api_Helper::json_error('Bénéficiaire introuvable', 404);
|
|
}
|
|
|
|
// Récupérer les 3 derniers RDV avec leurs incidents
|
|
$historique = CRVI_Event_Model::get_last_3_rdv_by_beneficiaire($beneficiaire_id);
|
|
|
|
return Api_Helper::json_success($historique);
|
|
}
|
|
|
|
/**
|
|
* Récupère les présences d'un événement
|
|
* @param \WP_REST_Request $request
|
|
* @return \WP_REST_Response|\WP_Error
|
|
*/
|
|
public static function get_event_presences($request) {
|
|
$event_id = (int) $request['id'];
|
|
|
|
if (!$event_id) {
|
|
return Api_Helper::json_error('ID d\'événement requis', 400);
|
|
}
|
|
|
|
// Vérifier que l'événement existe
|
|
$event = CRVI_Event_Model::load($event_id);
|
|
if (!$event) {
|
|
return Api_Helper::json_error('Événement introuvable', 404);
|
|
}
|
|
|
|
// Valider le type d'événement
|
|
$event_type = $event->type ?? '';
|
|
$assign = isset($event->assign) ? (int)$event->assign : 0;
|
|
|
|
// Bloquer les rendez-vous individuels
|
|
if ($event_type === 'individuel') {
|
|
return Api_Helper::json_error('Les présences ne sont pas applicables aux rendez-vous individuels', 400);
|
|
}
|
|
|
|
// Bloquer les permanences non attribuées
|
|
if ($event_type === 'permanence' && $assign === 0) {
|
|
return Api_Helper::json_error('Les présences ne sont pas applicables aux permanences non attribuées', 400);
|
|
}
|
|
|
|
// Seuls les rendez-vous de groupe sont acceptés
|
|
if ($event_type !== 'groupe') {
|
|
return Api_Helper::json_error('Cet endpoint est uniquement accessible pour les rendez-vous de groupe', 400);
|
|
}
|
|
|
|
// Récupérer les présences de l'événement
|
|
$presences = CRVI_Presence_Model::get_presences_by_event($event_id);
|
|
|
|
$statut = $event->statut ?? '';
|
|
|
|
$response = [
|
|
'presences' => $presences,
|
|
'event_type' => $event_type,
|
|
'statut' => $statut,
|
|
'has_presence_data' => !empty($presences)
|
|
];
|
|
|
|
return Api_Helper::json_success($response);
|
|
}
|
|
|
|
/**
|
|
* Enregistre les présences pour un rendez-vous de groupe
|
|
* @param \WP_REST_Request $request
|
|
* @return \WP_REST_Response|\WP_Error
|
|
*/
|
|
public static function save_group_presences($request) {
|
|
$event_id = (int) $request['id'];
|
|
|
|
if (!$event_id) {
|
|
return Api_Helper::json_error('ID d\'événement requis', 400);
|
|
}
|
|
|
|
// Vérifier que l'événement existe
|
|
$event = CRVI_Event_Model::load($event_id);
|
|
if (!$event) {
|
|
return Api_Helper::json_error('Événement introuvable', 404);
|
|
}
|
|
|
|
// Valider le type d'événement
|
|
$event_type = $event->type ?? '';
|
|
$assign = isset($event->assign) ? (int)$event->assign : 0;
|
|
|
|
// Bloquer les rendez-vous individuels
|
|
if ($event_type === 'individuel') {
|
|
return Api_Helper::json_error('Les présences ne sont pas applicables aux rendez-vous individuels', 400);
|
|
}
|
|
|
|
// Bloquer les permanences non attribuées
|
|
if ($event_type === 'permanence' && $assign === 0) {
|
|
return Api_Helper::json_error('Les présences ne sont pas applicables aux permanences non attribuées', 400);
|
|
}
|
|
|
|
// Seuls les rendez-vous de groupe sont acceptés
|
|
if ($event_type !== 'groupe') {
|
|
return Api_Helper::json_error('Cet événement n\'est pas un rendez-vous de groupe', 400);
|
|
}
|
|
|
|
// Récupérer les données de présences depuis le body
|
|
$body = $request->get_json_params();
|
|
$presences = $body['presences'] ?? [];
|
|
|
|
if (empty($presences) || !is_array($presences)) {
|
|
return Api_Helper::json_error('Les données de présences sont requises', 400);
|
|
}
|
|
|
|
// Récupérer la langue de l'événement (convertir ID/slug en nom)
|
|
$langue = $event->langue ?? '';
|
|
$langue_nom = '';
|
|
if (!empty($langue)) {
|
|
// Essayer d'abord par ID numérique
|
|
$langue_term = get_term((int)$langue, 'langue');
|
|
if ($langue_term && !is_wp_error($langue_term)) {
|
|
$langue_nom = $langue_term->name;
|
|
} else {
|
|
// Essayer par slug si l'ID ne fonctionne pas
|
|
$langue_term = get_term_by('slug', $langue, 'langue');
|
|
if ($langue_term && !is_wp_error($langue_term)) {
|
|
$langue_nom = $langue_term->name;
|
|
} else {
|
|
// Si ni ID ni slug ne fonctionnent, utiliser la valeur telle quelle
|
|
$langue_nom = $langue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Valider et enregistrer chaque présence
|
|
$saved_count = 0;
|
|
$errors = [];
|
|
|
|
try {
|
|
foreach ($presences as $index => $presence) {
|
|
// Valider les données
|
|
$nom = isset($presence['nom']) ? trim($presence['nom']) : '';
|
|
$prenom = isset($presence['prenom']) ? trim($presence['prenom']) : '';
|
|
$is_present = isset($presence['is_present']) ? (bool) $presence['is_present'] : false;
|
|
|
|
// Ignorer les lignes vides (nom et prénom vides)
|
|
if (empty($nom) && empty($prenom)) {
|
|
continue;
|
|
}
|
|
|
|
// Vérifier que nom et prénom sont remplis
|
|
if (empty($nom) || empty($prenom)) {
|
|
$errors[] = "Ligne " . ($index + 1) . ": Le nom et le prénom sont requis";
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
// Vérifier si un beneficiaire_id est fourni (bénéficiaire existant sélectionné)
|
|
$beneficiaire_id = isset($presence['beneficiaire_id']) && !empty($presence['beneficiaire_id'])
|
|
? (int) $presence['beneficiaire_id']
|
|
: null;
|
|
|
|
if ($beneficiaire_id !== null) {
|
|
// Utiliser l'ID du bénéficiaire existant
|
|
CRVI_Presence_Model::save_presence($event_id, null, $is_present, $beneficiaire_id);
|
|
} else {
|
|
// Créer une nouvelle personne dans wp_crvi_agenda_persons
|
|
$person_id = CRVI_Presence_Model::save_person($nom, $prenom, $langue_nom);
|
|
// Enregistrer la présence avec person_id
|
|
CRVI_Presence_Model::save_presence($event_id, $person_id, $is_present, null);
|
|
}
|
|
$saved_count++;
|
|
} catch (\Exception $e) {
|
|
$errors[] = "Ligne " . ($index + 1) . ": " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
if (!empty($errors) && $saved_count === 0) {
|
|
// Toutes les présences ont échoué
|
|
return Api_Helper::json_error('Erreurs lors de l\'enregistrement: ' . implode(', ', $errors), 400);
|
|
}
|
|
|
|
if (!empty($errors)) {
|
|
// Certaines présences ont échoué mais d'autres ont réussi
|
|
return Api_Helper::json_success([
|
|
'message' => "$saved_count présence(s) enregistrée(s) avec succès",
|
|
'warnings' => $errors,
|
|
'saved_count' => $saved_count
|
|
]);
|
|
}
|
|
|
|
return Api_Helper::json_success([
|
|
'message' => "$saved_count présence(s) enregistrée(s) avec succès",
|
|
'saved_count' => $saved_count
|
|
]);
|
|
|
|
} catch (\Exception $e) {
|
|
return Api_Helper::json_error('Erreur lors de l\'enregistrement des présences: ' . $e->getMessage(), 500);
|
|
}
|
|
}
|
|
} |