from fpdf import FPDF import os from datetime import datetime from asgiref.sync import sync_to_async from django.utils.timezone import make_aware, is_naive, is_aware import os # Определение абсолютного пути к папке "reports" BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) REPORTS_DIR = os.path.join(BASE_DIR, "reports") # Убедитесь, что директория существует os.makedirs(REPORTS_DIR, exist_ok=True) # Асинхронная функция для извлечения данных о бронировании def ensure_datetime(value): """ Преобразует строку или naive datetime в timezone-aware datetime. Если значение не удается преобразовать, возвращается None. """ if isinstance(value, datetime): return make_aware(value) if is_naive(value) else value if isinstance(value, str): try: return make_aware(datetime.strptime(value, '%Y-%m-%d %H:%M:%S')) except ValueError: print(f"[WARNING] Невозможно преобразовать строку в datetime: {value}") return None # @sync_to_async # def get_reservation_data(res): # print(f"[DEBUG] Processing reservation {res.id}") # # Убедитесь, что даты являются timezone-aware # check_in = ensure_datetime(res.check_in) # check_out = ensure_datetime(res.check_out) # result = { # "hotel_name": res.hotel.name, # "pms": getattr(res.hotel, 'pms', 'N/A'), # "reservation_id": res.reservation_id, # "room_number": res.room_number if res.room_number else "Не указан", # "room_type": res.room_type, # "check_in": check_in, # "check_out": check_out, # "status": res.status, # } # # print(f"[DEBUG] Reservation data: {result}") # return result @sync_to_async def get_reservation_data(res): check_in = ensure_datetime(res.check_in) check_out = ensure_datetime(res.check_out) if not check_in or not check_out: raise ValueError(f"Некорректные даты бронирования: check_in={res.check_in}, check_out={res.check_out}") return { "hotel_name": res.hotel.name, "pms": getattr(res.hotel, 'pms', 'N/A'), "reservation_id": res.reservation_id, "room_number": res.room_number if res.room_number else "Не указан", "room_type": res.room_type, "check_in": check_in, "check_out": check_out, "status": res.status, } class CustomPDF(FPDF): def __init__(self, hotel_name, start_date, end_date, *args, **kwargs): super().__init__(*args, **kwargs) self.font_folder = "bot/fonts/" self.add_font("DejaVuSans-Bold", "", os.path.join(self.font_folder, "DejaVuSans-Bold.ttf"), uni=True) self.add_font("DejaVuSans", "", os.path.join(self.font_folder, "DejaVuSans.ttf"), uni=True) self.creation_date = datetime.now().strftime("%d.%m.%Y %H:%M:%S") # Переданные параметры self.hotel_name = hotel_name self.start_date = start_date self.end_date = end_date def header(self): if self.page == 1: self.set_font("DejaVuSans-Bold", size=14) self.cell(0, 10, f"Отчет о бронированиях отеля {self.hotel_name}", ln=1, align="C") self.ln(5) self.set_font("DejaVuSans", size=10) self.cell( 0, 10, f"за период {self.start_date.strftime('%Y-%m-%d %H:%M:%S')} - {self.end_date.strftime('%Y-%m-%d %H:%M:%S')}", ln=1, align="C" ) self.ln(10) def footer(self): """Добавление колонтитула внизу страницы.""" self.set_y(-15) self.set_font("DejaVuSans", size=8) self.cell(60, 10, f"Copyright (C) 2024 by Touchh", align="L") self.cell(0, 10, f"Лист {self.page_no()} из {{nb}} / Дата генерации отчета: {self.creation_date}", align="C") def trim_text_right(self, text, max_width): """Обрезка текста справа.""" while self.get_string_width(text) > max_width: text = text[:-1] return text + "..." if len(text) > 3 else text # async def generate_pdf_report(hotel_name, reservations, start_date, end_date): # # Преобразование дат в timezone-aware datetime # start_date = ensure_datetime(start_date) # end_date = ensure_datetime(end_date) # if not start_date or not end_date: # raise ValueError(f"Некорректные периоды: start_date={start_date}, end_date={end_date}") # # Создание экземпляра PDF с передачей параметров # pdf = CustomPDF(hotel_name=hotel_name, start_date=start_date, end_date=end_date, orientation="L", unit="mm", format="A4") # pdf.alias_nb_pages() # pdf.add_page() # Заголовок отчёта и таблица будут добавлены через методы header и footer # # Таблица # pdf.set_font("DejaVuSans", size=8) # col_widths = [30, 30, 30, 60, 35, 35, 30] # row_height = 10 # for res in reservations: # try: # res_data = await get_reservation_data(res) # row_data = [ # res_data["hotel_name"], # str(res_data["reservation_id"]), # res_data["room_number"], # res_data["room_type"], # res_data["check_in"].strftime('%Y-%m-%d %H:%M:%S'), # res_data["check_out"].strftime('%Y-%m-%d %H:%M:%S'), # res_data["status"], # ] # for col_width, data in zip(col_widths, row_data): # pdf.cell(col_width, row_height, data, border=1, align="C") # pdf.ln() # except Exception as e: # print(f"pdf_report.py [ERROR] Error processing reservation {res.id}: {e}") # # Сохранение PDF # hotel_name_safe = hotel_name.replace(" ", "_").replace("/", "_") # start_date_str = start_date.strftime('%Y-%m-%d') # end_date_str = end_date.strftime('%Y-%m-%d') # pdf_output_path = os.path.join(REPORTS_DIR, f"{hotel_name_safe}_report_{start_date_str}-{end_date_str}.pdf") # pdf.output(pdf_output_path) # if not os.path.exists(pdf_output_path): # raise RuntimeError(f"PDF file was not created at: {pdf_output_path}") # return pdf_output_path async def generate_pdf_report(hotel_name, reservations, start_date, end_date): # Преобразование дат start_date = ensure_datetime(start_date) end_date = ensure_datetime(end_date) pdf = CustomPDF(hotel_name=hotel_name, start_date=start_date, end_date=end_date, orientation="L", unit="mm", format="A4") pdf.alias_nb_pages() pdf.add_page() pdf.set_font("DejaVuSans", size=8) col_widths = [30, 30, 30, 60, 35, 35, 30] row_height = 10 for res in reservations: try: res_data = await get_reservation_data(res) # Отладочный вывод print(f"[DEBUG] Reservation Data: {res_data}") print(f"[DEBUG] check_in type: {type(res_data['check_in'])}, value: {res_data['check_in']}") print(f"[DEBUG] check_out type: {type(res_data['check_out'])}, value: {res_data['check_out']}") # Проверка и корректировка данных res_data["check_in"] = ensure_datetime(res_data["check_in"]) res_data["check_out"] = ensure_datetime(res_data["check_out"]) row_data = [ str(res_data["hotel_name"]), str(res_data["reservation_id"]), str(res_data["room_number"]), str(res_data["room_type"]), res_data["check_in"].strftime('%Y-%m-%d %H:%M:%S'), res_data["check_out"].strftime('%Y-%m-%d %H:%M:%S'), str(res_data["status"]), ] for col_width, data in zip(col_widths, row_data): pdf.cell(col_width, row_height, data, border=1, align="C") pdf.ln() except Exception as e: print(f"[ERROR] Error processing reservation {res.id}: {e}") print("[DEBUG] PDF metadata:", vars(pdf)) pdf_output_path = os.path.join(REPORTS_DIR, f"{hotel_name.replace(' ', '_')}_report_{start_date.strftime('%Y-%m-%d')}-{end_date.strftime('%Y-%m-%d')}.pdf") pdf.output(pdf_output_path) if not os.path.exists(pdf_output_path): raise RuntimeError(f"PDF file was not created at: {pdf_output_path}") return pdf_output_path