Skip to main content

Overview

Work Order Reprogramming allows you to create new schedule versions of existing work orders while maintaining the link to the original order. Reprogrammed orders (Type 5) inherit key properties from their parent Initial Orders (Type 4) but allow updated schedules, resources, and budgets.
Reprogrammed orders maintain a link to the original work order through the ot_principal field and are sequentially numbered via num_reprogramacion.

Reprogramming Model Properties

The OTE model includes specific fields for managing reprogramming:
operaciones/models/ote_models.py
class OTE(models.Model):
    # ... other fields ...
    num_reprogramacion = models.IntegerField(null=True, blank=True)
    ot_principal = models.IntegerField(null=True, blank=True)
ot_principal
integer
ID of the original work order (only set for Type 5 reprogrammed orders)
num_reprogramacion
integer
Sequential reprogramming number (1st reprogram, 2nd reprogram, etc.)

Reprogramming Detection Properties

Two computed properties help track reprogramming relationships:
operaciones/models/ote_models.py
@property
def tiene_reprogramaciones(self):
    """Retorna True si esta OT tiene reprogramaciones asociadas"""
    if self.id_tipo_id != 4:  # Solo para OTs iniciales
        return False
    
    return OTE.objects.filter(
        ot_principal=self.id,
        id_tipo_id=5,
        estatus__in=[-1, 1]
    ).exists()

@property
def count_reprogramaciones(self):
    """Retorna el número de reprogramaciones asociadas"""
    if self.id_tipo_id != 4:
        return 0
    
    return OTE.objects.filter(
        ot_principal=self.id,
        id_tipo_id=5,
        estatus__in=[-1, 1]
    ).count()

tiene_reprogramaciones

Boolean check if initial order has active reprogrammed versions

count_reprogramaciones

Count of active reprogrammed orders linked to this initial order
These properties only return meaningful data for Initial Orders (Type 4). They filter for active status (-1 or 1) to exclude logically deleted records.

Creating Reprogrammed Orders

The creation process requires linking to an existing Initial Order:
operaciones/views/ote.py
if str(ot.id_tipo_id) == '5':
    ot_principal_id = request.POST.get('ot_principal')
    num_reprogramacion = request.POST.get('num_reprogramacion')

    if not ot_principal_id:
        return JsonResponse({
            'exito': False, 
            'tipo_aviso': 'advertencia', 
            'detalles': 'Para una reprogramación, debe seleccionar la OT Inicial.'
        })
    
    ot.ot_principal = ot_principal_id
    ot.num_reprogramacion = num_reprogramacion
else:
    ot.ot_principal = None
    ot.num_reprogramacion = None
1

Validate Principal Order

Ensure an Initial Order (Type 4) is selected as the parent
2

Set Reprogramming Fields

Link to parent via ot_principal and set sequential number
3

Save Work Order

Persist with Type 5 designation
Reprogrammed orders must have a valid ot_principal reference. The system will reject creation if this is missing.

Retrieving Initial Orders

To populate the Initial Order selector when creating reprogrammed orders:
operaciones/views/ote.py
@require_http_methods(["GET"])
def obtener_ots_principales(request):
    """Obtener todas las OTs para el selector de OT principal"""
    try:
        ot_id = request.GET.get('ot_id')

        ots = OTE.objects.filter(
            id_tipo=4, 
        ).exclude(
            estatus=0,
        ).values('id', 'orden_trabajo', 'descripcion_trabajo', 'oficio_ot').order_by('-id')
        
        data = list(ots)
        return JsonResponse(data, safe=False)
    except Exception as e:
        return JsonResponse([], safe=False)

Response Format

[
  {
    "id": 123,
    "orden_trabajo": "OT-2024-001",
    "descripcion_trabajo": "Pipeline inspection and repair",
    "oficio_ot": "BME-OT-001-2024"
  },
  {
    "id": 122,
    "orden_trabajo": "OT-2024-002",
    "descripcion_trabajo": "Platform structural assessment",
    "oficio_ot": "BME-OT-002-2024"
  }
]
Only active Initial Orders (id_tipo=4, estatus!=0) are returned, ordered by ID descending to show most recent first.

