-- ============================================================================ -- 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 -- ============================================================================