# -*- coding: utf-8 -*- from odoo import http from odoo.http import request import json class SkeenWhatsAppController(http.Controller): """Controladores API para integración con Hermes/WACRM""" @http.route('/skeen/api/v1/available_slots', type='json', auth='api_key', methods=['POST'], csrf=False) def get_available_slots(self, service_id, date_from, date_to, **kw): """Consultar slots disponibles para un servicio en un rango de fechas""" try: servicio = request.env['skeen.servicio'].browse(int(service_id)) if not servicio.exists(): return {'status': 'error', 'message': 'Servicio no encontrado'} slots = [] from datetime import datetime, timedelta current_date = datetime.strptime(date_from, '%Y-%m-%d').date() end_date = datetime.strptime(date_to, '%Y-%m-%d').date() while current_date <= end_date: day_slots = request.env['skeen.cita'].get_available_slots(current_date, int(service_id)) for slot in day_slots: slots.append({ 'date': current_date.strftime('%Y-%m-%d'), 'time': slot['time_str'], 'datetime': f"{current_date.strftime('%Y-%m-%d')} {slot['time_str']}", }) current_date += timedelta(days=1) return {'status': 'success', 'slots': slots} except Exception as e: return {'status': 'error', 'message': str(e)} @http.route('/skeen/api/v1/create_appointment', type='json', auth='api_key', methods=['POST'], csrf=False) def create_appointment(self, phone, name, service_id, date, time, **kw): """Crear una nueva cita""" try: # Buscar o crear paciente partner = request.env['res.partner'].search([('phone', '=', phone)], limit=1) if not partner: partner = request.env['res.partner'].create({ 'name': name, 'phone': phone, 'is_patient': True, 'source': 'whatsapp', }) # Convertir time a float time_parts = time.split(':') time_float = float(time_parts[0]) + float(time_parts[1]) / 60.0 # Crear cita cita = request.env['skeen.cita'].create({ 'partner_id': partner.id, 'servicio_id': int(service_id), 'date': date, 'time': time_float, 'state': 'confirmed', }) return { 'status': 'success', 'booking_id': cita.id, 'reference': cita.name, 'patient_id': partner.patient_id, } except Exception as e: return {'status': 'error', 'message': str(e)} @http.route('/skeen/api/v1/patient_balance', type='json', auth='api_key', methods=['POST'], csrf=False) def get_patient_balance(self, phone, **kw): """Obtener saldo pendiente y monedero del paciente""" try: partner = request.env['res.partner'].search([('phone', '=', phone)], limit=1) if not partner: return {'status': 'error', 'message': 'Paciente no encontrado'} # Citas pendientes de pago pending_citas = request.env['skeen.cita'].search([ ('partner_id', '=', partner.id), ('payment_state', '!=', 'paid'), ('state', 'in', ('confirmed', 'arrived', 'in_progress', 'done')), ]) pending_balance = sum(c.servicio_price for c in pending_citas) # Monedero monedero = request.env['skeen.monedero'].search([('partner_id', '=', partner.id)], limit=1) return { 'status': 'success', 'pending_balance': pending_balance, 'pending_invoices': [{ 'id': c.id, 'reference': c.name, 'service': c.servicio_id.name, 'amount': c.servicio_price, 'date': c.date.strftime('%Y-%m-%d'), } for c in pending_citas], 'wallet_points': monedero.points if monedero else 0, 'wallet_mxn': monedero.equivalent_mxn if monedero else 0, } except Exception as e: return {'status': 'error', 'message': str(e)} @http.route('/skeen/api/v1/wallet_redeem', type='json', auth='api_key', methods=['POST'], csrf=False) def redeem_wallet(self, phone, points, appointment_id=None, **kw): """Redimir puntos del monedero""" try: partner = request.env['res.partner'].search([('phone', '=', phone)], limit=1) if not partner: return {'status': 'error', 'message': 'Paciente no encontrado'} monedero = request.env['skeen.monedero'].search([('partner_id', '=', partner.id)], limit=1) if not monedero: return {'status': 'error', 'message': 'Monedero no encontrado'} monedero.redeem_points(int(points)) return { 'status': 'success', 'remaining_points': monedero.points, 'remaining_mxn': monedero.equivalent_mxn, } except Exception as e: return {'status': 'error', 'message': str(e)} @http.route('/skeen/api/v1/cancel_appointment', type='json', auth='api_key', methods=['POST'], csrf=False) def cancel_appointment(self, phone, booking_id=None, **kw): """Cancelar una cita""" try: domain = [ ('partner_id.phone', '=', phone), ('state', 'in', ('pending', 'confirmed')), ] if booking_id: domain.append(('id', '=', int(booking_id))) cita = request.env['skeen.cita'].search(domain, order='date asc', limit=1) if not cita: return {'status': 'error', 'message': 'No se encontró cita activa'} cita.write({'state': 'cancelled'}) return { 'status': 'success', 'cancelled_booking': cita.name, } except Exception as e: return {'status': 'error', 'message': str(e)} @http.route('/skeen/api/v1/services', type='json', auth='api_key', methods=['GET', 'POST'], csrf=False) def list_services(self, **kw): """Listar todos los servicios disponibles""" try: services = request.env['skeen.servicio'].search([('active', '=', True)]) return { 'status': 'success', 'services': [{ 'id': s.id, 'code': s.code, 'name': s.name, 'category': s.category, 'price': s.price, 'package_price': s.package_price, 'package_sessions': s.package_sessions, 'package_notes': s.package_notes or '', 'duration_min': s.duration_min, 'description': s.description, } for s in services], } except Exception as e: return {'status': 'error', 'message': str(e)} @http.route('/skeen/api/v1/health', type='json', auth='none', methods=['GET'], csrf=False) def health_check(self, **kw): """Health check para monitoreo""" return {'status': 'ok', 'service': 'skeen-odoo-api', 'version': '1.0.0'}