652 lines
12 KiB
Markdown
652 lines
12 KiB
Markdown
# 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*
|