This commit is contained in:
2024-12-28 09:44:58 +09:00
parent 5e3ed91b3a
commit 7350989113
11 changed files with 14160 additions and 324 deletions

View File

@@ -1,8 +1,4 @@
<<<<<<< HEAD
# Generated by Django 5.1.4 on 2024-12-19 12:42
=======
# Generated by Django 5.1.4 on 2024-12-25 04:55 # Generated by Django 5.1.4 on 2024-12-25 04:55
>>>>>>> PMSManager_refactor
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models

View File

@@ -1,34 +1,11 @@
<<<<<<< HEAD
=======
# settings/admin.py # settings/admin.py
>>>>>>> antifraud
from django.contrib import admin from django.contrib import admin
from .models import LocalDatabase, GlobalHotelSettings, GlobalSystemSettings, TelegramSettings, EmailSettings from .models import LocalDatabase, GlobalHotelSettings, GlobalSystemSettings, TelegramSettings, EmailSettings
@admin.register(LocalDatabase) @admin.register(LocalDatabase)
class LocalDatabaseAdmin(admin.ModelAdmin): class LocalDatabaseAdmin(admin.ModelAdmin):
list_display = ['name', 'host', 'port', 'user', 'database', 'is_active'] list_display = ['name', 'host', 'port', 'user', 'database', 'is_active']
<<<<<<< HEAD
search_fields = ['name', 'host', 'user', 'database']
@admin.register(GlobalHotelSettings)
class GlobalHotelSettingsAdmin(admin.ModelAdmin):
list_display = ['check_in_time', 'check_out_time', 'global_timezone']
list_filter = ['global_timezone']
@admin.register(GlobalSystemSettings)
class GlobalSystemSettingsAdmin(admin.ModelAdmin):
list_display = ['system_name', 'system_version', 'server_timezone']
@admin.register(TelegramSettings)
class TelegramSettingsAdmin(admin.ModelAdmin):
list_display = ['bot_token', 'username']
@admin.register(EmailSettings)
class EmailSettingsAdmin(admin.ModelAdmin):
list_display = ['smtp_server', 'smtp_port', 'smtp_user', 'from_email']
=======
search_fields = ['name', 'host','user', 'database'] search_fields = ['name', 'host','user', 'database']
@@ -47,4 +24,3 @@ class TelegramSettingsAdmin(admin.ModelAdmin):
admin.site.register(EmailSettings) # Register your models here. admin.site.register(EmailSettings) # Register your models here.
class EmailSettingsAdmin(admin.ModelAdmin): class EmailSettingsAdmin(admin.ModelAdmin):
list_display = ['email_host', 'email_port', 'email_host_user', 'email_host_password'] list_display = ['email_host', 'email_port', 'email_host_user', 'email_host_password']
>>>>>>> antifraud

View File

@@ -1,8 +1,6 @@
from .models import LocalDatabase
from decouple import config from decouple import config
from django.conf import settings from django.conf import settings
from .models import LocalDatabase
from django.apps import apps from django.apps import apps
def load_database_settings(databases): def load_database_settings(databases):

View File

@@ -1,19 +1,3 @@
<<<<<<< HEAD
from django.apps import AppConfig, apps
class AppSettingsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'app_settings'
def ready(self):
# Проверяем, что приложения готовы
if not apps.ready:
return
try:
import app_settings.signals # Регистрация сигналов
except ImportError as e:
print(f"Ошибка импорта signals: {e}")
=======
from django.apps import AppConfig from django.apps import AppConfig
@@ -21,4 +5,3 @@ class SettingsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'app_settings' name = 'app_settings'
verbose_name="Настройки системы" verbose_name="Настройки системы"
>>>>>>> antifraud

View File

@@ -85,11 +85,7 @@ class Migration(migrations.Migration):
), ),
] ]
<<<<<<< HEAD
# Generated by Django 5.1.4 on 2024-12-20 11:29
=======
# Generated by Django 5.1.4 on 2024-12-23 00:57 # Generated by Django 5.1.4 on 2024-12-23 00:57
>>>>>>> antifraud
from django.db import migrations, models from django.db import migrations, models

13373
bnovo_page_1.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -150,7 +150,7 @@ async def check_pms(update, context):
# Проверка наличия fetch_data и вызов плагина # Проверка наличия fetch_data и вызов плагина
if hasattr(pms_manager.plugin, 'fetch_data') and callable(pms_manager.plugin.fetch_data): if hasattr(pms_manager.plugin, 'fetch_data') and callable(pms_manager.plugin.fetch_data):
report = await pms_manager.plugin.fetch_data() report = await pms_manager.plugin.fetch_data()
logger.debug(f"Отчет типа: {type(report)}") logger.debug(f"Отчет типа: {type(report)}: {report}")
else: else:
logger.error("Плагин не поддерживает fetch_data.") logger.error("Плагин не поддерживает fetch_data.")
await query.edit_message_text("Подходящий способ интеграции с PMS не найден.") await query.edit_message_text("Подходящий способ интеграции с PMS не найден.")

View File

@@ -18,7 +18,6 @@ class PluginLoader:
print("Загрузка плагинов:") print("Загрузка плагинов:")
for file in os.listdir(PluginLoader.PLUGIN_PATH): for file in os.listdir(PluginLoader.PLUGIN_PATH):
if file.endswith("_pms.py") and not file.startswith("__"): if file.endswith("_pms.py") and not file.startswith("__"):
# print(f" Plugin {file}")
module_name = f"pms_integration.plugins.{file[:-3]}" module_name = f"pms_integration.plugins.{file[:-3]}"
try: try:
module = importlib.import_module(module_name) module = importlib.import_module(module_name)
@@ -26,8 +25,8 @@ class PluginLoader:
cls = getattr(module, attr) cls = getattr(module, attr)
if isinstance(cls, type) and issubclass(cls, BasePMSPlugin) and cls is not BasePMSPlugin: if isinstance(cls, type) and issubclass(cls, BasePMSPlugin) and cls is not BasePMSPlugin:
plugin_name = file[:-7] # Убираем `_pms` из имени файла plugin_name = file[:-7] # Убираем `_pms` из имени файла
# print(f" Загружен плагин {plugin_name}: {cls.__name__}")
plugins[plugin_name] = cls plugins[plugin_name] = cls
print(f" Загружен плагин {plugin_name}: {cls.__name__}")
except Exception as e: except Exception as e:
print(f" Ошибка загрузки плагина {module_name}: {e}") print(f" Ошибка загрузки плагина {module_name}: {e}")
return plugins return plugins
@@ -40,6 +39,7 @@ class PMSIntegrationManager:
""" """
self.hotel = hotel self.hotel = hotel
self.plugin = None self.plugin = None
self.plugins = PluginLoader.load_plugins()
def load_hotel(self): def load_hotel(self):
""" """
@@ -52,12 +52,14 @@ class PMSIntegrationManager:
""" """
Загружает плагин, соответствующий PMS конфигурации отеля. Загружает плагин, соответствующий PMS конфигурации отеля.
""" """
pms_name = self.hotel.pms.plugin_name.lower() # Приводим название плагина к нижнему регистру pms_name = self.hotel.pms.plugin_name.lower()
if pms_name == "ecvi_intermark" or pms_name == "ecvi": if pms_name in self.plugins:
from pms_integration.plugins.ecvi_pms import EcviPMSPlugin plugin_class = self.plugins[pms_name]
self.plugin = EcviPMSPlugin(self.hotel) self.plugin = plugin_class(self.hotel)
print(f"Плагин {pms_name} успешно загружен.")
else: else:
raise ValueError(f"Неизвестный PMS: {pms_name}") raise ValueError(f"Неизвестный PMS: {pms_name}")
def fetch_data(self): def fetch_data(self):
""" """
Получает данные из PMS с использованием загруженного плагина. Получает данные из PMS с использованием загруженного плагина.

