erp-construccion/docs/04-modelado/domain-models/ASSETS-CONTEXT.md

12 KiB

DOMAIN MODEL: Assets Context

Version: 1.0.0 Fecha: 2025-12-05 Modulos: MAE-015


Descripcion

El contexto de Assets gestiona el ciclo de vida completo de activos de construccion: maquinaria pesada, vehiculos, equipos y herramientas. Incluye control de ubicacion, asignaciones a proyectos, mantenimiento preventivo/correctivo y calculo de TCO.


Agregados

1. Asset Aggregate

Asset (Aggregate Root)
|
+-- AssetAssignment (Entity)
|   +-- Period (Value Object)
|
+-- AssetUsageLog (Entity)
|
+-- GPSTracking (Entity)
|
+-- DepreciationSchedule (Value Object)

Asset (Root)

interface Asset {
  id: UUID;
  tenantId: UUID;

  assetCode: string;
  name: string;
  description?: string;

  // Classification
  assetType: AssetType;
  categoryId?: UUID;
  subcategory?: string;

  // Specifications
  brand?: string;
  model?: string;
  serialNumber?: string;
  year?: number;
  capacity?: string;
  specifications?: Record<string, any>;

  // Current location
  currentLocation?: string;
  currentProjectId?: UUID;
  assignedTo?: UUID;

  // GPS/IoT
  gpsDeviceId?: string;
  lastGPSLocation?: Location;
  lastGPSUpdate?: Timestamp;

  // Financial
  purchaseDate?: Date;
  purchaseValue: Money;
  currentValue: Money;
  depreciationMethod?: DepreciationMethod;
  usefulLifeYears?: number;
  salvageValue: Money;
  accumulatedDepreciation: Money;

  // Operational
  operatingHours: number;
  odometerReading?: number;
  fuelType?: string;
  hourlyCost: Money;

  status: AssetStatus;

  imageUrl?: string;
  documentsUrls?: string[];

  assignments: AssetAssignment[];
  usageLogs: AssetUsageLog[];
  maintenancePlans: MaintenancePlan[];
}

enum AssetType {
  HEAVY_EQUIPMENT = 'heavy_equipment',
  VEHICLE = 'vehicle',
  TOOL = 'tool',
  EQUIPMENT = 'equipment',
  COMPUTER = 'computer',
  FURNITURE = 'furniture',
  OTHER = 'other'
}

enum AssetStatus {
  AVAILABLE = 'available',
  IN_USE = 'in_use',
  MAINTENANCE = 'maintenance',
  REPAIR = 'repair',
  OUT_OF_SERVICE = 'out_of_service',
  DISPOSED = 'disposed'
}

enum DepreciationMethod {
  STRAIGHT_LINE = 'straight_line',
  DECLINING_BALANCE = 'declining_balance',
  UNITS_OF_PRODUCTION = 'units_of_production'
}

AssetAssignment (Entity)

interface AssetAssignment {
  id: UUID;
  assetId: UUID;

  projectId?: UUID;
  assignedTo?: UUID;
  department?: string;

  startDate: Date;
  endDate?: Date;
  plannedEndDate?: Date;

  location?: string;
  locationCoordinates?: Location;

  status: AssignmentStatus;

  // Usage tracking
  initialHours?: number;
  finalHours?: number;
  initialOdometer?: number;
  finalOdometer?: number;

  notes?: string;
}

enum AssignmentStatus {
  ACTIVE = 'active',
  COMPLETED = 'completed',
  CANCELLED = 'cancelled'
}

AssetUsageLog (Entity)

interface AssetUsageLog {
  id: UUID;
  assetId: UUID;
  assignmentId?: UUID;

  logDate: Date;
  startTime?: Time;
  endTime?: Time;

  hoursUsed?: number;
  distanceTraveled?: number;
  fuelConsumed?: number;

  projectId?: UUID;
  activityDescription?: string;

  operatorId?: UUID;
  operatorName?: string;

  // Readings
  odometerStart?: number;
  odometerEnd?: number;
  hourMeterStart?: number;
  hourMeterEnd?: number;

  notes?: string;
}

2. Maintenance Aggregate

MaintenancePlan (Aggregate Root)
|
+-- MaintenanceSchedule (Value Object)
|
+-- NextDueCalculation (Value Object)

WorkOrder (Aggregate Root)
|
+-- WorkOrderTask (Entity)
+-- WorkOrderPart (Entity)
+-- WorkOrderLabor (Entity)

MaintenancePlan (Root)

interface MaintenancePlan {
  id: UUID;
  tenantId: UUID;
  assetId: UUID;

