# DDL-SPEC: Schema quality_management

## Identificacion

| Campo | Valor |
|-------|-------|
| **Schema** | quality_management |
| **Modulo** | MAI-006 |
| **Vertical** | Construccion |
| **Version** | 1.0 |
| **Estado** | En Diseno |
| **Autor** | Database-Agent |
| **Fecha** | 2025-12-06 |

---

## Descripcion General

El schema `quality_management` gestiona el control de calidad en proyectos de construccion, incluyendo inspecciones, no conformidades, acciones correctivas, pruebas de laboratorio y certificaciones.

### RF Cubiertos

| RF | Titulo | Tablas |
|----|--------|--------|
| RF-CAL-001 | Inspecciones de Calidad | inspections, inspection_items |
| RF-CAL-002 | No Conformidades | non_conformities, corrective_actions |
| RF-CAL-003 | Pruebas de Laboratorio | lab_tests |
| RF-CAL-004 | Certificaciones | certifications |

---

## Diagrama ER

```mermaid
erDiagram
    inspections {
        uuid id PK
        uuid tenant_id FK
        uuid project_id FK
        varchar inspection_code
        varchar inspection_type
        varchar title
        text description
        uuid inspector_id FK
        date inspection_date
        time inspection_time
        varchar status
        varchar result
        jsonb checklist
        jsonb attachments
        text observations
        uuid approved_by FK
        timestamptz approved_at
        timestamptz created_at
        timestamptz updated_at
    }

    inspection_items {
        uuid id PK
        uuid tenant_id FK
        uuid inspection_id FK
        int item_number
        varchar item_type
        varchar description
        varchar requirement
        varchar verification_method
        varchar result
        varchar conformity
        text observations
        jsonb measurements
        jsonb attachments
        timestamptz created_at
    }

    non_conformities {
        uuid id PK
        uuid tenant_id FK
        uuid project_id FK
        uuid inspection_id FK
        varchar nc_code
        varchar severity
        varchar category
        varchar title
        text description
        varchar location
        text root_cause
        varchar status
        uuid detected_by FK
        timestamptz detected_at
        uuid assigned_to FK
        date deadline
        uuid closed_by FK
        timestamptz closed_at
        jsonb attachments
        timestamptz created_at
        timestamptz updated_at
    }

    corrective_actions {
        uuid id PK
        uuid tenant_id FK
        uuid non_conformity_id FK
        int action_number
        varchar action_type
        text description
        uuid responsible_id FK
        date planned_date
        date completed_date
        varchar status
        text verification_method
        text results
        jsonb evidence
        timestamptz created_at
        timestamptz updated_at
    }

    lab_tests {
        uuid id PK
        uuid tenant_id FK
        uuid project_id FK
        uuid inspection_id FK
        varchar test_code
        varchar test_type
        varchar material_type
        varchar sample_id
        date sample_date
        varchar location
        uuid lab_id FK
        uuid technician_id FK
        date test_date
        varchar status
        varchar result
        jsonb test_parameters
        jsonb test_results
        boolean meets_spec
        text observations
        jsonb attachments
        timestamptz created_at
        timestamptz updated_at
    }

    certifications {
        uuid id PK
        uuid tenant_id FK
        uuid project_id FK
        varchar cert_code
        varchar cert_type
        varchar title
        varchar issuing_body
        date issue_date
        date expiry_date
        varchar status
        text scope
        jsonb requirements
        jsonb documents
        uuid verified_by FK
        timestamptz verified_at
        jsonb attachments
        timestamptz created_at
        timestamptz updated_at
    }

    inspections ||--o{ inspection_items : "contiene"
    inspections ||--o{ non_conformities : "genera"
    non_conformities ||--o{ corrective_actions : "requiere"
    inspections ||--o{ lab_tests : "solicita"
```

---

## Tablas

### 1. inspections

Registro de inspecciones de calidad realizadas.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `tenant_id` | UUID | NOT NULL | - | FK a tenants |
| `project_id` | UUID | NOT NULL | - | FK a projects |
| `inspection_code` | VARCHAR(50) | NOT NULL | - | Codigo unico de inspeccion |
| `inspection_type` | VARCHAR(50) | NOT NULL | - | Tipo de inspeccion (ENUM) |
| `title` | VARCHAR(255) | NOT NULL | - | Titulo de la inspeccion |
| `description` | TEXT | NULL | - | Descripcion detallada |
| `inspector_id` | UUID | NOT NULL | - | FK a users (inspector) |
| `inspection_date` | DATE | NOT NULL | - | Fecha de inspeccion |
| `inspection_time` | TIME | NULL | - | Hora de inspeccion |
| `location` | VARCHAR(255) | NULL | - | Ubicacion en obra |
| `status` | VARCHAR(20) | NOT NULL | 'scheduled' | Estado (ENUM) |
| `result` | VARCHAR(20) | NULL | - | Resultado (ENUM) |
| `checklist` | JSONB | NULL | '{}' | Checklist de verificacion |
| `attachments` | JSONB | NULL | '[]' | Fotos, documentos |
| `observations` | TEXT | NULL | - | Observaciones generales |
| `approved_by` | UUID | NULL | - | FK a users (aprobador) |
| `approved_at` | TIMESTAMPTZ | NULL | - | Fecha aprobacion |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |
| `created_by` | UUID | NOT NULL | - | FK a users |
| `updated_by` | UUID | NULL | - | FK a users |
| `is_active` | BOOLEAN | NOT NULL | true | Soft delete |
| `deleted_at` | TIMESTAMPTZ | NULL | - | Fecha eliminacion |
| `deleted_by` | UUID | NULL | - | FK a users |

