[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