Files
Touchh/pms_integration/plugins/shelter_pms.py
2024-12-12 07:05:26 +09:00

183 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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}")