HR-ATS-Portal/backend/migrations/manual/038_departments.sql

93 lines
4.2 KiB
SQL

-- 038_departments.sql
-- Departments as a managed entity (backend/department/models.py): name, short
-- code, subtitle (replaces the design's "Cost Center"), description, status,
-- head, parent department and region/location list. Plus the `department`
-- permission module (8 tags), a `department_management` bundle holding them,
-- and that bundle attached to the admin roles.
--
-- Idempotent, applied automatically at startup by alembic_setup.run_manual_sql()
-- and recorded in manual_migrations. Needed because prod boots with
-- DB_AUTOGENERATE=false and never autogenerates new tables. Index names match
-- the db_setup NAMING_CONVENTION so a dev DB that autogenerated first is a no-op.
-- Users must log in again afterwards — the frontend caches /users/me permissions.
-- =============================================================================
-- 1. Table
-- =============================================================================
CREATE TABLE IF NOT EXISTS app.departments (
id uuid PRIMARY KEY,
name varchar NOT NULL,
short_code varchar(10) NOT NULL,
subtitle varchar,
description varchar,
is_active boolean NOT NULL DEFAULT true,
parent_department_id uuid REFERENCES app.departments(id),
department_head_id uuid REFERENCES app.users(id),
location jsonb NOT NULL DEFAULT '[]'::jsonb,
created_at timestamptz NOT NULL DEFAULT NOW(),
updated_at timestamptz NOT NULL DEFAULT NOW(),
created_by uuid REFERENCES app.users(id),
updated_by uuid REFERENCES app.users(id)
);
CREATE UNIQUE INDEX IF NOT EXISTS ix_departments_name
ON app.departments (name);
CREATE UNIQUE INDEX IF NOT EXISTS ix_departments_short_code
ON app.departments (short_code);
CREATE INDEX IF NOT EXISTS ix_departments_parent_department_id
ON app.departments (parent_department_id);
-- =============================================================================
-- 2. The 8 department.* permission tags
-- =============================================================================
INSERT INTO app.permission_tags
(tag_name, module, action, description, created_at, updated_at, is_active, is_deleted)
VALUES
('department.view', 'department', 'view', NULL, NOW(), NOW(), true, false),
('department.create', 'department', 'create', NULL, NOW(), NOW(), true, false),
('department.edit', 'department', 'edit', NULL, NOW(), NOW(), true, false),
('department.delete', 'department', 'delete', NULL, NOW(), NOW(), true, false),
('department.approve', 'department', 'approve', NULL, NOW(), NOW(), true, false),
('department.export', 'department', 'export', NULL, NOW(), NOW(), true, false),
('department.manage', 'department', 'manage', NULL, NOW(), NOW(), true, false),
('department.configure', 'department', 'configure', NULL, NOW(), NOW(), true, false)
ON CONFLICT (tag_name) DO NOTHING;
-- =============================================================================
-- 3. Bundle holding all eight department tags
-- =============================================================================
INSERT INTO app.permissions (name, description, permission_tags, is_system, created_at, updated_at, is_active, is_deleted)
SELECT
'department_management',
'Departments: view, create, edit and manage departments',
(
SELECT COALESCE(jsonb_agg(id ORDER BY id), '[]'::jsonb)
FROM app.permission_tags
WHERE is_deleted = false
AND module = 'department'
),
true,
NOW(),
NOW(),
true,
false
WHERE NOT EXISTS (
SELECT 1 FROM app.permissions WHERE name = 'department_management'
);
-- =============================================================================
-- 4. Attach the bundle to the admin roles (idempotent)
-- =============================================================================
UPDATE app.roles r
SET permissions = COALESCE(r.permissions, '[]'::jsonb) || jsonb_build_array(p.id),
updated_at = NOW()
FROM app.permissions p
WHERE p.name = 'department_management'
AND r.role_name IN (
'system_administrator',
'hr_administrator'
)
AND NOT (COALESCE(r.permissions, '[]'::jsonb) @> jsonb_build_array(p.id));