Automatic Step Configuration

Reprogrammed orders use different step templates than initial orders:
operaciones/views/ote.py
if ot.id_tipo_id == 5:
    tipo_paso_busqueda = 5 if ot.id_cliente_id == 1 else 18 if ot.id_cliente_id in [2, 3, 4] else 5
else:
    tipo_paso_busqueda = 5

pasos_a_crear = PasoOt.objects.filter(tipo=tipo_paso_busqueda, activo=True).order_by('id')

if pasos_a_crear:
    detalles_a_crear = []
    for paso in pasos_a_crear:
        detalles_a_crear.append(OTDetalle(
            id_ot_id=ot.id,
            estatus_paso_id=1, 
            id_paso_id=paso.id,
            comentario=""
        ))
    
    OTDetalle.objects.bulk_create(detalles_a_crear)
Uses step template type 5
Uses step template type 18 (alternate client workflow)
Uses step template type 5 (standard workflow)

Reprogramming Number Management

During editing, the system validates and manages reprogramming numbers:
operaciones/views/ote.py
num_reprogramacion = request.POST.get('num_reprogramacion')
if num_reprogramacion and num_reprogramacion.strip(): 
    try:
        ot.num_reprogramacion = int(num_reprogramacion)
    except (ValueError, TypeError):
        return JsonResponse({
            'exito': False,
            'tipo_aviso': 'error',
            'detalles': 'El número de reprogramación debe ser un número válido'
        })
else:
    ot.num_reprogramacion = None
The reprogramming number must be a valid integer. Empty strings are converted to None to maintain database integrity.

DataTable Reprogramming Display

The DataTable includes reprogramming status for each work order:
operaciones/views/ote.py
data.append({
    'id': ot.id,
    'orden_trabajo': ot.orden_trabajo,
    'ot_principal': ot.ot_principal,
    'num_reprogramacion': ot.num_reprogramacion,
    'tiene_reprogramaciones': ot.tiene_reprogramaciones,
    'count_reprogramaciones': ot.count_reprogramaciones,
    # ... other fields ...
})
ot_principal
integer
ID of parent Initial Order (null for Type 4 orders)
num_reprogramacion
integer
Sequential reprogramming number
tiene_reprogramaciones
boolean
Whether this Initial Order has reprogrammed versions
count_reprogramaciones
integer
Count of active reprogrammed orders

Historical Import Script

The system includes a comprehensive import script for historical work orders with reprogramming:
importar_ot.py
def procesar_reprogramacion(fila, contador):
    """
    Procesa una reprogramación (tipo 5)
    """
    try:
        tipo_ot_id = 5  # Siempre 5 para reprogramaciones
        
        try:
            tipo_ot = Tipo.objects.get(id=5)
        except Tipo.DoesNotExist:
            tipo_ot = Tipo.objects.filter(
                nivel_afectacion=2,
                descripcion__icontains="REPROGRAMACION"
            ).first()
        
        # Buscar OT principal
        ot_principal = None
        ot_principal_id = None
        
        if not pd.isna(fila['ot_principal']) and fila['ot_principal']:
            orden_trabajo_principal = str(fila['ot_principal']).strip()
            ot_principal = obtener_ot_principal_por_orden_trabajo(
                orden_trabajo_principal, 
                tipo_ot_id
            )
            
            if ot_principal:
                ot_principal_id = ot_principal.id
                pte_principal = ot_principal.id_pte_header
            else:
                return False, f"No se encontró OT principal: {orden_trabajo_principal}"
        
        if not ot_principal:
            return False, "Reprogramación sin OT principal especificada"

Import Script Key Features

1

Validate Reprogramming Type

Ensures Type 5 designation for reprogrammed orders
2

Locate Parent Order

Searches for Initial Order by orden_trabajo field
3

Inherit PTE Reference

Copies PTE link from parent to maintain document hierarchy
4

Sequential Processing

Processes Initial Orders first, then Reprogrammed Orders to ensure parent existence

Parent Order Resolution

