Overview
PTEs (Propuestas Técnico-Económicas) are technical-economic proposals that represent the planning phase of projects. This guide covers the complete PTE lifecycle.
Understanding PTEs
What is a PTE? A PTE is a comprehensive proposal document that includes:
Project description and scope
Technical requirements
Timeline and milestones
Resource allocation
Cost estimates
Client information
PTE Workflow Stages
Creation
Initial proposal is created with basic information
Planning
Steps are defined and assigned
In Process
Work progresses through defined steps
Completion
All steps are finished and deliverables submitted
Work Order Creation
Approved PTEs can generate work orders (OTEs)
Creating a New PTE
Step-by-Step Guide
Navigate to PTE Module
Go to Operaciones > PTEs > Nuevo
Fill Basic Information
Enter required fields:
Oficio PTE Official document number (e.g., PTE-2026-001)
Oficio Solicitud Request document number
Cliente Select client organization
Set Deadlines
Configure timeline:
Fecha Solicitud : Request date
Plazo Días : Duration in days
Prioridad : Priority level (1-5)
Assign Responsibility
Select Responsable del Proyecto from the dropdown
Submit
Click Guardar to create the PTE
Backend Implementation
@require_http_methods ([ "POST" ])
@login_required
@registrar_actividad
def crear_pte ( request ):
# Extract form data
oficio_pte = request. POST .get( 'oficio_pte' , 'SIN FOLIO' )
descripcion_trabajo = request. POST .get( 'descripcion_trabajo' , 'POR DEFINIR' )
id_cliente = request. POST .get( 'id_cliente' )
responsable_proyecto = request. POST .get( 'responsable_proyecto' )
plazo_dias = int (request. POST .get( 'plazo_dias' , 0 ))
# Validate required fields
if not id_cliente:
return JsonResponse({
'exito' : False ,
'detalles' : 'El cliente es obligatorio'
})
# Create PTE header
pte_header = PTEHeader.objects.create(
oficio_pte = oficio_pte,
descripcion_trabajo = descripcion_trabajo,
id_responsable_proyecto_id = responsable_proyecto,
plazo_dias = plazo_dias,
id_cliente_id = id_cliente,
estatus = 1 # PENDIENTE
)
# Auto-create steps based on client type
clientes = Cliente.objects.get( id = id_cliente)
tipo_cliente = clientes.id_tipo_id
pasos = Paso.objects.filter( activo = 1 , id_tipo_cliente_id = tipo_cliente)
for paso in pasos:
PTEDetalle.objects.create(
id_pte_header_id = pte_header.id,
id_paso_id = paso.id,
estatus_paso_id = 1 , # PENDIENTE
comentario = ''
)
Steps are automatically created based on the client type when a PTE is created.
PTE Status Management
Status Types
Status Code Description Color PENDIENTE 1 Awaiting start Yellow PROCESO 2 Work in progress Blue ENTREGADA 3 Completed and delivered Green CANCELADA 4 Cancelled Red SUSPENDIDA 9 Temporarily suspended Gray
Changing PTE Status
Open PTE Details
Click on a PTE in the list to view details
Click Status Button
Click the “Cambiar Estatus” button
Select New Status
Choose the new status from the dropdown
Add Comments (Optional)
Provide explanation for status change
Set Delivery Date
For ENTREGADA status, set the delivery date
Save Changes
Click Guardar to apply the status change
@require_http_methods ([ "POST" ])
@login_required
@registrar_actividad
def cambiar_estatus_pte ( request ):
pte_id = request. POST .get( 'pte_id' )
nuevo_estatus = request. POST .get( 'nuevo_estatus' )
fecha_entrega = request. POST .get( 'fecha_entrega' , None )
pte = PTEHeader.objects.get( id = pte_id)
pte.estatus = nuevo_estatus
# Set delivery date when marking as completed
if int (nuevo_estatus) == 3 :
if fecha_entrega:
pte.fecha_entrega = fecha_entrega
else :
pte.fecha_entrega = timezone.now()
pte.save()
Managing PTE Steps
Viewing Steps
Each PTE has multiple steps that must be completed:
PTE Steps DataTable The steps table shows:
Step order and description
Current status
Start and end dates
Responsible person
Progress percentage
File attachments
Updating Step Status
Access Step Actions
Click the action button next to a step
Change Status
Select from:
PENDIENTE : Not started
PROCESO : In progress
COMPLETADO : Finished
NO APLICA : Not applicable
Set Dates
Update:
Fecha Inicio
Fecha Término
Fecha Entrega
Add Notes
Enter comments about progress or issues
Upload Files
Attach deliverables or evidence
@require_http_methods ([ "POST" ])
@login_required
@registrar_actividad
def cambiar_estatus_paso ( request ):
paso_id = request. POST .get( 'paso_id' )
nuevo_estatus = request. POST .get( 'nuevo_estatus' )
comentario = request. POST .get( 'comentario' , '' )
detalle = PTEDetalle.objects.get( id = paso_id)
detalle.estatus_paso_id = int (nuevo_estatus)
detalle.comentario = comentario
# Auto-set completion date
if int (nuevo_estatus) == 3 :
detalle.fecha_entrega = timezone.now()
detalle.save()
# Check if all steps completed
verificar_y_actualizar_paso_4(detalle.id_pte_header_id)
Progress Calculation
PTE Progress Formula 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 = (pasos_completados / total_pasos) * 100
Note : Status 3 = COMPLETADO, Status 14 = NO APLICA
Special Step: Volumetría (Step 4)
Step 4 often contains substeps that must all be completed:
Click on Step 4 to expand substeps (tipo=2)
Mark each substep as completed individually
When all substeps are done, Step 4 is automatically marked complete: def verificar_y_actualizar_paso_4 ( pte_header_id ):
subpasos = PTEDetalle.objects.filter(
id_pte_header_id = pte_header_id,
id_paso__tipo = 2
)
total_subpasos = subpasos.count()
subpasos_completados = subpasos.filter( estatus_paso__in = [ 3 , 14 ]).count()
if subpasos_completados == total_subpasos:
paso_4.estatus_paso_id = 3
paso_4.comentario = "Entregables finalizados"
paso_4.save()
Editing PTEs
Only users with operaciones.change_pteheader permission can edit PTEs.
Open Edit Form
Click Editar button on PTE details page
Modify Fields
Update any editable fields (status cannot be changed here)
Save Changes
Click Guardar to apply changes @require_http_methods ([ "POST" ])
@login_required
@registrar_actividad
def editar_pte ( request ):
pte_id = request. POST .get( 'id' )
pte = get_object_or_404(PTEHeader, id = pte_id)
pte.oficio_pte = request. POST .get( 'oficio_pte' , pte.oficio_pte)
pte.descripcion_trabajo = request. POST .get( 'descripcion_trabajo' )
pte.plazo_dias = float (request. POST .get( 'plazo_dias' ))
pte.save()
Searching and Filtering PTEs
Quick Search
Use the search box to find PTEs by:
Oficio PTE
Description
Responsible person
Advanced Filters
By Status Filter by PENDIENTE, PROCESO, ENTREGADA, etc.
By Type Filter by PTE type/category
By Year Filter PTEs by year (extracted from oficio_pte)
By Client Filter by client organization
By Responsible Filter by project manager
Creating Work Orders from PTEs
Once a PTE reaches 100% completion:
Verify 100% Complete
Ensure all steps are marked COMPLETADO or NO APLICA
Click 'Create OT'
Button appears when PTE is 100% complete
Enter OT Information
Provide:
Oficio OT (work order number)
Tipo OT (work order type)
Additional details
Submit
System creates work order and links it to PTE @registrar_actividad
def crear_ot_desde_pte ( request ):
pte_id = request. POST .get( 'pte_id' )
oficio_ot = request. POST .get( 'oficio' )
pte = PTEHeader.objects.get( id = pte_id)
# Verify 100% complete
detalles = PTEDetalle.objects.filter( id_pte_header_id = pte.id)
pasos_completados = detalles.filter( estatus_paso_id__in = [ 3 , 4 , 14 ]).count()
progreso = (pasos_completados / detalles.count()) * 100
if progreso < 100 :
return JsonResponse({
'exito' : False ,
'detalles' : 'PTE must be 100 % c omplete'
})
# Create work order
ote = OTE .objects.create(
id_pte_header = pte,
orden_trabajo = oficio_ot,
descripcion_trabajo = pte.descripcion_trabajo,
# Copy relevant fields from PTE
)
Some PTE steps (like volumetry) are automatically copied to the new work order.
Deleting PTEs
Deletion requires operaciones.delete_pteheader permission and is logical (not physical).
@require_http_methods ([ "POST" ])
@login_required
@registrar_actividad
def eliminar_pte ( request ):
if not request.user.has_perm( 'operaciones.delete_pteheader' ):
return JsonResponse({
'detalles' : 'No tienes permiso para eliminar PTEs' ,
'exito' : False
})
pte_id = request. POST .get( 'id' )
pte = PTEHeader.objects.get( id = pte_id)
pte.estatus = 0 # Logical deletion
pte.save()
Best Practices
Clear Descriptions Write detailed work descriptions for better tracking
Realistic Deadlines Set achievable timelines based on actual capacity
Regular Updates Update step status frequently to reflect progress
Document Everything Attach all deliverables and evidence to steps
Use Comments Add notes explaining decisions or issues
Review Before Complete Verify all information before marking as ENTREGADA
Executing Work Orders Learn how to manage work orders created from PTEs
Activity Logs View PTE modification history