[MAE-015] feat: Add assets schema DDL with 11 tables and 9 ENUMs

- asset_categories: Categorias de activos
- assets: Catalogo principal de activos
- asset_assignments: Asignaciones a proyectos
- maintenance_plans: Planes de mantenimiento preventivo
- maintenance_schedules: Programacion de mantenimientos
- work_orders: Ordenes de trabajo
- work_order_parts: Refacciones utilizadas
- maintenance_history: Historial de mantenimientos
- asset_costs: Costos TCO
- asset_locations: Ubicaciones GPS
- fuel_logs: Registro de combustible

Includes: RLS, indices, triggers, TCO calculation function

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Adrian Flores Cortes 2026-01-25 05:11:02 -06:00
parent 3e3d2d6eac
commit e927cafc90

View File

@ -0,0 +1,964 @@
-- ============================================================================
-- 09-assets-schema-ddl.sql
-- Schema: assets
-- ERP Construccion - Modulo Activos, Maquinaria y Mantenimiento (MAE-015)
-- ============================================================================
-- Descripcion: Gestion de activos fijos y maquinaria incluyendo:
-- - Catalogo de activos (maquinaria, equipo, vehiculos)
-- - Asignaciones a obras/proyectos
-- - Planes de mantenimiento preventivo
-- - Ordenes de trabajo de mantenimiento
-- - Historial de mantenimientos
-- - Costeo TCO (Total Cost of Ownership)
-- - Rastreo GPS (IoT)
-- ============================================================================
-- Autor: Claude-Arquitecto-Orquestador
-- Fecha: 2026-01-25
-- Version: 1.0.0
-- ============================================================================
-- Crear schema si no existe
CREATE SCHEMA IF NOT EXISTS assets;
-- ============================================================================
-- ENUMS
-- ============================================================================
-- Tipo de activo
CREATE TYPE assets.asset_type AS ENUM (
'heavy_machinery', -- Maquinaria pesada (excavadoras, gruas)
'light_equipment', -- Equipo ligero (compactadoras, mezcladoras)
'vehicle', -- Vehiculos (camiones, camionetas)
'tool', -- Herramientas (taladros, sierras)
'computer', -- Equipo de computo
'furniture', -- Mobiliario
'other' -- Otro
);
-- Estado del activo
CREATE TYPE assets.asset_status AS ENUM (
'available', -- Disponible
'assigned', -- Asignado a obra
'in_maintenance', -- En mantenimiento
'in_transit', -- En transito entre obras
'inactive', -- Inactivo
'retired', -- Dado de baja
'sold' -- Vendido
);
-- Tipo de propiedad
CREATE TYPE assets.ownership_type AS ENUM (
'owned', -- Propio
'leased', -- Arrendado
'rented', -- Rentado
'borrowed' -- Prestamo
);
-- Tipo de mantenimiento
CREATE TYPE assets.maintenance_type AS ENUM (
'preventive', -- Preventivo
'corrective', -- Correctivo
'predictive', -- Predictivo
'emergency' -- Emergencia
);
-- Frecuencia de mantenimiento
CREATE TYPE assets.maintenance_frequency AS ENUM (
'daily', -- Diario
'weekly', -- Semanal
'biweekly', -- Quincenal
'monthly', -- Mensual
'quarterly', -- Trimestral
'semiannual', -- Semestral
'annual', -- Anual
'by_hours', -- Por horas de operacion
'by_kilometers' -- Por kilometros
);
-- Estado de orden de trabajo
CREATE TYPE assets.work_order_status AS ENUM (
'draft', -- Borrador
'scheduled', -- Programada
'in_progress', -- En progreso
'on_hold', -- En espera
'completed', -- Completada
'cancelled' -- Cancelada
);
-- Prioridad de orden de trabajo
CREATE TYPE assets.work_order_priority AS ENUM (
'low', -- Baja
'medium', -- Media
'high', -- Alta
'critical' -- Critica
);
-- Tipo de costo
CREATE TYPE assets.cost_type AS ENUM (
'maintenance', -- Mantenimiento
'repair', -- Reparacion
'fuel', -- Combustible
'insurance', -- Seguro
'tax', -- Impuestos
'depreciation', -- Depreciacion
'operator', -- Operador
'other' -- Otro
);
-- ============================================================================
-- TABLAS
-- ============================================================================
-- ----------------------------------------------------------------------------
-- 1. Categorias de Activos
-- ----------------------------------------------------------------------------
CREATE TABLE assets.asset_categories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Informacion basica
code VARCHAR(20) NOT NULL,
name VARCHAR(100) NOT NULL,
description TEXT,
-- Jerarquia
parent_id UUID REFERENCES assets.asset_categories(id),
level INTEGER NOT NULL DEFAULT 1,
-- Configuracion de depreciacion
useful_life_years INTEGER,
depreciation_method VARCHAR(50), -- straight_line, declining_balance, units_of_production
salvage_value_percentage DECIMAL(5,2),
-- Estado
is_active BOOLEAN NOT NULL DEFAULT TRUE,
-- Metadatos
metadata JSONB,
-- Auditoria
created_by UUID,
updated_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
CONSTRAINT uq_asset_categories_tenant_code UNIQUE (tenant_id, code)
);
-- ----------------------------------------------------------------------------
-- 2. Activos (Catalogo Principal)
-- ----------------------------------------------------------------------------
CREATE TABLE assets.assets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Identificacion
asset_code VARCHAR(50) NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
-- Clasificacion
category_id UUID REFERENCES assets.asset_categories(id),
asset_type assets.asset_type NOT NULL,
status assets.asset_status NOT NULL DEFAULT 'available',
ownership_type assets.ownership_type NOT NULL DEFAULT 'owned',
-- Especificaciones tecnicas
brand VARCHAR(100),
model VARCHAR(100),
serial_number VARCHAR(100),
year_manufactured INTEGER,
specifications JSONB,
-- Capacidades
capacity VARCHAR(100),
power_rating VARCHAR(50),
fuel_type VARCHAR(50),
fuel_capacity DECIMAL(10,2),
fuel_consumption_rate DECIMAL(10,2), -- litros/hora o km/litro
-- Metricas de uso
current_hours DECIMAL(12,2) DEFAULT 0,
current_kilometers DECIMAL(12,2) DEFAULT 0,
last_usage_update TIMESTAMPTZ,
-- Ubicacion actual
current_project_id UUID,
current_location_name VARCHAR(255),
current_latitude DECIMAL(10,8),
current_longitude DECIMAL(11,8),
last_location_update TIMESTAMPTZ,
-- Informacion financiera
purchase_date DATE,
purchase_price DECIMAL(18,2),
purchase_currency VARCHAR(3) DEFAULT 'MXN',
supplier_id UUID,
invoice_number VARCHAR(100),
-- Depreciacion
useful_life_years INTEGER,
salvage_value DECIMAL(18,2),
current_book_value DECIMAL(18,2),
accumulated_depreciation DECIMAL(18,2) DEFAULT 0,
depreciation_method VARCHAR(50),
last_depreciation_date DATE,
-- Arrendamiento (si aplica)
lease_start_date DATE,
lease_end_date DATE,
lease_monthly_rate DECIMAL(18,2),
lease_contract_number VARCHAR(100),
lessor_name VARCHAR(255),
-- Seguro
insurance_policy_number VARCHAR(100),
insurance_company VARCHAR(255),
insurance_expiry_date DATE,
insurance_coverage_amount DECIMAL(18,2),
-- Documentos
photo_url VARCHAR(500),
manual_url VARCHAR(500),
registration_document_url VARCHAR(500),
-- Proximo mantenimiento
next_maintenance_date DATE,
next_maintenance_hours DECIMAL(12,2),
next_maintenance_kilometers DECIMAL(12,2),
-- Operador asignado
assigned_operator_id UUID,
-- Notas y metadatos
notes TEXT,
tags VARCHAR(255)[],
metadata JSONB,
-- Auditoria
created_by UUID,
updated_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
CONSTRAINT uq_assets_tenant_code UNIQUE (tenant_id, asset_code)
);
-- ----------------------------------------------------------------------------
-- 3. Asignaciones de Activos a Obras
-- ----------------------------------------------------------------------------
CREATE TABLE assets.asset_assignments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Activo y proyecto
asset_id UUID NOT NULL REFERENCES assets.assets(id),
project_id UUID NOT NULL,
project_code VARCHAR(50),
project_name VARCHAR(255),
-- Periodo de asignacion
start_date DATE NOT NULL,
end_date DATE,
is_current BOOLEAN NOT NULL DEFAULT TRUE,
-- Ubicacion especifica en obra
location_in_project VARCHAR(255),
latitude DECIMAL(10,8),
longitude DECIMAL(11,8),
-- Operador asignado
operator_id UUID,
operator_name VARCHAR(255),
-- Responsable
responsible_id UUID,
responsible_name VARCHAR(255),
-- Metricas al inicio/fin
hours_at_start DECIMAL(12,2),
hours_at_end DECIMAL(12,2),
kilometers_at_start DECIMAL(12,2),
kilometers_at_end DECIMAL(12,2),
-- Tarifas
daily_rate DECIMAL(18,2),
hourly_rate DECIMAL(18,2),
-- Razon de transferencia
transfer_reason TEXT,
transfer_notes TEXT,
-- Documento de entrega
delivery_document_url VARCHAR(500),
return_document_url VARCHAR(500),
-- Metadatos
metadata JSONB,
-- Auditoria
created_by UUID,
updated_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ----------------------------------------------------------------------------
-- 4. Planes de Mantenimiento
-- ----------------------------------------------------------------------------
CREATE TABLE assets.maintenance_plans (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Identificacion
plan_code VARCHAR(50) NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
-- Aplica a
asset_id UUID REFERENCES assets.assets(id), -- Si es especifico de un activo
category_id UUID REFERENCES assets.asset_categories(id), -- Si aplica a categoria
asset_type assets.asset_type, -- Si aplica a tipo
-- Tipo y frecuencia
maintenance_type assets.maintenance_type NOT NULL DEFAULT 'preventive',
frequency assets.maintenance_frequency NOT NULL,
frequency_value INTEGER, -- Cada cuantas unidades (dias, horas, km)
-- Actividades del plan
activities JSONB NOT NULL, -- Array de actividades con checklist
-- Duracion estimada
estimated_duration_hours DECIMAL(5,2),
-- Recursos necesarios
required_parts JSONB, -- Refacciones necesarias
required_tools JSONB, -- Herramientas necesarias
required_skills VARCHAR(255)[], -- Habilidades requeridas
-- Costos estimados
estimated_labor_cost DECIMAL(18,2),
estimated_parts_cost DECIMAL(18,2),
estimated_total_cost DECIMAL(18,2),
-- Estado
is_active BOOLEAN NOT NULL DEFAULT TRUE,
-- Metadatos
metadata JSONB,
-- Auditoria
created_by UUID,
updated_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
CONSTRAINT uq_maintenance_plans_tenant_code UNIQUE (tenant_id, plan_code)
);
-- ----------------------------------------------------------------------------
-- 5. Programacion de Mantenimientos
-- ----------------------------------------------------------------------------
CREATE TABLE assets.maintenance_schedules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Activo y plan
asset_id UUID NOT NULL REFERENCES assets.assets(id),
plan_id UUID REFERENCES assets.maintenance_plans(id),
-- Programacion
scheduled_date DATE NOT NULL,
scheduled_hours DECIMAL(12,2), -- Horas del equipo cuando debe hacerse
scheduled_kilometers DECIMAL(12,2), -- KM cuando debe hacerse
-- Estado
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending, completed, skipped, overdue
completed_date DATE,
-- Orden de trabajo generada
work_order_id UUID,
-- Alertas
alert_days_before INTEGER DEFAULT 7,
alert_sent BOOLEAN DEFAULT FALSE,
alert_sent_at TIMESTAMPTZ,
-- Notas
notes TEXT,
-- Auditoria
created_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ----------------------------------------------------------------------------
-- 6. Ordenes de Trabajo de Mantenimiento
-- ----------------------------------------------------------------------------
CREATE TABLE assets.work_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Identificacion
work_order_number VARCHAR(50) NOT NULL,
-- Activo
asset_id UUID NOT NULL REFERENCES assets.assets(id),
asset_code VARCHAR(50),
asset_name VARCHAR(255),
-- Tipo y estado
maintenance_type assets.maintenance_type NOT NULL,
status assets.work_order_status NOT NULL DEFAULT 'draft',
priority assets.work_order_priority NOT NULL DEFAULT 'medium',
-- Origen
schedule_id UUID REFERENCES assets.maintenance_schedules(id),
plan_id UUID REFERENCES assets.maintenance_plans(id),
is_scheduled BOOLEAN NOT NULL DEFAULT FALSE,
-- Descripcion
title VARCHAR(255) NOT NULL,
description TEXT,
problem_reported TEXT,
diagnosis TEXT,
-- Ubicacion
project_id UUID,
project_name VARCHAR(255),
location_description VARCHAR(255),
-- Fechas
requested_date DATE NOT NULL,
scheduled_start_date DATE,
scheduled_end_date DATE,
actual_start_date DATE,
actual_end_date DATE,
-- Metricas del equipo al momento
hours_at_work_order DECIMAL(12,2),
kilometers_at_work_order DECIMAL(12,2),
-- Asignacion
assigned_to_id UUID,
assigned_to_name VARCHAR(255),
team_ids UUID[],
-- Solicitante
requested_by_id UUID,
requested_by_name VARCHAR(255),
-- Aprobacion
approved_by_id UUID,
approved_at TIMESTAMPTZ,
-- Trabajo realizado
work_performed TEXT,
findings TEXT,
recommendations TEXT,
-- Checklist de actividades
activities_checklist JSONB,
-- Tiempos
estimated_hours DECIMAL(5,2),
actual_hours DECIMAL(5,2),
-- Costos
labor_cost DECIMAL(18,2) DEFAULT 0,
parts_cost DECIMAL(18,2) DEFAULT 0,
external_service_cost DECIMAL(18,2) DEFAULT 0,
other_costs DECIMAL(18,2) DEFAULT 0,
total_cost DECIMAL(18,2) DEFAULT 0,
-- Partes utilizadas (detalle en tabla separada)
parts_used_count INTEGER DEFAULT 0,
-- Documentos
photos_before JSONB, -- URLs de fotos antes
photos_after JSONB, -- URLs de fotos despues
documents JSONB, -- URLs de documentos adjuntos
-- Firma de conformidad
completed_by_id UUID,
completed_by_name VARCHAR(255),
completion_signature_url VARCHAR(500),
completion_notes TEXT,
-- Seguimiento
requires_followup BOOLEAN DEFAULT FALSE,
followup_notes TEXT,
followup_work_order_id UUID,
-- Notas y metadatos
notes TEXT,
metadata JSONB,
-- Auditoria
created_by UUID,
updated_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
CONSTRAINT uq_work_orders_tenant_number UNIQUE (tenant_id, work_order_number)
);
-- ----------------------------------------------------------------------------
-- 7. Partes/Refacciones Utilizadas en Ordenes de Trabajo
-- ----------------------------------------------------------------------------
CREATE TABLE assets.work_order_parts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Orden de trabajo
work_order_id UUID NOT NULL REFERENCES assets.work_orders(id) ON DELETE CASCADE,
-- Parte/Refaccion
part_id UUID, -- Referencia a inventario si existe
part_code VARCHAR(50),
part_name VARCHAR(255) NOT NULL,
part_description TEXT,
-- Cantidades
quantity_required DECIMAL(10,2) NOT NULL,
quantity_used DECIMAL(10,2),
-- Costos
unit_cost DECIMAL(18,2),
total_cost DECIMAL(18,2),
-- Origen
from_inventory BOOLEAN DEFAULT FALSE,
purchase_order_id UUID, -- Si se compro especificamente
-- Notas
notes TEXT,
-- Auditoria
created_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ----------------------------------------------------------------------------
-- 8. Historial de Mantenimientos
-- ----------------------------------------------------------------------------
CREATE TABLE assets.maintenance_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Activo
asset_id UUID NOT NULL REFERENCES assets.assets(id),
-- Orden de trabajo (si aplica)
work_order_id UUID REFERENCES assets.work_orders(id),
-- Fecha y tipo
maintenance_date DATE NOT NULL,
maintenance_type assets.maintenance_type NOT NULL,
-- Descripcion
description TEXT NOT NULL,
work_performed TEXT,
-- Metricas al momento del mantenimiento
hours_at_maintenance DECIMAL(12,2),
kilometers_at_maintenance DECIMAL(12,2),
-- Costos
labor_cost DECIMAL(18,2) DEFAULT 0,
parts_cost DECIMAL(18,2) DEFAULT 0,
total_cost DECIMAL(18,2) DEFAULT 0,
-- Ejecutor
performed_by_id UUID,
performed_by_name VARCHAR(255),
vendor_name VARCHAR(255), -- Si fue externo
-- Documentos
documents JSONB,
-- Notas
notes TEXT,
-- Auditoria
created_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ----------------------------------------------------------------------------
-- 9. Costos de Activos
-- ----------------------------------------------------------------------------
CREATE TABLE assets.asset_costs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Activo
asset_id UUID NOT NULL REFERENCES assets.assets(id),
-- Periodo
period_start DATE NOT NULL,
period_end DATE NOT NULL,
fiscal_year INTEGER NOT NULL,
fiscal_month INTEGER NOT NULL,
-- Proyecto (si aplica)
project_id UUID,
project_code VARCHAR(50),
-- Tipo de costo
cost_type assets.cost_type NOT NULL,
-- Descripcion
description VARCHAR(255),
reference_document VARCHAR(100),
-- Monto
amount DECIMAL(18,2) NOT NULL,
currency VARCHAR(3) DEFAULT 'MXN',
-- Uso asociado
hours_in_period DECIMAL(12,2),
kilometers_in_period DECIMAL(12,2),
-- Calculo de tarifa
cost_per_hour DECIMAL(18,4),
cost_per_kilometer DECIMAL(18,4),
-- Origen
source_module VARCHAR(50), -- work_order, fuel_log, invoice, etc.
source_id UUID,
-- Notas
notes TEXT,
metadata JSONB,
-- Auditoria
created_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ----------------------------------------------------------------------------
-- 10. Ubicaciones GPS (Historico)
-- ----------------------------------------------------------------------------
CREATE TABLE assets.asset_locations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Activo
asset_id UUID NOT NULL REFERENCES assets.assets(id),
-- Ubicacion
latitude DECIMAL(10,8) NOT NULL,
longitude DECIMAL(11,8) NOT NULL,
altitude DECIMAL(8,2),
accuracy DECIMAL(6,2),
heading DECIMAL(5,2),
speed DECIMAL(6,2),
-- Timestamp
recorded_at TIMESTAMPTZ NOT NULL,
-- Contexto
project_id UUID,
location_name VARCHAR(255),
address VARCHAR(500),
-- Telemetria adicional
engine_status VARCHAR(20), -- on, off, idle
fuel_level DECIMAL(5,2),
odometer DECIMAL(12,2),
engine_hours DECIMAL(12,2),
battery_voltage DECIMAL(5,2),
-- Dispositivo
device_id VARCHAR(100),
device_type VARCHAR(50),
-- Metadatos
raw_data JSONB,
-- Auditoria
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ----------------------------------------------------------------------------
-- 11. Registro de Combustible
-- ----------------------------------------------------------------------------
CREATE TABLE assets.fuel_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
-- Activo
asset_id UUID NOT NULL REFERENCES assets.assets(id),
-- Fecha y ubicacion
log_date DATE NOT NULL,
log_time TIME,
project_id UUID,
location VARCHAR(255),
-- Combustible
fuel_type VARCHAR(50) NOT NULL,
quantity_liters DECIMAL(10,2) NOT NULL,
unit_price DECIMAL(18,4) NOT NULL,
total_cost DECIMAL(18,2) NOT NULL,
-- Metricas al cargar
odometer_reading DECIMAL(12,2),
hours_reading DECIMAL(12,2),
-- Rendimiento calculado
kilometers_since_last DECIMAL(12,2),
hours_since_last DECIMAL(12,2),
liters_per_100km DECIMAL(8,2),
liters_per_hour DECIMAL(8,2),
-- Proveedor
vendor_name VARCHAR(255),
invoice_number VARCHAR(100),
-- Operador
operator_id UUID,
operator_name VARCHAR(255),
-- Notas
notes TEXT,
-- Auditoria
created_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- ============================================================================
-- INDICES
-- ============================================================================
-- Asset Categories
CREATE INDEX idx_asset_categories_tenant ON assets.asset_categories(tenant_id);
CREATE INDEX idx_asset_categories_parent ON assets.asset_categories(parent_id);
-- Assets
CREATE INDEX idx_assets_tenant ON assets.assets(tenant_id);
CREATE INDEX idx_assets_tenant_type ON assets.assets(tenant_id, asset_type);
CREATE INDEX idx_assets_tenant_status ON assets.assets(tenant_id, status);
CREATE INDEX idx_assets_tenant_category ON assets.assets(tenant_id, category_id);
CREATE INDEX idx_assets_current_project ON assets.assets(tenant_id, current_project_id);
CREATE INDEX idx_assets_serial ON assets.assets(tenant_id, serial_number);
-- Asset Assignments
CREATE INDEX idx_asset_assignments_tenant ON assets.asset_assignments(tenant_id);
CREATE INDEX idx_asset_assignments_asset ON assets.asset_assignments(tenant_id, asset_id);
CREATE INDEX idx_asset_assignments_project ON assets.asset_assignments(tenant_id, project_id);
CREATE INDEX idx_asset_assignments_current ON assets.asset_assignments(tenant_id, is_current) WHERE is_current = TRUE;
-- Maintenance Plans
CREATE INDEX idx_maintenance_plans_tenant ON assets.maintenance_plans(tenant_id);
CREATE INDEX idx_maintenance_plans_asset ON assets.maintenance_plans(tenant_id, asset_id);
CREATE INDEX idx_maintenance_plans_category ON assets.maintenance_plans(tenant_id, category_id);
-- Maintenance Schedules
CREATE INDEX idx_maintenance_schedules_tenant ON assets.maintenance_schedules(tenant_id);
CREATE INDEX idx_maintenance_schedules_asset ON assets.maintenance_schedules(tenant_id, asset_id);
CREATE INDEX idx_maintenance_schedules_date ON assets.maintenance_schedules(tenant_id, scheduled_date);
CREATE INDEX idx_maintenance_schedules_status ON assets.maintenance_schedules(tenant_id, status);
-- Work Orders
CREATE INDEX idx_work_orders_tenant ON assets.work_orders(tenant_id);
CREATE INDEX idx_work_orders_asset ON assets.work_orders(tenant_id, asset_id);
CREATE INDEX idx_work_orders_status ON assets.work_orders(tenant_id, status);
CREATE INDEX idx_work_orders_scheduled_date ON assets.work_orders(tenant_id, scheduled_start_date);
CREATE INDEX idx_work_orders_project ON assets.work_orders(tenant_id, project_id);
-- Work Order Parts
CREATE INDEX idx_work_order_parts_work_order ON assets.work_order_parts(work_order_id);
-- Maintenance History
CREATE INDEX idx_maintenance_history_tenant ON assets.maintenance_history(tenant_id);
CREATE INDEX idx_maintenance_history_asset ON assets.maintenance_history(tenant_id, asset_id);
CREATE INDEX idx_maintenance_history_date ON assets.maintenance_history(tenant_id, maintenance_date);
-- Asset Costs
CREATE INDEX idx_asset_costs_tenant ON assets.asset_costs(tenant_id);
CREATE INDEX idx_asset_costs_asset ON assets.asset_costs(tenant_id, asset_id);
CREATE INDEX idx_asset_costs_period ON assets.asset_costs(tenant_id, period_start, period_end);
CREATE INDEX idx_asset_costs_type ON assets.asset_costs(tenant_id, cost_type);
CREATE INDEX idx_asset_costs_project ON assets.asset_costs(tenant_id, project_id);
-- Asset Locations
CREATE INDEX idx_asset_locations_tenant ON assets.asset_locations(tenant_id);
CREATE INDEX idx_asset_locations_asset ON assets.asset_locations(tenant_id, asset_id);
CREATE INDEX idx_asset_locations_recorded ON assets.asset_locations(tenant_id, recorded_at);
CREATE INDEX idx_asset_locations_geo ON assets.asset_locations USING gist (
ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)
) WHERE longitude IS NOT NULL AND latitude IS NOT NULL;
-- Fuel Logs
CREATE INDEX idx_fuel_logs_tenant ON assets.fuel_logs(tenant_id);
CREATE INDEX idx_fuel_logs_asset ON assets.fuel_logs(tenant_id, asset_id);
CREATE INDEX idx_fuel_logs_date ON assets.fuel_logs(tenant_id, log_date);
-- ============================================================================
-- ROW LEVEL SECURITY (RLS)
-- ============================================================================
ALTER TABLE assets.asset_categories ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.assets ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.asset_assignments ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.maintenance_plans ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.maintenance_schedules ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.work_orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.work_order_parts ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.maintenance_history ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.asset_costs ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.asset_locations ENABLE ROW LEVEL SECURITY;
ALTER TABLE assets.fuel_logs ENABLE ROW LEVEL SECURITY;
-- ============================================================================
-- TRIGGERS DE AUDITORIA
-- ============================================================================
CREATE OR REPLACE FUNCTION assets.set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_asset_categories_updated_at
BEFORE UPDATE ON assets.asset_categories
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
CREATE TRIGGER trg_assets_updated_at
BEFORE UPDATE ON assets.assets
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
CREATE TRIGGER trg_asset_assignments_updated_at
BEFORE UPDATE ON assets.asset_assignments
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
CREATE TRIGGER trg_maintenance_plans_updated_at
BEFORE UPDATE ON assets.maintenance_plans
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
CREATE TRIGGER trg_maintenance_schedules_updated_at
BEFORE UPDATE ON assets.maintenance_schedules
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
CREATE TRIGGER trg_work_orders_updated_at
BEFORE UPDATE ON assets.work_orders
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
CREATE TRIGGER trg_work_order_parts_updated_at
BEFORE UPDATE ON assets.work_order_parts
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
CREATE TRIGGER trg_asset_costs_updated_at
BEFORE UPDATE ON assets.asset_costs
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
CREATE TRIGGER trg_fuel_logs_updated_at
BEFORE UPDATE ON assets.fuel_logs
FOR EACH ROW EXECUTE FUNCTION assets.set_updated_at();
-- ============================================================================
-- FUNCIONES AUXILIARES
-- ============================================================================
-- Funcion para calcular TCO de un activo
CREATE OR REPLACE FUNCTION assets.calculate_asset_tco(
p_asset_id UUID,
p_tenant_id UUID,
p_from_date DATE DEFAULT NULL,
p_to_date DATE DEFAULT NULL
)
RETURNS TABLE (
total_cost DECIMAL(18,2),
maintenance_cost DECIMAL(18,2),
fuel_cost DECIMAL(18,2),
depreciation_cost DECIMAL(18,2),
other_costs DECIMAL(18,2),
total_hours DECIMAL(12,2),
total_kilometers DECIMAL(12,2),
cost_per_hour DECIMAL(18,4),
cost_per_kilometer DECIMAL(18,4)
) AS $$
DECLARE
v_from_date DATE;
v_to_date DATE;
BEGIN
v_from_date := COALESCE(p_from_date, '1900-01-01'::DATE);
v_to_date := COALESCE(p_to_date, CURRENT_DATE);
RETURN QUERY
SELECT
COALESCE(SUM(ac.amount), 0) AS total_cost,
COALESCE(SUM(CASE WHEN ac.cost_type IN ('maintenance', 'repair') THEN ac.amount END), 0) AS maintenance_cost,
COALESCE(SUM(CASE WHEN ac.cost_type = 'fuel' THEN ac.amount END), 0) AS fuel_cost,
COALESCE(SUM(CASE WHEN ac.cost_type = 'depreciation' THEN ac.amount END), 0) AS depreciation_cost,
COALESCE(SUM(CASE WHEN ac.cost_type NOT IN ('maintenance', 'repair', 'fuel', 'depreciation') THEN ac.amount END), 0) AS other_costs,
COALESCE(SUM(ac.hours_in_period), 0) AS total_hours,
COALESCE(SUM(ac.kilometers_in_period), 0) AS total_kilometers,
CASE WHEN SUM(ac.hours_in_period) > 0 THEN SUM(ac.amount) / SUM(ac.hours_in_period) ELSE 0 END AS cost_per_hour,
CASE WHEN SUM(ac.kilometers_in_period) > 0 THEN SUM(ac.amount) / SUM(ac.kilometers_in_period) ELSE 0 END AS cost_per_kilometer
FROM assets.asset_costs ac
WHERE ac.asset_id = p_asset_id
AND ac.tenant_id = p_tenant_id
AND ac.period_start >= v_from_date
AND ac.period_end <= v_to_date;
END;
$$ LANGUAGE plpgsql;
-- Funcion para actualizar proximo mantenimiento de un activo
CREATE OR REPLACE FUNCTION assets.update_next_maintenance()
RETURNS TRIGGER AS $$
BEGIN
-- Actualizar proximo mantenimiento del activo
UPDATE assets.assets a
SET
next_maintenance_date = (
SELECT MIN(scheduled_date)
FROM assets.maintenance_schedules ms
WHERE ms.asset_id = a.id
AND ms.status = 'pending'
),
updated_at = NOW()
WHERE a.id = COALESCE(NEW.asset_id, OLD.asset_id);
RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_update_next_maintenance
AFTER INSERT OR UPDATE OR DELETE ON assets.maintenance_schedules
FOR EACH ROW EXECUTE FUNCTION assets.update_next_maintenance();
-- ============================================================================
-- COMENTARIOS DE DOCUMENTACION
-- ============================================================================
COMMENT ON SCHEMA assets IS 'Modulo de Activos, Maquinaria y Mantenimiento (MAE-015) - ERP Construccion';
COMMENT ON TABLE assets.asset_categories IS 'Categorias de activos para clasificacion';
COMMENT ON TABLE assets.assets IS 'Catalogo principal de activos (maquinaria, equipo, vehiculos)';
COMMENT ON TABLE assets.asset_assignments IS 'Asignaciones de activos a proyectos/obras';
COMMENT ON TABLE assets.maintenance_plans IS 'Planes de mantenimiento preventivo';
COMMENT ON TABLE assets.maintenance_schedules IS 'Programacion de mantenimientos por activo';
COMMENT ON TABLE assets.work_orders IS 'Ordenes de trabajo de mantenimiento';
COMMENT ON TABLE assets.work_order_parts IS 'Partes/refacciones utilizadas en ordenes de trabajo';
COMMENT ON TABLE assets.maintenance_history IS 'Historial de mantenimientos realizados';
COMMENT ON TABLE assets.asset_costs IS 'Costos de operacion y mantenimiento de activos';
COMMENT ON TABLE assets.asset_locations IS 'Historial de ubicaciones GPS de activos';
COMMENT ON TABLE assets.fuel_logs IS 'Registro de cargas de combustible';
-- ============================================================================
-- FIN DEL SCRIPT
-- ============================================================================