erp-mecanicas-diesel/docs/03-modelo-datos/SCHEMA-VEHICLE-MANAGEMENT.md

14 KiB

Schema: vehicle_management

Descripcion

Schema para gestion de vehiculos, flotas, especificaciones de motor y recordatorios de mantenimiento.

NOTA: Este schema depende de erp-core para autenticacion y tenants. La columna tenant_id referencia a core.tenants(id).

Tablas

engine_catalog

Catalogo global de motores diesel (sin tenant_id, es compartido).

CREATE TABLE vehicle_management.engine_catalog (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Identificacion
    make            VARCHAR(50) NOT NULL,
    model           VARCHAR(50) NOT NULL,

    -- Especificaciones
    cylinders       INTEGER CHECK (cylinders > 0),
    displacement    DECIMAL(5,2) CHECK (displacement > 0),

    fuel_type       VARCHAR(20) DEFAULT 'diesel',

    -- Potencia tipica
    horsepower_min  INTEGER CHECK (horsepower_min > 0),
    horsepower_max  INTEGER CHECK (horsepower_max > 0),
    torque_max      INTEGER CHECK (torque_max > 0),

    -- Sistema de inyeccion
    injection_system VARCHAR(50),

    -- Anos de produccion
    year_start      INTEGER CHECK (year_start >= 1950),
    year_end        INTEGER CHECK (year_end >= 1950),

    notes           TEXT,

    created_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    CONSTRAINT uq_engine_model UNIQUE(make, model),
    CONSTRAINT chk_horsepower CHECK (horsepower_max >= horsepower_min),
    CONSTRAINT chk_years CHECK (year_end IS NULL OR year_end >= year_start)
);

-- Datos iniciales de motores comunes
INSERT INTO vehicle_management.engine_catalog
    (make, model, cylinders, displacement, horsepower_min, horsepower_max, torque_max, injection_system)
VALUES
    ('Cummins', 'ISX15', 6, 14.9, 400, 600, 2050, 'common_rail'),
    ('Cummins', 'ISL9', 6, 8.9, 260, 380, 1250, 'common_rail'),
    ('Cummins', 'X15', 6, 14.9, 400, 605, 2050, 'common_rail'),
    ('Cummins', 'ISB6.7', 6, 6.7, 200, 325, 750, 'common_rail'),
    ('Detroit', 'DD15', 6, 14.8, 400, 505, 1850, 'common_rail'),
    ('Detroit', 'DD13', 6, 12.8, 350, 470, 1650, 'common_rail'),
    ('Paccar', 'MX-13', 6, 12.9, 380, 510, 1850, 'common_rail'),
    ('Paccar', 'MX-11', 6, 10.8, 355, 430, 1550, 'common_rail'),
    ('Navistar', 'MaxxForce 13', 6, 12.4, 410, 475, 1700, 'common_rail'),
    ('Volvo', 'D13', 6, 12.8, 375, 500, 1850, 'unit_injector'),
    ('Caterpillar', 'C15', 6, 15.2, 435, 625, 2050, 'unit_injector'),
    ('Caterpillar', 'C13', 6, 12.5, 380, 520, 1750, 'unit_injector');

fleets

Flotas de vehiculos.

