# 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)
```typescript
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)
```typescript
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)
```typescript
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)
```typescript
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)
```typescript
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)
```typescript
interface WorkOrderTask {
  id: UUID;
  workOrderId: UUID;

  taskNumber: number;
  description: string;

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

  result?: string;
  notes?: string;

  sortOrder: number;
}
```

#### WorkOrderPart (Entity)
```typescript
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)
```typescript
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)
```typescript
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)
```typescript
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
```typescript
interface Location {
  latitude: number;
  longitude: number;
  altitude?: number;
  heading?: number;
  speed?: number;
}
```

### Money
```typescript
interface Money {
  amount: number;
  currency: string;
}
```

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

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

---

## Domain Events

### Asset Lifecycle Events
```typescript
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
```typescript
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
```typescript
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

- [DDL-SPEC-assets.md](../database-design/schemas/DDL-SPEC-assets.md)

---

*Ultima actualizacion: 2025-12-05*