```sql
-- ============================================================================
-- Schema: quality_management
-- Tabla: inspections
-- Descripcion: Inspecciones de calidad en proyectos de construccion
-- Modulo: MAI-006
-- ============================================================================

-- ENUM: Tipos de inspeccion
CREATE TYPE quality_management.inspection_type AS ENUM (
    'initial',              -- Inspeccion inicial
    'progress',             -- Inspeccion de avance
    'final',                -- Inspeccion final
    'structural',           -- Inspeccion estructural
    'electrical',           -- Inspeccion electrica
    'plumbing',             -- Inspeccion de plomeria
    'hvac',                 -- Inspeccion HVAC
    'safety',               -- Inspeccion de seguridad
    'environmental',        -- Inspeccion ambiental
    'quality_control',      -- Control de calidad
    'material_reception',   -- Recepcion de materiales
    'equipment',            -- Inspeccion de equipos
    'finishing'             -- Inspeccion de acabados
);

-- ENUM: Estados de inspeccion
CREATE TYPE quality_management.inspection_status AS ENUM (
    'scheduled',    -- Programada
    'in_progress',  -- En progreso
    'completed',    -- Completada
    'approved',     -- Aprobada
    'rejected',     -- Rechazada
    'cancelled'     -- Cancelada
);

-- ENUM: Resultados de inspeccion
CREATE TYPE quality_management.inspection_result AS ENUM (
    'passed',           -- Aprobada
    'failed',           -- Rechazada
    'conditional',      -- Condicional
    'pending_review'    -- Pendiente de revision
);

-- Crear tabla
CREATE TABLE IF NOT EXISTS quality_management.inspections (
    -- Primary Key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Multi-tenant
    tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,

    -- Relaciones
    project_id UUID NOT NULL,
    inspector_id UUID NOT NULL,

    -- Datos de negocio
    inspection_code VARCHAR(50) NOT NULL,
    inspection_type quality_management.inspection_type NOT NULL,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    inspection_date DATE NOT NULL,
    inspection_time TIME,
    location VARCHAR(255),
    status quality_management.inspection_status NOT NULL DEFAULT 'scheduled',
    result quality_management.inspection_result,

    -- Datos estructurados
    checklist JSONB DEFAULT '{}',
    attachments JSONB DEFAULT '[]',
    observations TEXT,

    -- Aprobacion
    approved_by UUID,
    approved_at TIMESTAMPTZ,

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID,

    -- Soft delete
    is_active BOOLEAN NOT NULL DEFAULT true,
    deleted_at TIMESTAMPTZ,
    deleted_by UUID,

    -- Constraints
    CONSTRAINT uq_inspections_code_tenant UNIQUE (tenant_id, inspection_code),
    CONSTRAINT chk_inspections_approval CHECK (
        (approved_by IS NULL AND approved_at IS NULL) OR
        (approved_by IS NOT NULL AND approved_at IS NOT NULL)
    )
);

-- Comentarios
COMMENT ON TABLE quality_management.inspections IS 'Inspecciones de calidad en proyectos de construccion';
COMMENT ON COLUMN quality_management.inspections.inspection_code IS 'Codigo unico de inspeccion por tenant';
COMMENT ON COLUMN quality_management.inspections.inspection_type IS 'Tipo de inspeccion realizada';
COMMENT ON COLUMN quality_management.inspections.status IS 'Estado actual de la inspeccion';
COMMENT ON COLUMN quality_management.inspections.result IS 'Resultado de la inspeccion';
COMMENT ON COLUMN quality_management.inspections.checklist IS 'Lista de verificacion en formato JSON';

-- ============================================================================
-- INDICES
-- ============================================================================

CREATE INDEX idx_inspections_tenant ON quality_management.inspections(tenant_id);
CREATE INDEX idx_inspections_project ON quality_management.inspections(project_id, inspection_date DESC);
CREATE INDEX idx_inspections_inspector ON quality_management.inspections(inspector_id, inspection_date DESC);
CREATE INDEX idx_inspections_code ON quality_management.inspections(inspection_code);
CREATE INDEX idx_inspections_status ON quality_management.inspections(status) WHERE is_active = true;
CREATE INDEX idx_inspections_type ON quality_management.inspections(inspection_type, inspection_date DESC);
CREATE INDEX idx_inspections_date ON quality_management.inspections(inspection_date DESC);
CREATE INDEX idx_inspections_result ON quality_management.inspections(result) WHERE result IS NOT NULL;

-- ============================================================================
-- RLS POLICIES
-- ============================================================================

ALTER TABLE quality_management.inspections ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_select ON quality_management.inspections
    FOR SELECT
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_insert ON quality_management.inspections
    FOR INSERT
    WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_update ON quality_management.inspections
    FOR UPDATE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_delete ON quality_management.inspections
    FOR DELETE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- ============================================================================
-- TRIGGERS
-- ============================================================================

CREATE TRIGGER trg_inspections_update_timestamp
    BEFORE UPDATE ON quality_management.inspections
    FOR EACH ROW
    EXECUTE FUNCTION core_shared.set_updated_at();
```

---

### 2. inspection_items

Items individuales de cada inspeccion.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `tenant_id` | UUID | NOT NULL | - | FK a tenants |
| `inspection_id` | UUID | NOT NULL | - | FK a inspections |
| `item_number` | INTEGER | NOT NULL | - | Numero de item |
| `item_type` | VARCHAR(50) | NOT NULL | - | Tipo de item |
| `description` | VARCHAR(500) | NOT NULL | - | Descripcion del item |
| `requirement` | VARCHAR(500) | NOT NULL | - | Requisito a verificar |
| `verification_method` | VARCHAR(100) | NOT NULL | - | Metodo de verificacion |
| `result` | VARCHAR(20) | NULL | - | Resultado del item |
| `conformity` | VARCHAR(20) | NULL | - | Conformidad (ENUM) |
| `observations` | TEXT | NULL | - | Observaciones |
| `measurements` | JSONB | NULL | '{}' | Mediciones realizadas |
| `attachments` | JSONB | NULL | '[]' | Evidencias |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `created_by` | UUID | NOT NULL | - | FK a users |