View File

@@ -1,3 +1,413 @@
# import requests
# import json
# from datetime import datetime, timedelta
# from asgiref.sync import sync_to_async
# from .base_plugin import BasePMSPlugin
# from pms_integration.models import PMSConfiguration
# from hotels.models import Reservation, Hotel
# from touchh.utils.log import CustomLogger
# import logging
# import logging
# # Настройка логирования
# logging.basicConfig(
# level=logging.WARNING, # Установите уровень логов для всех обработчиков
# format='%(asctime)s [%(levelname)s] %(message)s',
# handlers=[
# logging.FileHandler("bnovo_plugin.log"), # Логи пишутся в файл
# logging.StreamHandler() # Логи выводятся в консоль
# ]
# )
# # Создаем отдельный логгер для консоли с уровнем INFO
# console_handler = logging.StreamHandler()
# console_handler.setLevel(logging.INFO) # Меняем уровень логов для консоли
# console_handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(message)s'))
# # Основной логгер
# logger = logging.getLogger("BnovoPMS Plugin")
# logger.addHandler(console_handler)
# logger.setLevel(logging.WARNING) # Основной уровень логов
# class BnovoPMSPlugin(BasePMSPlugin):
# """Плагин для работы с PMS Bnovo."""
# def __init__(self, hotel):
# super().__init__(hotel)
# if not isinstance(hotel, Hotel):
# logger.error("Ожидался объект Hotel, но получен другой тип.")
# raise ValueError("Для инициализации плагина требуется объект Hotel.")
# self.hotel = hotel
# self.pms_config = hotel.pms # Связь отеля с PMSConfiguration
# if not self.pms_config:
# logger.error(f"Отель {hotel.id} не связан с конфигурацией PMS.")
# raise ValueError(f"Отель {hotel.id} не связан с конфигурацией PMS.")
# self.api_url = self.pms_config.url.rstrip("/")
# self.username = self.pms_config.username
# self.password = self.pms_config.password
# self.token = None
# if not self.api_url:
# logger.error("Не указан URL для работы плагина.")
# raise ValueError("Не указан URL для работы плагина.")
# if not self.username or not self.password:
# logger.error("Не указаны логин или пароль для авторизации.")
# raise ValueError("Не указаны логин или пароль для авторизации.")
# def get_default_parser_settings(self):
# """Возвращает настройки по умолчанию для обработки данных."""
# logger.debug("Получение настроек парсера по умолчанию.")
# return {
# "date_format": "%Y-%m-%dT%H:%M:%S",
# "timezone": "UTC"
# }
# async def _get_stored_token(self):
# """Получение токена из конфигурации PMS отеля."""
# try:
# logger.debug(f"Попытка получения токена для отеля {self.hotel.id}.")
# token = self.pms_config.token
# logger.debug(f"Токен из базы данных: {token}")
# return token
# except Exception as e:
# logger.warning(f"Ошибка при получении токена для отеля {self.hotel.id}: {e}")
# return None
# async def _save_token_to_db(self, sid):
# """Сохраняет токен (SID) в конфигурации PMS отеля."""
# try:
# logger.debug(f"Сохранение токена для отеля {self.hotel.id}: {sid}")
# self.pms_config.token = sid
# await sync_to_async(self.pms_config.save)()
# logger.debug("Токен успешно сохранен.")
# except Exception as e:
# logger.error(f"Ошибка сохранения токена для отеля {self.hotel.id}: {e}")
# async def _ensure_token(self):
# """
# Убеждается, что токен (SID) доступен. Если его нет, выполняет авторизацию.
# """
# logger.debug(f"Проверка токена для отеля {self.hotel.id}.")
# if not self.token:
# self.token = await self._get_stored_token()
# if not self.token:
# logger.info("Токен отсутствует, выполняем авторизацию.")
# await self._fetch_session()
# else:
# logger.debug(f"Используется сохраненный токен: {self.token}")
# def _get_auth_headers(self):
# """Создает заголовки авторизации."""
# logger.debug("Создание заголовков авторизации.")
# headers = {
# "Content-Type": "application/json",
# "Accept": "application/json",
# }
# if self.token:
# headers["Cookie"] = f"SID={self.token}"
# logger.debug(f"Добавлен токен в заголовки: {self.token}")
# return headers
# async def _fetch_session(self):
# """Получение нового токена (SID) через запрос."""
# url = f"{self.api_url}/"
# payload = {"username": self.username, "password": self.password}
# headers = self._get_auth_headers()
# logger.debug(f"Авторизация по адресу: {url} с данными: {json.dumps(payload)}")
# response = requests.post(url, json=payload, headers=headers, allow_redirects=False)
# logger.debug(f"Ответ авторизации: статус {response.status_code}, заголовки {response.headers}")
# if response.status_code == 302:
# cookies = response.cookies.get_dict()
# sid = cookies.get("SID")
# if sid:
# self.token = sid
# logger.debug(f"Получен новый SID: {sid}")
# await self._save_token_to_db(sid)
# else:
# logger.error("Не удалось извлечь SID из ответа.")
# raise ValueError("Не удалось извлечь SID из ответа.")
# else:
# logger.error(f"Ошибка авторизации: {response.status_code}, {response.text}")
# raise ValueError(f"Ошибка авторизации: {response.status_code}, {response.text}")
# async def _fetch_account_data(self):
# """Получение данных аккаунта через эндпоинт /account/current."""
# logger.info(f"Начало получения данных аккаунта для отеля {self.hotel.id}.")
# self.token = await self._get_stored_token()
# if not self.token:
# logger.info("Токен отсутствует, выполняем авторизацию.")
# await self._fetch_session()
# url = f"{self.api_url}/account/current"
# headers = self._get_auth_headers()
# logger.debug(f"Выполнение запроса к {url}")
# response = requests.get(url, headers=headers)
# if response.status_code != 200:
# logger.error(f"Ошибка при запросе данных аккаунта: {response.status_code}, {response.text}")
# raise ValueError("Ошибка запроса к /account/current")
# try:
# account_data = response.json()
# logger.debug(f"Полученные данные аккаунта:")
# except json.JSONDecodeError as e:
# logger.error(f"Ошибка декодирования JSON: {e}")
# raise ValueError(f"Ошибка декодирования JSON: {e}")
# return account_data
# async def _fetch_and_log_account_data(self):
# """Вызов метода _fetch_account_data и вывод результата в лог."""
# logger.info(f"Запуск получения и логирования данных аккаунта для отеля {self.hotel.id}.")
# try:
# account_data = await self._fetch_account_data()
# logger.info(f"Успешно полученные данные аккаунта:")
# return account_data
# except Exception as e:
# logger.error(f"Ошибка при получении данных аккаунта: {e}")
# raise
# async def _fetch_data_with_account_info(self):
# """Получение данных аккаунта и бронирований."""
# logger.info(f"Запуск процесса получения данных аккаунта и бронирований для отеля {self.hotel.id}.")
# try:
# account_data = await self.fetch_and_log_account_data()
# logger.info("Данные аккаунта успешно получены, продолжение с бронированиями.")
# await self.__fetch_data()
# except Exception as e:
# logger.error(f"Ошибка при выполнении полной операции: {e}")
# async def _fetch_paginated_data(self):
# """
# Получает все данные с API, обрабатывая страницы с пагинацией.
# """
# logger.info("Начало получения данных с пагинацией.")
# await self._ensure_token()
# url = f"{self.api_url}/dashboard"
# headers = self._get_auth_headers()
# now = datetime.now()
# create_from = (now - timedelta(days=1)).strftime("%d.%m.%Y")
# create_to = now.strftime("%d.%m.%Y")
# params = {
# "create_from": create_from,
# "create_to": create_to,
# "advanced_search": 2,
# "c": 100,
# "page": 1,
# "order_by": "create_date.asc",
# }
# all_bookings = []
# try:
# while True:
# logger.debug(f"Запрос к {url} с параметрами: {json.dumps(params, indent=2)}")
# response = requests.get(url, headers=headers, params=params, allow_redirects=False)
# if response.status_code == 302:
# logger.warning("Получен код 302. Перенаправление.")
# await self._fetch_session()
# headers = self._get_auth_headers()
# response = requests.get(url, headers=headers, params=params)
# if response.status_code != 200:
# logger.error(f"Ошибка при запросе: {response.status_code}, {response.text}")
# raise ValueError("Ошибка при получении данных.")
# data = response.json()
# bookings = data.get("bookings", [])
# all_bookings.extend(bookings)
# # Проверка окончания пагинации
# pages_info = data.get("pages", {})
# current_page = pages_info.get("current_page", 1)
# total_pages = pages_info.get("total_pages", 1)
# logger.debug(f"Информация о страницах: текущая {current_page}, всего {total_pages}")
# if current_page >= total_pages:
# break
# params["page"] += 1
# except Exception as e:
# logger.error(f"Ошибка при обработке данных: {e}")
# raise
# logger.info(f"Всего бронирований: {len(all_bookings)}")
# return all_bookings
# async def _save_hotel_data(self, hotel_data):
# """
# Сохраняет данные об отеле в базу.
# """
# try:
# hotel_id = hotel_data.get("id")
# if not hotel_id:
# logger.warning("Данные об отеле не содержат идентификатор.")
# return
# await sync_to_async(Hotel.objects.update_or_create)(
# external_id=hotel_id,
# defaults={
# "name": hotel_data.get("name"),
# "address": hotel_data.get("address"),
# "city": hotel_data.get("city"),
# "timezone": hotel_data.get("timezone"),
# "rating": hotel_data.get("rating"),
# "phone": hotel_data.get("phone"),
# "email": hotel_data.get("email"),
# "country": hotel_data.get("country"),
# "booking_url": hotel_data.get("booking_url"),
# "tripadvisor_url": hotel_data.get("tripadvisor_url"),
# },
# )
# logger.info(f"Информация об отеле {hotel_id} успешно обновлена.")
# except Exception as e:
# logger.error(f"Ошибка при сохранении данных об отеле: {e}")
# async def _fetch_data(self):
# """
# Получает данные о бронированиях с API и возвращает их.
# """
# logger.info("Начало процесса получения данных о бронированиях.")
# try:
# await self._ensure_token() # Проверка токена
# url = f"{self.api_url}/dashboard"
# headers = self._get_auth_headers()
# now = datetime.now()
# create_from = (now - timedelta(days=1)).strftime("%d.%m.%Y")
# create_to = now.strftime("%d.%m.%Y")
# params = {
# "create_from": create_from,
# "create_to": create_to,
# "advanced_search": 2,
# "c": 100,
# "page": 1,
# "order_by": "create_date.asc",
# }
# all_data = []
# while True:
# logger.debug(f"Запрос к {url} с параметрами: {json.dumps(params, indent=2)}")
# response = requests.get(url, headers=headers, params=params, allow_redirects=False)
# if response.status_code == 302:
# logger.warning("Получен код 302. Перенаправление.")
# await self._fetch_session() # Обновляем токен
# headers = self._get_auth_headers()
# continue
# if response.status_code != 200:
# logger.error(f"Ошибка при запросе: {response.status_code}, {response.text}")
# raise ValueError(f"Ошибка при получении данных: {response.text}")
# try:
# data = response.json()
# except json.JSONDecodeError as e:
# logger.error(f"Ошибка декодирования JSON: {e}. Ответ: {response.text}")
# raise ValueError(f"Ошибка декодирования JSON: {e}")
# bookings = data.get("bookings", [])
# logger.debug(f"Получено бронирований: {len(bookings)}")
# all_data.extend(bookings)
# # Проверка окончания пагинации
# pages_info = data.get("pages", {})
# current_page = pages_info.get("current_page", 1)
# total_pages = pages_info.get("total_pages", 1)
# logger.debug(f"Текущая страница: {current_page}, всего страниц: {total_pages}")
# if current_page >= total_pages:
# break
# params["page"] += 1
# if not all_data:
# logger.error("Полученные данные пусты или отсутствуют бронирования.")
# raise ValueError("API вернуло пустые данные.")
# logger.info(f"Всего бронирований: {len(all_data)}")
# return all_data
# except Exception as e:
# logger.error(f"Ошибка при получении данных: {e}")
# raise
# async def _process_and_save_data(self, data):
# """
# Обрабатывает и сохраняет данные о бронированиях в базу.
# """
# logger.info("Начало обработки данных о бронированиях для сохранения в базу.")
# processed_items = 0
# errors = []
# for record in data:
# try:
# booking_id = record.get("id")
# room_number = record.get("current_room")
# arrival = record.get("arrival")
# departure = record.get("departure")
# status = record.get("status_name")
# # Проверка обязательных данных
# if not (booking_id and room_number and arrival and departure and status):
# logger.warning(f"Пропуск записи из-за отсутствия обязательных данных: {record}")
# continue
# # Сохраняем или обновляем запись в базе данных
# reservation, created = await sync_to_async(Reservation.objects.update_or_create)(
# external_id=booking_id,
# defaults={
# "hotel": self.hotel,
# "status": status,
# "room_number": room_number,
# "check_in": arrival,
# "check_out": departure,
# },
# )
# if created:
# logger.info(f"Создана новая запись бронирования: {reservation}")
# else:
# logger.info(f"Обновлено существующее бронирование: {reservation}")
# processed_items += 1
# except Exception as e:
# logger.error(f"Ошибка обработки бронирования {record.get('id')}: {e}")
# errors.append(str(e))
# logger.info(f"Обработано бронирований: {processed_items}, ошибок: {len(errors)}")
# return {"processed_items": processed_items, "errors": errors}
# async def fetch_and_process_data(self):
# """
# Загружает данные с API и сохраняет их в базу.
# """
# logger.info("Начало процесса загрузки и обработки данных.")
# try:
# data = await self._fetch_paginated_data()
# report = await self._process_and_save_data(data)
# logger.info(f"Загрузка и обработка завершены. Отчет: {report}")
# return report
# except Exception as e:
# logger.error(f"Ошибка в процессе загрузки и обработки данных: {e}")
# raise
import requests import requests
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
@@ -5,61 +415,38 @@ from asgiref.sync import sync_to_async
from .base_plugin import BasePMSPlugin from .base_plugin import BasePMSPlugin
from pms_integration.models import PMSConfiguration from pms_integration.models import PMSConfiguration
from hotels.models import Reservation, Hotel from hotels.models import Reservation, Hotel
from touchh.utils.log import CustomLogger
import logging
import logging import logging
# Настройка логирования # Настройка логирования
logging.basicConfig( logging.basicConfig(
level=logging.WARNING, # Установите уровень логов для всех обработчиков level=logging.WARNING,
format='%(asctime)s [%(levelname)s] %(message)s', format='%(asctime)s [%(levelname)s] %(message)s',
handlers=[ handlers=[
logging.FileHandler("bnovo_plugin.log"), # Логи пишутся в файл logging.FileHandler("bnovo_plugin.log"),
logging.StreamHandler() # Логи выводятся в консоль logging.StreamHandler()
] ]
) )
# Создаем отдельный логгер для консоли с уровнем INFO
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING) # Меняем уровень логов для консоли
console_handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(message)s'))
# Основной логгер
logger = logging.getLogger("BnovoPMS Plugin") logger = logging.getLogger("BnovoPMS Plugin")
logger.addHandler(console_handler) logger.setLevel(logging.INFO)
logger.setLevel(logging.WARNING) # Основной уровень логов
class BnovoPMSPlugin(BasePMSPlugin): class BnovoPMSPlugin(BasePMSPlugin):
"""Плагин для работы с PMS Bnovo.""" """Плагин для работы с PMS Bnovo."""
def __init__(self, hotel): def __init__(self, hotel):
super().__init__(hotel) super().__init__(hotel)
if not isinstance(hotel, Hotel):
logger.error("Ожидался объект Hotel, но получен другой тип.")
raise ValueError("Для инициализации плагина требуется объект Hotel.")
self.hotel = hotel self.hotel = hotel
self.pms_config = hotel.pms # Связь отеля с PMSConfiguration self.pms_config = hotel.pms
if not self.pms_config:
logger.error(f"Отель {hotel.id} не связан с конфигурацией PMS.")
raise ValueError(f"Отель {hotel.id} не связан с конфигурацией PMS.")
self.api_url = self.pms_config.url.rstrip("/") self.api_url = self.pms_config.url.rstrip("/")
self.username = self.pms_config.username self.username = self.pms_config.username
self.password = self.pms_config.password self.password = self.pms_config.password
self.token = None self.token = None
if not self.api_url:
logger.error("Не указан URL для работы плагина.")
raise ValueError("Не указан URL для работы плагина.")
if not self.username or not self.password:
logger.error("Не указаны логин или пароль для авторизации.")
raise ValueError("Не указаны логин или пароль для авторизации.")
def get_default_parser_settings(self): def get_default_parser_settings(self):
"""Возвращает настройки по умолчанию для обработки данных.""" """
Возвращает настройки по умолчанию для обработки данных.
"""
logger.debug("Получение настроек парсера по умолчанию.") logger.debug("Получение настроек парсера по умолчанию.")
return { return {
"date_format": "%Y-%m-%dT%H:%M:%S", "date_format": "%Y-%m-%dT%H:%M:%S",
@@ -67,28 +454,25 @@ class BnovoPMSPlugin(BasePMSPlugin):
} }
async def _get_stored_token(self): async def _get_stored_token(self):
"""Получение токена из конфигурации PMS отеля.""" """
Получает токен из конфигурации PMS отеля.
"""
try: try:
logger.debug(f"Попытка получения токена для отеля {self.hotel.id}.") logger.debug(f"Попытка получения токена для отеля {self.hotel.id}.")
token = self.pms_config.token token = self.pms_config.token
logger.debug(f"Токен из базы данных: {token}") if not token:
logger.info("Токен отсутствует в конфигурации.")
else:
logger.debug(f"Токен найден: {token}")
return token return token
except Exception as e: except Exception as e:
logger.warning(f"Ошибка при получении токена для отеля {self.hotel.id}: {e}") logger.error(f"Ошибка при получении токена для отеля {self.hotel.id}: {e}")
return None return None
async def _save_token_to_db(self, sid):
"""Сохраняет токен (SID) в конфигурации PMS отеля."""
try:
logger.debug(f"Сохранение токена для отеля {self.hotel.id}: {sid}")
self.pms_config.token = sid
await sync_to_async(self.pms_config.save)()
logger.debug("Токен успешно сохранен.")
except Exception as e:
logger.error(f"Ошибка сохранения токена для отеля {self.hotel.id}: {e}")
def _get_auth_headers(self): def _get_auth_headers(self):
"""Создает заголовки авторизации.""" """
Создает заголовки авторизации для запросов к API.
"""
logger.debug("Создание заголовков авторизации.") logger.debug("Создание заголовков авторизации.")
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -97,96 +481,64 @@ class BnovoPMSPlugin(BasePMSPlugin):
if self.token: if self.token:
headers["Cookie"] = f"SID={self.token}" headers["Cookie"] = f"SID={self.token}"
logger.debug(f"Добавлен токен в заголовки: {self.token}") logger.debug(f"Добавлен токен в заголовки: {self.token}")
else:
logger.warning("Токен отсутствует, запрос может быть неавторизованным.")
return headers return headers
async def _fetch_session(self):
"""Получение нового токена (SID) через запрос."""
url = f"{self.api_url}/"
payload = {"username": self.username, "password": self.password}
headers = self._get_auth_headers()
logger.debug(f"Авторизация по адресу: {url} с данными: {json.dumps(payload)}") async def _ensure_token(self):
response = requests.post(url, json=payload, headers=headers, allow_redirects=False) """
Убеждается, что токен (SID) доступен. Если его нет, выполняет авторизацию.
logger.debug(f"Ответ авторизации: статус {response.status_code}, заголовки {response.headers}") """
if response.status_code == 302: logger.debug(f"Проверка токена для отеля {self.hotel.id}.")
cookies = response.cookies.get_dict() if not self.token:
sid = cookies.get("SID") self.token = await self._get_stored_token()
if sid:
self.token = sid
logger.debug(f"Получен новый SID: {sid}")
await self._save_token_to_db(sid)
else:
logger.error("Не удалось извлечь SID из ответа.")
raise ValueError("Не удалось извлечь SID из ответа.")
else:
logger.error(f"Ошибка авторизации: {response.status_code}, {response.text}")
raise ValueError(f"Ошибка авторизации: {response.status_code}, {response.text}")
async def _fetch_account_data(self):
"""Получение данных аккаунта через эндпоинт /account/current."""
logger.info(f"Начало получения данных аккаунта для отеля {self.hotel.id}.")
self.token = await self._get_stored_token()
if not self.token: if not self.token:
logger.info("Токен отсутствует, выполняем авторизацию.") logger.info("Токен отсутствует, выполняем авторизацию.")
await self._fetch_session() await self._fetch_session()
else:
logger.debug(f"Используется сохраненный токен: {self.token}")
url = f"{self.api_url}/account/current" async def _save_token_to_db(self, sid):
headers = self._get_auth_headers() """
Сохраняет токен (SID) в конфигурации PMS отеля.
logger.debug(f"Выполнение запроса к {url}") """
response = requests.get(url, headers=headers)
if response.status_code != 200:
logger.error(f"Ошибка при запросе данных аккаунта: {response.status_code}, {response.text}")
raise ValueError("Ошибка запроса к /account/current")
try: try:
account_data = response.json() logger.debug(f"Сохранение токена для отеля {self.hotel.id}: {sid}")
logger.debug(f"Полученные данные аккаунта:") self.pms_config.token = sid
except json.JSONDecodeError as e: await sync_to_async(self.pms_config.save)()
logger.error(f"Ошибка декодирования JSON: {e}") logger.info(f"Токен {sid} успешно сохранен в базу данных.")
raise ValueError(f"Ошибка декодирования JSON: {e}")
return account_data
async def _fetch_and_log_account_data(self):
"""Вызов метода _fetch_account_data и вывод результата в лог."""
logger.info(f"Запуск получения и логирования данных аккаунта для отеля {self.hotel.id}.")
try:
account_data = await self._fetch_account_data()
logger.info(f"Успешно полученные данные аккаунта:")
return account_data
except Exception as e: except Exception as e:
logger.error(f"Ошибка при получении данных аккаунта: {e}") logger.error(f"Ошибка сохранения токена для отеля {self.hotel.id}: {e}")
raise raise
async def _fetch_data_with_account_info(self): async def _fetch_session(self):
"""Получение данных аккаунта и бронирований.""" """Получение токена (SID) через авторизацию."""
logger.info(f"Запуск процесса получения данных аккаунта и бронирований для отеля {self.hotel.id}.") url = f"{self.api_url}/"
try: payload = {"username": self.username, "password": self.password}
account_data = await self.fetch_and_log_account_data() headers = {"Content-Type": "application/json"}
logger.info("Данные аккаунта успешно получены, продолжение с бронированиями.") await self._save_token_to_db(self.token)
await self.__fetch_data()
except Exception as e:
logger.error(f"Ошибка при выполнении полной операции: {e}")
async def _fetch_data(self): response = requests.post(url, json=payload, headers=headers, allow_redirects=False)
"""Получение данных о бронированиях с помощью эндпоинта /dashboard.""" if response.status_code == 302:
logger.info("Начало процесса получения данных о бронированиях.") self.token = response.cookies.get("SID")
await self._save_token_to_db(self.token)
# # Вызов функции получения данных аккаунта else:
# try: logger.error(f"Ошибка авторизации: {response.status_code}, {response.text}")
# account_data = await self._fetch_and_log_account_data() raise ValueError("Ошибка авторизации")
# logger.info(f"Данные аккаунта успешно получены:")
# except Exception as e:
# logger.error(f"Ошибка получения данных аккаунта: {e}")
# raise
async def _fetch_paginated_data(self):
"""
Получает все данные с API, обрабатывая страницы с пагинацией.
"""
logger.info("Начало получения данных с пагинацией.")
await self._ensure_token()
url = f"{self.api_url}/dashboard" url = f"{self.api_url}/dashboard"
headers = self._get_auth_headers()
now = datetime.now() now = datetime.now()
create_from = (now - timedelta(days=1)).strftime("%d.%m.%Y") create_from = (now - timedelta(days=7)).strftime("%d.%m.%Y") # Получаем данные за последнюю неделю
create_to = now.strftime("%d.%m.%Y") create_to = now.strftime("%d.%m.%Y")
params = { params = {
@@ -197,85 +549,111 @@ class BnovoPMSPlugin(BasePMSPlugin):
"page": 1, "page": 1,
"order_by": "create_date.asc", "order_by": "create_date.asc",
} }
headers = self._get_auth_headers()
all_bookings = [] all_bookings = []
while True: try:
logger.debug(f"Запрос к {url} с параметрами: {json.dumps(params, indent=2)}") while True:
try: logger.debug(f"Запрос к {url} с параметрами: {json.dumps(params, indent=2)}")
response = requests.get(url, headers=headers, params=params, allow_redirects=False) response = requests.get(url, headers=headers, params=params, allow_redirects=False)
if response.status_code == 302: if response.status_code == 302:
logger.warning("Получен код 302. Перенаправление.") logger.warning("Получен код 302. Перенаправление.")
redirected_url = response.headers.get("Location") await self._fetch_session()
if redirected_url: headers = self._get_auth_headers()
logger.debug(f"Перенаправление на {redirected_url}") continue
url = redirected_url
continue
else:
logger.error("Ответ с кодом 302 не содержит заголовка Location.")
raise ValueError("Перенаправление без указанного URL.")
if response.status_code != 200: if response.status_code != 200:
logger.error(f"Ошибка при запросе: {response.status_code}, {response.text}") logger.error(f"Ошибка при запросе: {response.status_code}, {response.text}")
raise ValueError("Ошибка запроса к /dashboard") raise ValueError(f"Ошибка при получении данных: {response.text}")
data = response.json() data = response.json()
logger.debug(f"Полученный ответ API: {json.dumps(data, indent=2, ensure_ascii=False)}")
bookings = data.get("bookings", []) bookings = data.get("bookings", [])
rooms = data.get("rooms", []) logger.info(f"Получено бронирований: {len(bookings)}")
logger.debug(f'bookings: {bookings}\n rooms: {rooms}')
all_bookings.extend(bookings) all_bookings.extend(bookings)
logger.info(f"Получено бронирований: {len(bookings)}. Всего: {len(all_bookings)}.")
pages_info = data.get("pages", {}) pages_info = data.get("pages", {})
current_page = pages_info.get("current_page", 1) current_page = pages_info.get("current_page", 1)
total_pages = pages_info.get("total_pages", 1) total_pages = pages_info.get("total_pages", 1)
logger.debug(f"Информация о страницах: текущая {current_page}, всего {total_pages}")
if current_page >= total_pages: if current_page >= total_pages:
break break
params["page"] += 1 params["page"] += 1
except Exception as e:
logger.error(f"Ошибка при обработке данных: {e}")
raise
except json.JSONDecodeError as e:
logger.error(f"Ошибка декодирования JSON: {e}. Ответ: {response.text}")
raise ValueError(f"Ошибка декодирования JSON: {e}")
except Exception as e: async def _process_and_save_bookings(self, bookings):
logger.error(f"Неизвестная ошибка при обработке запроса: {e}") """
raise Обрабатывает и сохраняет бронирования в базу.
"""
logger.info("Начало обработки данных о бронированиях для сохранения в базу.")
processed_items = 0
errors = []
# Сопоставляем бронирования с существующими записями for record in bookings:
for booking in all_bookings:
try: try:
booking_id = booking.get("id") booking_id = record.get("id")
hotel_id = booking.get("hotel_id") room_number = record.get("current_room")
arrival = record.get("arrival")
departure = record.get("departure")
status = record.get("status_name")
if hotel_id != str(self.hotel.external_id_pms): # Проверка обязательных данных
logger.debug(f"Бронирование {booking_id} не относится к отелю {self.hotel.external_id_pms}. Пропуск.") if not (booking_id and room_number and arrival and departure and status):
logger.warning(f"Пропуск записи из-за отсутствия обязательных данных: {record}")
continue continue
# Сохраняем или обновляем запись в базе данных
reservation, created = await sync_to_async(Reservation.objects.update_or_create)( reservation, created = await sync_to_async(Reservation.objects.update_or_create)(
external_id=booking_id, external_id=booking_id,
defaults = { defaults={
"hotel": self.hotel, # Объект модели Hotel "hotel": self.hotel,
"status": booking.get("status_name"), # Статус бронирования "status": status,
"room_number": booking.get("current_room"), # Номер комнаты (исправлено с create_date) "room_number": room_number,
"check_in": booking.get("arrival"), # Дата заезда "check_in": arrival,
"check_out": booking.get("departure"), # Дата выезда "check_out": departure,
"room_type": booking.get("initial_room_type_name") # Тип комнаты },
}
) )
if created: if created:
logger.info(f"Создана новая запись бронирования: {reservation}") logger.info(f"Создана новая запись бронирования: {reservation}")
print(reservation)
else: else:
logger.info(f"Обновлено существующее бронирование: {reservation}") logger.info(f"Обновлено существующее бронирование: {reservation}")
except Exception as e: processed_items += 1
logger.error(f"Ошибка обработки бронирования {booking.get('id')}: {e}")
logger.info(f"Все бронирования получены и обработаны. Итоговое количество: {len(all_bookings)}") except Exception as e:
return all_bookings logger.error(f"Ошибка обработки бронирования {record.get('id')}: {e}")
errors.append(str(e))
logger.info(f"Обработано бронирований: {processed_items}, ошибок: {len(errors)}")
return {"processed_items": processed_items, "errors": errors}
async def _fetch_data(self):
"""
Получает данные о бронированиях с API и возвращает их.
"""
logger.info("Начало процесса получения данных о бронированиях.")
try:
bookings = await self._fetch_paginated_data() # Получаем данные с пагинацией
return bookings
except Exception as e:
logger.error(f"Ошибка в процессе получения данных: {e}")
raise ValueError("Ошибка при получении данных о бронированиях")
async def fetch_and_process_data(self):
"""Получение данных с API, обработка и сохранение в базу."""
logger.info("Начало загрузки данных с API")
try:
bookings = await self._fetch_paginated_data()
report = await self._process_and_save_bookings(bookings)
logger.info(f"Данные успешно обработаны. Отчет: {report}")
return report
except Exception as e:
logger.error(f"Ошибка загрузки и обработки данных: {e}")
raise

View File

@@ -1,3 +1,130 @@
# import logging
# import requests
# from datetime import datetime, timedelta
# from asgiref.sync import sync_to_async
# from pms_integration.models import PMSConfiguration
# from hotels.models import Hotel, Reservation
# from .base_plugin import BasePMSPlugin
# class ShelterPMSPlugin(BasePMSPlugin):
# """
# Плагин для интеграции с PMS Shelter.
# """
# def __init__(self, pms_config):
# super().__init__(pms_config)
# # Настройка логирования
# self.logger = logging.getLogger(self.__class__.__name__)
# handler = logging.StreamHandler()
# formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# handler.setFormatter(formatter)
# self.logger.addHandler(handler)
# self.logger.setLevel(logging.DEBUG)
# # Инициализация параметров API
# self.api_url = pms_config.url
# self.token = pms_config.token
# self.username = pms_config.username
# self.password = pms_config.password
# def get_default_parser_settings(self):
# """
# Возвращает настройки парсера по умолчанию.
# """
# return {
# "field_mapping": {
# "check_in": "checkin",
# "check_out": "checkout",
# "room_number": "room_number",
# "room_type": "room_type",
# "status": "status",
# },
# "date_format": "%Y-%m-%dT%H:%M:%S"
# }
# async def _fetch_data(self):
# """
# Получает данные из Shelter PMS API и сохраняет их в базу данных.
# """
# now = datetime.now()
# start_date = (now - timedelta(days=1)).strftime('%Y-%m-%d')
# end_date = now.strftime('%Y-%m-%d')
# headers = {
# "Authorization": f"Bearer {self.token}",
# "Content-Type": "application/json",
# }
# params = {
# "start_date": start_date,
# "end_date": end_date,
# }
# try:
# response = await sync_to_async(requests.get)(f"{self.api_url}/reservations", headers=headers, params=params)
# response.raise_for_status()
# data = response.json()
# self.logger.debug(f"Получены данные с API: {data}")
# except requests.exceptions.RequestException as e:
# self.logger.error(f"Ошибка запроса к API Shelter: {e}")
# return []
# # Обработка и сохранение данных
# processed_data = []
# for item in data.get("reservations", []):
# processed_item = {
# "room_number": item.get("room_number"),
# "check_in": datetime.strptime(item.get("check_in"), '%Y-%m-%dT%H:%M:%S'),
# "check_out": datetime.strptime(item.get("check_out"), '%Y-%m-%dT%H:%M:%S'),
# "status": item.get("status"),
# "room_type": item.get("room_type"),
# }
# processed_data.append(processed_item)
# await self._save_to_db(processed_item)
# self.logger.debug("Все данные успешно сохранены в базу данных.")
# return processed_data
# async def _save_to_db(self, item):
# """
# Сохраняет данные в базу данных.
# """
# try:
# hotel = await sync_to_async(Hotel.objects.get)(pms=self.pms_config)
# reservation, created = await sync_to_async(Reservation.objects.update_or_create)(
# room_number=item["room_number"],
# check_in=item["check_in"],
# defaults={
# "check_out": item["check_out"],
# "status": item["status"],
# "hotel": hotel,
# "room_type": item["room_type"],
# },
# )
# if created:
# self.logger.debug(f"Создана новая запись бронирования: {reservation}")
# else:
# self.logger.debug(f"Обновлено существующее бронирование: {reservation}")
# except Exception as e:
# self.logger.error(f"Ошибка при сохранении данных в БД: {e}")
# async def fetch_and_process_data(self):
# """
# Загружает данные с API Shelter и сохраняет их в базу данных.
# """
# self.logger.info("Начало процесса загрузки данных из Shelter PMS.")
# try:
# data = await self._fetch_data()
# self.logger.info(f"Загрузка и обработка данных завершены. Обработано записей: {len(data)}")
# return data
# except Exception as e:
# self.logger.error(f"Ошибка в процессе загрузки данных: {e}")
# raise
import logging import logging
import requests import requests
from datetime import datetime, timedelta from datetime import datetime, timedelta
@@ -6,144 +133,132 @@ from pms_integration.models import PMSConfiguration
from hotels.models import Hotel, Reservation from hotels.models import Hotel, Reservation
from .base_plugin import BasePMSPlugin from .base_plugin import BasePMSPlugin
class ShelterPMSPlugin(BasePMSPlugin):
class EcviPMSPlugin(BasePMSPlugin):
""" """
Плагин для интеграции с PMS Ecvi (интерфейс для получения данных об отеле). Плагин для интеграции с PMS Shelter (интерфейс для получения данных об отеле).
""" """
def __init__(self, pms_config): def __init__(self, pms_config):
super().__init__(pms_config) super().__init__(pms_config)
self.logger = logging.getLogger(self.__class__.__name__)
# Инициализация логгера handler = logging.StreamHandler()
self.logger = logging.getLogger(self.__class__.__name__) # Логгер с именем класса
handler = logging.StreamHandler() # Потоковый обработчик для вывода в консоль
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter) handler.setFormatter(formatter)
self.logger.addHandler(handler) self.logger.addHandler(handler)
self.logger.setLevel(logging.DEBUG) # Уровень логирования self.logger.setLevel(logging.DEBUG)
# Инициализация параметров API self.api_url = "https://pms.frontdesk24.ru/sheltercloudapi/Reservations/"
self.api_url = pms_config.url self.access_token = "679CA9C5-9847-4151-883E-5F61181AA37E"
self.token = pms_config.token
self.username = pms_config.username
self.password = pms_config.password
self.pagination_count = 50 # Максимальное количество записей на страницу (если используется пагинация)
def get_default_parser_settings(self): def get_default_parser_settings(self):
""" """
Возвращает настройки парсера по умолчанию. Возвращает настройки по умолчанию для обработки данных.
""" """
return { return {
"field_mapping": { "field_mapping": {
"check_in": "checkin", "check_in": "check_in",
"check_out": "checkout", "check_out": "check_out",
"room_number": "room_name", "room_number": "room_number",
"room_type_name": "room_type", "status": "status",
"status": "occupancy",
}, },
"date_format": "%Y-%m-%dT%H:%M:%S" "date_format": "%Y-%m-%dT%H:%M:%S",
} }
async def _fetch_data(self): async def _fetch_data(self):
""" """
Получает данные из PMS API, фильтрует и сохраняет в базу данных. Получает данные бронирований с API PMS Shelter.
""" """
now = datetime.now()
current_date = now.strftime('%Y-%m-%d')
yesterday_date = (now - timedelta(days=1)).strftime('%Y-%m-%d')
headers = {
"Content-Type": "application/json",
}
data = {
"token": self.token,
}
try: try:
# Запрос данных из PMS API # Формируем параметры запроса
response = await sync_to_async(requests.post)(self.api_url, headers=headers, json=data, auth=(self.username, self.password)) now = datetime.now()
response.raise_for_status() # Если ошибка, выбросит исключение create_from = (now - timedelta(days=7)).strftime("%Y-%m-%d")
data = response.json() # Преобразуем ответ в JSON create_to = now.strftime("%Y-%m-%d")
self.logger.debug(f"Получены данные с API: {data}")
except requests.exceptions.RequestException as e:
self.logger.error(f"Ошибка запроса: {e}")
return []
# Фильтрация данных data = {
filtered_data = [] "from": create_from,
for item in data: "until": create_to,
if item.get('occupancy') in ['проживание', 'под выезд', 'под заезд']: "pagination": {
filtered_item = { "from": 0,
'checkin': datetime.strptime(item.get('checkin'), '%Y-%m-%d %H:%M:%S'), "count": 100
'checkout': datetime.strptime(item.get('checkout'), '%Y-%m-%d %H:%M:%S'),
'room_number': item.get('room_name'),
'room_type': item.get('room_type'),
'status': item.get('occupancy')
} }
filtered_data.append(filtered_item) }
# Логируем результат фильтрации headers = {
self.logger.debug(f"Отфильтрованные данные: {filtered_data}") "Content-Type": "application/json",
"Accept": "text/plain",
"Authorization": f"Bearer {self.access_token}"
}
# Сохранение данных в базу данных # Логирование запроса
for item in filtered_data: self.logger.debug(f"Отправка запроса к API: {self.api_url}")
await self._save_to_db(item) self.logger.debug(f"Тело запроса: {data}")
self.logger.debug(f"Заголовки: {headers}")
self.logger.debug(f"Данные успешно сохранены.") # Выполняем запрос
return filtered_data response = requests.post(self.api_url, json=data, headers=headers)
response.raise_for_status()
async def _save_to_db(self, item): # Обрабатываем ответ
bookings = response.json()
self.logger.info(f"Получено бронирований: {len(bookings)}")
return bookings
except requests.HTTPError as http_err:
self.logger.error(f"HTTP ошибка: {http_err}")
self.logger.error(f"Текст ответа: {response.text if 'response' in locals() else 'Нет данных'}")
raise
except Exception as e:
self.logger.error(f"Ошибка получения данных PMS Shelter: {e}")
raise
async def fetch_and_process_data(self):
""" """
Сохраняет данные в БД (например, информацию о номере). Получение данных с API, обработка и сохранение в базу.
""" """
self.logger.info("Начало загрузки данных с API Shelter")
try: try:
# Проверяем, что item — это словарь bookings = await self._fetch_data()
if not isinstance(item, dict): report = await self._process_and_save_bookings(bookings)
self.logger.error(f"Ожидался словарь, но получен: {type(item)}. Данные: {item}") self.logger.info(f"Данные успешно обработаны. Отчет: {report}")
return return report
except Exception as e:
self.logger.error(f"Ошибка загрузки и обработки данных: {e}")
raise
# Получаем отель по настройкам PMS async def _process_and_save_bookings(self, bookings):
hotel = await sync_to_async(Hotel.objects.get)(pms=self.pms_config) """
self.logger.debug(f"Отель найден: {hotel.name}") Обрабатывает и сохраняет бронирования в базу данных.
"""
self.logger.info("Начало обработки данных о бронированиях для сохранения в базу.")
processed_items = 0
errors = []
# Сохраняем данные бронирования for record in bookings:
room_number = item.get("room_number") try:
check_in = item.get("checkin") # Пример обработки данных бронирования
check_out = item.get("checkout") booking_id = record.get("id")
room_type = item.get("room_type") room_number = record.get("room_number")
check_in = record.get("check_in")
check_out = record.get("check_out")
status = record.get("status")
# Логируем полученные данные # Сохраняем или обновляем запись в базе данных
self.logger.debug(f"Полученные данные для сохранения: room_number={room_number}, check_in={check_in}, check_out={check_out}, room_type={room_type}") reservation, created = await sync_to_async(Reservation.objects.update_or_create)(
external_id=booking_id,
# Проверяем, существует ли запись с таким номером и датой заезда
existing_reservation = await sync_to_async(
Reservation.objects.filter(room_number=room_number, check_in=check_in).first
)()
if existing_reservation:
self.logger.debug(f"Резервация с таким номером и датой заезда уже существует. Обновляем...")
await sync_to_async(Reservation.objects.update_or_create)(
room_number=room_number,
check_in=check_in,
defaults={ defaults={
"hotel": self.pms_config.hotel,
"room_number": room_number,
"check_in": check_in,
"check_out": check_out, "check_out": check_out,
"hotel": hotel, "status": status,
"room_type": room_type,
}, },
) )
self.logger.debug(f"Обновлена существующая резервация.")
else:
self.logger.debug(f"Резервация не найдена, создаем новую...")
reservation = await sync_to_async(Reservation.objects.create)(
room_number=room_number,
check_in=check_in,
check_out=check_out,
hotel=hotel,
room_type=room_type,
)
self.logger.debug(f"Создана новая резервация. ID: {reservation.reservation_id}")
except Exception as e: processed_items += 1
self.logger.error(f"Ошибка сохранения данных: {e}")
except Exception as e:
self.logger.error(f"Ошибка обработки бронирования {record.get('id')}: {e}")
errors.append(str(e))
self.logger.info(f"Обработано бронирований: {processed_items}, ошибок: {len(errors)}")
return {"processed_items": processed_items, "errors": errors}

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1.4 on 2024-12-27 05:52
import scheduler.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('scheduler', '0013_alter_scheduledtask_function_path'),
]
operations = [
migrations.AlterField(
model_name='scheduledtask',
name='function_path',
field=models.CharField(choices=scheduler.models.get_available_functions, max_length=500, verbose_name='Путь к функции (модуль.функция)'),
),
]