import requests import json from datetime import datetime, timedelta from .base_plugin import BasePMSPlugin from asgiref.sync import sync_to_async from pms_integration.models import PMSConfiguration class BnovoPMSPlugin(BasePMSPlugin): """Плагин для работы с PMS Bnovo.""" def __init__(self, config): super().__init__(config) self.api_url = config.url.rstrip("/") # Убираем лишний `/` в конце URL self.username = config.username self.password = config.password self.token = None # SID if not self.api_url: raise ValueError("Не указан URL для работы плагина.") if not self.username or not self.password: raise ValueError("Не указаны логин или пароль для авторизации.") def get_default_parser_settings(self): """Возвращает настройки по умолчанию для обработки данных.""" return { "date_format": "%Y-%m-%dT%H:%M:%S", "timezone": "UTC" } async def _save_token_to_db(self, sid): """Сохраняет токен (SID) в базу данных.""" try: await sync_to_async(PMSConfiguration.objects.update_or_create)( plugin_name="bnovo", defaults={"token": sid} ) print(f"[DEBUG] Токен сохранен в БД: {sid}") except Exception as e: print(f"[ERROR] Ошибка сохранения токена в БД: {e}") def _get_auth_headers(self): """Создает заголовки авторизации.""" headers = { "Content-Type": "application/json", "Accept": "application/json", } if self.token: headers["Cookie"] = f"SID={self.token}" return headers async def _fetch_session(self): """Получает идентификатор сессии (SID) через запрос.""" url = f"{self.api_url}/" payload = { "username": self.username, "password": self.password, } print(f"[DEBUG] URL авторизации: {url}") print(f"[DEBUG] Тело запроса: {json.dumps(payload, indent=2)}") headers = self._get_auth_headers() session = requests.Session() response = session.post(url, json=payload, headers=headers, allow_redirects=False) print(f"[DEBUG] Статус ответа: {response.status_code}") print(f"[DEBUG] Ответ заголовков: {response.headers}") print(f"[DEBUG] Cookies: {session.cookies}") if response.status_code == 302 and "SID" in session.cookies: sid = session.cookies.get("SID") self.token = sid print(f"[DEBUG] Получен SID: {sid}") # Правильное сохранение в БД через sync_to_async try: await self._save_token_to_db(sid) print(f"[DEBUG] Токен сохранен в БД") except Exception as e: print(f"[ERROR] Ошибка сохранения токена в БД: {e}") else: raise ValueError(f"Не удалось получить SID из ответа: {response.text}") async def _fetch_data(self): """Получает данные о бронированиях с помощью эндпоинта `/dashboard`.""" await self._fetch_session() # Авторизуемся перед каждым запросом now = datetime.now() create_from = (now - timedelta(days=90)).strftime("%d.%m.%Y") # Диапазон: последние 90 дней create_to = now.strftime("%d.%m.%Y") params = { "create_from": create_from, "create_to": create_to, "status_ids": "1", "advanced_search": 2, # Обязательный параметр "c": 100, # Количество элементов на странице (максимум 100) "page": 1, # Начальная страница "order_by": "create_date.asc", # Сортировка по возрастанию даты создания } headers = self._get_auth_headers() all_bookings = [] # Для сохранения всех бронирований while True: print(f"[DEBUG] Запрос к /dashboard с параметрами: {json.dumps(params, indent=2)}") response = requests.get(f"{self.api_url}/dashboard", headers=headers, params=params) print(f"[DEBUG] Статус ответа: {response.status_code}") if response.status_code != 200: raise ValueError(f"Ошибка при получении данных: {response.status_code}, {response.text}") data = response.json() print(json.dumps(data, indent=2)) bookings = data.get("bookings", []) all_bookings.extend(bookings) print(f"[DEBUG] Получено бронирований: {len(bookings)}") print(f"[DEBUG] Всего бронирований: {len(all_bookings)}") # Проверка на наличие следующей страницы pages_info = data.get("pages", {}) current_page = pages_info.get("current_page", 1) total_pages = pages_info.get("total_pages", 1) if current_page >= total_pages: break # Все страницы загружены params["page"] += 1 # Переход на следующую страницу if not all_bookings: print("[DEBUG] Нет бронирований за указанный период.") else: print(f"[DEBUG] Полученные бронирования: {json.dumps(all_bookings, indent=2)}") return all_bookings