Skip to main content

Overview

Each PTE contains multiple workflow steps (PTEDetalle records) that track the progress of specific tasks. These steps have their own statuses, dates, and can include substeps for complex processes.

Step Status Lifecycle

Workflow steps use the following status values:

1 - PENDIENTE

Step has not been started yet

2 - PROCESO

Work is actively being performed

3 - COMPLETADO

Step has been successfully completed

14 - NO APLICA

Step is not applicable for this PTE

4 - CANCELADO

Step has been cancelled

15 - SUSPENDIDA

Step has been temporarily suspended

Retrieving Step Details

The datatable_pte_detalle function provides detailed step information for a specific PTE: URL: /pte/detalle/datatable/
Method: GET
Parameters: pte_header_id
def datatable_pte_detalle(request):
    """Datatable para detalle de PTE"""
    pte_header_id = request.GET.get('pte_header_id')
    
    detalles = PTEDetalle.objects.filter(
        id_pte_header_id=pte_header_id, 
        id_paso__tipo=1  # Main steps only
    ).select_related('id_paso','id_pte_header').annotate(
        estatus_pte_texto=Case(
            When(estatus_paso=1, then=Value('PENDIENTE')),
            When(estatus_paso=2, then=Value('PROCESO')),
            When(estatus_paso=3, then=Value('COMPLETADO')),
            When(estatus_paso=4, then=Value('CANCELADO')),
            When(estatus_paso=14, then=Value('NO APLICA')),
            When(estatus_paso=15, then=Value('SUSPENDIDA')),
            default=Value('DESCONOCIDO'),
            output_field=CharField()
        )
    ).order_by('id')
The query uses select_related for performance optimization, reducing database queries by prefetching related Paso and PTEHeader objects.

Substeps (Tipo=2)

Some steps, particularly Step 4 (Volumetría), contain substeps for tracking detailed deliverables:
for detalle in detalles:
    if detalle.id_paso_id == 4:  # Volumetría step
        subpasos = PTEDetalle.objects.filter(
            id_pte_header_id=pte_header_id,
            id_paso__tipo=2  # Substeps
        )
        total_subpasos = subpasos.count()
        subpasos_completados = subpasos.filter(estatus_paso__in=[3,14]).count()
        
        if total_subpasos > 0:
            detalle.progreso_subpasos = (subpasos_completados / total_subpasos) * 100
        else:
            detalle.progreso_subpasos = 0
Substeps are identified by id_paso.tipo=2, while main steps have tipo=1. This allows hierarchical tracking of complex workflows.

Updating Step Status

The cambiar_estatus_paso function handles step status updates: URL: /pte/cambiar_estatus_paso/
Method: POST
Authentication: Required
Activity Logging: Enabled

Parameters

paso_id
integer
required
ID of the PTEDetalle record to update
nuevo_estatus
integer
required
New status value (1-15)
comentario
string
Optional comment about the status change
fecha_entrega
date
Delivery date (automatically set to now if status=3 and not provided)

Implementation

@require_http_methods(["POST"])
@login_required
@registrar_actividad
def cambiar_estatus_paso(request):
    """Cambiar estatus de un paso del PTE"""
    try:
        paso_id = request.POST.get('paso_id')
        nuevo_estatus = request.POST.get('nuevo_estatus')
        comentario = request.POST.get('comentario','')
        fecha_entrega = request.POST.get('fecha_entrega','')
        
        if not paso_id or not nuevo_estatus:
            return JsonResponse({
                'exito': False,
                'tipo_aviso': 'advertencia',
                'detalles': 'Datos incompletos'
            })
        
        detalle = PTEDetalle.objects.get(id=paso_id)
        pte_header_id = detalle.id_pte_header_id
        
        estatus_anterior = detalle.estatus_paso_id
        
        # Update status and comment
        detalle.estatus_paso_id = int(nuevo_estatus)
        if comentario:
            detalle.comentario = comentario
        else:
            detalle.comentario = None

        # Auto-set delivery date when completing
        if int(nuevo_estatus) == 3:
            if fecha_entrega:
                detalle.fecha_entrega = fecha_entrega
            else:
                detalle.fecha_entrega = timezone.now()        
        elif estatus_anterior == 3 and int(nuevo_estatus) != 3:
            detalle.fecha_entrega = None
            
        detalle.save()
        
        # Check if this updates Step 4 automatically
        paso_actualizado_4 = verificar_y_actualizar_paso_4(pte_header_id)
        
        # Recalculate overall progress
        detalles_pte = PTEDetalle.objects.filter(id_pte_header_id=detalle.id_pte_header_id)
        total_pasos = detalles_pte.count()
        pasos_completados = detalles_pte.filter(estatus_paso=3).count()
        
        progreso = 0
        if total_pasos > 0:
            progreso = (pasos_completados / total_pasos) * 100
        
        return JsonResponse({
            'exito': True,
            'tipo_aviso': 'exito',
            'detalles': 'Estatus actualizado correctamente',
            'progreso': round(progreso),
            'pasos_completados': pasos_completados,
            'total_pasos': total_pasos,
            'paso_actualizado_4': paso_actualizado_4
        })
When changing a step’s status FROM “Completado” (3) to any other status, the fecha_entrega is automatically cleared to maintain data integrity.

Automatic Step 4 Completion