```sql
-- ============================================================================
-- Tabla: inspection_items
-- Descripcion: Items individuales de cada inspeccion
-- ============================================================================

-- ENUM: Conformidad
CREATE TYPE quality_management.conformity_status AS ENUM (
    'conforming',       -- Conforme
    'non_conforming',   -- No conforme
    'partially',        -- Parcialmente conforme
    'not_applicable'    -- No aplica
);

CREATE TABLE IF NOT EXISTS quality_management.inspection_items (
    -- Primary Key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Multi-tenant
    tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,

    -- Relaciones
    inspection_id UUID NOT NULL REFERENCES quality_management.inspections(id) ON DELETE CASCADE,

    -- Datos de negocio
    item_number INTEGER NOT NULL,
    item_type VARCHAR(50) NOT NULL,
    description VARCHAR(500) NOT NULL,
    requirement VARCHAR(500) NOT NULL,
    verification_method VARCHAR(100) NOT NULL,
    result VARCHAR(20),
    conformity quality_management.conformity_status,
    observations TEXT,

    -- Datos estructurados
    measurements JSONB DEFAULT '{}',
    attachments JSONB DEFAULT '[]',

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,

    -- Constraints
    CONSTRAINT uq_inspection_items_number UNIQUE (inspection_id, item_number)
);

-- Comentarios
COMMENT ON TABLE quality_management.inspection_items IS 'Items individuales de verificacion en inspecciones';
COMMENT ON COLUMN quality_management.inspection_items.item_number IS 'Numero secuencial del item dentro de la inspeccion';
COMMENT ON COLUMN quality_management.inspection_items.conformity IS 'Estado de conformidad del item';
COMMENT ON COLUMN quality_management.inspection_items.measurements IS 'Mediciones y valores registrados en formato JSON';

-- Indices
CREATE INDEX idx_inspection_items_tenant ON quality_management.inspection_items(tenant_id);
CREATE INDEX idx_inspection_items_inspection ON quality_management.inspection_items(inspection_id, item_number);
CREATE INDEX idx_inspection_items_conformity ON quality_management.inspection_items(conformity);

-- RLS
ALTER TABLE quality_management.inspection_items ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_select ON quality_management.inspection_items
    FOR SELECT
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_insert ON quality_management.inspection_items
    FOR INSERT
    WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_update ON quality_management.inspection_items
    FOR UPDATE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_delete ON quality_management.inspection_items
    FOR DELETE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
```

---

### 3. non_conformities

No conformidades detectadas durante inspecciones.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `tenant_id` | UUID | NOT NULL | - | FK a tenants |
| `project_id` | UUID | NOT NULL | - | FK a projects |
| `inspection_id` | UUID | NULL | - | FK a inspections |
| `nc_code` | VARCHAR(50) | NOT NULL | - | Codigo de NC |
| `severity` | VARCHAR(20) | NOT NULL | - | Severidad (ENUM) |
| `category` | VARCHAR(50) | NOT NULL | - | Categoria |
| `title` | VARCHAR(255) | NOT NULL | - | Titulo |
| `description` | TEXT | NOT NULL | - | Descripcion detallada |
| `location` | VARCHAR(255) | NOT NULL | - | Ubicacion |
| `root_cause` | TEXT | NULL | - | Causa raiz |
| `status` | VARCHAR(20) | NOT NULL | 'open' | Estado (ENUM) |
| `detected_by` | UUID | NOT NULL | - | FK a users |
| `detected_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha deteccion |
| `assigned_to` | UUID | NULL | - | FK a users |
| `deadline` | DATE | NULL | - | Fecha limite |
| `closed_by` | UUID | NULL | - | FK a users |
| `closed_at` | TIMESTAMPTZ | NULL | - | Fecha cierre |
| `attachments` | JSONB | NULL | '[]' | Evidencias |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |
| `created_by` | UUID | NOT NULL | - | FK a users |
| `updated_by` | UUID | NULL | - | FK a users |
| `is_active` | BOOLEAN | NOT NULL | true | Soft delete |
| `deleted_at` | TIMESTAMPTZ | NULL | - | Fecha eliminacion |
| `deleted_by` | UUID | NULL | - | FK a users |

```sql
-- ============================================================================
-- Tabla: non_conformities
-- Descripcion: No conformidades detectadas
-- ============================================================================

-- ENUM: Severidad de NC
CREATE TYPE quality_management.nc_severity AS ENUM (
    'critical',     -- Critica
    'major',        -- Mayor
    'minor',        -- Menor
    'observation'   -- Observacion
);

-- ENUM: Estados de NC
CREATE TYPE quality_management.nc_status AS ENUM (
    'open',             -- Abierta
    'assigned',         -- Asignada
    'in_progress',      -- En progreso
    'pending_review',   -- Pendiente revision
    'closed',           -- Cerrada
    'rejected'          -- Rechazada
);

CREATE TABLE IF NOT EXISTS quality_management.non_conformities (
    -- Primary Key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Multi-tenant
    tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,

    -- Relaciones
    project_id UUID NOT NULL,
    inspection_id UUID REFERENCES quality_management.inspections(id),

    -- Datos de negocio
    nc_code VARCHAR(50) NOT NULL,
    severity quality_management.nc_severity NOT NULL,
    category VARCHAR(50) NOT NULL,
    title VARCHAR(255) NOT NULL,
    description TEXT NOT NULL,
    location VARCHAR(255) NOT NULL,
    root_cause TEXT,
    status quality_management.nc_status NOT NULL DEFAULT 'open',

    -- Gestion
    detected_by UUID NOT NULL,
    detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    assigned_to UUID,
    deadline DATE,
    closed_by UUID,
    closed_at TIMESTAMPTZ,

    -- Datos estructurados
    attachments JSONB DEFAULT '[]',

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID,

    -- Soft delete
    is_active BOOLEAN NOT NULL DEFAULT true,
    deleted_at TIMESTAMPTZ,
    deleted_by UUID,

    -- Constraints
    CONSTRAINT uq_nc_code_tenant UNIQUE (tenant_id, nc_code),
    CONSTRAINT chk_nc_closure CHECK (
        (closed_by IS NULL AND closed_at IS NULL) OR
        (closed_by IS NOT NULL AND closed_at IS NOT NULL)
    )
);

