From f57d7328e17662b1531c56d0657e5628cd9295a9 Mon Sep 17 00:00:00 2001 From: ronalds Date: Fri, 28 Nov 2025 09:58:18 -0300 Subject: [PATCH] Backend v1.0.82: PDF con logos dual (empresa + checklist) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Generación de PDF ahora muestra dos logos en el encabezado - Logo izquierda: logo de la empresa (AIConfiguration) - Logo derecha: logo del checklist específico (o empresa como fallback) - Nueva función helper load_logo() para reutilización - Layout horizontal con tabla de 3 columnas para separación visual - Frontend: sin cambios (v1.0.79) --- backend/app/main.py | 92 ++++++++++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 26 deletions(-) diff --git a/backend/app/main.py b/backend/app/main.py index 13a34a3..e1ca000 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -204,7 +204,7 @@ def send_completed_inspection_to_n8n(inspection, db): # No lanzamos excepción para no interrumpir el flujo normal -BACKEND_VERSION = "1.0.81" +BACKEND_VERSION = "1.0.82" app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION) # S3/MinIO configuration @@ -1401,57 +1401,97 @@ def generate_inspection_pdf(inspection_id: int, db: Session) -> str: mechanic = db.query(models.User).filter(models.User.id == inspection.mechanic_id).first() checklist = db.query(models.Checklist).filter(models.Checklist.id == inspection.checklist_id).first() - # Obtener logo principal de configuración para el PDF + # Obtener logo principal de configuración (empresa) config = db.query(models.AIConfiguration).filter(models.AIConfiguration.is_active == True).first() - logo_url_to_use = None + company_logo_url = None if config and getattr(config, "logo_url", None): - logo_url_to_use = config.logo_url - print(f"📸 Usando logo principal de configuración: {logo_url_to_use}") + company_logo_url = config.logo_url + print(f"📸 Logo de la empresa: {company_logo_url}") else: - print("ℹ️ No hay logo principal configurado") + print("ℹ️ No hay logo de empresa configurado") + + # Obtener logo del checklist + checklist_logo_url = None + if checklist and getattr(checklist, "logo_url", None): + checklist_logo_url = checklist.logo_url + print(f"📋 Logo del checklist: {checklist_logo_url}") + else: + # Si no tiene logo, usar el de la empresa como fallback + checklist_logo_url = company_logo_url + print(f"ℹ️ Checklist sin logo, usando logo de empresa como fallback") # ===== PORTADA ===== elements.append(Spacer(1, 10*mm)) - # Logo principal (si existe) - if logo_url_to_use: + # Función helper para cargar y dimensionar logos + def load_logo(logo_url, max_width_mm=45, max_height_mm=35): + """Carga un logo desde URL y retorna objeto Image con dimensiones ajustadas""" + if not logo_url: + return None try: - print(f"🔍 Intentando cargar logo desde: {logo_url_to_use}") - logo_resp = requests.get(logo_url_to_use, timeout=10) - print(f"📡 Respuesta del servidor: {logo_resp.status_code}") + print(f"🔍 Cargando logo desde: {logo_url}") + logo_resp = requests.get(logo_url, timeout=10) + print(f"📡 Respuesta: {logo_resp.status_code}") if logo_resp.status_code == 200: logo_bytes = BytesIO(logo_resp.content) - # Crear imagen con tamaño máximo, manteniendo proporciones logo_img = RLImage(logo_bytes) - # Ajustar tamaño manteniendo aspect ratio (máximo 50mm de ancho) + # Ajustar tamaño manteniendo aspect ratio aspect = logo_img.imageHeight / float(logo_img.imageWidth) - logo_width = 50*mm + logo_width = max_width_mm * mm logo_height = logo_width * aspect - # Si la altura es muy grande, ajustar por altura - if logo_height > 40*mm: - logo_height = 40*mm + # Si la altura excede el máximo, ajustar por altura + if logo_height > max_height_mm * mm: + logo_height = max_height_mm * mm logo_width = logo_height / aspect logo_img.drawWidth = logo_width logo_img.drawHeight = logo_height - logo_table = Table([[logo_img]], colWidths=[180*mm]) - logo_table.setStyle(TableStyle([ - ('ALIGN', (0, 0), (-1, -1), 'CENTER'), - ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), - ])) - elements.append(logo_table) - elements.append(Spacer(1, 5*mm)) - print(f"✅ Logo cargado correctamente ({logo_width/mm:.1f}mm x {logo_height/mm:.1f}mm)") + print(f"✅ Logo cargado ({logo_width/mm:.1f}mm x {logo_height/mm:.1f}mm)") + return logo_img else: - print(f"❌ Error HTTP al cargar logo: {logo_resp.status_code}") + print(f"❌ Error HTTP: {logo_resp.status_code}") except Exception as e: print(f"⚠️ Error cargando logo: {e}") import traceback traceback.print_exc() + return None + + # Cargar ambos logos + company_logo = load_logo(company_logo_url, max_width_mm=50, max_height_mm=35) + checklist_logo = load_logo(checklist_logo_url, max_width_mm=50, max_height_mm=35) + + # Crear tabla con logos en los extremos + logo_row = [] + + # Logo empresa (izquierda) + if company_logo: + logo_row.append(company_logo) + else: + logo_row.append(Paragraph("", styles['Normal'])) # Espacio vacío + + # Espaciador central + logo_row.append(Paragraph("", styles['Normal'])) + + # Logo checklist (derecha) + if checklist_logo: + logo_row.append(checklist_logo) + else: + logo_row.append(Paragraph("", styles['Normal'])) # Espacio vacío + + # Crear tabla con logos + logo_table = Table([logo_row], colWidths=[60*mm, 60*mm, 60*mm]) + logo_table.setStyle(TableStyle([ + ('ALIGN', (0, 0), (0, 0), 'LEFT'), # Logo empresa a la izquierda + ('ALIGN', (1, 0), (1, 0), 'CENTER'), # Centro vacío + ('ALIGN', (2, 0), (2, 0), 'RIGHT'), # Logo checklist a la derecha + ('VALIGN', (0, 0), (-1, -1), 'TOP'), + ])) + elements.append(logo_table) + elements.append(Spacer(1, 5*mm)) # Título con diseño moderno elements.append(Paragraph("📋 INFORME DE INSPECCIÓN VEHICULAR", title_style))