import requests import json from datetime import datetime, timedelta, timezone from asgiref.sync import sync_to_async from hotels.models import Reservation, Hotel from .base_plugin import BasePMSPlugin from pms_integration.models import PMSConfiguration class Shelter(BasePMSPlugin): """ Плагин для интеграции с Shelter PMS. """ def __init__(self, pms_config): super().__init__(pms_config) self.api_url = pms_config.url self.token = pms_config.token self.pagination_count = 50 def get_default_parser_settings(self): """ Возвращает настройки по умолчанию для разбора данных PMS Shelter. """ return { "fields_mapping": { "reservation_id": "id", "hotel_id": "hotelId", "hotel_name": "hotelName", "check_in": "from", "check_out": "until", "reservation_date": "date", "room_type_id": "roomTypeId", "room_id": "roomId", "room_number": "roomNumber", "room_type_name": "roomTypeName", "check_in_status": "checkInStatus", "is_annul": "isAnnul", "tariff_id": "tariffId", "reservation_price": "reservationPrice", "discount": "discount", "guests": "guests", }, "date_format": "%Y-%m-%dT%H:%M:%S", "timezone": "UTC", } async def _get_last_saved_date(self): """ Получает дату последнего сохраненного бронирования для отеля. """ try: last_reservation = await sync_to_async( Reservation.objects.filter(hotel__pms=self.pms_config).order_by('-check_in').first )() return last_reservation.check_in if last_reservation else None except Exception as e: print(f"[ERROR] Ошибка получения последнего сохраненного бронирования: {e}") return None async def _fetch_data(self): """ Получает данные о бронированиях из PMS Shelter. """ try: now = datetime.utcnow() start_date = await self._get_last_saved_date() or (now - timedelta(days=60)) end_date = now + timedelta(days=60) total_count = None from_index = 0 headers = { 'accept': 'text/plain', 'Authorization': f'Bearer {self.token}', 'Content-Type': 'application/json', } print(f"[DEBUG] Start date: {start_date}, End date: {end_date}") while total_count is None or from_index < total_count: payload = { "from": start_date.strftime('%Y-%m-%dT%H:%M:%SZ'), "until": end_date.strftime('%Y-%m-%dT%H:%M:%SZ'), "pagination": { "from": from_index, "count": self.pagination_count, }, } print(f"[DEBUG] Payload: {json.dumps(payload)}") try: response = await sync_to_async(requests.post)(self.api_url, headers=headers, data=json.dumps(payload)) except requests.RequestException as e: print(f"[ERROR] Ошибка HTTP-запроса: {e}") raise ValueError(f"Ошибка HTTP-запроса: {e}") print(f"[DEBUG] Response status: {response.status_code}") if response.status_code != 200: print(f"[ERROR] Request error: {response.status_code}, {response.text}") raise ValueError(f"Ошибка запроса: {response.status_code}, {response.text}") try: data = response.json() except json.JSONDecodeError as e: print(f"[ERROR] Ошибка декодирования JSON: {e}") raise ValueError(f"Ошибка декодирования JSON: {e}") # Проверяем, что ответ содержит ключи "count" и "items" if not isinstance(data, dict) or "count" not in data or "items" not in data: print(f"[ERROR] Неверный формат данных: {data}") raise ValueError(f"Неверный формат данных: {data}") total_count = data.get("count", 0) items = data.get("items", []) print(f"[DEBUG] Total count: {total_count}, Items retrieved: {len(items)}") if not isinstance(items, list): print(f"[ERROR] Неверный тип items: {type(items)}. Ожидался list.") raise ValueError(f"Неверный тип items: {type(items)}. Ожидался list.") for item in items: if not isinstance(item, dict): print(f"[ERROR] Неверный формат элемента items: {item}") continue try: await self._save_to_db(item) except Exception as e: print(f"[ERROR] Ошибка сохранения бронирования {item.get('id')}: {e}") from_index += len(items) print(f"[DEBUG] Updated from_index: {from_index}") except Exception as e: print(f"[ERROR] Общая ошибка в методе _fetch_data: {e}") async def _save_to_db(self, item): """ Сохраняет данные о бронировании в БД. """ try: print(f"[DEBUG] Fetching hotel for PMS: {self.pms_config}") hotel = await sync_to_async(Hotel.objects.get)(pms=self.pms_config) print(f"[DEBUG] Hotel found: {hotel.name}") # Учитываем формат даты без 'Z' date_format = '%Y-%m-%dT%H:%M:%S' print(f"[DEBUG] Parsing check-in and check-out dates for reservation {item['id']}") check_in = datetime.strptime(item["from"], date_format).replace(tzinfo=timezone.utc) check_out = datetime.strptime(item["until"], date_format).replace(tzinfo=timezone.utc) # Проверяем room_number и устанавливаем значение по умолчанию, если оно отсутствует room_number = item.get("roomNumber", "") or "Unknown" print(f"[DEBUG] Room number determined: {room_number}") # Сохраняем бронирование print(f"[DEBUG] Saving reservation {item['id']} to database") reservation, created = await sync_to_async(Reservation.objects.update_or_create)( reservation_id=item["id"], hotel=hotel, defaults={ "room_number": room_number, "room_type": item.get("roomTypeName", ""), "check_in": check_in, "check_out": check_out, "status": item.get("checkInStatus", ""), "price": item.get("reservationPrice", 0), "discount": item.get("discount", 0), }, ) print(f"[DEBUG] {'Created' if created else 'Updated'} reservation {item['id']}") except KeyError as ke: print(f"[ERROR] Ошибка обработки ключей в элементе {item}: {ke}") except ValueError as ve: print(f"[ERROR] Ошибка обработки данных для бронирования {item['id']}: {ve}") except Exception as e: print(f"[ERROR] Общая ошибка сохранения бронирования {item.get('id', 'Unknown')}: {e}")