-- Comentarios
COMMENT ON TABLE quality_management.non_conformities IS 'No conformidades detectadas en inspecciones';
COMMENT ON COLUMN quality_management.non_conformities.nc_code IS 'Codigo unico de no conformidad';
COMMENT ON COLUMN quality_management.non_conformities.severity IS 'Nivel de severidad de la NC';
COMMENT ON COLUMN quality_management.non_conformities.status IS 'Estado actual de la NC';

-- Indices
CREATE INDEX idx_nc_tenant ON quality_management.non_conformities(tenant_id);
CREATE INDEX idx_nc_project ON quality_management.non_conformities(project_id, detected_at DESC);
CREATE INDEX idx_nc_inspection ON quality_management.non_conformities(inspection_id);
CREATE INDEX idx_nc_code ON quality_management.non_conformities(nc_code);
CREATE INDEX idx_nc_status ON quality_management.non_conformities(status) WHERE is_active = true;
CREATE INDEX idx_nc_severity ON quality_management.non_conformities(severity, detected_at DESC);
CREATE INDEX idx_nc_assigned ON quality_management.non_conformities(assigned_to) WHERE status IN ('assigned', 'in_progress');
CREATE INDEX idx_nc_overdue ON quality_management.non_conformities(deadline)
    WHERE status NOT IN ('closed', 'rejected') AND deadline < CURRENT_DATE;

-- RLS
ALTER TABLE quality_management.non_conformities ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_select ON quality_management.non_conformities
    FOR SELECT
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_insert ON quality_management.non_conformities
    FOR INSERT
    WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_update ON quality_management.non_conformities
    FOR UPDATE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_delete ON quality_management.non_conformities
    FOR DELETE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Triggers
CREATE TRIGGER trg_nc_update_timestamp
    BEFORE UPDATE ON quality_management.non_conformities
    FOR EACH ROW
    EXECUTE FUNCTION core_shared.set_updated_at();
```

---

### 4. corrective_actions

Acciones correctivas para resolver no conformidades.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `tenant_id` | UUID | NOT NULL | - | FK a tenants |
| `non_conformity_id` | UUID | NOT NULL | - | FK a non_conformities |
| `action_number` | INTEGER | NOT NULL | - | Numero de accion |
| `action_type` | VARCHAR(50) | NOT NULL | - | Tipo de accion (ENUM) |
| `description` | TEXT | NOT NULL | - | Descripcion |
| `responsible_id` | UUID | NOT NULL | - | FK a users |
| `planned_date` | DATE | NOT NULL | - | Fecha planificada |
| `completed_date` | DATE | NULL | - | Fecha completada |
| `status` | VARCHAR(20) | NOT NULL | 'planned' | Estado (ENUM) |
| `verification_method` | TEXT | NULL | - | Metodo de verificacion |
| `results` | TEXT | NULL | - | Resultados obtenidos |
| `evidence` | JSONB | NULL | '[]' | Evidencias |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |
| `created_by` | UUID | NOT NULL | - | FK a users |
| `updated_by` | UUID | NULL | - | FK a users |

```sql
-- ============================================================================
-- Tabla: corrective_actions
-- Descripcion: Acciones correctivas para NCs
-- ============================================================================

-- ENUM: Tipos de accion correctiva
CREATE TYPE quality_management.action_type AS ENUM (
    'immediate',        -- Accion inmediata
    'corrective',       -- Accion correctiva
    'preventive',       -- Accion preventiva
    'improvement'       -- Mejora
);

-- ENUM: Estados de accion
CREATE TYPE quality_management.action_status AS ENUM (
    'planned',      -- Planificada
    'in_progress',  -- En progreso
    'completed',    -- Completada
    'verified',     -- Verificada
    'rejected'      -- Rechazada
);

CREATE TABLE IF NOT EXISTS quality_management.corrective_actions (
    -- Primary Key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Multi-tenant
    tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,

    -- Relaciones
    non_conformity_id UUID NOT NULL REFERENCES quality_management.non_conformities(id) ON DELETE CASCADE,

    -- Datos de negocio
    action_number INTEGER NOT NULL,
    action_type quality_management.action_type NOT NULL,
    description TEXT NOT NULL,
    responsible_id UUID NOT NULL,
    planned_date DATE NOT NULL,
    completed_date DATE,
    status quality_management.action_status NOT NULL DEFAULT 'planned',
    verification_method TEXT,
    results TEXT,

    -- Datos estructurados
    evidence JSONB DEFAULT '[]',

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID,

    -- Constraints
    CONSTRAINT uq_actions_number UNIQUE (non_conformity_id, action_number),
    CONSTRAINT chk_action_completion CHECK (
        (completed_date IS NULL) OR
        (completed_date >= planned_date)
    )
);

-- Comentarios
COMMENT ON TABLE quality_management.corrective_actions IS 'Acciones correctivas para resolver no conformidades';
COMMENT ON COLUMN quality_management.corrective_actions.action_type IS 'Tipo de accion: inmediata, correctiva, preventiva o mejora';
COMMENT ON COLUMN quality_management.corrective_actions.status IS 'Estado actual de la accion';

-- Indices
CREATE INDEX idx_actions_tenant ON quality_management.corrective_actions(tenant_id);
CREATE INDEX idx_actions_nc ON quality_management.corrective_actions(non_conformity_id, action_number);
CREATE INDEX idx_actions_responsible ON quality_management.corrective_actions(responsible_id, status);
CREATE INDEX idx_actions_status ON quality_management.corrective_actions(status);
CREATE INDEX idx_actions_planned ON quality_management.corrective_actions(planned_date)
    WHERE status IN ('planned', 'in_progress');

