Backend v1.0.82: PDF con logos dual (empresa + checklist)

- 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)
This commit is contained in:
2025-11-28 09:58:18 -03:00
parent 6d006d3b13
commit f57d7328e1

View File

@@ -204,7 +204,7 @@ def send_completed_inspection_to_n8n(inspection, db):
# No lanzamos excepción para no interrumpir el flujo normal # 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) app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
# S3/MinIO configuration # 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() 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() 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() 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): if config and getattr(config, "logo_url", None):
logo_url_to_use = config.logo_url company_logo_url = config.logo_url
print(f"📸 Usando logo principal de configuración: {logo_url_to_use}") print(f"📸 Logo de la empresa: {company_logo_url}")
else: 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 ===== # ===== PORTADA =====
elements.append(Spacer(1, 10*mm)) elements.append(Spacer(1, 10*mm))
# Logo principal (si existe) # Función helper para cargar y dimensionar logos
if logo_url_to_use: 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: try:
print(f"🔍 Intentando cargar logo desde: {logo_url_to_use}") print(f"🔍 Cargando logo desde: {logo_url}")
logo_resp = requests.get(logo_url_to_use, timeout=10) logo_resp = requests.get(logo_url, timeout=10)
print(f"📡 Respuesta del servidor: {logo_resp.status_code}") print(f"📡 Respuesta: {logo_resp.status_code}")
if logo_resp.status_code == 200: if logo_resp.status_code == 200:
logo_bytes = BytesIO(logo_resp.content) logo_bytes = BytesIO(logo_resp.content)
# Crear imagen con tamaño máximo, manteniendo proporciones
logo_img = RLImage(logo_bytes) 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) aspect = logo_img.imageHeight / float(logo_img.imageWidth)
logo_width = 50*mm logo_width = max_width_mm * mm
logo_height = logo_width * aspect logo_height = logo_width * aspect
# Si la altura es muy grande, ajustar por altura # Si la altura excede el máximo, ajustar por altura
if logo_height > 40*mm: if logo_height > max_height_mm * mm:
logo_height = 40*mm logo_height = max_height_mm * mm
logo_width = logo_height / aspect logo_width = logo_height / aspect
logo_img.drawWidth = logo_width logo_img.drawWidth = logo_width
logo_img.drawHeight = logo_height logo_img.drawHeight = logo_height
logo_table = Table([[logo_img]], colWidths=[180*mm]) print(f"✅ Logo cargado ({logo_width/mm:.1f}mm x {logo_height/mm:.1f}mm)")
logo_table.setStyle(TableStyle([ return logo_img
('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)")
else: else:
print(f"❌ Error HTTP al cargar logo: {logo_resp.status_code}") print(f"❌ Error HTTP: {logo_resp.status_code}")
except Exception as e: except Exception as e:
print(f"⚠️ Error cargando logo: {e}") print(f"⚠️ Error cargando logo: {e}")
import traceback import traceback
traceback.print_exc() 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 # Título con diseño moderno
elements.append(Paragraph("📋 INFORME DE INSPECCIÓN VEHICULAR", title_style)) elements.append(Paragraph("📋 INFORME DE INSPECCIÓN VEHICULAR", title_style))