erp-construccion-backend-v2/src/modules/assets/entities/work-order.entity.ts
Adrian Flores Cortes f14829d2ce [MAE-015] fix: Fix TypeScript errors in assets module
- Fix controllers to use Promise<void> return types
- Replace 'return res.status()' with 'res.status(); return;'
- Add NextFunction parameter to handlers
- Fix enum-style references with string literals
- Replace 'deletedAt: null' with 'IsNull()' for TypeORM
- Remove unused parameters and imports
- Fix grouped object initialization in getByStatus
- Remove non-existent 'updatedBy' property from WorkOrderPart

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 09:51:01 -06:00

281 lines
7.9 KiB
TypeScript

/**
* WorkOrder Entity - Ordenes de Trabajo de Mantenimiento
*
* Registro de mantenimientos preventivos y correctivos.
*
* @module Assets (MAE-015)
*/
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
OneToMany,
JoinColumn,
Index,
} from 'typeorm';
import { Asset } from './asset.entity';
import { WorkOrderPart } from './work-order-part.entity';
export type MaintenanceType = 'preventive' | 'corrective' | 'predictive' | 'emergency';
export type WorkOrderStatus = 'draft' | 'scheduled' | 'in_progress' | 'on_hold' | 'completed' | 'cancelled';
export type WorkOrderPriority = 'low' | 'medium' | 'high' | 'critical';
// Const objects for runtime use
export const WorkOrderStatusValues: Record<string, WorkOrderStatus> = {
DRAFT: 'draft',
SCHEDULED: 'scheduled',
IN_PROGRESS: 'in_progress',
ON_HOLD: 'on_hold',
COMPLETED: 'completed',
CANCELLED: 'cancelled',
} as const;
export const WorkOrderPriorityValues: Record<string, WorkOrderPriority> = {
LOW: 'low',
MEDIUM: 'medium',
HIGH: 'high',
CRITICAL: 'critical',
} as const;
@Entity('work_orders', { schema: 'assets' })
@Index(['tenantId', 'workOrderNumber'], { unique: true })
@Index(['tenantId', 'assetId'])
@Index(['tenantId', 'status'])
@Index(['tenantId', 'scheduledStartDate'])
@Index(['tenantId', 'projectId'])
export class WorkOrder {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ name: 'tenant_id', type: 'uuid' })
@Index()
tenantId!: string;
// Identificacion
@Column({ name: 'work_order_number', length: 50 })
workOrderNumber!: string;
// Activo
@Column({ name: 'asset_id', type: 'uuid' })
assetId!: string;
@ManyToOne(() => Asset)
@JoinColumn({ name: 'asset_id' })
asset!: Asset;
@Column({ name: 'asset_code', length: 50, nullable: true })
assetCode?: string;
@Column({ name: 'asset_name', length: 255, nullable: true })
assetName?: string;
// Tipo y estado
@Column({
name: 'maintenance_type',
type: 'enum',
enum: ['preventive', 'corrective', 'predictive', 'emergency'],
enumName: 'maintenance_type',
})
maintenanceType!: MaintenanceType;
@Column({
type: 'enum',
enum: ['draft', 'scheduled', 'in_progress', 'on_hold', 'completed', 'cancelled'],
enumName: 'work_order_status',
default: 'draft',
})
status!: WorkOrderStatus;
@Column({
type: 'enum',
enum: ['low', 'medium', 'high', 'critical'],
enumName: 'work_order_priority',
default: 'medium',
})
priority!: WorkOrderPriority;
// Origen
@Column({ name: 'schedule_id', type: 'uuid', nullable: true })
scheduleId?: string;
@Column({ name: 'plan_id', type: 'uuid', nullable: true })
planId?: string;
@Column({ name: 'is_scheduled', type: 'boolean', default: false })
isScheduled!: boolean;
// Descripcion
@Column({ length: 255 })
title!: string;
@Column({ type: 'text', nullable: true })
description?: string;
@Column({ name: 'problem_reported', type: 'text', nullable: true })
problemReported?: string;
@Column({ type: 'text', nullable: true })
diagnosis?: string;
// Ubicacion
@Column({ name: 'project_id', type: 'uuid', nullable: true })
projectId?: string;
@Column({ name: 'project_name', length: 255, nullable: true })
projectName?: string;
@Column({ name: 'location_description', length: 255, nullable: true })
locationDescription?: string;
// Fechas
@Column({ name: 'requested_date', type: 'date' })
requestedDate!: Date;
@Column({ name: 'scheduled_start_date', type: 'date', nullable: true })
scheduledStartDate?: Date;
@Column({ name: 'scheduled_end_date', type: 'date', nullable: true })
scheduledEndDate?: Date;
@Column({ name: 'actual_start_date', type: 'date', nullable: true })
actualStartDate?: Date;
@Column({ name: 'actual_end_date', type: 'date', nullable: true })
actualEndDate?: Date;
// Metricas del equipo al momento
@Column({ name: 'hours_at_work_order', type: 'decimal', precision: 12, scale: 2, nullable: true })
hoursAtWorkOrder?: number;
@Column({ name: 'kilometers_at_work_order', type: 'decimal', precision: 12, scale: 2, nullable: true })
kilometersAtWorkOrder?: number;
// Asignacion
@Column({ name: 'assigned_to_id', type: 'uuid', nullable: true })
assignedToId?: string;
@Column({ name: 'assigned_to_name', length: 255, nullable: true })
assignedToName?: string;
@Column({ name: 'team_ids', type: 'uuid', array: true, nullable: true })
teamIds?: string[];
// Solicitante
@Column({ name: 'requested_by_id', type: 'uuid', nullable: true })
requestedById?: string;
@Column({ name: 'requested_by_name', length: 255, nullable: true })
requestedByName?: string;
// Aprobacion
@Column({ name: 'approved_by_id', type: 'uuid', nullable: true })
approvedById?: string;
@Column({ name: 'approved_at', type: 'timestamptz', nullable: true })
approvedAt?: Date;
// Trabajo realizado
@Column({ name: 'work_performed', type: 'text', nullable: true })
workPerformed?: string;
@Column({ type: 'text', nullable: true })
findings?: string;
@Column({ type: 'text', nullable: true })
recommendations?: string;
// Checklist de actividades
@Column({ name: 'activities_checklist', type: 'jsonb', nullable: true })
activitiesChecklist?: Record<string, any>[];
// Tiempos
@Column({ name: 'estimated_hours', type: 'decimal', precision: 5, scale: 2, nullable: true })
estimatedHours?: number;
@Column({ name: 'actual_hours', type: 'decimal', precision: 5, scale: 2, nullable: true })
actualHours?: number;
// Costos
@Column({ name: 'labor_cost', type: 'decimal', precision: 18, scale: 2, default: 0 })
laborCost!: number;
@Column({ name: 'parts_cost', type: 'decimal', precision: 18, scale: 2, default: 0 })
partsCost!: number;
@Column({ name: 'external_service_cost', type: 'decimal', precision: 18, scale: 2, default: 0 })
externalServiceCost!: number;
@Column({ name: 'other_costs', type: 'decimal', precision: 18, scale: 2, default: 0 })
otherCosts!: number;
@Column({ name: 'total_cost', type: 'decimal', precision: 18, scale: 2, default: 0 })
totalCost!: number;
// Partes utilizadas
@Column({ name: 'parts_used_count', type: 'int', default: 0 })
partsUsedCount!: number;
@OneToMany(() => WorkOrderPart, (part) => part.workOrder, { cascade: true })
partsUsed?: WorkOrderPart[];
// Documentos
@Column({ name: 'photos_before', type: 'jsonb', nullable: true })
photosBefore?: string[];
@Column({ name: 'photos_after', type: 'jsonb', nullable: true })
photosAfter?: string[];
@Column({ type: 'jsonb', nullable: true })
documents?: string[];
// Firma de conformidad
@Column({ name: 'completed_by_id', type: 'uuid', nullable: true })
completedById?: string;
@Column({ name: 'completed_by_name', length: 255, nullable: true })
completedByName?: string;
@Column({ name: 'completion_signature_url', length: 500, nullable: true })
completionSignatureUrl?: string;
@Column({ name: 'completion_notes', type: 'text', nullable: true })
completionNotes?: string;
// Seguimiento
@Column({ name: 'requires_followup', type: 'boolean', default: false })
requiresFollowup!: boolean;
@Column({ name: 'followup_notes', type: 'text', nullable: true })
followupNotes?: string;
@Column({ name: 'followup_work_order_id', type: 'uuid', nullable: true })
followupWorkOrderId?: string;
// Notas y metadatos
@Column({ type: 'text', nullable: true })
notes?: string;
@Column({ type: 'jsonb', nullable: true })
metadata?: Record<string, any>;
// Auditoria
@Column({ name: 'created_by', type: 'uuid', nullable: true })
createdBy?: string;
@Column({ name: 'updated_by', type: 'uuid', nullable: true })
updatedBy?: string;
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
updatedAt!: Date;
@Column({ name: 'deleted_at', type: 'timestamptz', nullable: true })
deletedAt?: Date;
}