Skip to main content

Overview

SASCOP tracks both planned and actual schedules for work orders, calculating progress based on time elapsed and step completion. The system supports dual timeline tracking for programmed vs. real execution dates.
Progress is calculated using a weighted formula: 70% time-based progress + 30% step completion

Schedule Fields

Each work order maintains four key date fields:
fecha_inicio_programado
date
Planned start date for the work order
fecha_inicio_real
date
Actual date when work began
fecha_termino_programado
date
Planned completion date
fecha_termino_real
date
Actual date when work was completed
plazo_dias
integer
Total duration in days (calculated or manually set)

Progress Calculation Algorithm

The system calculates progress metrics in the DataTable view:
operaciones/views/ote.py
today = date.today()

for ot in ots:
    detalles = ot.detalles.all()
    total_pasos = detalles.count()
    pasos_completados = detalles.filter(estatus_paso_id__in=[3]).count()
    
    progreso_pasos = 0
    if total_pasos > 0:
        progreso_pasos = int((pasos_completados / total_pasos) * 100)
    
    progreso_tiempo = 0
    dias_restantes = 0
    dias_transcurridos = 0
    plazo_total = 0
    
    if ot.fecha_inicio_programado and ot.fecha_termino_programado:
        fecha_inicio = ot.fecha_inicio_programado
        fecha_termino = ot.fecha_termino_programado
        
        if fecha_inicio and fecha_termino:
            plazo_total = (fecha_termino - fecha_inicio + timedelta(days=1)).days
            if plazo_total > 0:
                if today < fecha_inicio:
                    progreso_tiempo = 0
                    dias_restantes = plazo_total
                elif today > fecha_termino:
                    progreso_tiempo = 100
                    dias_restantes = 0
                    dias_transcurridos = plazo_total
                else:
                    dias_transcurridos = (today - fecha_inicio).days
                    progreso_tiempo = int((dias_transcurridos / plazo_total) * 100)
                    dias_restantes = max(0, plazo_total - dias_transcurridos)

Progress Components

1

Step Progress Calculation

Count completed steps (estatus_paso_id = 3) and divide by total steps
progreso_pasos = int((pasos_completados / total_pasos) * 100)
2

Time Progress Calculation

Calculate elapsed days as percentage of total timeline
dias_transcurridos = (today - fecha_inicio).days
progreso_tiempo = int((dias_transcurridos / plazo_total) * 100)
3

Weighted Final Progress

Combine both metrics with 70/30 weighting
progreso_final = int((progreso_tiempo * 0.7) + (progreso_pasos * 0.3))
If the current date is before the start date, progress is 0%. If after the end date, time progress is 100%.

Real vs. Programmed Progress

The system tracks two parallel progress calculations:

Programmed Progress

Based on planned dates (fecha_inicio_programado and fecha_termino_programado):
operaciones/views/ote.py
if ot.fecha_inicio_programado and ot.fecha_termino_programado:
    fecha_inicio = ot.fecha_inicio_programado
    fecha_termino = ot.fecha_termino_programado
    
    plazo_total = (fecha_termino - fecha_inicio + timedelta(days=1)).days
    if plazo_total > 0:
        if today < fecha_inicio:
            progreso_tiempo = 0
            dias_restantes = plazo_total
        elif today > fecha_termino:
            progreso_tiempo = 100
            dias_restantes = 0
            dias_transcurridos = plazo_total
        else:
            dias_transcurridos = (today - fecha_inicio).days
            progreso_tiempo = int((dias_transcurridos / plazo_total) * 100)
            dias_restantes = max(0, plazo_total - dias_transcurridos)

Real Progress

