Files
zibo-dashboard/docs/employee-profile-install.md
T
2026-05-14 16:10:10 +03:00

11 KiB

1. Database migration

ALTER TABLE employees
  ADD COLUMN address     varchar(500) DEFAULT NULL AFTER last_name,
  ADD COLUMN phone       varchar(255) DEFAULT NULL AFTER address,
  ADD COLUMN email       varchar(255) DEFAULT NULL AFTER phone,
  ADD COLUMN job_role_id int(10) UNSIGNED DEFAULT NULL AFTER department_id;

-- Replace ENUM status with plain VARCHAR for easier maintenance.
ALTER TABLE employees
  MODIFY status varchar(255) NOT NULL DEFAULT 'active';

CREATE TABLE IF NOT EXISTS job_roles (
  id          int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  name        varchar(255) NOT NULL,
  description text DEFAULT NULL,
  sort_order  int(10) UNSIGNED NOT NULL DEFAULT 999,
  is_active   tinyint(1) NOT NULL DEFAULT 1,
  created_at  timestamp NULL DEFAULT current_timestamp(),
  updated_at  timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (id),
  UNIQUE KEY uniq_job_roles_name (name),
  KEY idx_job_roles_active (is_active),
  KEY idx_job_roles_sort_order (sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

ALTER TABLE employees
  ADD KEY idx_employees_job_role_id (job_role_id);

ALTER TABLE employees
  ADD CONSTRAINT fk_employees_job_role
  FOREIGN KEY (job_role_id) REFERENCES job_roles (id)
  ON DELETE SET NULL
  ON UPDATE CASCADE;

-- 1) Seed job_roles with every distinct non-empty value of employees.position.
INSERT IGNORE INTO job_roles (name, is_active, sort_order, created_at, updated_at)
SELECT DISTINCT TRIM(position), 1, 999, NOW(), NOW()
FROM employees
WHERE position IS NOT NULL AND TRIM(position) <> '';

-- 2) Backfill employees.job_role_id by matching position text to job_roles.name.
UPDATE employees e
JOIN job_roles jr ON jr.name = TRIM(e.position)
SET e.job_role_id = jr.id
WHERE e.position IS NOT NULL AND TRIM(e.position) <> '';

-- 3) Drop the legacy column.
ALTER TABLE employees DROP COLUMN position;