  planName: string;
  description?: string;
  maintenanceType: MaintenanceType;

  // Frequency
  frequencyType: FrequencyType;
  frequencyValue: number;
  toleranceValue: number;

  // Last and next
  lastPerformedAt?: Timestamp;
  lastPerformedHours?: number;
  lastPerformedDistance?: number;
  nextDueAt?: Timestamp;
  nextDueHours?: number;
  nextDueDistance?: number;

  // Tasks
  tasksChecklist?: TaskChecklistItem[];
  estimatedDurationHours?: number;
  estimatedCost: Money;

  isActive: boolean;
}

enum MaintenanceType {
  PREVENTIVE = 'preventive',
  CORRECTIVE = 'corrective',
  PREDICTIVE = 'predictive',
  INSPECTION = 'inspection',
  CALIBRATION = 'calibration'
}

enum FrequencyType {
  HOURS = 'hours',
  DAYS = 'days',
  DISTANCE = 'distance',
  CALENDAR = 'calendar'
}

interface TaskChecklistItem {
  id: string;
  description: string;
  isMandatory: boolean;
  estimatedMinutes?: number;
}

WorkOrder (Root)

interface WorkOrder {
  id: UUID;
  tenantId: UUID;
  assetId: UUID;

  woNumber: string;
  maintenanceType: MaintenanceType;
  priority: MaintenancePriority;

  title: string;
  description?: string;
  problemReported?: string;

  // Origin
  maintenancePlanId?: UUID;
  reportedBy?: UUID;
  reportedAt: Timestamp;

  // Assignment
  assignedTo?: UUID;
  assignedTeam?: string;

  // Schedule
  scheduledDate?: Date;
  scheduledStartTime?: Time;
  estimatedDurationHours?: number;
  actualStartAt?: Timestamp;
  actualEndAt?: Timestamp;

  // Location
  workLocation?: string;
  projectId?: UUID;

  // Costs
  estimatedCost: Money;
  actualLaborCost: Money;
  actualPartsCost: Money;
  actualTotalCost: Money;

  status: WorkOrderStatus;

  // Result
  workPerformed?: string;
  rootCause?: string;
  recommendations?: string;

  // Readings at time of work
  hoursAtWork?: number;
  odometerAtWork?: number;

  tasks: WorkOrderTask[];
  parts: WorkOrderPart[];
  labor: WorkOrderLabor[];
}

enum MaintenancePriority {
  LOW = 'low',
  MEDIUM = 'medium',
  HIGH = 'high',
  CRITICAL = 'critical'
}

enum WorkOrderStatus {
  DRAFT = 'draft',
  SCHEDULED = 'scheduled',
  IN_PROGRESS = 'in_progress',
  ON_HOLD = 'on_hold',
  COMPLETED = 'completed',
  CANCELLED = 'cancelled'
}

WorkOrderTask (Entity)

interface WorkOrderTask {
  id: UUID;
  workOrderId: UUID;

  taskNumber: number;
  description: string;

  isCompleted: boolean;
  completedAt?: Timestamp;
  completedBy?: UUID;

  result?: string;
  notes?: string;

  sortOrder: number;
}

WorkOrderPart (Entity)

interface WorkOrderPart {
  id: UUID;
  workOrderId: UUID;

  productId?: UUID;
  productCode?: string;
  productName: string;

  quantity: number;
  unitCode?: string;

  unitCost: Money;
  totalCost: Money;

  warehouseId?: UUID;
  purchaseOrderId?: UUID;
}

WorkOrderLabor (Entity)

interface WorkOrderLabor {
  id: UUID;
  workOrderId: UUID;

  technicianId?: UUID;
  technicianName: string;

  workDate: Date;
  hoursWorked: number;
  overtimeHours: number;

  hourlyRate: Money;
  overtimeRate: Money;
  totalCost: Money;

  workDescription?: string;
}

3. Geofence Aggregate

Geofence (Aggregate Root)
|
+-- GeofenceEvent (Entity)
|
+-- AlertRecipient (Value Object)

Geofence (Root)

interface Geofence {
  id: UUID;
  tenantId: UUID;

  name: string;
  description?: string;

  boundary: Polygon;
  centerPoint?: Location;
  radiusMeters?: number;

  projectId?: UUID;

  alertOnEntry: boolean;
  alertOnExit: boolean;
  alertRecipients?: UUID[];

  isActive: boolean;
}

GeofenceEvent (Entity)

interface GeofenceEvent {
  id: UUID;
  assetId: UUID;
  geofenceId: UUID;

  eventType: GeofenceEventType;
  eventTime: Timestamp;
  location?: Location;