CREATE TABLE vehicle_management.fleets (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,

    -- Identificacion
    name            VARCHAR(200) NOT NULL,
    code            VARCHAR(20),

    -- Contacto
    contact_name    VARCHAR(200),
    contact_email   VARCHAR(200),
    contact_phone   VARCHAR(20),

    -- Condiciones comerciales
    discount_labor_pct  DECIMAL(5,2) DEFAULT 0
        CHECK (discount_labor_pct >= 0 AND discount_labor_pct <= 100),
    discount_parts_pct  DECIMAL(5,2) DEFAULT 0
        CHECK (discount_parts_pct >= 0 AND discount_parts_pct <= 100),
    credit_days     INTEGER DEFAULT 0 CHECK (credit_days >= 0),
    credit_limit    DECIMAL(12,2) DEFAULT 0 CHECK (credit_limit >= 0),

    -- Estadisticas (calculadas via trigger)
    vehicle_count   INTEGER DEFAULT 0 CHECK (vehicle_count >= 0),

    notes           TEXT,
    is_active       BOOLEAN DEFAULT TRUE,

    created_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_fleets_tenant ON vehicle_management.fleets(tenant_id);
CREATE INDEX idx_fleets_name ON vehicle_management.fleets(name);

SELECT create_tenant_rls_policies('vehicle_management', 'fleets');

vehicles

Vehiculos registrados.

CREATE TABLE vehicle_management.vehicles (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,

    -- Propietario
    customer_id     UUID NOT NULL,  -- Referencia a core.partners
    fleet_id        UUID REFERENCES vehicle_management.fleets(id),

    -- Identificacion
    vin             VARCHAR(17),
    license_plate   VARCHAR(15) NOT NULL,
    economic_number VARCHAR(20),

    -- Datos del vehiculo
    make            VARCHAR(50) NOT NULL,
    model           VARCHAR(100) NOT NULL,
    year            INTEGER NOT NULL CHECK (year >= 1950 AND year <= 2100),
    color           VARCHAR(30),

    -- Tipo
    vehicle_type    VARCHAR(30) DEFAULT 'truck'
        CHECK (vehicle_type IN ('truck', 'trailer', 'bus', 'pickup', 'other')),

    -- Kilometraje
    current_odometer INTEGER CHECK (current_odometer >= 0),
    odometer_updated_at TIMESTAMP WITH TIME ZONE,

    -- Foto
    photo_url       VARCHAR(500),

    -- Estado
    status          VARCHAR(20) DEFAULT 'active'
        CHECK (status IN ('active', 'inactive', 'sold')),

    notes           TEXT,

    -- Audit
    created_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    CONSTRAINT uq_vehicle_plate UNIQUE(tenant_id, license_plate)
);

CREATE INDEX idx_vehicles_tenant ON vehicle_management.vehicles(tenant_id);
CREATE INDEX idx_vehicles_customer ON vehicle_management.vehicles(customer_id);
CREATE INDEX idx_vehicles_fleet ON vehicle_management.vehicles(fleet_id);
CREATE INDEX idx_vehicles_vin ON vehicle_management.vehicles(vin);
CREATE INDEX idx_vehicles_plate ON vehicle_management.vehicles(license_plate);

SELECT create_tenant_rls_policies('vehicle_management', 'vehicles');

vehicle_engines

Especificaciones del motor del vehiculo.

CREATE TABLE vehicle_management.vehicle_engines (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    vehicle_id      UUID NOT NULL REFERENCES vehicle_management.vehicles(id) ON DELETE CASCADE,

    -- Motor base (catalogo)
    engine_catalog_id UUID REFERENCES vehicle_management.engine_catalog(id),

    -- Identificacion
    serial_number   VARCHAR(50),

    -- Especificaciones especificas
    horsepower      INTEGER CHECK (horsepower > 0),
    torque          INTEGER CHECK (torque > 0),

    -- ECM
    ecm_model       VARCHAR(50),
    ecm_software    VARCHAR(50),

    -- Sistema inyeccion
    injection_system VARCHAR(50),
    rail_pressure_max DECIMAL(10,2) CHECK (rail_pressure_max > 0),
    injector_count  INTEGER CHECK (injector_count > 0),

    -- Turbo
    turbo_type      VARCHAR(50)
        CHECK (turbo_type IN ('VGT', 'wastegate', 'twin', 'compound')),
    turbo_make      VARCHAR(50),
    turbo_model     VARCHAR(50),

    -- Fechas
    manufacture_date DATE,
    rebuild_date    DATE,
    rebuild_odometer INTEGER CHECK (rebuild_odometer >= 0),

    notes           TEXT,

    created_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_vehicle_engines_vehicle ON vehicle_management.vehicle_engines(vehicle_id);
CREATE INDEX idx_vehicle_engines_serial ON vehicle_management.vehicle_engines(serial_number);
CREATE INDEX idx_vehicle_engines_catalog ON vehicle_management.vehicle_engines(engine_catalog_id);

vehicle_history

Historial de cambios del vehiculo.

CREATE TABLE vehicle_management.vehicle_history (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    vehicle_id      UUID NOT NULL REFERENCES vehicle_management.vehicles(id) ON DELETE CASCADE,

    field_name      VARCHAR(50) NOT NULL,
    old_value       TEXT,
    new_value       TEXT,

    changed_by      UUID,
    changed_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_vehicle_history_vehicle ON vehicle_management.vehicle_history(vehicle_id);
CREATE INDEX idx_vehicle_history_date ON vehicle_management.vehicle_history(changed_at DESC);

maintenance_reminders

Recordatorios de mantenimiento.

CREATE TABLE vehicle_management.maintenance_reminders (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id       UUID NOT NULL,

    vehicle_id      UUID NOT NULL REFERENCES vehicle_management.vehicles(id) ON DELETE CASCADE,

    -- Tipo de servicio
    service_type    VARCHAR(100) NOT NULL,
    service_id      UUID,  -- Referencia a service_management.services

    -- Frecuencia
    frequency_type  VARCHAR(20) NOT NULL
        CHECK (frequency_type IN ('time', 'odometer', 'both')),

    interval_days   INTEGER CHECK (interval_days > 0),
    interval_km     INTEGER CHECK (interval_km > 0),

    -- Ultimo servicio
    last_service_date DATE,
    last_service_km INTEGER,

    -- Proximo servicio (calculado)
    next_due_date   DATE,
    next_due_km     INTEGER,

    -- Notificaciones
    notify_days_before INTEGER DEFAULT 7 CHECK (notify_days_before >= 0),
    notify_km_before INTEGER DEFAULT 1000 CHECK (notify_km_before >= 0),

    -- Estado
    status          VARCHAR(20) DEFAULT 'active'
        CHECK (status IN ('active', 'paused', 'completed')),

    notes           TEXT,

    created_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_reminders_tenant ON vehicle_management.maintenance_reminders(tenant_id);
CREATE INDEX idx_reminders_vehicle ON vehicle_management.maintenance_reminders(vehicle_id);
CREATE INDEX idx_reminders_due_date ON vehicle_management.maintenance_reminders(next_due_date);

SELECT create_tenant_rls_policies('vehicle_management', 'maintenance_reminders');

reminder_notifications

Notificaciones enviadas.

CREATE TABLE vehicle_management.reminder_notifications (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    reminder_id     UUID NOT NULL REFERENCES vehicle_management.maintenance_reminders(id) ON DELETE CASCADE,

    notification_type VARCHAR(20) NOT NULL
        CHECK (notification_type IN ('email', 'sms', 'push', 'whatsapp')),

    sent_to         VARCHAR(200),
    sent_at         TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    status          VARCHAR(20) DEFAULT 'sent'
        CHECK (status IN ('sent', 'delivered', 'failed')),

    error_message   TEXT
);

CREATE INDEX idx_reminder_notif_reminder ON vehicle_management.reminder_notifications(reminder_id);

vehicle_documents

Documentos del vehiculo.

CREATE TABLE vehicle_management.vehicle_documents (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    vehicle_id      UUID NOT NULL REFERENCES vehicle_management.vehicles(id) ON DELETE CASCADE,

    document_type   VARCHAR(50) NOT NULL
        CHECK (document_type IN ('registration', 'insurance', 'permit', 'verification', 'other')),

    document_number VARCHAR(100),

    issue_date      DATE,
    expiry_date     DATE,

    file_url        VARCHAR(500),

    notes           TEXT,

    created_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_vehicle_docs_vehicle ON vehicle_management.vehicle_documents(vehicle_id);
CREATE INDEX idx_vehicle_docs_expiry ON vehicle_management.vehicle_documents(expiry_date);

Vistas

vw_vehicle_summary

Vista resumida de vehiculos con filtro RLS automatico.

CREATE OR REPLACE VIEW vehicle_management.vw_vehicle_summary AS
SELECT
    v.id,
    v.tenant_id,
    v.license_plate,
    v.economic_number,
    v.make,
    v.model,
    v.year,
    v.current_odometer,
    v.status,
    v.customer_id,
    f.name as fleet_name,
    f.id as fleet_id,
    ec.make as engine_make,
    ec.model as engine_model,
    ve.serial_number as engine_serial,
    ve.horsepower,
    (SELECT COUNT(*) FROM service_management.service_orders so
     WHERE so.vehicle_id = v.id) as total_orders,
    (SELECT MAX(so.completed_at) FROM service_management.service_orders so
     WHERE so.vehicle_id = v.id AND so.status = 'completed') as last_service_date
FROM vehicle_management.vehicles v
LEFT JOIN vehicle_management.fleets f ON v.fleet_id = f.id
LEFT JOIN vehicle_management.vehicle_engines ve ON ve.vehicle_id = v.id
LEFT JOIN vehicle_management.engine_catalog ec ON ve.engine_catalog_id = ec.id
WHERE v.tenant_id = get_current_tenant_id();

COMMENT ON VIEW vehicle_management.vw_vehicle_summary IS 'Vista resumida de vehiculos con filtro RLS';

Triggers

Actualizar conteo de vehiculos en flotas

CREATE OR REPLACE FUNCTION vehicle_management.update_fleet_vehicle_count()
RETURNS TRIGGER AS $$
BEGIN
    -- Actualizar conteo de la flota anterior (si existe)
    IF TG_OP = 'DELETE' OR (TG_OP = 'UPDATE' AND OLD.fleet_id IS DISTINCT FROM NEW.fleet_id) THEN
        IF OLD.fleet_id IS NOT NULL THEN
            UPDATE vehicle_management.fleets
            SET vehicle_count = (
                SELECT COUNT(*) FROM vehicle_management.vehicles
                WHERE fleet_id = OLD.fleet_id AND status = 'active'
            )
            WHERE id = OLD.fleet_id;
        END IF;
    END IF;

    -- Actualizar conteo de la nueva flota
    IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND OLD.fleet_id IS DISTINCT FROM NEW.fleet_id) THEN
        IF NEW.fleet_id IS NOT NULL THEN
            UPDATE vehicle_management.fleets
            SET vehicle_count = (
                SELECT COUNT(*) FROM vehicle_management.vehicles
                WHERE fleet_id = NEW.fleet_id AND status = 'active'
            )
            WHERE id = NEW.fleet_id;
        END IF;
    END IF;

    IF TG_OP = 'DELETE' THEN
        RETURN OLD;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_update_fleet_count
    AFTER INSERT OR UPDATE OR DELETE ON vehicle_management.vehicles
    FOR EACH ROW EXECUTE FUNCTION vehicle_management.update_fleet_vehicle_count();

Relacion con erp-core

Este schema utiliza las siguientes referencias a erp-core:

Columna Referencia erp-core
tenant_id core.tenants(id)
customer_id core.partners(id)
changed_by auth.users(id)

Creado por: Requirements-Analyst Fecha: 2025-12-06 Actualizado: 2025-12-06 (Correccion tenant_id, CHECK constraints, RLS completo)