Based on actual execution dates (fecha_inicio_real and fecha_termino_programado):
operaciones/views/ote.py
if ot.fecha_inicio_real and ot.fecha_termino_programado:
    fecha_inicio_r = ot.fecha_inicio_real
    fecha_termino_r = ot.fecha_termino_programado

    plazo_total_real = (fecha_termino_r - fecha_inicio_r).days + 1
    
    if plazo_total_real > 0:
        if today < fecha_inicio_r:
            progreso_tiempo_real = 0
            dias_restantes_real = plazo_total_real
            dias_transcurridos_real = 0
        elif today > fecha_termino_r:
            progreso_tiempo_real = 100
            dias_restantes_real = 0
            dias_transcurridos_real = plazo_total_real
        else:
            dias_transcurridos_real = (today - fecha_inicio_r).days
            progreso_tiempo_real = int((dias_transcurridos_real / plazo_total_real) * 100)
            dias_restantes_real = max(0, plazo_total_real - dias_transcurridos_real)

progreso_final_real = int((progreso_tiempo_real * 0.7) + (progreso_pasos * 0.3))

Programmed Progress

Tracks adherence to the original plan using planned dates

Real Progress

Reflects actual execution using real start date and planned end date

Progress Retrieval API

Real-time progress data can be fetched via AJAX:
operaciones/views/ote.py
@require_http_methods(["GET"])
@login_required
def obtener_progreso_general_ot(request):
    """
    Obtener progreso general actualizado de una OT.
    """
    try:
        ot_id = request.GET.get('ot_id')
        
        if not ot_id:
            return JsonResponse({
                'exito': False,
                'detalles': 'ID de OT no proporcionado'
            })
        
        try:
            ot = OTE.objects.get(pk=ot_id) 
        except OTE.DoesNotExist:
            return JsonResponse({'exito': False, 'detalles': 'OT no encontrada'})

        detalles = ot.detalles.all() 
        total_pasos = detalles.count()
        pasos_completados = detalles.filter(estatus_paso=3).count() 
        
        progreso_pasos = 0
        if total_pasos > 0:
            progreso_pasos = int((pasos_completados / total_pasos) * 100)

        progreso_tiempo = 0
        dias_restantes = 0
        dias_transcurridos = 0
        plazo_total = 0
        today = date.today()

        if ot.fecha_inicio_programado and ot.fecha_termino_programado:
            fecha_inicio = ot.fecha_inicio_programado
            fecha_termino = ot.fecha_termino_programado
            
            plazo_total = (fecha_termino - fecha_inicio).days + 1
            
            if plazo_total > 0:
                if today < fecha_inicio:
                    progreso_tiempo = 0
                    dias_restantes = plazo_total
                    dias_transcurridos = 0
                elif today > fecha_termino:
                    progreso_tiempo = 100
                    dias_restantes = 0
                    dias_transcurridos = plazo_total
                else:
                    dias_transcurridos = (today - fecha_inicio).days
                    progreso_tiempo = int((dias_transcurridos / plazo_total) * 100)
                    dias_restantes = max(0, plazo_total - dias_transcurridos)

        progreso_final = int((progreso_tiempo * 0.7) + (progreso_pasos * 0.3))
        progreso_final = min(100, max(0, progreso_final))

        return JsonResponse({
            'exito': True,
            'ot_id': ot_id,
            'progreso': progreso_final,
            'progreso_pasos': progreso_pasos,
            'pasos_completados': pasos_completados,
            'total_pasos': total_pasos,
            'dias_transcurridos': dias_transcurridos,
            'plazo_total': plazo_total,
            'dias_transcurridos_real': dias_transcurridos_real,
            'plazo_total_real': plazo_total_real,
            'progreso_tiempo': progreso_tiempo,
            'dias_restantes': dias_restantes
        })

Response Format

{
  "exito": true,
  "ot_id": "123",
  "progreso": 65,
  "progreso_pasos": 50,
  "pasos_completados": 5,
  "total_pasos": 10,
  "dias_transcurridos": 45,
  "plazo_total": 90,
  "dias_transcurridos_real": 40,
  "plazo_total_real": 85,
  "progreso_tiempo": 50,
  "dias_restantes": 45
}
progreso
integer
Overall progress percentage (0-100)
progreso_pasos
integer
Step completion percentage
progreso_tiempo
integer
Time-based progress percentage
dias_transcurridos
integer
Days elapsed since programmed start
dias_restantes
integer
Days remaining until programmed end
plazo_total
integer
Total programmed duration in days