-- RLS
ALTER TABLE quality_management.corrective_actions ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_select ON quality_management.corrective_actions
    FOR SELECT
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_insert ON quality_management.corrective_actions
    FOR INSERT
    WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_update ON quality_management.corrective_actions
    FOR UPDATE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_delete ON quality_management.corrective_actions
    FOR DELETE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Triggers
CREATE TRIGGER trg_actions_update_timestamp
    BEFORE UPDATE ON quality_management.corrective_actions
    FOR EACH ROW
    EXECUTE FUNCTION core_shared.set_updated_at();
```

---

### 5. lab_tests

Pruebas de laboratorio de materiales.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `tenant_id` | UUID | NOT NULL | - | FK a tenants |
| `project_id` | UUID | NOT NULL | - | FK a projects |
| `inspection_id` | UUID | NULL | - | FK a inspections |
| `test_code` | VARCHAR(50) | NOT NULL | - | Codigo de prueba |
| `test_type` | VARCHAR(50) | NOT NULL | - | Tipo de prueba (ENUM) |
| `material_type` | VARCHAR(50) | NOT NULL | - | Tipo de material |
| `sample_id` | VARCHAR(50) | NOT NULL | - | ID de muestra |
| `sample_date` | DATE | NOT NULL | - | Fecha de muestreo |
| `location` | VARCHAR(255) | NOT NULL | - | Ubicacion de muestreo |
| `lab_id` | UUID | NULL | - | FK a laboratorio |
| `technician_id` | UUID | NULL | - | FK a users (tecnico) |
| `test_date` | DATE | NULL | - | Fecha de prueba |
| `status` | VARCHAR(20) | NOT NULL | 'pending' | Estado (ENUM) |
| `result` | VARCHAR(20) | NULL | - | Resultado (ENUM) |
| `test_parameters` | JSONB | NULL | '{}' | Parametros de prueba |
| `test_results` | JSONB | NULL | '{}' | Resultados detallados |
| `meets_spec` | BOOLEAN | NULL | - | Cumple especificacion |
| `observations` | TEXT | NULL | - | Observaciones |
| `attachments` | JSONB | NULL | '[]' | Certificados, reportes |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |
| `created_by` | UUID | NOT NULL | - | FK a users |
| `updated_by` | UUID | NULL | - | FK a users |
| `is_active` | BOOLEAN | NOT NULL | true | Soft delete |
| `deleted_at` | TIMESTAMPTZ | NULL | - | Fecha eliminacion |
| `deleted_by` | UUID | NULL | - | FK a users |

```sql
-- ============================================================================
-- Tabla: lab_tests
-- Descripcion: Pruebas de laboratorio de materiales
-- ============================================================================

-- ENUM: Tipos de prueba
CREATE TYPE quality_management.lab_test_type AS ENUM (
    'concrete_compression',     -- Compresion de concreto
    'concrete_slump',           -- Revenimiento
    'steel_tensile',            -- Tension de acero
    'soil_compaction',          -- Compactacion de suelo
    'soil_bearing',             -- Capacidad portante
    'asphalt_density',          -- Densidad de asfalto
    'mortar_compression',       -- Compresion de mortero
    'brick_absorption',         -- Absorcion de ladrillo
    'aggregate_gradation',      -- Granulometria
    'chemical_analysis',        -- Analisis quimico
    'moisture_content',         -- Contenido de humedad
    'other'                     -- Otra
);

-- ENUM: Estados de prueba
CREATE TYPE quality_management.lab_test_status AS ENUM (
    'pending',          -- Pendiente
    'sample_collected', -- Muestra recolectada
    'in_lab',           -- En laboratorio
    'testing',          -- En prueba
    'completed',        -- Completada
    'certified'         -- Certificada
);

-- ENUM: Resultados de prueba
CREATE TYPE quality_management.lab_test_result AS ENUM (
    'passed',       -- Aprobada
    'failed',       -- Rechazada
    'conditional',  -- Condicional
    'retest'        -- Re-ensayo
);

CREATE TABLE IF NOT EXISTS quality_management.lab_tests (
    -- Primary Key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Multi-tenant
    tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,

    -- Relaciones
    project_id UUID NOT NULL,
    inspection_id UUID REFERENCES quality_management.inspections(id),
    lab_id UUID,
    technician_id UUID,

    -- Datos de negocio
    test_code VARCHAR(50) NOT NULL,
    test_type quality_management.lab_test_type NOT NULL,
    material_type VARCHAR(50) NOT NULL,
    sample_id VARCHAR(50) NOT NULL,
    sample_date DATE NOT NULL,
    location VARCHAR(255) NOT NULL,
    test_date DATE,
    status quality_management.lab_test_status NOT NULL DEFAULT 'pending',
    result quality_management.lab_test_result,
    meets_spec BOOLEAN,
    observations TEXT,

    -- Datos estructurados
    test_parameters JSONB DEFAULT '{}',
    test_results JSONB DEFAULT '{}',
    attachments JSONB DEFAULT '[]',

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID,

    -- Soft delete
    is_active BOOLEAN NOT NULL DEFAULT true,
    deleted_at TIMESTAMPTZ,
    deleted_by UUID,

    -- Constraints
    CONSTRAINT uq_lab_tests_code_tenant UNIQUE (tenant_id, test_code),
    CONSTRAINT chk_lab_test_dates CHECK (
        (test_date IS NULL) OR (test_date >= sample_date)
    )
);

-- Comentarios
COMMENT ON TABLE quality_management.lab_tests IS 'Pruebas de laboratorio de materiales de construccion';
COMMENT ON COLUMN quality_management.lab_tests.test_type IS 'Tipo de prueba de laboratorio';
COMMENT ON COLUMN quality_management.lab_tests.test_parameters IS 'Parametros de la prueba en formato JSON';
COMMENT ON COLUMN quality_management.lab_tests.test_results IS 'Resultados detallados en formato JSON';

