from fpdf import FPDF import os from datetime import datetime, timedelta from asgiref.sync import sync_to_async from django.utils.timezone import make_aware, is_aware, localtime import pytz from bot.utils.date_utils import ensure_datetime from touchh.utils.log import CustomLogger logger = CustomLogger(name="CustomPDF Report", log_level="DEBUG").get_logger() # Определение абсолютного пути к папке "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) @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, } def sanitize_text(text): return text.replace("\n", " ").strip() if isinstance(text, str) else text 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 = ensure_datetime(datetime.now(pytz.UTC)) 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") async def generate_pdf_report(hotel_name, reservations, start_date, end_date): start_date = ensure_datetime(start_date) end_date = ensure_datetime(end_date) logger.debug(f"Start_DATE: {start_date} / TYPE: {type(start_date)}") logger.debug(f"END_DATE: {end_date} / TYPE: {type(end_date)}") if not start_date or not end_date: raise ValueError("Некорректные даты для генерации отчета.") 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) res_data["check_in"] = ensure_datetime(res_data["check_in"]) res_data["check_out"] = ensure_datetime(res_data["check_out"]) row_data = [ sanitize_text(res_data["hotel_name"]), sanitize_text(str(res_data["reservation_id"])), sanitize_text(str(res_data["room_number"])), sanitize_text(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'), sanitize_text(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: logger.error(f"[ERROR] Error processing reservation {res.id}: {e}") 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") logger.debug(f"PDF output path: {pdf_output_path}") 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