-- ============================================================= -- ARCHIVO: 45-hr.sql -- DESCRIPCION: Sistema de Recursos Humanos (RRHH) -- VERSION: 1.0.0 -- PROYECTO: ERP-Core -- FECHA: 2026-01-26 -- MODULO: MGN-010 (RRHH Basico) -- REFERENCIA: odoo-hr-analysis.md, hr-domain.md -- ============================================================= -- ===================== -- SCHEMA: hr -- ===================== CREATE SCHEMA IF NOT EXISTS hr; -- ===================== -- ENUMS -- ===================== -- Estado del empleado DO $$ BEGIN CREATE TYPE hr.employee_status AS ENUM ( 'active', 'inactive', 'on_leave', 'terminated' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; -- Tipo de contrato DO $$ BEGIN CREATE TYPE hr.contract_type AS ENUM ( 'permanent', 'temporary', 'contractor', 'internship', 'part_time' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; -- Estado del contrato DO $$ BEGIN CREATE TYPE hr.contract_status AS ENUM ( 'draft', 'active', 'expired', 'terminated', 'cancelled' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; -- Tipo de ausencia DO $$ BEGIN CREATE TYPE hr.leave_type_category AS ENUM ( 'vacation', 'sick', 'personal', 'maternity', 'paternity', 'bereavement', 'unpaid', 'other' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; -- Estado de ausencia DO $$ BEGIN CREATE TYPE hr.leave_status AS ENUM ( 'draft', 'submitted', 'approved', 'rejected', 'cancelled' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; -- Tipo de asignacion DO $$ BEGIN CREATE TYPE hr.allocation_type AS ENUM ( 'fixed', 'accrual', 'unlimited' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; -- ===================== -- TABLA: hr.departments -- Departamentos organizacionales -- ===================== CREATE TABLE IF NOT EXISTS hr.departments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, company_id UUID NOT NULL REFERENCES core.companies(id) ON DELETE CASCADE, -- Identificacion name VARCHAR(255) NOT NULL, code VARCHAR(50), -- Jerarquia parent_id UUID REFERENCES hr.departments(id) ON DELETE SET NULL, manager_id UUID, -- FK a employees (se agrega despues) -- Estado is_active BOOLEAN NOT NULL DEFAULT true, -- Metadata description TEXT, color VARCHAR(7), -- Audit created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(tenant_id, company_id, code) ); -- Indices CREATE INDEX IF NOT EXISTS idx_departments_tenant ON hr.departments(tenant_id); CREATE INDEX IF NOT EXISTS idx_departments_company ON hr.departments(company_id); CREATE INDEX IF NOT EXISTS idx_departments_parent ON hr.departments(parent_id); CREATE INDEX IF NOT EXISTS idx_departments_active ON hr.departments(is_active) WHERE is_active = true; -- ===================== -- TABLA: hr.job_positions -- Puestos de trabajo -- ===================== CREATE TABLE IF NOT EXISTS hr.job_positions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, company_id UUID NOT NULL REFERENCES core.companies(id) ON DELETE CASCADE, -- Identificacion name VARCHAR(255) NOT NULL, code VARCHAR(50), description TEXT, -- Relaciones department_id UUID REFERENCES hr.departments(id) ON DELETE SET NULL, -- Estado is_active BOOLEAN NOT NULL DEFAULT true, -- Audit created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(tenant_id, company_id, code) ); -- Indices CREATE INDEX IF NOT EXISTS idx_job_positions_tenant ON hr.job_positions(tenant_id); CREATE INDEX IF NOT EXISTS idx_job_positions_department ON hr.job_positions(department_id); CREATE INDEX IF NOT EXISTS idx_job_positions_active ON hr.job_positions(is_active) WHERE is_active = true; -- ===================== -- TABLA: hr.employees -- Empleados -- ===================== CREATE TABLE IF NOT EXISTS hr.employees ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, company_id UUID NOT NULL REFERENCES core.companies(id) ON DELETE CASCADE, -- Identificacion employee_number VARCHAR(50) NOT NULL, first_name VARCHAR(100) NOT NULL, last_name VARCHAR(100) NOT NULL, full_name VARCHAR(255) GENERATED ALWAYS AS (first_name || ' ' || last_name) STORED, -- Relaciones con usuario y partner user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL, partner_id UUID, -- REFERENCES partners.partners(id) -- Organizacion department_id UUID REFERENCES hr.departments(id) ON DELETE SET NULL, job_position_id UUID REFERENCES hr.job_positions(id) ON DELETE SET NULL, manager_id UUID REFERENCES hr.employees(id) ON DELETE SET NULL, -- Contacto laboral work_email VARCHAR(255), work_phone VARCHAR(50), work_mobile VARCHAR(50), work_location VARCHAR(255), -- Datos personales personal_email VARCHAR(255), personal_phone VARCHAR(50), birth_date DATE, gender VARCHAR(20), marital_status VARCHAR(20), -- Identificacion oficial identification_type VARCHAR(50), identification_number VARCHAR(100), tax_id VARCHAR(50), -- RFC en Mexico social_security_number VARCHAR(50), -- NSS/IMSS -- Direccion street VARCHAR(255), city VARCHAR(100), state VARCHAR(100), postal_code VARCHAR(20), country VARCHAR(100), -- Empleo hire_date DATE NOT NULL, termination_date DATE, status hr.employee_status NOT NULL DEFAULT 'active', -- Emergencia emergency_contact_name VARCHAR(255), emergency_contact_phone VARCHAR(50), emergency_contact_relationship VARCHAR(50), -- Metadata notes TEXT, avatar_url TEXT, -- Audit created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(tenant_id, employee_number) ); -- Indices CREATE INDEX IF NOT EXISTS idx_employees_tenant ON hr.employees(tenant_id); CREATE INDEX IF NOT EXISTS idx_employees_company ON hr.employees(company_id); CREATE INDEX IF NOT EXISTS idx_employees_department ON hr.employees(department_id); CREATE INDEX IF NOT EXISTS idx_employees_job ON hr.employees(job_position_id); CREATE INDEX IF NOT EXISTS idx_employees_manager ON hr.employees(manager_id); CREATE INDEX IF NOT EXISTS idx_employees_user ON hr.employees(user_id); CREATE INDEX IF NOT EXISTS idx_employees_status ON hr.employees(status); CREATE INDEX IF NOT EXISTS idx_employees_active ON hr.employees(status) WHERE status = 'active'; CREATE INDEX IF NOT EXISTS idx_employees_name ON hr.employees(full_name); -- Agregar FK de manager_id en departments ALTER TABLE hr.departments ADD CONSTRAINT fk_departments_manager FOREIGN KEY (manager_id) REFERENCES hr.employees(id) ON DELETE SET NULL; -- ===================== -- TABLA: hr.contracts -- Contratos laborales -- ===================== CREATE TABLE IF NOT EXISTS hr.contracts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, company_id UUID NOT NULL REFERENCES core.companies(id) ON DELETE CASCADE, -- Relaciones employee_id UUID NOT NULL REFERENCES hr.employees(id) ON DELETE CASCADE, -- Identificacion name VARCHAR(255) NOT NULL, reference VARCHAR(50), -- Tipo y estado contract_type hr.contract_type NOT NULL DEFAULT 'permanent', status hr.contract_status NOT NULL DEFAULT 'draft', -- Vigencia date_start DATE NOT NULL, date_end DATE, -- Puesto y departamento al momento del contrato job_position_id UUID REFERENCES hr.job_positions(id) ON DELETE SET NULL, department_id UUID REFERENCES hr.departments(id) ON DELETE SET NULL, -- Compensacion wage DECIMAL(15,2) NOT NULL, wage_type VARCHAR(20) NOT NULL DEFAULT 'monthly' CHECK (wage_type IN ('hourly', 'daily', 'weekly', 'biweekly', 'monthly', 'annual')), currency VARCHAR(3) NOT NULL DEFAULT 'MXN', -- Jornada hours_per_week DECIMAL(5,2) DEFAULT 48, schedule_type VARCHAR(50), -- Probatorio trial_period_months INTEGER DEFAULT 0, trial_date_end DATE, -- Metadata notes TEXT, document_url TEXT, -- Audit created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, activated_at TIMESTAMPTZ, terminated_at TIMESTAMPTZ, terminated_by UUID REFERENCES auth.users(id), termination_reason TEXT ); -- Indices CREATE INDEX IF NOT EXISTS idx_contracts_tenant ON hr.contracts(tenant_id); CREATE INDEX IF NOT EXISTS idx_contracts_employee ON hr.contracts(employee_id); CREATE INDEX IF NOT EXISTS idx_contracts_status ON hr.contracts(status); CREATE INDEX IF NOT EXISTS idx_contracts_active ON hr.contracts(employee_id, status) WHERE status = 'active'; CREATE INDEX IF NOT EXISTS idx_contracts_dates ON hr.contracts(date_start, date_end); -- ===================== -- TABLA: hr.leave_types -- Tipos de ausencia -- ===================== CREATE TABLE IF NOT EXISTS hr.leave_types ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, company_id UUID NOT NULL REFERENCES core.companies(id) ON DELETE CASCADE, -- Identificacion name VARCHAR(255) NOT NULL, code VARCHAR(50) NOT NULL, description TEXT, color VARCHAR(7) DEFAULT '#3B82F6', -- Categoria leave_category hr.leave_type_category NOT NULL DEFAULT 'other', -- Configuracion allocation_type hr.allocation_type NOT NULL DEFAULT 'fixed', requires_approval BOOLEAN NOT NULL DEFAULT true, requires_document BOOLEAN NOT NULL DEFAULT false, -- Limites max_days_per_request INTEGER, max_days_per_year INTEGER, min_days_notice INTEGER DEFAULT 0, -- Pago is_paid BOOLEAN NOT NULL DEFAULT true, pay_percentage DECIMAL(5,2) DEFAULT 100, -- Estado is_active BOOLEAN NOT NULL DEFAULT true, -- Audit created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE(tenant_id, company_id, code) ); -- Indices CREATE INDEX IF NOT EXISTS idx_leave_types_tenant ON hr.leave_types(tenant_id); CREATE INDEX IF NOT EXISTS idx_leave_types_company ON hr.leave_types(company_id); CREATE INDEX IF NOT EXISTS idx_leave_types_category ON hr.leave_types(leave_category); CREATE INDEX IF NOT EXISTS idx_leave_types_active ON hr.leave_types(is_active) WHERE is_active = true; -- ===================== -- TABLA: hr.leave_allocations -- Asignacion de dias por empleado -- ===================== CREATE TABLE IF NOT EXISTS hr.leave_allocations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, -- Relaciones employee_id UUID NOT NULL REFERENCES hr.employees(id) ON DELETE CASCADE, leave_type_id UUID NOT NULL REFERENCES hr.leave_types(id) ON DELETE CASCADE, -- Asignacion days_allocated DECIMAL(5,2) NOT NULL, days_used DECIMAL(5,2) NOT NULL DEFAULT 0, days_remaining DECIMAL(5,2) GENERATED ALWAYS AS (days_allocated - days_used) STORED, -- Periodo date_from DATE NOT NULL, date_to DATE NOT NULL, -- Metadata notes TEXT, -- Audit created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, CHECK (date_to > date_from), CHECK (days_allocated >= 0), CHECK (days_used >= 0) ); -- Indices CREATE INDEX IF NOT EXISTS idx_leave_allocations_tenant ON hr.leave_allocations(tenant_id); CREATE INDEX IF NOT EXISTS idx_leave_allocations_employee ON hr.leave_allocations(employee_id); CREATE INDEX IF NOT EXISTS idx_leave_allocations_type ON hr.leave_allocations(leave_type_id); CREATE INDEX IF NOT EXISTS idx_leave_allocations_period ON hr.leave_allocations(date_from, date_to); -- ===================== -- TABLA: hr.leaves -- Solicitudes de ausencia -- ===================== CREATE TABLE IF NOT EXISTS hr.leaves ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE, company_id UUID NOT NULL REFERENCES core.companies(id) ON DELETE CASCADE, -- Relaciones employee_id UUID NOT NULL REFERENCES hr.employees(id) ON DELETE CASCADE, leave_type_id UUID NOT NULL REFERENCES hr.leave_types(id) ON DELETE RESTRICT, allocation_id UUID REFERENCES hr.leave_allocations(id) ON DELETE SET NULL, -- Periodo solicitado date_from DATE NOT NULL, date_to DATE NOT NULL, days_requested DECIMAL(5,2) NOT NULL, -- Si es parcial is_half_day BOOLEAN NOT NULL DEFAULT false, half_day_type VARCHAR(20) CHECK (half_day_type IN ('morning', 'afternoon')), -- Estado status hr.leave_status NOT NULL DEFAULT 'draft', -- Aprobacion approver_id UUID REFERENCES auth.users(id), approved_at TIMESTAMPTZ, rejection_reason TEXT, -- Metadata request_reason TEXT, document_url TEXT, notes TEXT, -- Audit created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, submitted_at TIMESTAMPTZ, cancelled_at TIMESTAMPTZ, cancelled_by UUID REFERENCES auth.users(id), CHECK (date_to >= date_from), CHECK (days_requested > 0) ); -- Indices CREATE INDEX IF NOT EXISTS idx_leaves_tenant ON hr.leaves(tenant_id); CREATE INDEX IF NOT EXISTS idx_leaves_company ON hr.leaves(company_id); CREATE INDEX IF NOT EXISTS idx_leaves_employee ON hr.leaves(employee_id); CREATE INDEX IF NOT EXISTS idx_leaves_type ON hr.leaves(leave_type_id); CREATE INDEX IF NOT EXISTS idx_leaves_status ON hr.leaves(status); CREATE INDEX IF NOT EXISTS idx_leaves_dates ON hr.leaves(date_from, date_to); CREATE INDEX IF NOT EXISTS idx_leaves_pending ON hr.leaves(status) WHERE status IN ('submitted'); CREATE INDEX IF NOT EXISTS idx_leaves_approver ON hr.leaves(approver_id) WHERE status = 'submitted'; -- ===================== -- RLS POLICIES -- ===================== -- Departments ALTER TABLE hr.departments ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_departments ON hr.departments; CREATE POLICY tenant_isolation_departments ON hr.departments FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- Job Positions ALTER TABLE hr.job_positions ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_job_positions ON hr.job_positions; CREATE POLICY tenant_isolation_job_positions ON hr.job_positions FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- Employees ALTER TABLE hr.employees ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_employees ON hr.employees; CREATE POLICY tenant_isolation_employees ON hr.employees FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- Contracts ALTER TABLE hr.contracts ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_contracts ON hr.contracts; CREATE POLICY tenant_isolation_contracts ON hr.contracts FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- Leave Types ALTER TABLE hr.leave_types ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_leave_types ON hr.leave_types; CREATE POLICY tenant_isolation_leave_types ON hr.leave_types FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- Leave Allocations ALTER TABLE hr.leave_allocations ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_leave_allocations ON hr.leave_allocations; CREATE POLICY tenant_isolation_leave_allocations ON hr.leave_allocations FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- Leaves ALTER TABLE hr.leaves ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS tenant_isolation_leaves ON hr.leaves; CREATE POLICY tenant_isolation_leaves ON hr.leaves FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); -- ===================== -- TRIGGERS -- ===================== CREATE OR REPLACE FUNCTION hr.update_timestamp() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = CURRENT_TIMESTAMP; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_departments_updated_at BEFORE UPDATE ON hr.departments FOR EACH ROW EXECUTE FUNCTION hr.update_timestamp(); CREATE TRIGGER trg_job_positions_updated_at BEFORE UPDATE ON hr.job_positions FOR EACH ROW EXECUTE FUNCTION hr.update_timestamp(); CREATE TRIGGER trg_employees_updated_at BEFORE UPDATE ON hr.employees FOR EACH ROW EXECUTE FUNCTION hr.update_timestamp(); CREATE TRIGGER trg_contracts_updated_at BEFORE UPDATE ON hr.contracts FOR EACH ROW EXECUTE FUNCTION hr.update_timestamp(); CREATE TRIGGER trg_leave_types_updated_at BEFORE UPDATE ON hr.leave_types FOR EACH ROW EXECUTE FUNCTION hr.update_timestamp(); CREATE TRIGGER trg_leave_allocations_updated_at BEFORE UPDATE ON hr.leave_allocations FOR EACH ROW EXECUTE FUNCTION hr.update_timestamp(); CREATE TRIGGER trg_leaves_updated_at BEFORE UPDATE ON hr.leaves FOR EACH ROW EXECUTE FUNCTION hr.update_timestamp(); -- ===================== -- FUNCIONES DE UTILIDAD -- ===================== -- Obtener subordinados directos de un manager CREATE OR REPLACE FUNCTION hr.get_direct_subordinates(p_manager_id UUID) RETURNS TABLE ( id UUID, employee_number VARCHAR(50), full_name VARCHAR(255), department_name VARCHAR(255), job_title VARCHAR(255) ) AS $$ BEGIN RETURN QUERY SELECT e.id, e.employee_number, e.full_name, d.name, j.name FROM hr.employees e LEFT JOIN hr.departments d ON d.id = e.department_id LEFT JOIN hr.job_positions j ON j.id = e.job_position_id WHERE e.manager_id = p_manager_id AND e.status = 'active' ORDER BY e.full_name; END; $$ LANGUAGE plpgsql STABLE; -- Obtener saldo de dias de un tipo de ausencia CREATE OR REPLACE FUNCTION hr.get_leave_balance( p_employee_id UUID, p_leave_type_id UUID, p_date DATE DEFAULT CURRENT_DATE ) RETURNS TABLE ( days_allocated DECIMAL(5,2), days_used DECIMAL(5,2), days_remaining DECIMAL(5,2) ) AS $$ BEGIN RETURN QUERY SELECT COALESCE(SUM(la.days_allocated), 0), COALESCE(SUM(la.days_used), 0), COALESCE(SUM(la.days_remaining), 0) FROM hr.leave_allocations la WHERE la.employee_id = p_employee_id AND la.leave_type_id = p_leave_type_id AND p_date BETWEEN la.date_from AND la.date_to; END; $$ LANGUAGE plpgsql STABLE; -- Obtener contrato activo de un empleado CREATE OR REPLACE FUNCTION hr.get_active_contract(p_employee_id UUID) RETURNS hr.contracts AS $$ DECLARE v_contract hr.contracts; BEGIN SELECT * INTO v_contract FROM hr.contracts WHERE employee_id = p_employee_id AND status = 'active' LIMIT 1; RETURN v_contract; END; $$ LANGUAGE plpgsql STABLE; -- Verificar si hay solapamiento de ausencias CREATE OR REPLACE FUNCTION hr.check_leave_overlap( p_employee_id UUID, p_date_from DATE, p_date_to DATE, p_exclude_leave_id UUID DEFAULT NULL ) RETURNS BOOLEAN AS $$ DECLARE v_overlap_count INTEGER; BEGIN SELECT COUNT(*) INTO v_overlap_count FROM hr.leaves l WHERE l.employee_id = p_employee_id AND l.status IN ('submitted', 'approved') AND l.date_from <= p_date_to AND l.date_to >= p_date_from AND (p_exclude_leave_id IS NULL OR l.id != p_exclude_leave_id); RETURN v_overlap_count > 0; END; $$ LANGUAGE plpgsql STABLE; -- ===================== -- SEED DATA: Tipos de Ausencia Base -- ===================== -- Nota: Este seed se ejecuta por tenant cuando se crea la empresa -- Aqui solo documentamos los tipos base /* INSERT INTO hr.leave_types (tenant_id, company_id, name, code, leave_category, allocation_type, requires_approval, is_paid, max_days_per_year) VALUES -- Vacaciones (Mexico: 12 dias primer ano, incrementa) (tenant_id, company_id, 'Vacaciones', 'VAC', 'vacation', 'fixed', true, true, 30), -- Enfermedad (tenant_id, company_id, 'Incapacidad por Enfermedad', 'SICK', 'sick', 'unlimited', false, true, NULL), -- Maternidad (Mexico: 84 dias) (tenant_id, company_id, 'Licencia de Maternidad', 'MAT', 'maternity', 'fixed', true, true, 84), -- Paternidad (Mexico: 5 dias) (tenant_id, company_id, 'Licencia de Paternidad', 'PAT', 'paternity', 'fixed', true, true, 5), -- Luto (tenant_id, company_id, 'Permiso por Luto', 'BER', 'bereavement', 'fixed', true, true, 5), -- Personal sin goce (tenant_id, company_id, 'Permiso Personal (Sin Goce)', 'UNPAID', 'unpaid', 'unlimited', true, false, NULL), -- Personal con goce (tenant_id, company_id, 'Permiso Personal (Con Goce)', 'PERS', 'personal', 'fixed', true, true, 3); */ -- ===================== -- COMENTARIOS -- ===================== COMMENT ON SCHEMA hr IS 'Schema para gestion de Recursos Humanos'; COMMENT ON TABLE hr.departments IS 'Departamentos organizacionales con jerarquia'; COMMENT ON TABLE hr.job_positions IS 'Puestos de trabajo'; COMMENT ON TABLE hr.employees IS 'Empleados de la empresa'; COMMENT ON TABLE hr.contracts IS 'Contratos laborales'; COMMENT ON TABLE hr.leave_types IS 'Tipos de ausencia configurables'; COMMENT ON TABLE hr.leave_allocations IS 'Asignacion de dias por empleado y tipo'; COMMENT ON TABLE hr.leaves IS 'Solicitudes de ausencia'; COMMENT ON FUNCTION hr.get_direct_subordinates IS 'Obtiene subordinados directos de un manager'; COMMENT ON FUNCTION hr.get_leave_balance IS 'Obtiene saldo de dias de ausencia'; COMMENT ON FUNCTION hr.get_active_contract IS 'Obtiene contrato activo de un empleado'; COMMENT ON FUNCTION hr.check_leave_overlap IS 'Verifica solapamiento de ausencias';