-- Indices
CREATE INDEX idx_lab_tests_tenant ON quality_management.lab_tests(tenant_id);
CREATE INDEX idx_lab_tests_project ON quality_management.lab_tests(project_id, sample_date DESC);
CREATE INDEX idx_lab_tests_inspection ON quality_management.lab_tests(inspection_id);
CREATE INDEX idx_lab_tests_code ON quality_management.lab_tests(test_code);
CREATE INDEX idx_lab_tests_status ON quality_management.lab_tests(status) WHERE is_active = true;
CREATE INDEX idx_lab_tests_type ON quality_management.lab_tests(test_type, sample_date DESC);
CREATE INDEX idx_lab_tests_result ON quality_management.lab_tests(result) WHERE result IS NOT NULL;
CREATE INDEX idx_lab_tests_failed ON quality_management.lab_tests(project_id)
    WHERE result = 'failed' AND is_active = true;

-- RLS
ALTER TABLE quality_management.lab_tests ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_select ON quality_management.lab_tests
    FOR SELECT
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_insert ON quality_management.lab_tests
    FOR INSERT
    WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_update ON quality_management.lab_tests
    FOR UPDATE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_delete ON quality_management.lab_tests
    FOR DELETE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Triggers
CREATE TRIGGER trg_lab_tests_update_timestamp
    BEFORE UPDATE ON quality_management.lab_tests
    FOR EACH ROW
    EXECUTE FUNCTION core_shared.set_updated_at();
```

---

### 6. certifications

Certificaciones de calidad de proyectos.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `tenant_id` | UUID | NOT NULL | - | FK a tenants |
| `project_id` | UUID | NOT NULL | - | FK a projects |
| `cert_code` | VARCHAR(50) | NOT NULL | - | Codigo de certificacion |
| `cert_type` | VARCHAR(50) | NOT NULL | - | Tipo de certificacion (ENUM) |
| `title` | VARCHAR(255) | NOT NULL | - | Titulo |
| `issuing_body` | VARCHAR(255) | NOT NULL | - | Organismo emisor |
| `issue_date` | DATE | NOT NULL | - | Fecha emision |
| `expiry_date` | DATE | NULL | - | Fecha vencimiento |
| `status` | VARCHAR(20) | NOT NULL | 'pending' | Estado (ENUM) |
| `scope` | TEXT | NULL | - | Alcance |
| `requirements` | JSONB | NULL | '{}' | Requisitos |
| `documents` | JSONB | NULL | '[]' | Documentos |
| `verified_by` | UUID | NULL | - | FK a users |
| `verified_at` | TIMESTAMPTZ | NULL | - | Fecha verificacion |
| `attachments` | JSONB | NULL | '[]' | Certificados |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |
| `created_by` | UUID | NOT NULL | - | FK a users |
| `updated_by` | UUID | NULL | - | FK a users |
| `is_active` | BOOLEAN | NOT NULL | true | Soft delete |
| `deleted_at` | TIMESTAMPTZ | NULL | - | Fecha eliminacion |
| `deleted_by` | UUID | NULL | - | FK a users |

```sql
-- ============================================================================
-- Tabla: certifications
-- Descripcion: Certificaciones de calidad
-- ============================================================================

-- ENUM: Tipos de certificacion
CREATE TYPE quality_management.certification_type AS ENUM (
    'iso_9001',             -- ISO 9001
    'iso_14001',            -- ISO 14001
    'iso_45001',            -- ISO 45001
    'leed',                 -- LEED
    'breeam',               -- BREEAM
    'well',                 -- WELL Building
    'edge',                 -- EDGE
    'project_completion',   -- Finalizacion de proyecto
    'structural',           -- Estructural
    'electrical',           -- Electrica
    'plumbing',             -- Sanitaria
    'fire_safety',          -- Contra incendios
    'seismic',              -- Sismica
    'environmental',        -- Ambiental
    'other'                 -- Otra
);

-- ENUM: Estados de certificacion
CREATE TYPE quality_management.certification_status AS ENUM (
    'pending',      -- Pendiente
    'in_process',   -- En proceso
    'issued',       -- Emitida
    'expired',      -- Vencida
    'revoked'       -- Revocada
);

CREATE TABLE IF NOT EXISTS quality_management.certifications (
    -- Primary Key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Multi-tenant
    tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,

    -- Relaciones
    project_id UUID NOT NULL,

    -- Datos de negocio
    cert_code VARCHAR(50) NOT NULL,
    cert_type quality_management.certification_type NOT NULL,
    title VARCHAR(255) NOT NULL,
    issuing_body VARCHAR(255) NOT NULL,
    issue_date DATE NOT NULL,
    expiry_date DATE,
    status quality_management.certification_status NOT NULL DEFAULT 'pending',
    scope TEXT,

    -- Datos estructurados
    requirements JSONB DEFAULT '{}',
    documents JSONB DEFAULT '[]',
    attachments JSONB DEFAULT '[]',

    -- Verificacion
    verified_by UUID,
    verified_at TIMESTAMPTZ,

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID,

    -- Soft delete
    is_active BOOLEAN NOT NULL DEFAULT true,
    deleted_at TIMESTAMPTZ,
    deleted_by UUID,

    -- Constraints
    CONSTRAINT uq_certifications_code_tenant UNIQUE (tenant_id, cert_code),
    CONSTRAINT chk_cert_dates CHECK (
        (expiry_date IS NULL) OR (expiry_date > issue_date)
    )
);

-- Comentarios
COMMENT ON TABLE quality_management.certifications IS 'Certificaciones de calidad de proyectos';
COMMENT ON COLUMN quality_management.certifications.cert_type IS 'Tipo de certificacion';
COMMENT ON COLUMN quality_management.certifications.issuing_body IS 'Organismo o entidad emisora';
COMMENT ON COLUMN quality_management.certifications.requirements IS 'Requisitos de la certificacion en formato JSON';