  notificationSent: boolean;
  notificationSentAt?: Timestamp;
}

enum GeofenceEventType {
  ENTRY = 'entry',
  EXIT = 'exit'
}

Value Objects

Location

interface Location {
  latitude: number;
  longitude: number;
  altitude?: number;
  heading?: number;
  speed?: number;
}

Money

interface Money {
  amount: number;
  currency: string;
}

TCOResult

interface TCOResult {
  purchaseCost: Money;
  depreciationCost: Money;
  maintenanceCost: Money;
  fuelCost: Money;
  operatingHours: number;
  costPerHour: Money;
  totalTCO: Money;
}

DepreciationSchedule

interface DepreciationSchedule {
  method: DepreciationMethod;
  purchaseValue: Money;
  salvageValue: Money;
  usefulLifeYears: number;
  currentYear: number;
  annualDepreciation: Money;
  accumulatedDepreciation: Money;
  bookValue: Money;
}

Domain Events

Asset Lifecycle Events

interface AssetCreated {
  assetId: UUID;
  assetCode: string;
  assetType: AssetType;
  purchaseValue: Money;
  timestamp: Timestamp;
}

interface AssetStatusChanged {
  assetId: UUID;
  previousStatus: AssetStatus;
  newStatus: AssetStatus;
  reason?: string;
  timestamp: Timestamp;
}

interface AssetAssigned {
  assetId: UUID;
  assignmentId: UUID;
  projectId?: UUID;
  assignedTo?: UUID;
  startDate: Date;
  timestamp: Timestamp;
}

interface AssetDisposed {
  assetId: UUID;
  disposalReason: string;
  disposalValue: Money;
  timestamp: Timestamp;
}

Maintenance Events

interface MaintenanceDue {
  assetId: UUID;
  maintenancePlanId: UUID;
  dueAt: Timestamp;
  dueHours?: number;
  maintenanceType: MaintenanceType;
  timestamp: Timestamp;
}

interface WorkOrderCreated {
  workOrderId: UUID;
  assetId: UUID;
  woNumber: string;
  maintenanceType: MaintenanceType;
  priority: MaintenancePriority;
  timestamp: Timestamp;
}

interface WorkOrderCompleted {
  workOrderId: UUID;
  assetId: UUID;
  actualCost: Money;
  laborHours: number;
  timestamp: Timestamp;
}

GPS/Geofence Events

interface LocationUpdated {
  assetId: UUID;
  location: Location;
  speed?: number;
  timestamp: Timestamp;
}

interface GeofenceViolation {
  assetId: UUID;
  geofenceId: UUID;
  eventType: GeofenceEventType;
  location: Location;
  timestamp: Timestamp;
}

Business Rules

Asset Rules

  1. Un activo solo puede estar asignado a un proyecto a la vez
  2. El status cambia automaticamente al crear orden de trabajo
  3. La depreciacion no puede exceder el valor de compra menos salvage
  4. Activos con status 'disposed' no pueden modificarse

Maintenance Rules

  1. El mantenimiento preventivo genera ordenes automaticamente
  2. La tolerancia de frecuencia permite variacion sin alerta
  3. Ordenes criticas requieren atencion en 24 horas
  4. Las tareas obligatorias deben completarse para cerrar orden

Usage Rules

  1. Los registros de uso no pueden tener horas negativas
  2. El odometro final debe ser >= inicial
  3. Las horas de operacion acumulan en el activo
  4. El consumo de combustible se usa para TCO

Geofence Rules

  1. Las geocercas requieren al menos 3 puntos de poligono
  2. Los eventos se registran incluso si notificacion falla
  3. Solo activos con GPS activo generan eventos

Invariantes

  1. Asset.currentValue = purchaseValue - accumulatedDepreciation
  2. Asset.accumulatedDepreciation <= purchaseValue - salvageValue
  3. WorkOrder.actualTotalCost = actualLaborCost + actualPartsCost
  4. AssetUsageLog.hoursUsed = hourMeterEnd - hourMeterStart
  5. Asset.operatingHours += SUM(usageLogs.hoursUsed)

Integraciones

Con Project Context

  • Activos asignados a proyectos
  • Costos de maquinaria en presupuestos
  • Ubicacion en obra

Con Finance Context

  • Depreciacion genera polizas
  • Costos de mantenimiento como gastos
  • TCO para analisis financiero

Con Inventory Context

  • Repuestos desde almacen
  • Consumibles para mantenimiento

Con GPS/IoT (Externo)

  • Tracking en tiempo real
  • Telemetria de activos
  • Alertas de geocerca

Referencias


Ultima actualizacion: 2025-12-05