# DDL-SPEC: Schema core_settings ## Identificacion | Campo | Valor | |-------|-------| | **Schema** | core_settings | | **Modulo** | MGN-006 | | **Version** | 1.0 | | **Estado** | En Diseno | | **Autor** | Requirements-Analyst | | **Fecha** | 2025-12-05 | --- ## Descripcion General El schema `core_settings` gestiona configuraciones del sistema, por tenant, preferencias de usuario y feature flags. Implementa herencia de configuraciones y evaluacion eficiente de flags. ### RF Cubiertos | RF | Titulo | Tablas | |----|--------|--------| | RF-SETTINGS-001 | Configuraciones Sistema | system_settings | | RF-SETTINGS-002 | Configuraciones Tenant | tenant_settings, plan_settings | | RF-SETTINGS-003 | Preferencias Usuario | user_preferences | | RF-SETTINGS-004 | Feature Flags | feature_flags, feature_flag_overrides | --- ## Diagrama ER ```mermaid erDiagram system_settings { uuid id PK varchar key UK jsonb value varchar data_type varchar category text description boolean is_public boolean is_editable jsonb default_value jsonb validation_rules } plan_settings { uuid id PK uuid plan_id FK varchar key jsonb value } tenant_settings { uuid id PK uuid tenant_id FK varchar key jsonb value varchar inherited_from boolean is_overridden } user_preferences { uuid id PK uuid user_id FK varchar key jsonb value timestamptz synced_at } feature_flags { uuid id PK varchar key UK varchar name text description varchar flag_type jsonb default_value jsonb rollout_config boolean is_active timestamptz expires_at } feature_flag_overrides { uuid id PK uuid feature_flag_id FK varchar level uuid level_id jsonb value boolean is_active } plan_settings }o--|| subscription_plans : "plan" tenant_settings }o--|| tenants : "tenant" user_preferences }o--|| users : "user" feature_flag_overrides }o--|| feature_flags : "flag" ``` --- ## Tablas ### 1. system_settings Configuraciones globales del sistema. | Columna | Tipo | Nullable | Default | Descripcion | |---------|------|----------|---------|-------------| | `id` | UUID | NOT NULL | gen_random_uuid() | PK | | `key` | VARCHAR(100) | NOT NULL | - | Clave unica | | `value` | JSONB | NOT NULL | - | Valor actual | | `data_type` | VARCHAR(20) | NOT NULL | 'string' | Tipo de dato | | `category` | VARCHAR(50) | NOT NULL | - | Categoria | | `description` | TEXT | NULL | - | Descripcion UI | | `is_public` | BOOLEAN | NOT NULL | false | Visible a tenants | | `is_editable` | BOOLEAN | NOT NULL | true | Modificable runtime | | `default_value` | JSONB | NULL | - | Valor por defecto | | `validation_rules` | JSONB | NULL | '{}' | Reglas validacion | | `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion | | `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion | | `updated_by` | UUID | NULL | - | Usuario modificador | ```sql CONSTRAINT pk_system_settings PRIMARY KEY (id), CONSTRAINT uk_system_settings_key UNIQUE (key), CONSTRAINT chk_system_settings_data_type CHECK (data_type IN ('string', 'number', 'boolean', 'json', 'array', 'secret')) ``` ```sql CREATE INDEX idx_system_settings_category ON core_settings.system_settings(category); CREATE INDEX idx_system_settings_public ON core_settings.system_settings(is_public) WHERE is_public = true; ``` --- ### 2. plan_settings Configuraciones por defecto por plan de suscripcion. | Columna | Tipo | Nullable | Default | Descripcion | |---------|------|----------|---------|-------------| | `id` | UUID | NOT NULL | gen_random_uuid() | PK | | `plan_id` | UUID | NOT NULL | - | FK a subscription_plans | | `key` | VARCHAR(100) | NOT NULL | - | Clave de setting | | `value` | JSONB | NOT NULL | - | Valor para el plan | | `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion | | `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion | ```sql CONSTRAINT pk_plan_settings PRIMARY KEY (id), CONSTRAINT uk_plan_settings_plan_key UNIQUE (plan_id, key), CONSTRAINT fk_plan_settings_plan FOREIGN KEY (plan_id) REFERENCES core_tenants.subscription_plans(id) ON DELETE CASCADE ``` --- ### 3. tenant_settings Configuraciones personalizadas por tenant. | Columna | Tipo | Nullable | Default | Descripcion | |---------|------|----------|---------|-------------| | `id` | UUID | NOT NULL | gen_random_uuid() | PK | | `tenant_id` | UUID | NOT NULL | - | FK a tenants | | `key` | VARCHAR(100) | NOT NULL | - | Clave de setting | | `value` | JSONB | NOT NULL | - | Valor personalizado | | `inherited_from` | VARCHAR(20) | NOT NULL | 'custom' | system/plan/custom | | `is_overridden` | BOOLEAN | NOT NULL | true | Sobreescribe herencia | | `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion | | `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion | ```sql CONSTRAINT pk_tenant_settings PRIMARY KEY (id), CONSTRAINT uk_tenant_settings_tenant_key UNIQUE (tenant_id, key), CONSTRAINT fk_tenant_settings_tenant FOREIGN KEY (tenant_id) REFERENCES core_tenants.tenants(id) ON DELETE CASCADE, CONSTRAINT chk_tenant_settings_inherited CHECK (inherited_from IN ('system', 'plan', 'custom')) ``` ```sql -- RLS ALTER TABLE core_settings.tenant_settings ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON core_settings.tenant_settings FOR ALL USING (tenant_id = current_setting('app.current_tenant_id')::uuid); ``` --- ### 4. user_preferences Preferencias personales de usuario. | Columna | Tipo | Nullable | Default | Descripcion | |---------|------|----------|---------|-------------| | `id` | UUID | NOT NULL | gen_random_uuid() | PK | | `user_id` | UUID | NOT NULL | - | FK a users | | `key` | VARCHAR(100) | NOT NULL | - | Clave de preferencia | | `value` | JSONB | NOT NULL | - | Valor | | `synced_at` | TIMESTAMPTZ | NOT NULL | NOW() | Ultima sync | | `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion | | `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion | ```sql CONSTRAINT pk_user_preferences PRIMARY KEY (id), CONSTRAINT uk_user_preferences_user_key UNIQUE (user_id, key), CONSTRAINT fk_user_preferences_user FOREIGN KEY (user_id) REFERENCES core_users.users(id) ON DELETE CASCADE ``` --- ### 5. feature_flags Definicion de feature flags. | Columna | Tipo | Nullable | Default | Descripcion | |---------|------|----------|---------|-------------| | `id` | UUID | NOT NULL | gen_random_uuid() | PK | | `key` | VARCHAR(100) | NOT NULL | - | Clave unica | | `name` | VARCHAR(255) | NOT NULL | - | Nombre descriptivo | | `description` | TEXT | NULL | - | Descripcion | | `flag_type` | VARCHAR(20) | NOT NULL | 'boolean' | boolean/percentage/variant | | `default_value` | JSONB | NOT NULL | 'false' | Valor por defecto | | `rollout_config` | JSONB | NULL | '{}' | Config de rollout | | `is_active` | BOOLEAN | NOT NULL | true | Flag activo | | `expires_at` | TIMESTAMPTZ | NULL | - | Expiracion | | `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion | | `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion | | `created_by` | UUID | NULL | - | Usuario creador | ```sql CONSTRAINT pk_feature_flags PRIMARY KEY (id), CONSTRAINT uk_feature_flags_key UNIQUE (key), CONSTRAINT chk_feature_flags_type CHECK (flag_type IN ('boolean', 'percentage', 'variant')) ``` --- ### 6. feature_flag_overrides Overrides de flags por nivel. | Columna | Tipo | Nullable | Default | Descripcion | |---------|------|----------|---------|-------------| | `id` | UUID | NOT NULL | gen_random_uuid() | PK | | `feature_flag_id` | UUID | NOT NULL | - | FK a feature_flags | | `level` | VARCHAR(20) | NOT NULL | - | plan/tenant/user | | `level_id` | UUID | NOT NULL | - | ID del nivel | | `value` | JSONB | NOT NULL | - | Valor override | | `is_active` | BOOLEAN | NOT NULL | true | Override activo | | `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion | | `created_by` | UUID | NULL | - | Usuario creador | ```sql CONSTRAINT pk_feature_flag_overrides PRIMARY KEY (id), CONSTRAINT uk_feature_flag_overrides UNIQUE (feature_flag_id, level, level_id), CONSTRAINT fk_feature_flag_overrides_flag FOREIGN KEY (feature_flag_id) REFERENCES core_settings.feature_flags(id) ON DELETE CASCADE, CONSTRAINT chk_feature_flag_overrides_level CHECK (level IN ('plan', 'tenant', 'user')) ``` ```sql CREATE INDEX idx_feature_flag_overrides_lookup ON core_settings.feature_flag_overrides(feature_flag_id, level, level_id); ``` --- ## Funciones de Utilidad ### Obtener Setting Efectivo del Tenant ```sql CREATE OR REPLACE FUNCTION core_settings.get_tenant_setting( p_tenant_id UUID, p_key VARCHAR ) RETURNS JSONB AS $$ DECLARE v_value JSONB; v_plan_id UUID; BEGIN -- 1. Buscar en tenant_settings SELECT value INTO v_value FROM core_settings.tenant_settings WHERE tenant_id = p_tenant_id AND key = p_key AND is_overridden = true; IF v_value IS NOT NULL THEN RETURN v_value; END IF; -- 2. Buscar en plan_settings SELECT t.plan_id INTO v_plan_id FROM core_tenants.tenants t WHERE t.id = p_tenant_id; SELECT value INTO v_value FROM core_settings.plan_settings WHERE plan_id = v_plan_id AND key = p_key; IF v_value IS NOT NULL THEN RETURN v_value; END IF; -- 3. Retornar system default SELECT COALESCE(value, default_value) INTO v_value FROM core_settings.system_settings WHERE key = p_key; RETURN v_value; END; $$ LANGUAGE plpgsql STABLE; ``` ### Evaluar Feature Flag ```sql CREATE OR REPLACE FUNCTION core_settings.evaluate_feature_flag( p_key VARCHAR, p_tenant_id UUID DEFAULT NULL, p_user_id UUID DEFAULT NULL ) RETURNS JSONB AS $$ DECLARE v_flag RECORD; v_override JSONB; v_result JSONB; BEGIN -- Obtener flag base SELECT * INTO v_flag FROM core_settings.feature_flags WHERE key = p_key AND is_active = true AND (expires_at IS NULL OR expires_at > NOW()); IF v_flag IS NULL THEN RETURN jsonb_build_object('enabled', false, 'source', 'not_found'); END IF; -- Buscar override por user IF p_user_id IS NOT NULL THEN SELECT value INTO v_override FROM core_settings.feature_flag_overrides WHERE feature_flag_id = v_flag.id AND level = 'user' AND level_id = p_user_id AND is_active = true; IF v_override IS NOT NULL THEN RETURN jsonb_build_object('enabled', v_override, 'source', 'user_override'); END IF; END IF; -- Buscar override por tenant IF p_tenant_id IS NOT NULL THEN SELECT value INTO v_override FROM core_settings.feature_flag_overrides WHERE feature_flag_id = v_flag.id AND level = 'tenant' AND level_id = p_tenant_id AND is_active = true; IF v_override IS NOT NULL THEN RETURN jsonb_build_object('enabled', v_override, 'source', 'tenant_override'); END IF; END IF; -- Retornar default RETURN jsonb_build_object('enabled', v_flag.default_value, 'source', 'default'); END; $$ LANGUAGE plpgsql STABLE; ``` --- ## Seed Data ### System Settings ```sql INSERT INTO core_settings.system_settings (key, value, data_type, category, description, is_editable) VALUES ('security.max_login_attempts', '5', 'number', 'security', 'Intentos maximos de login', true), ('security.lockout_duration_minutes', '15', 'number', 'security', 'Duracion bloqueo en minutos', true), ('security.token_expiry_hours', '24', 'number', 'security', 'Expiracion token en horas', true), ('security.password_min_length', '8', 'number', 'security', 'Longitud minima password', true), ('email.smtp_host', '""', 'string', 'email', 'Host SMTP', true), ('email.smtp_port', '587', 'number', 'email', 'Puerto SMTP', true), ('storage.max_file_size_mb', '10', 'number', 'storage', 'Tamano maximo archivo MB', true), ('storage.allowed_extensions', '["pdf","jpg","png","xlsx"]', 'array', 'storage', 'Extensiones permitidas', true), ('performance.cache_ttl_seconds', '300', 'number', 'performance', 'TTL cache segundos', true), ('performance.pagination_max_limit', '100', 'number', 'performance', 'Max items por pagina', false); ``` ### Feature Flags ```sql INSERT INTO core_settings.feature_flags (key, name, flag_type, default_value, description) VALUES ('feature.oauth_login', 'Login con OAuth', 'boolean', 'true', 'Habilita login con proveedores OAuth'), ('feature.dark_mode', 'Modo Oscuro', 'boolean', 'true', 'Habilita tema oscuro'), ('feature.export_excel', 'Exportar Excel', 'boolean', 'true', 'Permite exportar a Excel'), ('feature.bulk_operations', 'Operaciones Masivas', 'boolean', 'true', 'Permite operaciones en lote'), ('feature.ai_suggestions', 'Sugerencias IA', 'boolean', 'false', 'Sugerencias con IA'); ``` --- ## Historial | Version | Fecha | Autor | Cambios | |---------|-------|-------|---------| | 1.0 | 2025-12-05 | Requirements-Analyst | Creacion inicial |