-- Indices
CREATE INDEX idx_certifications_tenant ON quality_management.certifications(tenant_id);
CREATE INDEX idx_certifications_project ON quality_management.certifications(project_id, issue_date DESC);
CREATE INDEX idx_certifications_code ON quality_management.certifications(cert_code);
CREATE INDEX idx_certifications_status ON quality_management.certifications(status) WHERE is_active = true;
CREATE INDEX idx_certifications_type ON quality_management.certifications(cert_type, issue_date DESC);
CREATE INDEX idx_certifications_expiry ON quality_management.certifications(expiry_date)
    WHERE expiry_date IS NOT NULL AND status = 'issued';

-- RLS
ALTER TABLE quality_management.certifications ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_select ON quality_management.certifications
    FOR SELECT
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_insert ON quality_management.certifications
    FOR INSERT
    WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_update ON quality_management.certifications
    FOR UPDATE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

CREATE POLICY tenant_isolation_delete ON quality_management.certifications
    FOR DELETE
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Triggers
CREATE TRIGGER trg_certifications_update_timestamp
    BEFORE UPDATE ON quality_management.certifications
    FOR EACH ROW
    EXECUTE FUNCTION core_shared.set_updated_at();
```

---

## Funciones y Triggers de Flujo

### Funcion: Auto-transicion de NC

```sql
-- ============================================================================
-- Funcion: Actualizar estado de NC basado en acciones correctivas
-- ============================================================================

CREATE OR REPLACE FUNCTION quality_management.update_nc_status_on_actions()
RETURNS TRIGGER AS $$
DECLARE
    v_total_actions INTEGER;
    v_completed_actions INTEGER;
BEGIN
    -- Contar acciones totales y completadas
    SELECT
        COUNT(*),
        COUNT(*) FILTER (WHERE status IN ('completed', 'verified'))
    INTO v_total_actions, v_completed_actions
    FROM quality_management.corrective_actions
    WHERE non_conformity_id = COALESCE(NEW.non_conformity_id, OLD.non_conformity_id)
    AND (SELECT is_active FROM quality_management.non_conformities WHERE id = COALESCE(NEW.non_conformity_id, OLD.non_conformity_id));

    -- Si todas las acciones estan completadas, cambiar NC a pending_review
    IF v_total_actions > 0 AND v_total_actions = v_completed_actions THEN
        UPDATE quality_management.non_conformities
        SET status = 'pending_review',
            updated_at = NOW()
        WHERE id = COALESCE(NEW.non_conformity_id, OLD.non_conformity_id)
        AND status = 'in_progress';
    END IF;

    -- Si alguna accion esta en progreso, cambiar NC a in_progress
    IF EXISTS (
        SELECT 1 FROM quality_management.corrective_actions
        WHERE non_conformity_id = COALESCE(NEW.non_conformity_id, OLD.non_conformity_id)
        AND status = 'in_progress'
    ) THEN
        UPDATE quality_management.non_conformities
        SET status = 'in_progress',
            updated_at = NOW()
        WHERE id = COALESCE(NEW.non_conformity_id, OLD.non_conformity_id)
        AND status = 'assigned';
    END IF;

    RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;

-- Trigger para actualizar NC cuando cambian las acciones
CREATE TRIGGER trg_update_nc_on_action_change
    AFTER INSERT OR UPDATE OR DELETE ON quality_management.corrective_actions
    FOR EACH ROW
    EXECUTE FUNCTION quality_management.update_nc_status_on_actions();

COMMENT ON FUNCTION quality_management.update_nc_status_on_actions() IS
    'Actualiza automaticamente el estado de la NC basado en el progreso de sus acciones correctivas';
```

### Funcion: Validar resultado de inspeccion

```sql
-- ============================================================================
-- Funcion: Validar y actualizar resultado de inspeccion
-- ============================================================================

CREATE OR REPLACE FUNCTION quality_management.update_inspection_result()
RETURNS TRIGGER AS $$
DECLARE
    v_total_items INTEGER;
    v_non_conforming_items INTEGER;
    v_new_result quality_management.inspection_result;
BEGIN
    -- Solo ejecutar cuando se completa la inspeccion
    IF NEW.status = 'completed' AND OLD.status != 'completed' THEN
        -- Contar items conformes y no conformes
        SELECT
            COUNT(*),
            COUNT(*) FILTER (WHERE conformity = 'non_conforming')
        INTO v_total_items, v_non_conforming_items
        FROM quality_management.inspection_items
        WHERE inspection_id = NEW.id;

        -- Determinar resultado
        IF v_total_items = 0 THEN
            v_new_result := 'pending_review';
        ELSIF v_non_conforming_items = 0 THEN
            v_new_result := 'passed';
        ELSIF v_non_conforming_items < v_total_items THEN
            v_new_result := 'conditional';
        ELSE
            v_new_result := 'failed';
        END IF;

        NEW.result := v_new_result;
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Trigger para validar resultado
CREATE TRIGGER trg_validate_inspection_result
    BEFORE UPDATE ON quality_management.inspections
    FOR EACH ROW
    EXECUTE FUNCTION quality_management.update_inspection_result();

COMMENT ON FUNCTION quality_management.update_inspection_result() IS
    'Calcula automaticamente el resultado de la inspeccion basado en los items verificados';
```

### Funcion: Notificar NC criticas

```sql
-- ============================================================================
-- Funcion: Crear alerta para NCs criticas
-- ============================================================================