Status Management

Work order status affects timeline tracking:
operaciones/views/ote.py
@require_http_methods(["POST"])
@login_required
@registrar_actividad
def cambiar_estatus_ot(request):
    """Cambiar estatus de OT"""
    try:
        ot_id = request.POST.get('ot_id')
        nuevo_estatus_id = request.POST.get('nuevo_estatus_id')
        comentario = request.POST.get('comentario', '')
        fecha_entrega = request.POST.get('fecha_entrega', None)
        
        ot = OTE.objects.get(id=ot_id)
        estatus_anterior = ot.id_estatus_ot_id
        ot.id_estatus_ot_id = nuevo_estatus_id
        ot.comentario = comentario

        if int(nuevo_estatus_id) == 10 and int(estatus_anterior) != 10:
            if fecha_entrega:
                ot.fecha_termino_real = fecha_entrega
            else:
                ot.fecha_termino_real = datetime.now()
        elif int(nuevo_estatus_id) != 10 and int(estatus_anterior) == 10:
            ot.fecha_termino_real = None
        
        ot.save()
When status changes to 10 (TERMINADA/Completed), the fecha_termino_real is automatically set to today if not provided.

Step Date Management

Individual step dates can be updated independently:
operaciones/views/ote.py
@require_http_methods(["POST"])
@login_required
@registrar_actividad
def actualizar_fecha_ot(request):
    """Actualizar fechas de un paso de OT"""
    try:
        id_paso = request.POST.get('id_paso')
        fecha = request.POST.get('fecha')
        tipo = request.POST.get('tipo')
        
        paso_detalle = OTDetalle.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()
tipo
string
required
Date type to update:
  • "1": Start date (fecha_inicio)
  • "2": End date (fecha_termino)
  • "3": Delivery date (fecha_entrega)

Yard Phase Scheduling

For work orders requiring yard phases:
operaciones/models/ote_models.py
requiere_patio = models.BooleanField(default=False, verbose_name="Requiere Fase en Patio")
fecha_inicio_patio = models.DateField(blank=True, null=True, verbose_name="Inicio Fase Patio")
fecha_fin_patio = models.DateField(blank=True, null=True, verbose_name="Fin Fase Patio")
Yard phase dates track the land-based preparation period before offshore deployment. This is separate from the main work order timeline.

DataTable Progress Display

The progress data is returned in the DataTable API for grid display:
operaciones/views/ote.py
data.append({
    'id': ot.id,
    'orden_trabajo': ot.orden_trabajo,
    'total_pasos': total_pasos,
    'pasos_completados': pasos_completados,
    'progreso_pasos': progreso_pasos,
    'progreso_tiempo': progreso_tiempo,
    'progreso_final': progreso_final,
    'dias_restantes': dias_restantes,
    'dias_transcurridos': dias_transcurridos,
    'plazo_total': plazo_total,
    'progreso_tiempo_real': progreso_tiempo_real,
    'progreso_final_real': progreso_final_real,
    'dias_restantes_real': dias_restantes_real,
    'dias_transcurridos_real': dias_transcurridos_real,
    'plazo_total_real': plazo_total_real,
})

Best Practices

Both fecha_inicio_programado and fecha_termino_programado should be set to enable progress tracking. Without these, time-based progress will be 0%.
Set fecha_inicio_real when work actually begins to track schedule variance. The system compares real vs. programmed timelines.
Mark steps complete (estatus_paso_id = 3) to maintain accurate overall progress. Step progress contributes 30% to the final percentage.
Review both progreso_final (programmed) and progreso_final_real (actual) to identify schedule delays early.

Next Steps

Reprogramming

Handle schedule changes and create reprogrammed orders

Step Management

Manage workflow steps and deliverables

Import Schedules

Import project schedules from Excel and MPP files

Build docs developers (and LLMs) love