Database Schema Reference
MovaLab uses PostgreSQL (via Supabase) with 48+ tables, complete RLS coverage, and sophisticated views for capacity planning.
48+
Tables
100+
RLS Policies
29
Functions
3
Views
Core Tables by Category
User Management
user_profilesRLSExtended user profile linked to Supabase Auth
id, email, name, image, bio, skills, workload_sentiment, is_superadmin
user_rolesRLSJunction table for user-role assignments
id, user_id, role_id, assigned_at, assigned_by
Roles & Permissions
rolesRLSRole definitions with JSONB permissions
id, name, department_id, permissions (JSONB), reporting_role_id, hierarchy_level, is_system_role
role_hierarchy_auditRLSAudit trail for role hierarchy changes
id, role_id, changed_by, action, old_reporting_role_id, new_reporting_role_id
Organizational Structure
departmentsRLSBusiness units (Engineering, Sales, etc.)
id, name, description, created_at, updated_at
accountsRLSClient/customer accounts
id, name, description, primary_contact_email, account_manager_id, service_tier, status
account_membersRLSUser-account assignments
id, user_id, account_id, created_at
Project Management
projectsRLSCore project entity with timeline and effort tracking
id, name, account_id, status, priority, start_date, end_date, estimated_hours, actual_hours
project_assignmentsRLSUser assignments to projects
id, project_id, user_id, role_in_project, assigned_at, removed_at
project_updatesRLSJournal-style project status updates
id, project_id, content, created_by, created_at
project_issuesRLSProject blockers and problems
id, project_id, content, status, resolved_by, resolved_at
Task Management
tasksRLSIndividual work items within projects
id, name, project_id, status, priority, due_date, estimated_hours, assigned_to
task_dependenciesTask dependency relationships
id, task_id, depends_on_task_id, dependency_type
task_week_allocationsRLSPlanned task hours per week
id, task_id, week_start_date, allocated_hours, assigned_user_id
Time & Capacity
user_availabilityRLSWeekly work capacity per user (default 40h)
id, user_id, week_start_date, available_hours, schedule_data (JSONB)
time_entriesRLSActual time logged on tasks
id, task_id, user_id, hours_logged, entry_date, clock_session_id
clock_sessionsRLSActive clock-in/out sessions
id, user_id, clock_in_time, clock_out_time, is_active, is_auto_clock_out
Database Views
Pre-computed views for capacity planning and analytics dashboards.
weekly_capacity_summary
Aggregated weekly metrics per user for capacity planning.
-- Columns: user_id, week_start_date, available_hours, -- allocated_hours, actual_hours, utilization_rate, -- remaining_capacity
department_capacity_summary
Department-wide capacity rollup for manager oversight.
-- Columns: department_id, week_start_date, team_size, -- total_available_hours, total_allocated_hours, -- total_actual_hours, department_utilization_rate
project_capacity_summary
Project-level capacity allocation and tracking.
-- Columns: project_id, week_start_date, assigned_users, -- allocated_hours, actual_hours, total_estimated_hours
Critical Database Functions
get_week_start_date(input_date DATE)Returns Monday of the week for any date (ISO week standard). Used throughout the system for consistent week calculations.
user_has_permission(user_id UUID, permission TEXT)SECURITY DEFINER function for RLS policies. Checks if user has a specific permission via their roles.
user_is_superadmin(user_id UUID)SECURITY DEFINER function that checks if user is a superadmin via system role or is_superadmin flag.
auto_clock_out_stale_sessions()Auto-closes clock sessions after 16 hours. Prevents runaway sessions from skewing time tracking data.
Migration Strategy
MovaLab uses Supabase migrations with a dual-track approach for local and cloud environments.
# Migration order (IMPORTANT): 1. 01_schema_base.sql - Base table schemas 2. 02_functions_fixed.sql - Database functions (SECURITY DEFINER) 3. 03_views.sql - Capacity views 4. 04_rls_policies_fixed.sql - RLS policies 5. 05_triggers.sql - Auto-update triggers # Apply locally npm run docker:reset # Apply to cloud supabase link --project-ref <your-ref> supabase db push
Local Docker
All migrations run on npm run docker:start. Full reset with npm run docker:reset.
Cloud Supabase
Link with supabase link, push with supabase db push. Always test locally first.
Row Level Security
Every table has RLS policies enforcing access control at the database level. This provides defense in depth.
-- Example: Projects table RLS policy
CREATE POLICY "Users can view accessible projects"
ON public.projects
FOR SELECT
TO authenticated
USING (
user_is_superadmin(auth.uid())
OR created_by = auth.uid()
OR assigned_user_id = auth.uid()
OR EXISTS (
SELECT 1 FROM project_assignments pa
WHERE pa.project_id = projects.id
AND pa.user_id = auth.uid()
AND pa.removed_at IS NULL
)
OR EXISTS (
SELECT 1 FROM account_members am
WHERE am.account_id = projects.account_id
AND am.user_id = auth.uid()
)
);RLS policies use SECURITY DEFINER functions to avoid circular dependencies when checking permissions.
Key Entity Relationships
Entity Relationship Overview:
auth.users (Supabase Auth)
↓ (1:1)
user_profiles
↓ (1:many via user_roles)
roles ← departments (many:1)
↓ (permissions JSONB)
[40 permissions]
accounts
↓ (1:many)
projects
↓ (1:many)
tasks
↓ (many:many via task_assignments)
user_profiles
projects ← project_assignments → user_profiles
accounts ← account_members → user_profiles
time_entries → tasks → projects → accounts
clock_sessions → user_profiles
user_availability → user_profiles (weekly capacity)Explore the Schema
View the full schema in Supabase Studio or explore migration files on GitHub.