bemade-addons/portal_planning/controllers/api.py
2025-06-19 11:31:33 -04:00

253 lines
11 KiB
Python

# -*- coding: utf-8 -*-
from odoo import http, _
from odoo.http import request, Response
from odoo.exceptions import AccessError, MissingError, UserError
import json
from datetime import datetime
import logging
_logger = logging.getLogger(__name__)
class PlanningPortalAPI(http.Controller):
"""Contrôleur pour l'API JSON du planning dans le portail."""
def _get_employee_from_user(self):
"""Récupère l'employé correspondant à l'utilisateur connecté."""
return request.env['hr.employee'].sudo().search([('user_id', '=', request.env.user.id)], limit=1)
def _check_slot_access(self, slot_id):
"""Vérifie que l'utilisateur a accès au créneau de planning."""
employee = self._get_employee_from_user()
if not employee:
return False, "Aucun employé associé à cet utilisateur"
slot = request.env['planning.slot'].sudo().browse(slot_id)
if not slot.exists():
return False, "Créneau de planning introuvable"
if slot.employee_id.id != employee.id:
return False, "Vous n'avez pas accès à ce créneau de planning"
return True, slot
@http.route(['/planning/api/slot/update'], type='json', auth="user", methods=['POST'], website=True)
def update_slot(self, **kw):
"""Endpoint pour mettre à jour un créneau de planning."""
slot_id = kw.get('slot_id')
if not slot_id:
return {'success': False, 'error': "ID du créneau manquant"}
access, result = self._check_slot_access(slot_id)
if not access:
return {'success': False, 'error': result}
slot = result
# Récupérer les données de mise à jour
start_datetime = kw.get('start_datetime')
end_datetime = kw.get('end_datetime')
role_id = kw.get('role_id')
notes = kw.get('notes')
# Valider les données
if start_datetime and end_datetime:
try:
start_dt = datetime.fromisoformat(start_datetime.replace('Z', '+00:00'))
end_dt = datetime.fromisoformat(end_datetime.replace('Z', '+00:00'))
if start_dt >= end_dt:
return {'success': False, 'error': "La date de début doit être antérieure à la date de fin"}
except ValueError:
return {'success': False, 'error': "Format de date invalide"}
# Préparer les valeurs pour la modification
vals = {}
if start_datetime:
vals['start_datetime'] = start_datetime
if end_datetime:
vals['end_datetime'] = end_datetime
if role_id:
vals['role_id'] = int(role_id)
try:
# Créer une modification via le portail
modification_vals = {
'slot_id': slot.id,
'user_id': request.env.user.id,
'start_datetime': start_datetime if start_datetime else slot.start_datetime,
'end_datetime': end_datetime if end_datetime else slot.end_datetime,
'role_id': int(role_id) if role_id else slot.role_id.id if slot.role_id else False,
'notes': notes,
'state': 'draft'
}
modification = request.env['portal.planning.modification'].sudo().create(modification_vals)
# Soumettre la modification
if hasattr(modification, 'action_submit'):
modification.action_submit()
return {
'success': True,
'message': "Demande de modification soumise avec succès",
'modification_id': modification.id,
'state': modification.state
}
except Exception as e:
_logger.error("Erreur lors de la mise à jour du créneau: %s", str(e))
return {'success': False, 'error': str(e)}
@http.route(['/planning/api/slot/confirm'], type='json', auth="user", methods=['POST'], website=True)
def confirm_slot(self, **kw):
"""Endpoint pour confirmer un créneau de planning."""
slot_id = kw.get('slot_id')
if not slot_id:
return {'success': False, 'error': "ID du créneau manquant"}
access, result = self._check_slot_access(slot_id)
if not access:
return {'success': False, 'error': result}
slot = result
try:
# Confirmer le créneau
if hasattr(slot, 'action_confirm_portal'):
slot.action_confirm_portal()
return {
'success': True,
'message': "Créneau confirmé avec succès",
'portal_status': slot.portal_status if hasattr(slot, 'portal_status') else 'confirmed'
}
except Exception as e:
_logger.error("Erreur lors de la confirmation du créneau: %s", str(e))
return {'success': False, 'error': str(e)}
@http.route(['/planning/api/slot/exchange/request'], type='json', auth="user", methods=['POST'], website=True)
def request_exchange(self, **kw):
"""Endpoint pour demander un échange de créneau."""
source_slot_id = kw.get('source_slot_id')
target_slot_id = kw.get('target_slot_id')
is_open_request = kw.get('is_open_request', False)
preferred_start = kw.get('preferred_start')
preferred_end = kw.get('preferred_end')
notes = kw.get('notes')
if not source_slot_id:
return {'success': False, 'error': "ID du créneau source manquant"}
access, result = self._check_slot_access(source_slot_id)
if not access:
return {'success': False, 'error': result}
source_slot = result
# Valider les données pour une demande ouverte
if is_open_request and preferred_start and preferred_end:
try:
start_dt = datetime.fromisoformat(preferred_start.replace('Z', '+00:00'))
end_dt = datetime.fromisoformat(preferred_end.replace('Z', '+00:00'))
if start_dt >= end_dt:
return {'success': False, 'error': "La date de début doit être antérieure à la date de fin"}
except ValueError:
return {'success': False, 'error': "Format de date invalide"}
# Valider le créneau cible pour une demande spécifique
if not is_open_request and target_slot_id:
target_slot = request.env['planning.slot'].sudo().browse(target_slot_id)
if not target_slot.exists():
return {'success': False, 'error': "Créneau cible introuvable"}
# Vérifier que le créneau cible n'appartient pas à l'utilisateur
if target_slot.employee_id.user_id.id == request.env.user.id:
return {'success': False, 'error': "Vous ne pouvez pas échanger avec votre propre créneau"}
try:
# Préparer les valeurs pour la demande d'échange
slot_name = source_slot.name if hasattr(source_slot, 'name') and source_slot.name else 'créneau'
slot_date = source_slot.start_datetime.strftime('%d/%m/%Y') if hasattr(source_slot, 'start_datetime') else ''
exchange_vals = {
'name': f"Demande d'échange pour {slot_name} du {slot_date}",
'source_slot_id': source_slot.id,
'source_user_id': request.env.user.id,
'notes': notes,
'state': 'draft',
'is_open_request': is_open_request
}
if is_open_request:
if preferred_start:
exchange_vals['preferred_start'] = preferred_start
if preferred_end:
exchange_vals['preferred_end'] = preferred_end
else:
if target_slot_id:
exchange_vals['target_slot_id'] = target_slot_id
exchange_vals['target_user_id'] = target_slot.employee_id.user_id.id
# Créer la demande d'échange
exchange = request.env['portal.planning.exchange'].sudo().create(exchange_vals)
# Soumettre la demande
exchange.action_submit()
return {
'success': True,
'message': "Demande d'échange soumise avec succès",
'exchange_id': exchange.id,
'state': exchange.state
}
except Exception as e:
_logger.error("Erreur lors de la création de la demande d'échange: %s", str(e))
return {'success': False, 'error': str(e)}
@http.route(['/planning/api/slots'], type='json', auth="user", methods=['POST'], website=True)
def get_slots(self, **kw):
"""Endpoint pour récupérer les créneaux de planning pour le calendrier."""
start_date = kw.get('start_date')
end_date = kw.get('end_date')
if not start_date or not end_date:
return {'success': False, 'error': "Dates de début et de fin requises"}
try:
# Convertir les dates en objets datetime
start_dt = datetime.strptime(start_date, '%Y-%m-%d')
end_dt = datetime.strptime(end_date, '%Y-%m-%d')
end_dt = end_dt.replace(hour=23, minute=59, second=59) # Inclure toute la journée de fin
# Récupérer l'employé correspondant à l'utilisateur connecté
employee = self._get_employee_from_user()
if not employee:
return {'success': False, 'error': "Aucun employé associé à cet utilisateur"}
# Récupérer les créneaux de planning pour l'employé dans la période spécifiée
domain = [
('employee_id', '=', employee.id),
'|',
'&', ('start_datetime', '>=', start_dt), ('start_datetime', '<=', end_dt),
'&', ('end_datetime', '>=', start_dt), ('end_datetime', '<=', end_dt)
]
slots = request.env['planning.slot'].sudo().search_read(domain, [
'id', 'name', 'start_datetime', 'end_datetime', 'employee_id', 'role_id',
'allocated_hours', 'portal_status', 'portal_confirmed', 'portal_modified',
'portal_can_modify'
])
return {
'success': True,
'slots': slots
}
except ValueError as e:
return {'success': False, 'error': "Format de date invalide"}
except Exception as e:
_logger.error("Erreur lors de la récupération des créneaux: %s", str(e))
return {'success': False, 'error': str(e)}