Step 4 (Volumetría de Materiales) is automatically completed when all of its substeps are done:
def verificar_y_actualizar_paso_4(pte_header_id):
    """Verificar si todos los subpasos del paso 4 están completados y actualizarlo automáticamente"""
    try:
        paso_4 = PTEDetalle.objects.filter(
            id_pte_header_id=pte_header_id,
            id_paso_id=4  # Volumetría step
        ).first()
        
        if not paso_4:
            return False
        
        subpasos = PTEDetalle.objects.filter(
            id_pte_header_id=pte_header_id,
            id_paso__tipo=2  # Substeps
        )
        
        total_subpasos = subpasos.count()
        
        if total_subpasos == 0:
            return False
        
        subpasos_completados = subpasos.filter(estatus_paso__in=[3, 14]).count()
        
        # Auto-complete if all substeps are done
        if subpasos_completados == total_subpasos and paso_4.estatus_paso_id != 3:
            paso_4.estatus_paso_id = 3
            paso_4.comentario = "Entregables finalizados"
            paso_4.fecha_termino = timezone.now().date()
            paso_4.save()
            return True  # Indicates paso 4 was auto-updated
        
        return False
This automation ensures that parent steps accurately reflect the completion of all their child substeps without manual intervention.

Date Management

Each step tracks three important dates:

Date Fields

FieldPurposeWhen Set
fecha_inicioStart dateWhen work begins
fecha_terminoCompletion dateWhen work finishes
fecha_entregaDelivery dateAuto-set when status=3

Updating Dates

URL: /pte/actualizar-fecha/
Method: POST
@require_http_methods(["POST"])
@login_required
@registrar_actividad
def actualizar_fecha(request):
    """Actualizar fecha de inicio de un paso"""
    try:
        id_paso = request.POST.get('id_paso')
        fecha = request.POST.get('fecha')
        tipo = request.POST.get('tipo')  # 1=inicio, 2=termino, 3=entrega
        
        paso_detalle = PTEDetalle.objects.get(id=id_paso)
        
        if tipo == '1':
            paso_detalle.fecha_inicio = fecha if fecha else None
        elif tipo == '2':
            paso_detalle.fecha_termino = fecha if fecha else None
        elif tipo == '3':
            paso_detalle.fecha_entrega = fecha if fecha else None
            
        paso_detalle.save()
        
        return JsonResponse({
            'exito': True,
            'tipo_aviso': 'exito',
            'detalles': 'Fecha actualizada correctamente',
            'fecha_actualizada': fecha
        })

File Attachments

Steps can have associated files/URLs stored in the archivo field: URL: /pte/guardar_archivo_pte/
Method: POST
@require_http_methods(["POST"])
@login_required
@registrar_actividad
def guardar_archivo_pte(request):
    """Guardar Archivo de entregables de pte"""
    try:
        paso_id = request.POST.get('paso_id')
        url = request.POST.get('archivo')
        
        paso = PTEDetalle.objects.get(id=paso_id)
        paso.archivo = url
        paso.save()
        
        return JsonResponse({
            'tipo_aviso': 'exito',
            'detalles': 'URL asignada correctamente',
            'exito': True
        })
The archivo field stores text (typically URLs or file paths) rather than binary file data, allowing flexibility in how files are stored and accessed.

Progress Tracking

There are two progress tracking endpoints:

Overall PTE Progress

URL: /pte/obtener-progreso-general-pte/
def obtener_progreso_general_pte(request):
    pte_id = request.GET.get('pte_id')
    
    detalles = PTEDetalle.objects.filter(id_pte_header_id=pte_id)
    total_pasos = detalles.count()
    pasos_completados = detalles.filter(estatus_paso__in=[3, 14]).count()
    
    progreso = 0
    if total_pasos > 0:
        progreso = (pasos_completados / total_pasos) * 100
    
    return JsonResponse({
        'exito': True,
        'progreso': round(progreso),
        'pasos_completados': pasos_completados,
        'total_pasos': total_pasos
    })

Step 4 (Volumetría) Progress

URL: /pte/obtener-progreso-paso4/
def obtener_progreso_paso4(request):
    pte_header_id = request.GET.get('pte_header_id')
    
    subpasos = PTEDetalle.objects.filter(
        id_pte_header_id=pte_header_id,
        id_paso__tipo=2  # Substeps
    )
    
    total_subpasos = subpasos.count()
    subpasos_completados = subpasos.filter(estatus_paso__in=[3, 14]).count()
    
    progreso = 0
    if total_subpasos > 0:
        progreso = (subpasos_completados / total_subpasos) * 100
    
    return JsonResponse({
        'exito': True,
        'progreso': round(progreso),
        'subpasos_completados': subpasos_completados,
        'total_subpasos': total_subpasos
    })

DataTable Response Format

{
  "draw": 1,
  "recordsTotal": 15,
  "recordsFiltered": 15,
  "data": [
    {
      "id": 123,
      "orden": "1",
      "desc_paso": "Recepción de Solicitud",
      "tipo_paso": 1,
      "estatus_pte": 3,
      "estatus_pte_texto": "COMPLETADO",
      "fecha_entrega": "15/01/2024",
      "fecha_inicio": "2024-01-10",
      "fecha_termino": "2024-01-15",
      "comentario": "Recibido correctamente",
      "es_subpaso": false,
      "progreso_subpasos": null,
      "folio_pte": "PTE-2024-001",
      "archivo": "https://example.com/doc.pdf"
    }
  ]
}

Status Tracking

Learn about overall PTE status management

Creating PTEs

Understand how workflow steps are initialized

Build docs developers (and LLMs) love