Cambios Adicionales
✅ Importado or_ de SQLAlchemy para query del reporte ✅ Backend: 1.0.86 → 1.0.87 🎯 Resultado ✅ Inspecciones solo muestran preguntas activas del checklist ✅ PDFs correctos sin preguntas eliminadas ✅ Cálculo de score preciso (solo preguntas vigentes) ✅ Webhooks envían solo datos relevantes ✅ Reportes con métricas correctas ✅ Respuestas huérfanas de preguntas eliminadas se ignoran automáticamente
This commit is contained in:
@@ -5,7 +5,7 @@ from fastapi import FastAPI, File, UploadFile, Form, Depends, HTTPException, sta
|
|||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||||
from sqlalchemy.orm import Session, joinedload
|
from sqlalchemy.orm import Session, joinedload
|
||||||
from sqlalchemy import func, case
|
from sqlalchemy import func, case, or_
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import os
|
import os
|
||||||
@@ -100,11 +100,14 @@ def send_completed_inspection_to_n8n(inspection, db):
|
|||||||
# Obtener checklist
|
# Obtener checklist
|
||||||
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 todas las respuestas con sus imágenes
|
# Obtener todas las respuestas con sus imágenes - SOLO de preguntas NO eliminadas
|
||||||
answers = db.query(models.Answer).options(
|
answers = db.query(models.Answer).options(
|
||||||
joinedload(models.Answer.media_files),
|
joinedload(models.Answer.media_files),
|
||||||
joinedload(models.Answer.question)
|
joinedload(models.Answer.question)
|
||||||
).filter(models.Answer.inspection_id == inspection.id).all()
|
).join(models.Question).filter(
|
||||||
|
models.Answer.inspection_id == inspection.id,
|
||||||
|
models.Question.is_deleted == False # Excluir preguntas eliminadas
|
||||||
|
).all()
|
||||||
|
|
||||||
# Preparar respuestas con imágenes
|
# Preparar respuestas con imágenes
|
||||||
respuestas_data = []
|
respuestas_data = []
|
||||||
@@ -204,7 +207,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.86"
|
BACKEND_VERSION = "1.0.87"
|
||||||
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
|
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
|
||||||
|
|
||||||
# S3/MinIO configuration
|
# S3/MinIO configuration
|
||||||
@@ -1226,7 +1229,7 @@ def get_inspection(
|
|||||||
current_user: models.User = Depends(get_current_user)
|
current_user: models.User = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
inspection = db.query(models.Inspection).options(
|
inspection = db.query(models.Inspection).options(
|
||||||
joinedload(models.Inspection.checklist).joinedload(models.Checklist.questions),
|
joinedload(models.Inspection.checklist),
|
||||||
joinedload(models.Inspection.mechanic),
|
joinedload(models.Inspection.mechanic),
|
||||||
joinedload(models.Inspection.answers).joinedload(models.Answer.question),
|
joinedload(models.Inspection.answers).joinedload(models.Answer.question),
|
||||||
joinedload(models.Inspection.answers).joinedload(models.Answer.media_files)
|
joinedload(models.Inspection.answers).joinedload(models.Answer.media_files)
|
||||||
@@ -1235,6 +1238,13 @@ def get_inspection(
|
|||||||
if not inspection:
|
if not inspection:
|
||||||
raise HTTPException(status_code=404, detail="Inspección no encontrada")
|
raise HTTPException(status_code=404, detail="Inspección no encontrada")
|
||||||
|
|
||||||
|
# Cargar solo preguntas NO eliminadas del checklist
|
||||||
|
if inspection.checklist:
|
||||||
|
inspection.checklist.questions = db.query(models.Question).filter(
|
||||||
|
models.Question.checklist_id == inspection.checklist.id,
|
||||||
|
models.Question.is_deleted == False
|
||||||
|
).order_by(models.Question.order).all()
|
||||||
|
|
||||||
return inspection
|
return inspection
|
||||||
|
|
||||||
|
|
||||||
@@ -1672,12 +1682,13 @@ def generate_inspection_pdf(inspection_id: int, db: Session) -> str:
|
|||||||
elements.append(Paragraph("📝 DETALLE DE LA INSPECCIÓN", section_header_style))
|
elements.append(Paragraph("📝 DETALLE DE LA INSPECCIÓN", section_header_style))
|
||||||
elements.append(Spacer(1, 5*mm))
|
elements.append(Spacer(1, 5*mm))
|
||||||
|
|
||||||
# Obtener respuestas agrupadas por sección
|
# Obtener respuestas agrupadas por sección - SOLO de preguntas NO eliminadas
|
||||||
answers = db.query(models.Answer).options(
|
answers = db.query(models.Answer).options(
|
||||||
joinedload(models.Answer.media_files),
|
joinedload(models.Answer.media_files),
|
||||||
joinedload(models.Answer.question)
|
joinedload(models.Answer.question)
|
||||||
).join(models.Question).filter(
|
).join(models.Question).filter(
|
||||||
models.Answer.inspection_id == inspection_id
|
models.Answer.inspection_id == inspection_id,
|
||||||
|
models.Question.is_deleted == False # Excluir preguntas eliminadas
|
||||||
).order_by(
|
).order_by(
|
||||||
models.Question.section,
|
models.Question.section,
|
||||||
models.Question.order
|
models.Question.order
|
||||||
@@ -1905,8 +1916,11 @@ def complete_inspection(
|
|||||||
if not inspection:
|
if not inspection:
|
||||||
raise HTTPException(status_code=404, detail="Inspección no encontrada")
|
raise HTTPException(status_code=404, detail="Inspección no encontrada")
|
||||||
|
|
||||||
# Calcular score
|
# Calcular score - SOLO de preguntas NO eliminadas
|
||||||
answers = db.query(models.Answer).filter(models.Answer.inspection_id == inspection_id).all()
|
answers = db.query(models.Answer).join(models.Question).filter(
|
||||||
|
models.Answer.inspection_id == inspection_id,
|
||||||
|
models.Question.is_deleted == False # Excluir preguntas eliminadas
|
||||||
|
).all()
|
||||||
total_score = sum(a.points_earned for a in answers)
|
total_score = sum(a.points_earned for a in answers)
|
||||||
flagged_count = sum(1 for a in answers if a.is_flagged)
|
flagged_count = sum(1 for a in answers if a.is_flagged)
|
||||||
|
|
||||||
@@ -2078,9 +2092,10 @@ def update_answer(
|
|||||||
if inspection.status == "completed":
|
if inspection.status == "completed":
|
||||||
print(f"🔄 Regenerando PDF para inspección completada #{inspection.id}")
|
print(f"🔄 Regenerando PDF para inspección completada #{inspection.id}")
|
||||||
|
|
||||||
# Recalcular score de la inspección
|
# Recalcular score de la inspección - SOLO de preguntas NO eliminadas
|
||||||
answers = db.query(models.Answer).filter(
|
answers = db.query(models.Answer).join(models.Question).filter(
|
||||||
models.Answer.inspection_id == inspection.id
|
models.Answer.inspection_id == inspection.id,
|
||||||
|
models.Question.is_deleted == False # Excluir preguntas eliminadas
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
inspection.score = sum(a.points_earned for a in answers)
|
inspection.score = sum(a.points_earned for a in answers)
|
||||||
@@ -2243,9 +2258,10 @@ def admin_edit_answer(
|
|||||||
if inspection and inspection.status == "completed":
|
if inspection and inspection.status == "completed":
|
||||||
print(f"🔄 Regenerando PDF para inspección completada #{inspection.id} (admin-edit)")
|
print(f"🔄 Regenerando PDF para inspección completada #{inspection.id} (admin-edit)")
|
||||||
|
|
||||||
# Recalcular score de la inspección
|
# Recalcular score de la inspección - SOLO de preguntas NO eliminadas
|
||||||
answers = db.query(models.Answer).filter(
|
answers = db.query(models.Answer).join(models.Question).filter(
|
||||||
models.Answer.inspection_id == inspection.id
|
models.Answer.inspection_id == inspection.id,
|
||||||
|
models.Question.is_deleted == False # Excluir preguntas eliminadas
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
inspection.score = sum(a.points_earned for a in answers)
|
inspection.score = sum(a.points_earned for a in answers)
|
||||||
@@ -3298,7 +3314,11 @@ def get_inspections_report(
|
|||||||
.join(models.Checklist, models.Inspection.checklist_id == models.Checklist.id)\
|
.join(models.Checklist, models.Inspection.checklist_id == models.Checklist.id)\
|
||||||
.join(models.User, models.Inspection.mechanic_id == models.User.id)\
|
.join(models.User, models.Inspection.mechanic_id == models.User.id)\
|
||||||
.outerjoin(models.Answer, models.Answer.inspection_id == models.Inspection.id)\
|
.outerjoin(models.Answer, models.Answer.inspection_id == models.Inspection.id)\
|
||||||
.filter(models.Inspection.is_active == True)
|
.outerjoin(models.Question, models.Answer.question_id == models.Question.id)\
|
||||||
|
.filter(
|
||||||
|
models.Inspection.is_active == True,
|
||||||
|
or_(models.Question.is_deleted == False, models.Question.id == None) # Solo contar answers de preguntas no eliminadas o si no hay answer
|
||||||
|
)
|
||||||
|
|
||||||
# Aplicar filtros
|
# Aplicar filtros
|
||||||
if start_date:
|
if start_date:
|
||||||
|
|||||||
Reference in New Issue
Block a user