CREATE OR REPLACE FUNCTION quality_management.notify_critical_nc()
RETURNS TRIGGER AS $$
BEGIN
    -- Si la NC es critica o mayor, crear notificacion
    IF NEW.severity IN ('critical', 'major') THEN
        -- Insertar en tabla de notificaciones (asumiendo que existe)
        -- Esta funcion se implementaria con la integracion del modulo de notificaciones
        PERFORM pg_notify(
            'quality_alert',
            json_build_object(
                'type', 'critical_nc',
                'nc_id', NEW.id,
                'nc_code', NEW.nc_code,
                'severity', NEW.severity,
                'project_id', NEW.project_id,
                'tenant_id', NEW.tenant_id
            )::text
        );
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Trigger para notificar
CREATE TRIGGER trg_notify_critical_nc
    AFTER INSERT ON quality_management.non_conformities
    FOR EACH ROW
    EXECUTE FUNCTION quality_management.notify_critical_nc();

COMMENT ON FUNCTION quality_management.notify_critical_nc() IS
    'Envia notificacion cuando se crea una NC critica o mayor';
```

---

## Vistas de Consulta

### Vista: Resumen de inspecciones por proyecto

```sql
CREATE OR REPLACE VIEW quality_management.v_inspection_summary AS
SELECT
    i.tenant_id,
    i.project_id,
    i.inspection_type,
    COUNT(*) as total_inspections,
    COUNT(*) FILTER (WHERE i.status = 'completed') as completed,
    COUNT(*) FILTER (WHERE i.status = 'approved') as approved,
    COUNT(*) FILTER (WHERE i.result = 'passed') as passed,
    COUNT(*) FILTER (WHERE i.result = 'failed') as failed,
    COUNT(*) FILTER (WHERE i.result = 'conditional') as conditional,
    MAX(i.inspection_date) as last_inspection_date
FROM quality_management.inspections i
WHERE i.is_active = true
GROUP BY i.tenant_id, i.project_id, i.inspection_type;

COMMENT ON VIEW quality_management.v_inspection_summary IS
    'Resumen de inspecciones agrupadas por proyecto y tipo';
```

### Vista: NCs abiertas por severidad

```sql
CREATE OR REPLACE VIEW quality_management.v_open_nc_by_severity AS
SELECT
    nc.tenant_id,
    nc.project_id,
    nc.severity,
    COUNT(*) as total_nc,
    COUNT(*) FILTER (WHERE nc.status = 'open') as open,
    COUNT(*) FILTER (WHERE nc.status = 'assigned') as assigned,
    COUNT(*) FILTER (WHERE nc.status = 'in_progress') as in_progress,
    COUNT(*) FILTER (WHERE nc.deadline < CURRENT_DATE) as overdue,
    MIN(nc.detected_at) as oldest_nc_date,
    AVG(EXTRACT(EPOCH FROM (COALESCE(nc.closed_at, NOW()) - nc.detected_at))/86400)::INTEGER as avg_days_to_close
FROM quality_management.non_conformities nc
WHERE nc.is_active = true
AND nc.status NOT IN ('closed', 'rejected')
GROUP BY nc.tenant_id, nc.project_id, nc.severity;

COMMENT ON VIEW quality_management.v_open_nc_by_severity IS
    'NCs abiertas agrupadas por proyecto y severidad con metricas';
```

### Vista: Pruebas de laboratorio pendientes

```sql
CREATE OR REPLACE VIEW quality_management.v_pending_lab_tests AS
SELECT
    lt.id,
    lt.tenant_id,
    lt.project_id,
    lt.test_code,
    lt.test_type,
    lt.material_type,
    lt.sample_date,
    lt.status,
    CURRENT_DATE - lt.sample_date as days_pending,
    lt.location,
    lt.technician_id
FROM quality_management.lab_tests lt
WHERE lt.is_active = true
AND lt.status NOT IN ('completed', 'certified')
ORDER BY lt.sample_date ASC;

COMMENT ON VIEW quality_management.v_pending_lab_tests IS
    'Pruebas de laboratorio pendientes ordenadas por antiguedad';
```

---

## Indices de Performance

```sql
-- Indices adicionales para reportes y analytics
CREATE INDEX idx_inspections_project_type_date ON quality_management.inspections(project_id, inspection_type, inspection_date DESC)
    WHERE is_active = true;

CREATE INDEX idx_nc_project_severity_status ON quality_management.non_conformities(project_id, severity, status)
    WHERE is_active = true;

CREATE INDEX idx_lab_tests_project_type_date ON quality_management.lab_tests(project_id, test_type, sample_date DESC)
    WHERE is_active = true;

-- Indice para busqueda de texto en descripciones (usando trigram)
CREATE INDEX idx_nc_description_trgm ON quality_management.non_conformities
    USING gin (description gin_trgm_ops)
    WHERE is_active = true;
```

---

## Consideraciones de Performance

| Tabla | Volumen Esperado | Estrategia |
|-------|------------------|------------|
| inspections | Medio (miles/proyecto) | Indices por proyecto y fecha |
| inspection_items | Alto (decenas/inspeccion) | Cascade delete, indices compuestos |
| non_conformities | Medio-bajo | Indices por severidad y estado |
| corrective_actions | Medio | Indices por responsable y estado |
| lab_tests | Medio | Indices por tipo y fecha |
| certifications | Bajo | Indices simples |

---

## Validaciones Pre-Commit

- [x] Todas las tablas tienen tenant_id
- [x] RLS habilitado en todas las tablas
- [x] 4 policies RLS creadas (SELECT, INSERT, UPDATE, DELETE)
- [x] Indices en tenant_id
- [x] Columnas de auditoria completas
- [x] Comentarios en tablas y columnas
- [x] ENUMs definidos para tipos y estados
- [x] Triggers para flujo de NC
- [x] Funciones de utilidad documentadas
- [x] Vistas de consulta creadas
- [x] Nomenclatura correcta

---

## Historial

| Version | Fecha | Autor | Cambios |
|---------|-------|-------|---------|
| 1.0 | 2025-12-06 | Database-Agent | Creacion inicial del schema quality_management |
