[SAAS-022] feat: Add Goals module DDL
- 00-schema.sql: Schema creation with grants - 01-enums.sql: 8 enums (goal_type, metric_type, period_type, etc.) - 02-tables.sql: 4 tables (definitions, assignments, progress_log, milestone_notifications) - 04-rls.sql: 16 RLS policies for tenant isolation - 05-indexes.sql: Performance indexes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a3f354528a
commit
6e5244b612
14
ddl/schemas/goals/00-schema.sql
Normal file
14
ddl/schemas/goals/00-schema.sql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- SAAS-022: Goals Schema
|
||||||
|
-- Sistema de metas y objetivos
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- Create schema
|
||||||
|
CREATE SCHEMA IF NOT EXISTS goals;
|
||||||
|
|
||||||
|
-- Grant permissions
|
||||||
|
GRANT USAGE ON SCHEMA goals TO template_saas_user;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA goals TO template_saas_user;
|
||||||
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA goals TO template_saas_user;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA goals GRANT ALL ON TABLES TO template_saas_user;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA goals GRANT ALL ON SEQUENCES TO template_saas_user;
|
||||||
70
ddl/schemas/goals/01-enums.sql
Normal file
70
ddl/schemas/goals/01-enums.sql
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- SAAS-022: Goals Enums
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- Tipo de meta
|
||||||
|
CREATE TYPE goals.goal_type AS ENUM (
|
||||||
|
'target', -- Alcanzar un objetivo
|
||||||
|
'limit', -- No exceder un limite
|
||||||
|
'maintain' -- Mantener un valor
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tipo de metrica
|
||||||
|
CREATE TYPE goals.metric_type AS ENUM (
|
||||||
|
'number', -- Numero entero
|
||||||
|
'currency', -- Monto monetario
|
||||||
|
'percentage', -- Porcentaje
|
||||||
|
'boolean', -- Si/No
|
||||||
|
'count' -- Conteo
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tipo de periodo
|
||||||
|
CREATE TYPE goals.period_type AS ENUM (
|
||||||
|
'daily',
|
||||||
|
'weekly',
|
||||||
|
'monthly',
|
||||||
|
'quarterly',
|
||||||
|
'yearly',
|
||||||
|
'custom'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Fuente de datos para tracking automatico
|
||||||
|
CREATE TYPE goals.data_source AS ENUM (
|
||||||
|
'manual', -- Actualizado manualmente
|
||||||
|
'sales', -- Desde modulo Sales
|
||||||
|
'billing', -- Desde modulo Billing
|
||||||
|
'commissions', -- Desde modulo Commissions
|
||||||
|
'custom' -- Fuente personalizada
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Estado de la meta
|
||||||
|
CREATE TYPE goals.goal_status AS ENUM (
|
||||||
|
'draft', -- Borrador
|
||||||
|
'active', -- Activa
|
||||||
|
'paused', -- Pausada
|
||||||
|
'completed', -- Completada
|
||||||
|
'cancelled' -- Cancelada
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tipo de asignado
|
||||||
|
CREATE TYPE goals.assignee_type AS ENUM (
|
||||||
|
'user', -- Usuario individual
|
||||||
|
'team', -- Equipo
|
||||||
|
'tenant' -- Todo el tenant
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Estado de asignacion
|
||||||
|
CREATE TYPE goals.assignment_status AS ENUM (
|
||||||
|
'active', -- En progreso
|
||||||
|
'achieved', -- Lograda
|
||||||
|
'failed', -- No alcanzada
|
||||||
|
'cancelled' -- Cancelada
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Fuente del progreso
|
||||||
|
CREATE TYPE goals.progress_source AS ENUM (
|
||||||
|
'manual', -- Entrada manual
|
||||||
|
'automatic', -- Tracking automatico
|
||||||
|
'import', -- Importacion
|
||||||
|
'api' -- Via API externa
|
||||||
|
);
|
||||||
155
ddl/schemas/goals/02-tables.sql
Normal file
155
ddl/schemas/goals/02-tables.sql
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- SAAS-022: Goals Tables
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Definiciones de metas
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
CREATE TABLE goals.definitions (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
-- Info basica
|
||||||
|
name VARCHAR(200) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
category VARCHAR(100),
|
||||||
|
|
||||||
|
-- Tipo de meta
|
||||||
|
type goals.goal_type NOT NULL DEFAULT 'target',
|
||||||
|
metric goals.metric_type NOT NULL DEFAULT 'number',
|
||||||
|
|
||||||
|
-- Objetivo
|
||||||
|
target_value DECIMAL(15,2) NOT NULL,
|
||||||
|
unit VARCHAR(50), -- 'USD', 'units', '%', etc.
|
||||||
|
|
||||||
|
-- Periodo
|
||||||
|
period goals.period_type NOT NULL DEFAULT 'monthly',
|
||||||
|
starts_at DATE NOT NULL,
|
||||||
|
ends_at DATE NOT NULL,
|
||||||
|
|
||||||
|
-- Fuente de datos (para tracking automatico)
|
||||||
|
source goals.data_source NOT NULL DEFAULT 'manual',
|
||||||
|
source_config JSONB DEFAULT '{}',
|
||||||
|
-- Ejemplo:
|
||||||
|
-- {
|
||||||
|
-- "module": "sales",
|
||||||
|
-- "entity": "opportunities",
|
||||||
|
-- "filter": { "status": "won" },
|
||||||
|
-- "aggregation": "sum",
|
||||||
|
-- "field": "amount"
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- Hitos para notificaciones
|
||||||
|
milestones JSONB DEFAULT '[]',
|
||||||
|
-- Ejemplo: [{ "percentage": 25, "notify": true }, { "percentage": 50, "notify": true }]
|
||||||
|
|
||||||
|
-- Estado
|
||||||
|
status goals.goal_status NOT NULL DEFAULT 'draft',
|
||||||
|
|
||||||
|
-- Metadata
|
||||||
|
tags JSONB DEFAULT '[]',
|
||||||
|
|
||||||
|
-- Timestamps
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES users.users(id),
|
||||||
|
|
||||||
|
-- Constraints
|
||||||
|
CONSTRAINT valid_date_range CHECK (ends_at >= starts_at),
|
||||||
|
CONSTRAINT positive_target CHECK (target_value > 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Asignaciones de metas
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
CREATE TABLE goals.assignments (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
||||||
|
definition_id UUID NOT NULL REFERENCES goals.definitions(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
-- Asignado a
|
||||||
|
assignee_type goals.assignee_type NOT NULL DEFAULT 'user',
|
||||||
|
user_id UUID REFERENCES users.users(id) ON DELETE CASCADE,
|
||||||
|
team_id UUID, -- Para uso futuro con modulo de equipos
|
||||||
|
|
||||||
|
-- Objetivo personalizado (override del definition)
|
||||||
|
custom_target DECIMAL(15,2),
|
||||||
|
|
||||||
|
-- Progreso
|
||||||
|
current_value DECIMAL(15,2) NOT NULL DEFAULT 0,
|
||||||
|
progress_percentage DECIMAL(5,2) NOT NULL DEFAULT 0,
|
||||||
|
last_updated_at TIMESTAMPTZ,
|
||||||
|
|
||||||
|
-- Estado
|
||||||
|
status goals.assignment_status NOT NULL DEFAULT 'active',
|
||||||
|
achieved_at TIMESTAMPTZ,
|
||||||
|
|
||||||
|
-- Metadata
|
||||||
|
notes TEXT,
|
||||||
|
|
||||||
|
-- Timestamps
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
|
-- Constraints
|
||||||
|
CONSTRAINT valid_progress CHECK (progress_percentage >= 0 AND progress_percentage <= 100),
|
||||||
|
CONSTRAINT valid_assignee CHECK (
|
||||||
|
(assignee_type = 'user' AND user_id IS NOT NULL) OR
|
||||||
|
(assignee_type = 'team' AND team_id IS NOT NULL) OR
|
||||||
|
(assignee_type = 'tenant')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Historial de progreso
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
CREATE TABLE goals.progress_log (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
||||||
|
assignment_id UUID NOT NULL REFERENCES goals.assignments(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
-- Valores
|
||||||
|
previous_value DECIMAL(15,2),
|
||||||
|
new_value DECIMAL(15,2) NOT NULL,
|
||||||
|
change_amount DECIMAL(15,2),
|
||||||
|
|
||||||
|
-- Fuente del cambio
|
||||||
|
source goals.progress_source NOT NULL DEFAULT 'manual',
|
||||||
|
source_reference VARCHAR(200), -- ID de la transaccion origen
|
||||||
|
|
||||||
|
notes TEXT,
|
||||||
|
|
||||||
|
-- Timestamps
|
||||||
|
logged_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
logged_by UUID REFERENCES users.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Notificaciones de hitos alcanzados
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
CREATE TABLE goals.milestone_notifications (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
||||||
|
assignment_id UUID NOT NULL REFERENCES goals.assignments(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
milestone_percentage INTEGER NOT NULL,
|
||||||
|
achieved_value DECIMAL(15,2) NOT NULL,
|
||||||
|
|
||||||
|
notified_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
|
-- Evitar duplicados
|
||||||
|
CONSTRAINT unique_milestone_notification UNIQUE (assignment_id, milestone_percentage)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Triggers para updated_at
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
CREATE TRIGGER update_definitions_updated_at
|
||||||
|
BEFORE UPDATE ON goals.definitions
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_assignments_updated_at
|
||||||
|
BEFORE UPDATE ON goals.assignments
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
72
ddl/schemas/goals/04-rls.sql
Normal file
72
ddl/schemas/goals/04-rls.sql
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- SAAS-022: Goals RLS Policies
|
||||||
|
-- Tenant isolation for all tables
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Definitions RLS
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
ALTER TABLE goals.definitions ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE POLICY definitions_tenant_isolation_select ON goals.definitions
|
||||||
|
FOR SELECT USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY definitions_tenant_isolation_insert ON goals.definitions
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY definitions_tenant_isolation_update ON goals.definitions
|
||||||
|
FOR UPDATE USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY definitions_tenant_isolation_delete ON goals.definitions
|
||||||
|
FOR DELETE USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Assignments RLS
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
ALTER TABLE goals.assignments ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE POLICY assignments_tenant_isolation_select ON goals.assignments
|
||||||
|
FOR SELECT USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY assignments_tenant_isolation_insert ON goals.assignments
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY assignments_tenant_isolation_update ON goals.assignments
|
||||||
|
FOR UPDATE USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY assignments_tenant_isolation_delete ON goals.assignments
|
||||||
|
FOR DELETE USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Progress Log RLS
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
ALTER TABLE goals.progress_log ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE POLICY progress_log_tenant_isolation_select ON goals.progress_log
|
||||||
|
FOR SELECT USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY progress_log_tenant_isolation_insert ON goals.progress_log
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY progress_log_tenant_isolation_update ON goals.progress_log
|
||||||
|
FOR UPDATE USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY progress_log_tenant_isolation_delete ON goals.progress_log
|
||||||
|
FOR DELETE USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Milestone Notifications RLS
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
ALTER TABLE goals.milestone_notifications ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE POLICY milestone_notifications_tenant_isolation_select ON goals.milestone_notifications
|
||||||
|
FOR SELECT USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY milestone_notifications_tenant_isolation_insert ON goals.milestone_notifications
|
||||||
|
FOR INSERT WITH CHECK (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY milestone_notifications_tenant_isolation_update ON goals.milestone_notifications
|
||||||
|
FOR UPDATE USING (tenant_id = auth.get_current_tenant());
|
||||||
|
|
||||||
|
CREATE POLICY milestone_notifications_tenant_isolation_delete ON goals.milestone_notifications
|
||||||
|
FOR DELETE USING (tenant_id = auth.get_current_tenant());
|
||||||
109
ddl/schemas/goals/05-indexes.sql
Normal file
109
ddl/schemas/goals/05-indexes.sql
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- SAAS-022: Goals Indexes
|
||||||
|
-- Performance optimization
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Definitions Indexes
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Tenant + status (most common query)
|
||||||
|
CREATE INDEX idx_definitions_tenant_status
|
||||||
|
ON goals.definitions (tenant_id, status);
|
||||||
|
|
||||||
|
-- Period queries
|
||||||
|
CREATE INDEX idx_definitions_tenant_period
|
||||||
|
ON goals.definitions (tenant_id, period, starts_at, ends_at);
|
||||||
|
|
||||||
|
-- Date range queries
|
||||||
|
CREATE INDEX idx_definitions_dates
|
||||||
|
ON goals.definitions (starts_at, ends_at);
|
||||||
|
|
||||||
|
-- Category filtering
|
||||||
|
CREATE INDEX idx_definitions_category
|
||||||
|
ON goals.definitions (tenant_id, category)
|
||||||
|
WHERE category IS NOT NULL;
|
||||||
|
|
||||||
|
-- Active goals (partial index)
|
||||||
|
CREATE INDEX idx_definitions_active
|
||||||
|
ON goals.definitions (tenant_id, starts_at, ends_at)
|
||||||
|
WHERE status = 'active';
|
||||||
|
|
||||||
|
-- Created by user
|
||||||
|
CREATE INDEX idx_definitions_created_by
|
||||||
|
ON goals.definitions (created_by)
|
||||||
|
WHERE created_by IS NOT NULL;
|
||||||
|
|
||||||
|
-- Tags (GIN for JSONB array)
|
||||||
|
CREATE INDEX idx_definitions_tags
|
||||||
|
ON goals.definitions USING GIN (tags);
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Assignments Indexes
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Tenant + status (most common query)
|
||||||
|
CREATE INDEX idx_assignments_tenant_status
|
||||||
|
ON goals.assignments (tenant_id, status);
|
||||||
|
|
||||||
|
-- Definition lookup
|
||||||
|
CREATE INDEX idx_assignments_definition
|
||||||
|
ON goals.assignments (definition_id);
|
||||||
|
|
||||||
|
-- User assignments (most common filter)
|
||||||
|
CREATE INDEX idx_assignments_user
|
||||||
|
ON goals.assignments (user_id, status)
|
||||||
|
WHERE user_id IS NOT NULL;
|
||||||
|
|
||||||
|
-- Team assignments
|
||||||
|
CREATE INDEX idx_assignments_team
|
||||||
|
ON goals.assignments (team_id, status)
|
||||||
|
WHERE team_id IS NOT NULL;
|
||||||
|
|
||||||
|
-- Active assignments (partial index)
|
||||||
|
CREATE INDEX idx_assignments_active
|
||||||
|
ON goals.assignments (tenant_id, user_id)
|
||||||
|
WHERE status = 'active';
|
||||||
|
|
||||||
|
-- Progress percentage (for reports)
|
||||||
|
CREATE INDEX idx_assignments_progress
|
||||||
|
ON goals.assignments (tenant_id, progress_percentage);
|
||||||
|
|
||||||
|
-- Achieved goals
|
||||||
|
CREATE INDEX idx_assignments_achieved
|
||||||
|
ON goals.assignments (achieved_at)
|
||||||
|
WHERE status = 'achieved';
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Progress Log Indexes
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Assignment history
|
||||||
|
CREATE INDEX idx_progress_log_assignment
|
||||||
|
ON goals.progress_log (assignment_id, logged_at DESC);
|
||||||
|
|
||||||
|
-- Tenant + date range
|
||||||
|
CREATE INDEX idx_progress_log_tenant_date
|
||||||
|
ON goals.progress_log (tenant_id, logged_at);
|
||||||
|
|
||||||
|
-- Source reference (for deduplication)
|
||||||
|
CREATE INDEX idx_progress_log_source_ref
|
||||||
|
ON goals.progress_log (source_reference)
|
||||||
|
WHERE source_reference IS NOT NULL;
|
||||||
|
|
||||||
|
-- By user who logged
|
||||||
|
CREATE INDEX idx_progress_log_logged_by
|
||||||
|
ON goals.progress_log (logged_by)
|
||||||
|
WHERE logged_by IS NOT NULL;
|
||||||
|
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
-- Milestone Notifications Indexes
|
||||||
|
-- ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Assignment milestones
|
||||||
|
CREATE INDEX idx_milestone_notifications_assignment
|
||||||
|
ON goals.milestone_notifications (assignment_id);
|
||||||
|
|
||||||
|
-- Tenant notifications
|
||||||
|
CREATE INDEX idx_milestone_notifications_tenant
|
||||||
|
ON goals.milestone_notifications (tenant_id, notified_at);
|
||||||
Loading…
Reference in New Issue
Block a user