CREATE TABLE IF NOT EXISTS training_topics (
  id                       int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  name                     varchar(255) NOT NULL,
  description              text DEFAULT NULL,
  default_frequency_months int(10) UNSIGNED DEFAULT NULL,
  default_reminder_days    int(10) UNSIGNED NOT NULL DEFAULT 30,
  sort_order               int(10) UNSIGNED NOT NULL DEFAULT 999,
  is_active                tinyint(1) NOT NULL DEFAULT 1,
  is_mandatory             tinyint(1) NOT NULL DEFAULT 0,
  created_at               timestamp NULL DEFAULT current_timestamp(),
  updated_at               timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (id),
  UNIQUE KEY uniq_training_topics_name (name),
  KEY idx_training_topics_active (is_active),
  KEY idx_training_topics_mandatory (is_mandatory),
  KEY idx_training_topics_sort_order (sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS employee_documents (
  id            int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  employee_id   int(10) UNSIGNED NOT NULL,
  category      varchar(255) NOT NULL DEFAULT 'other',
  original_name varchar(500) NOT NULL,
  stored_name   varchar(500) NOT NULL,
  mime_type     varchar(255) DEFAULT NULL,
  size          int(10) UNSIGNED DEFAULT NULL,
  notes         text DEFAULT NULL,
  uploaded_by   int(10) UNSIGNED DEFAULT NULL,
  created_at    timestamp NULL DEFAULT current_timestamp(),
  PRIMARY KEY (id),
  KEY idx_employee_documents_employee (employee_id),
  KEY idx_employee_documents_category (category),
  KEY idx_employee_documents_uploaded_by (uploaded_by),
  CONSTRAINT fk_employee_documents_employee
    FOREIGN KEY (employee_id) REFERENCES employees (id)
    ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT fk_employee_documents_uploaded_by
    FOREIGN KEY (uploaded_by) REFERENCES auth_users (id)
    ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS employee_ppe (
  id            int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  employee_id   int(10) UNSIGNED NOT NULL,
  item_name     varchar(255) NOT NULL,
  delivery_date date DEFAULT NULL,
  delivered_by  varchar(255) DEFAULT NULL,
  notes         text DEFAULT NULL,
  created_by    int(10) UNSIGNED DEFAULT NULL,
  created_at    timestamp NULL DEFAULT current_timestamp(),
  updated_at    timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (id),
  KEY idx_employee_ppe_employee (employee_id),
  KEY idx_employee_ppe_delivery_date (delivery_date),
  CONSTRAINT fk_employee_ppe_employee
    FOREIGN KEY (employee_id) REFERENCES employees (id)
    ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT fk_employee_ppe_created_by
    FOREIGN KEY (created_by) REFERENCES auth_users (id)
    ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS employee_trainings (
  id                      int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  employee_id             int(10) UNSIGNED NOT NULL,
  training_topic_id       int(10) UNSIGNED NOT NULL,
  completed_date          date NOT NULL,
  delivered_by            varchar(255) DEFAULT NULL,
  description             text DEFAULT NULL,
  training_type           varchar(255) NOT NULL DEFAULT 'initial',
  update_frequency_months int(10) UNSIGNED DEFAULT NULL,
  reminder_days           int(10) UNSIGNED DEFAULT NULL,
  next_due_date           date DEFAULT NULL,
  created_by              int(10) UNSIGNED DEFAULT NULL,
  created_at              timestamp NULL DEFAULT current_timestamp(),
  updated_at              timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (id),
  KEY idx_employee_trainings_employee (employee_id),
  KEY idx_employee_trainings_topic (training_topic_id),
  KEY idx_employee_trainings_next_due (next_due_date),
  KEY idx_employee_trainings_employee_topic (employee_id, training_topic_id),
  KEY idx_employee_trainings_created_by (created_by),
  CONSTRAINT fk_employee_trainings_employee
    FOREIGN KEY (employee_id) REFERENCES employees (id)
    ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT fk_employee_trainings_topic
    FOREIGN KEY (training_topic_id) REFERENCES training_topics (id)
    ON DELETE RESTRICT ON UPDATE CASCADE,
  CONSTRAINT fk_employee_trainings_created_by
    FOREIGN KEY (created_by) REFERENCES auth_users (id)
    ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS employee_training_attachments (
  id            int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  training_id   int(10) UNSIGNED NOT NULL,
  original_name varchar(500) NOT NULL,
  stored_name   varchar(500) NOT NULL,
  mime_type     varchar(255) DEFAULT NULL,
  size          int(10) UNSIGNED DEFAULT NULL,
  uploaded_by   int(10) UNSIGNED DEFAULT NULL,
  created_at    timestamp NULL DEFAULT current_timestamp(),
  PRIMARY KEY (id),
  KEY idx_employee_training_attachments_training (training_id),
  KEY idx_employee_training_attachments_uploaded_by (uploaded_by),
  CONSTRAINT fk_employee_training_attachments_training
    FOREIGN KEY (training_id) REFERENCES employee_trainings (id)
    ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT fk_employee_training_attachments_uploaded_by
    FOREIGN KEY (uploaded_by) REFERENCES auth_users (id)
    ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS employee_training_log (
  id          int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  employee_id int(10) UNSIGNED DEFAULT NULL,
  training_id int(10) UNSIGNED DEFAULT NULL,
  action      varchar(255) NOT NULL,
  field       varchar(255) DEFAULT NULL,
  old_value   text DEFAULT NULL,
  new_value   text DEFAULT NULL,
  changed_by  int(10) UNSIGNED DEFAULT NULL,
  changed_at  timestamp NULL DEFAULT current_timestamp(),
  PRIMARY KEY (id),
  KEY idx_employee_training_log_employee (employee_id),
  KEY idx_employee_training_log_training (training_id),
  KEY idx_employee_training_log_changed_at (changed_at),
  CONSTRAINT fk_employee_training_log_employee
    FOREIGN KEY (employee_id) REFERENCES employees (id)
    ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT fk_employee_training_log_training
    FOREIGN KEY (training_id) REFERENCES employee_trainings (id)
    ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT fk_employee_training_log_changed_by
    FOREIGN KEY (changed_by) REFERENCES auth_users (id)
    ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO auth_roles (name, display_name, description, removable, created_at, updated_at) VALUES
    ('employee',    'Employee',   'Read-only access to own employee profile.', 1, NOW(), NOW()),
    ('employee-hr', 'HR Manager', 'Can manage employee profiles, documents, PPE and training records.', 1, NOW(), NOW()),
    ('manager',     'Manager',    'Same permissions as HR Manager.', 1, NOW(), NOW())
ON DUPLICATE KEY UPDATE
    display_name = VALUES(display_name),
    description  = VALUES(description),
    updated_at   = NOW();

CREATE TABLE IF NOT EXISTS training_reminder_log (
  id                int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  training_id       int(10) UNSIGNED DEFAULT NULL,
  employee_id       int(10) UNSIGNED DEFAULT NULL,
  training_topic_id int(10) UNSIGNED DEFAULT NULL,
  addressee_email   varchar(255) NOT NULL,
  next_due_date     date DEFAULT NULL,
  status_at_send    varchar(255) NOT NULL,
  sent_at           timestamp NULL DEFAULT current_timestamp(),
  PRIMARY KEY (id),
  KEY idx_training_reminder_log_dedup (training_id, addressee_email, next_due_date),
  KEY idx_training_reminder_log_dedup_missing (employee_id, training_topic_id, addressee_email),
  KEY idx_training_reminder_log_sent_at (sent_at),
  CONSTRAINT fk_training_reminder_log_training
    FOREIGN KEY (training_id) REFERENCES employee_trainings (id)
    ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT fk_training_reminder_log_employee
    FOREIGN KEY (employee_id) REFERENCES employees (id)
    ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT fk_training_reminder_log_topic
    FOREIGN KEY (training_topic_id) REFERENCES training_topics (id)
    ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

2. Upload storage folder

Create the storage directory with the correct permissions for the web server:

mkdir -p /var/www/zibo-dashboard/public/userarea/files/employees
chown -R www-data:www-data /var/www/zibo-dashboard/public/userarea/files
chmod -R 775 /var/www/zibo-dashboard/public/userarea/files

Uploaded files will be organized as:

files/employees/{employee_id}/documents/   # File Repository (HR)
files/employees/{employee_id}/trainings/   # Training certificates

3. Cron for automated emails

0 7 * * * /usr/bin/php /var/www/zibo-dashboard/public/userarea/cron/send_training_reminders.php \
    >> /var/www/zibo-dashboard/storage/logs/training_reminders.log 2>&1