feat: integrate Grafana dashboards into BOC DashboardPage
- Add Infrastructure Health section with CPU/Memory/Disk panels - Add Service Status section with PM2/Docker panels - Create GrafanaPanel component for iframe embedding - Build passes successfully
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
-- =====================================================
|
||||
-- Migration 003: Multi-Tenancy Enhancement
|
||||
-- =====================================================
|
||||
|
||||
-- Utöka boc_tenants med koncernstöd
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS org_number VARCHAR(20);
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS vat_number VARCHAR(20);
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS address TEXT;
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS phone VARCHAR(20);
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS email VARCHAR(255);
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS currency VARCHAR(3) DEFAULT 'SEK';
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS language VARCHAR(5) DEFAULT 'sv';
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS timezone VARCHAR(50) DEFAULT 'Europe/Stockholm';
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS fiscal_year_start DATE DEFAULT '2026-01-01';
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS parent_id UUID REFERENCES boc_tenants(id);
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS ledger_db VARCHAR(255);
|
||||
ALTER TABLE boc_tenants ADD COLUMN IF NOT EXISTS settings JSONB DEFAULT '{}';
|
||||
|
||||
-- Skapa index för snabb lookup
|
||||
CREATE INDEX IF NOT EXISTS idx_tenants_slug ON boc_tenants(slug);
|
||||
CREATE INDEX IF NOT EXISTS idx_tenants_parent ON boc_tenants(parent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tenants_status ON boc_tenants(status);
|
||||
|
||||
-- Tenant-avgränsning på alla tabeller
|
||||
-- Lägg till tenant_id där det saknas
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Kolla vilka tabeller som saknar tenant_id
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'boc_tickets' AND column_name = 'tenant_id') THEN
|
||||
ALTER TABLE boc_tickets ADD COLUMN tenant_id UUID REFERENCES boc_tenants(id);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'boc_deals' AND column_name = 'tenant_id') THEN
|
||||
ALTER TABLE boc_deals ADD COLUMN tenant_id UUID REFERENCES boc_tenants(id);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'boc_projects' AND column_name = 'tenant_id') THEN
|
||||
ALTER TABLE boc_projects ADD COLUMN tenant_id UUID REFERENCES boc_tenants(id);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Uppdatera befintliga poster med default tenant
|
||||
UPDATE boc_tickets SET tenant_id = '11111111-1111-1111-1111-111111111111' WHERE tenant_id IS NULL;
|
||||
UPDATE boc_deals SET tenant_id = '11111111-1111-1111-1111-111111111111' WHERE tenant_id IS NULL;
|
||||
UPDATE boc_projects SET tenant_id = '11111111-1111-1111-1111-111111111111' WHERE tenant_id IS NULL;
|
||||
|
||||
-- Skapa tenant-isolerade vyer
|
||||
CREATE OR REPLACE VIEW v_tenant_summary AS
|
||||
SELECT
|
||||
t.id as tenant_id,
|
||||
t.name as tenant_name,
|
||||
t.slug,
|
||||
t.status,
|
||||
COUNT(DISTINCT u.id) as user_count,
|
||||
COUNT(DISTINCT c.id) as customer_count,
|
||||
COUNT(DISTINCT d.id) as deal_count,
|
||||
COUNT(DISTINCT e.id) as employee_count,
|
||||
COALESCE(SUM(d.value), 0) as total_pipeline
|
||||
FROM boc_tenants t
|
||||
LEFT JOIN boc_users u ON u.tenant_id = t.id
|
||||
LEFT JOIN boc_customers c ON c.tenant_id = t.id
|
||||
LEFT JOIN boc_deals d ON d.tenant_id = t.id AND d.status = 'open'
|
||||
LEFT JOIN boc_employees e ON e.tenant_id = t.id
|
||||
WHERE t.status = 'active'
|
||||
GROUP BY t.id, t.name, t.slug, t.status;
|
||||
|
||||
-- Koncern-vy (hierarkisk)
|
||||
CREATE OR REPLACE VIEW v_tenant_hierarchy AS
|
||||
WITH RECURSIVE tenant_tree AS (
|
||||
-- Root tenants (koncerner)
|
||||
SELECT
|
||||
id, name, slug, parent_id, 0 as level,
|
||||
id as root_id, name as root_name
|
||||
FROM boc_tenants
|
||||
WHERE parent_id IS NULL AND status = 'active'
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Child tenants (dotterbolag)
|
||||
SELECT
|
||||
t.id, t.name, t.slug, t.parent_id, tt.level + 1,
|
||||
tt.root_id, tt.root_name
|
||||
FROM boc_tenants t
|
||||
JOIN tenant_tree tt ON t.parent_id = tt.id
|
||||
WHERE t.status = 'active'
|
||||
)
|
||||
SELECT * FROM tenant_tree;
|
||||
|
||||
-- Audit log för tenant-ändringar
|
||||
CREATE TABLE IF NOT EXISTS boc_tenant_audit (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
action VARCHAR(50) NOT NULL, -- created, updated, deleted, switched
|
||||
performed_by UUID REFERENCES boc_users(id),
|
||||
details JSONB,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tenant_audit_tenant ON boc_tenant_audit(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tenant_audit_created ON boc_tenant_audit(created_at);
|
||||
@@ -0,0 +1,162 @@
|
||||
-- =====================================================
|
||||
-- Migration 004: Contracts & Legal Structure
|
||||
-- =====================================================
|
||||
|
||||
-- Huvudtabell för avtal
|
||||
CREATE TABLE IF NOT EXISTS boc_contracts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES boc_tenants(id),
|
||||
template_type VARCHAR(50) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Motpart
|
||||
counterparty VARCHAR(255) NOT NULL,
|
||||
counterparty_org VARCHAR(255),
|
||||
counterparty_org_number VARCHAR(20),
|
||||
counterparty_address TEXT,
|
||||
counterparty_contact VARCHAR(255),
|
||||
counterparty_email VARCHAR(255),
|
||||
|
||||
-- Avtalsstatus
|
||||
status VARCHAR(20) DEFAULT 'draft', -- draft, pending_signature, active, expired, terminated, disputed
|
||||
|
||||
-- Ekonomi
|
||||
value DECIMAL(15,2),
|
||||
currency VARCHAR(3) DEFAULT 'SEK',
|
||||
payment_terms VARCHAR(50),
|
||||
|
||||
-- Tidsperiod
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
renewal_date DATE,
|
||||
notice_period VARCHAR(20),
|
||||
auto_renewal BOOLEAN DEFAULT false,
|
||||
|
||||
-- Ansvarig
|
||||
responsible UUID REFERENCES boc_users(id),
|
||||
legal_contact UUID REFERENCES boc_users(id),
|
||||
|
||||
-- Produktkoppling
|
||||
product_id VARCHAR(100),
|
||||
product_module VARCHAR(50),
|
||||
|
||||
-- Avtalsinnehåll
|
||||
sections JSONB DEFAULT '[]',
|
||||
clauses JSONB DEFAULT '[]',
|
||||
variables JSONB DEFAULT '{}',
|
||||
terms JSONB DEFAULT '{}',
|
||||
|
||||
-- Dokument
|
||||
document_url VARCHAR(500),
|
||||
signed_document_url VARCHAR(500),
|
||||
|
||||
-- Signering
|
||||
signature_status VARCHAR(20) DEFAULT 'pending', -- pending, partially_signed, signed
|
||||
signed_by_tenant_at TIMESTAMP WITH TIME ZONE,
|
||||
signed_by_counterparty_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
-- Metadata
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
created_by UUID REFERENCES boc_users(id),
|
||||
version INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Index för snabb sökning
|
||||
CREATE INDEX IF NOT EXISTS idx_contracts_tenant ON boc_contracts(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_contracts_status ON boc_contracts(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_contracts_template ON boc_contracts(template_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_contracts_product ON boc_contracts(product_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_contracts_counterparty ON boc_contracts(counterparty);
|
||||
CREATE INDEX IF NOT EXISTS idx_contracts_dates ON boc_contracts(start_date, end_date);
|
||||
|
||||
-- Avtalsändringar/historik
|
||||
CREATE TABLE IF NOT EXISTS boc_contract_revisions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
contract_id UUID NOT NULL REFERENCES boc_contracts(id) ON DELETE CASCADE,
|
||||
revision_number INTEGER NOT NULL,
|
||||
changes JSONB NOT NULL,
|
||||
changed_by UUID REFERENCES boc_users(id),
|
||||
change_reason TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_contract_revisions_contract ON boc_contract_revisions(contract_id);
|
||||
|
||||
-- Påminnelser för avtal (förnyelse, uppsägning, etc.)
|
||||
CREATE TABLE IF NOT EXISTS boc_contract_reminders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
contract_id UUID NOT NULL REFERENCES boc_contracts(id) ON DELETE CASCADE,
|
||||
reminder_type VARCHAR(50) NOT NULL, -- renewal, expiration, payment, review
|
||||
reminder_date DATE NOT NULL,
|
||||
status VARCHAR(20) DEFAULT 'pending', -- pending, sent, acknowledged
|
||||
notified_users UUID[],
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_contract_reminders_date ON boc_contract_reminders(reminder_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_contract_reminders_status ON boc_contract_reminders(status);
|
||||
|
||||
-- Bilagor till avtal
|
||||
CREATE TABLE IF NOT EXISTS boc_contract_attachments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
contract_id UUID NOT NULL REFERENCES boc_contracts(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
file_url VARCHAR(500),
|
||||
file_type VARCHAR(50),
|
||||
file_size INTEGER,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
created_by UUID REFERENCES boc_users(id)
|
||||
);
|
||||
|
||||
-- Standardavtal (templates) - lagras som JSON för flexibilitet
|
||||
CREATE TABLE IF NOT EXISTS boc_contract_templates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
type VARCHAR(50) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(50), -- aamos, quixzoom, legal, hr
|
||||
product_module VARCHAR(50),
|
||||
version VARCHAR(10) DEFAULT '1.0',
|
||||
status VARCHAR(20) DEFAULT 'active',
|
||||
language VARCHAR(5) DEFAULT 'sv',
|
||||
jurisdiction VARCHAR(50) DEFAULT 'Sweden',
|
||||
content JSONB NOT NULL,
|
||||
default_terms JSONB,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Produkt-avtal kopplingar
|
||||
CREATE TABLE IF NOT EXISTS boc_product_contract_links (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
product_id VARCHAR(100) NOT NULL,
|
||||
product_name VARCHAR(255) NOT NULL,
|
||||
product_type VARCHAR(50) NOT NULL,
|
||||
required_templates JSONB DEFAULT '[]',
|
||||
optional_templates JSONB DEFAULT '[]',
|
||||
default_terms JSONB,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Skapa unikt index
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_product_contract_links_product
|
||||
ON boc_product_contract_links(product_id);
|
||||
|
||||
-- Triggers för updated_at
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
DROP TRIGGER IF EXISTS update_contracts_updated_at ON boc_contracts;
|
||||
CREATE TRIGGER update_contracts_updated_at
|
||||
BEFORE UPDATE ON boc_contracts
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
@@ -0,0 +1,474 @@
|
||||
-- =====================================================
|
||||
-- Migration 005: Employee Lifecycle Platform
|
||||
-- =====================================================
|
||||
|
||||
-- ==========================================
|
||||
-- 1. CORE TABLES
|
||||
-- ==========================================
|
||||
|
||||
-- Departments (Avdelningar)
|
||||
CREATE TABLE IF NOT EXISTS departments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
name VARCHAR(100) NOT NULL,
|
||||
code VARCHAR(20),
|
||||
description TEXT,
|
||||
parent_id UUID REFERENCES departments(id),
|
||||
manager_id UUID,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_departments_tenant ON departments(tenant_id);
|
||||
|
||||
-- Teams
|
||||
CREATE TABLE IF NOT EXISTS teams (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
department_id UUID REFERENCES departments(id),
|
||||
lead_id UUID,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_teams_tenant ON teams(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_teams_department ON teams(department_id);
|
||||
|
||||
-- Roles (Rolldefinitioner)
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
level INTEGER DEFAULT 1, -- hierarkisk nivå
|
||||
permissions JSONB DEFAULT '[]',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_roles_tenant ON roles(tenant_id);
|
||||
|
||||
-- Employees (Medarbetare)
|
||||
CREATE TABLE IF NOT EXISTS employees (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
|
||||
-- Personuppgifter
|
||||
first_name VARCHAR(100) NOT NULL,
|
||||
last_name VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
phone VARCHAR(20),
|
||||
personal_number VARCHAR(20),
|
||||
birth_date DATE,
|
||||
|
||||
-- Profil
|
||||
avatar_url VARCHAR(500),
|
||||
bio TEXT,
|
||||
|
||||
-- Anställning
|
||||
employment_type VARCHAR(20) DEFAULT 'full_time', -- full_time, part_time, consultant, intern, contractor
|
||||
employment_status VARCHAR(20) DEFAULT 'active', -- active, probation, notice, terminated, suspended
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
probation_end_date DATE,
|
||||
notice_period_days INTEGER DEFAULT 30,
|
||||
|
||||
-- Organisation
|
||||
department_id UUID REFERENCES departments(id),
|
||||
team_id UUID REFERENCES teams(id),
|
||||
manager_id UUID REFERENCES employees(id),
|
||||
role_id UUID REFERENCES roles(id),
|
||||
|
||||
-- Ekonomi
|
||||
salary DECIMAL(12,2),
|
||||
salary_currency VARCHAR(3) DEFAULT 'SEK',
|
||||
bank_account VARCHAR(50),
|
||||
bank_clearing VARCHAR(10),
|
||||
|
||||
-- Kontakt
|
||||
address TEXT,
|
||||
postal_code VARCHAR(10),
|
||||
city VARCHAR(100),
|
||||
country VARCHAR(2) DEFAULT 'SE',
|
||||
|
||||
-- Nödfall
|
||||
emergency_contact_name VARCHAR(100),
|
||||
emergency_contact_phone VARCHAR(20),
|
||||
emergency_contact_relation VARCHAR(50),
|
||||
|
||||
-- Metadata
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
created_by UUID,
|
||||
version INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_tenant ON employees(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_department ON employees(department_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_team ON employees(team_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_manager ON employees(manager_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_status ON employees(employment_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_email ON employees(email);
|
||||
|
||||
-- Employee Timeline Events
|
||||
CREATE TABLE IF NOT EXISTS employee_timeline_events (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
employee_id UUID NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
event_type VARCHAR(50) NOT NULL, -- hired, promoted, review, leave, termination, role_change, salary_change, etc.
|
||||
event_date DATE NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_by UUID,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_timeline_employee ON employee_timeline_events(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_timeline_date ON employee_timeline_events(event_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_timeline_type ON employee_timeline_events(event_type);
|
||||
|
||||
-- ==========================================
|
||||
-- 2. COMPETENCE SYSTEM
|
||||
-- ==========================================
|
||||
|
||||
-- Competences (Kompetenser)
|
||||
CREATE TABLE IF NOT EXISTS competences (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
name VARCHAR(100) NOT NULL,
|
||||
category VARCHAR(50), -- technical, soft_skill, language, leadership, domain
|
||||
description TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_competences_tenant ON competences(tenant_id);
|
||||
|
||||
-- Employee Competences
|
||||
CREATE TABLE IF NOT EXISTS employee_competences (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
employee_id UUID NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
competence_id UUID NOT NULL REFERENCES competences(id),
|
||||
level INTEGER CHECK (level BETWEEN 1 AND 5), -- 1=beginner, 2=intermediate, 3=advanced, 4=expert, 5=master
|
||||
acquired_date DATE,
|
||||
validated_by UUID REFERENCES employees(id),
|
||||
validation_date DATE,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(employee_id, competence_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_comp_employee ON employee_competences(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_comp_competence ON employee_competences(competence_id);
|
||||
|
||||
-- ==========================================
|
||||
-- 3. DOCUMENT SYSTEM
|
||||
-- ==========================================
|
||||
|
||||
-- Document Templates
|
||||
CREATE TABLE IF NOT EXISTS document_templates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
document_type VARCHAR(50) NOT NULL, -- contract, nda, policy, handbook, equipment_agreement
|
||||
description TEXT,
|
||||
content JSONB, -- strukturerat innehåll med variabler
|
||||
variables JSONB DEFAULT '[]', -- lista över variabler
|
||||
default_terms JSONB,
|
||||
language VARCHAR(5) DEFAULT 'sv',
|
||||
version INTEGER DEFAULT 1,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_doc_templates_tenant ON document_templates(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_doc_templates_type ON document_templates(document_type);
|
||||
|
||||
-- Employee Documents
|
||||
CREATE TABLE IF NOT EXISTS employee_documents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
employee_id UUID NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
template_id UUID REFERENCES document_templates(id),
|
||||
document_type VARCHAR(50) NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
|
||||
-- Innehåll
|
||||
content JSONB,
|
||||
file_url VARCHAR(500),
|
||||
file_type VARCHAR(50),
|
||||
file_size INTEGER,
|
||||
|
||||
-- Signering
|
||||
signature_status VARCHAR(20) DEFAULT 'draft', -- draft, sent, partially_signed, signed, expired
|
||||
signed_by_employee_at TIMESTAMP WITH TIME ZONE,
|
||||
signed_by_company_at TIMESTAMP WITH TIME ZONE,
|
||||
employee_signature_data JSONB,
|
||||
company_signature_data JSONB,
|
||||
signature_method VARCHAR(20), -- digital, manual, bankid
|
||||
|
||||
-- Versionering
|
||||
version INTEGER DEFAULT 1,
|
||||
parent_id UUID REFERENCES employee_documents(id),
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
created_by UUID
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_docs_employee ON employee_documents(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_docs_type ON employee_documents(document_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_docs_status ON employee_documents(signature_status);
|
||||
|
||||
-- ==========================================
|
||||
-- 4. TRAINING SYSTEM
|
||||
-- ==========================================
|
||||
|
||||
-- Trainings (Utbildningar)
|
||||
CREATE TABLE IF NOT EXISTS trainings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(50), -- onboarding, compliance, skill, safety, leadership
|
||||
format VARCHAR(20), -- video, text, quiz, workshop, mixed
|
||||
content JSONB, -- modulär struktur
|
||||
duration_minutes INTEGER,
|
||||
mandatory BOOLEAN DEFAULT false,
|
||||
valid_for_days INTEGER, -- hur länge certifiering gäller
|
||||
passing_score INTEGER DEFAULT 80, -- procent för godkänt
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_trainings_tenant ON trainings(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_trainings_category ON trainings(category);
|
||||
|
||||
-- Employee Trainings
|
||||
CREATE TABLE IF NOT EXISTS employee_trainings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
employee_id UUID NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
training_id UUID NOT NULL REFERENCES trainings(id),
|
||||
status VARCHAR(20) DEFAULT 'assigned', -- assigned, in_progress, completed, expired, failed
|
||||
progress_percent INTEGER DEFAULT 0,
|
||||
started_at TIMESTAMP WITH TIME ZONE,
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
expires_at TIMESTAMP WITH TIME ZONE,
|
||||
score INTEGER,
|
||||
certificate_url VARCHAR(500),
|
||||
certificate_number VARCHAR(100),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(employee_id, training_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_trainings_employee ON employee_trainings(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_trainings_status ON employee_trainings(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_trainings_expires ON employee_trainings(expires_at);
|
||||
|
||||
-- ==========================================
|
||||
-- 5. TASK SYSTEM
|
||||
-- ==========================================
|
||||
|
||||
-- Task Templates
|
||||
CREATE TABLE IF NOT EXISTS task_templates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(50), -- onboarding, role, compliance, project, recurring
|
||||
priority VARCHAR(20) DEFAULT 'medium', -- low, medium, high, critical
|
||||
estimated_duration_minutes INTEGER,
|
||||
checklist JSONB DEFAULT '[]',
|
||||
required_role_ids UUID[],
|
||||
required_competence_ids UUID[],
|
||||
instructions TEXT,
|
||||
resources JSONB DEFAULT '[]', -- länkar, dokument, etc.
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_task_templates_tenant ON task_templates(tenant_id);
|
||||
|
||||
-- Employee Tasks
|
||||
CREATE TABLE IF NOT EXISTS employee_tasks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
employee_id UUID NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
template_id UUID REFERENCES task_templates(id),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(20) DEFAULT 'pending', -- pending, in_progress, completed, cancelled, overdue
|
||||
priority VARCHAR(20) DEFAULT 'medium',
|
||||
due_date DATE,
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
completed_by UUID REFERENCES employees(id),
|
||||
checklist_progress JSONB DEFAULT '[]',
|
||||
assigned_by UUID REFERENCES employees(id),
|
||||
assigned_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
source VARCHAR(50), -- onboarding, role, manual, automation, performance
|
||||
source_id UUID,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_tasks_employee ON employee_tasks(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_tasks_status ON employee_tasks(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_emp_tasks_due ON employee_tasks(due_date);
|
||||
|
||||
-- ==========================================
|
||||
-- 6. PERFORMANCE SYSTEM
|
||||
-- ==========================================
|
||||
|
||||
-- Performance Reviews
|
||||
CREATE TABLE IF NOT EXISTS performance_reviews (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
employee_id UUID NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
reviewer_id UUID NOT NULL REFERENCES employees(id),
|
||||
review_period_start DATE NOT NULL,
|
||||
review_period_end DATE NOT NULL,
|
||||
review_type VARCHAR(20), -- annual, quarterly, probation, ad_hoc, 360
|
||||
|
||||
-- Bedömningar
|
||||
overall_rating INTEGER CHECK (overall_rating BETWEEN 1 AND 5),
|
||||
ratings JSONB DEFAULT '{}', -- {competence_id: score, ...}
|
||||
goals JSONB DEFAULT '[]', -- [{goal, target, achievement, status}]
|
||||
strengths TEXT[],
|
||||
improvements TEXT[],
|
||||
|
||||
-- Feedback
|
||||
employee_comments TEXT,
|
||||
reviewer_comments TEXT,
|
||||
development_plan TEXT,
|
||||
|
||||
status VARCHAR(20) DEFAULT 'draft', -- draft, submitted, acknowledged, disputed
|
||||
acknowledged_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_perf_reviews_employee ON performance_reviews(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_perf_reviews_status ON performance_reviews(status);
|
||||
|
||||
-- Performance KPIs
|
||||
CREATE TABLE IF NOT EXISTS performance_kpis (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
employee_id UUID NOT NULL REFERENCES employees(id) ON DELETE CASCADE,
|
||||
kpi_name VARCHAR(100) NOT NULL,
|
||||
kpi_value DECIMAL(10,2),
|
||||
target_value DECIMAL(10,2),
|
||||
unit VARCHAR(20),
|
||||
period VARCHAR(20), -- weekly, monthly, quarterly, yearly
|
||||
period_start DATE,
|
||||
period_end DATE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_perf_kpis_employee ON performance_kpis(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_perf_kpis_period ON performance_kpis(period_start);
|
||||
|
||||
-- ==========================================
|
||||
-- 7. ONBOARDING SYSTEM
|
||||
-- ==========================================
|
||||
|
||||
-- Onboardings
|
||||
CREATE TABLE IF NOT EXISTS onboardings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES boc_tenants(id),
|
||||
employee_id UUID NOT NULL REFERENCES employees(id),
|
||||
|
||||
-- Status
|
||||
status VARCHAR(20) DEFAULT 'in_progress', -- in_progress, completed, cancelled
|
||||
progress_percent INTEGER DEFAULT 0,
|
||||
|
||||
-- Steg
|
||||
steps JSONB DEFAULT '[]', -- [{step, status, completed_at, data}]
|
||||
|
||||
-- Tidslinje
|
||||
started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
target_completion_date DATE,
|
||||
|
||||
-- Ansvarig
|
||||
hr_manager_id UUID REFERENCES employees(id),
|
||||
buddy_id UUID REFERENCES employees(id), -- mentor/kompis
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_onboardings_tenant ON onboardings(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_onboardings_employee ON onboardings(employee_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_onboardings_status ON onboardings(status);
|
||||
|
||||
-- ==========================================
|
||||
-- 8. AUDIT LOG
|
||||
-- ==========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS employee_audit_log (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
table_name VARCHAR(50) NOT NULL,
|
||||
record_id UUID NOT NULL,
|
||||
action VARCHAR(20) NOT NULL, -- create, update, delete
|
||||
old_values JSONB,
|
||||
new_values JSONB,
|
||||
changed_by UUID,
|
||||
changed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
ip_address INET,
|
||||
user_agent TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_table ON employee_audit_log(table_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_record ON employee_audit_log(record_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_changed_at ON employee_audit_log(changed_at);
|
||||
|
||||
-- ==========================================
|
||||
-- 9. TRIGGERS
|
||||
-- ==========================================
|
||||
|
||||
-- Auto-update updated_at
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Apply triggers
|
||||
DROP TRIGGER IF EXISTS update_employees_updated_at ON employees;
|
||||
CREATE TRIGGER update_employees_updated_at
|
||||
BEFORE UPDATE ON employees
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
DROP TRIGGER IF EXISTS update_employee_documents_updated_at ON employee_documents;
|
||||
CREATE TRIGGER update_employee_documents_updated_at
|
||||
BEFORE UPDATE ON employee_documents
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
DROP TRIGGER IF EXISTS update_trainings_updated_at ON trainings;
|
||||
CREATE TRIGGER update_trainings_updated_at
|
||||
BEFORE UPDATE ON trainings
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
DROP TRIGGER IF EXISTS update_employee_trainings_updated_at ON employee_trainings;
|
||||
CREATE TRIGGER update_employee_trainings_updated_at
|
||||
BEFORE UPDATE ON employee_trainings
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
DROP TRIGGER IF EXISTS update_employee_tasks_updated_at ON employee_tasks;
|
||||
CREATE TRIGGER update_employee_tasks_updated_at
|
||||
BEFORE UPDATE ON employee_tasks
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
DROP TRIGGER IF EXISTS update_performance_reviews_updated_at ON performance_reviews;
|
||||
CREATE TRIGGER update_performance_reviews_updated_at
|
||||
BEFORE UPDATE ON performance_reviews
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
DROP TRIGGER IF EXISTS update_onboardings_updated_at ON onboardings;
|
||||
CREATE TRIGGER update_onboardings_updated_at
|
||||
BEFORE UPDATE ON onboardings
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
Reference in New Issue
Block a user