importar_ot.py
def obtener_ot_principal_por_orden_trabajo(orden_trabajo_principal, tipo_ot_actual):
    """
    Busca OT principal (tipo 4) por orden_trabajo para las reprogramadas (tipo 5)
    """
    if tipo_ot_actual != 5:
        return None
    
    try:
        ot_principal = OTE.objects.filter(
            orden_trabajo=orden_trabajo_principal,
            id_tipo_id=4  # Solo OTs iniciales
        ).first()
        
        if ot_principal:
            print(f"✓ OT principal encontrada: {orden_trabajo_principal} (ID: {ot_principal.id})")
            return ot_principal
        else:
            print(f"⚠ OT principal NO encontrada para reprogramación: {orden_trabajo_principal}")
            return None
    except Exception as e:
        print(f"✗ Error buscando OT principal: {e}")
        return None
The import script validates that parent orders are Type 4 and exist before creating reprogrammed orders. This prevents orphaned reprogramming records.

PTE Inheritance

Reprogrammed orders inherit the PTE reference from their parent:
importar_ot.py
if ot_principal:
    ot_principal_id = ot_principal.id
    pte_principal = ot_principal.id_pte_header
    print(f"✓ Usando PTE de OT principal: {pte_principal.oficio_pte}")

# Later in the code...
ot.id_pte_header = pte_principal  # Usar PTE de la OT principal
ot.ot_principal = ot_principal_id
This ensures the entire family of work orders (initial + all reprogrammed versions) remain linked to the same planning document (PTE).

Automatic Sequential Numbering

If no reprogramming number is provided, the system can auto-assign:
importar_ot.py
if not pd.isna(fila['num_reprogramacion']):
    try:
        ot.num_reprogramacion = int(fila['num_reprogramacion'])
    except (ValueError, TypeError):
        if ot_principal_id:
            reprogramaciones = OTE.objects.filter(
                ot_principal=ot_principal_id,
                id_tipo_id=5
            ).count()
            ot.num_reprogramacion = reprogramaciones + 1
        else:
            ot.num_reprogramacion = 1
1

Try Explicit Number

Use provided num_reprogramacion if valid
2

Count Existing Reprograms

Query for existing reprogrammed orders linked to parent
3

Assign Next Sequential

Set number to count + 1 for automatic sequencing

Filtering by Initial Order

The DataTable supports filtering reprogrammed orders by their parent:
operaciones/views/ote.py
tipo_id = request.GET.get('tipo', '4') 
ot_principal_id = request.GET.get('ot_principal', None)

filters = {'estatus__in': [-1, 1]}

if tipo_id:
    filters['id_tipo_id'] = tipo_id
    
if ot_principal_id:
    filters['ot_principal'] = ot_principal_id

ots = OTE.objects.filter(**filters)
tipo
string
Filter by work order type: "4" for initial, "5" for reprogrammed
ot_principal
integer
Filter to show only reprogrammed orders of a specific initial order

Best Practices

Maintain sequential reprogramming numbers (1, 2, 3…) for clear version tracking. The system can auto-generate these if needed.
Reprogrammed orders inherit the PTE reference automatically. Don’t override this unless you have a specific business reason.
While basic info is inherited, always update schedule dates (fecha_inicio_programado, fecha_termino_programado) to reflect the new plan.
Use the comentario field to explain why reprogramming was necessary. This provides valuable audit trail information.

Common Reprogramming Scenarios

Weather Delays

Create reprogrammed order with extended dates when offshore work is delayed by weather

Resource Changes

Reprogram when different vessels, crews, or equipment become necessary

Scope Modifications

Create new version when work scope changes require schedule adjustment

Client Requests

Reprogram to accommodate client-requested timeline changes

URL Endpoints

operaciones/urls.py
path('ot/crear-ot-reprogramacion/', ote.crear_ot, name='crear_ot'),
path('ot/obtener_ot_iniciales/', ote.obtener_ots_principales, name='obtener_ots_principales'),

Next Steps

Work Order Overview

Understand the complete OTE model structure

Scheduling

Learn about timeline and progress tracking

Import Historical Data

Bulk import work orders with reprogramming relationships

Build docs developers (and LLMs) love