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:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user