# Employees plugin - directory database contract Read-only lookups against a **separate** employee/HR directory database. Powers: - Employee search (add people to a notification, browse the directory) - Single + batch SSO lookup - Recognition notifications (name + photo on the shopfloor / lobby displays) The plugin never writes to this database. Use a read-only account. > **This is the integration most likely to differ per site.** HR / directory > systems vary widely, so expect to map a site's schema to the contract below - > the `CREATE VIEW` recipe at the end is the normal way to do it. ## Connection Credentials resolve **settings-first, then environment**, except the password which is env-only (never stored in the app database). | Field | Setting key (editable in the setup wizard) | Env var (fallback) | | -------- | ------------------------------------------ | ---------------------- | | Host | `employee_db_host` | `EMPLOYEE_DB_HOST` | | Database | `employee_db_name` | `EMPLOYEE_DB_NAME` | | User | `employee_db_user` | `EMPLOYEE_DB_USER` | | Password | (not stored) | `EMPLOYEE_DB_PASSWORD` | Set the password in `.env`; the setup wizard's Features step shows the exact line to paste. Engine is MySQL/MariaDB (pymysql). ## Required schema The plugin runs these queries verbatim, so a site's database must expose a table (or **view** - see below) named `employees` with these columns: | Column | Type | Notes | | ------------ | ------------------- | ------------------------------------------------- | | `SSO` | INT | Unique person id. Lookups require it to be numeric | | `First_Name` | VARCHAR | Displayed as the given name | | `Last_Name` | VARCHAR | Displayed as the surname; sort key | | `Team` | VARCHAR | Team / group label | | `Role` | VARCHAR | Job title / role | | `Picture` | VARCHAR | **Image filename** (see Photos) | Queries actually executed: ```sql -- search SELECT SSO, First_Name, Last_Name, Team, Role, Picture FROM employees WHERE First_Name LIKE %s OR Last_Name LIKE %s OR CAST(SSO AS CHAR) LIKE %s ORDER BY Last_Name, First_Name LIMIT %s; -- single / batch SELECT SSO, First_Name, Last_Name, Team, Role, Picture FROM employees WHERE SSO = %s; SELECT SSO, First_Name, Last_Name, Team, Role, Picture FROM employees WHERE SSO IN (...); ``` Rows are returned to the API **with these exact column names**. The frontend (EmployeeSearch, NotificationForm, EmployeeDetail, ShopfloorDashboard) reads `SSO`, `First_Name`, `Last_Name`, `Team`, `Role`, `Picture` as-is - do not rename them in the response. ## Photos `Picture` holds an **image filename** (e.g. `123456.jpg`), not a path or blob. The app renders it as `/static/employees/`, so the image files must live in the app's `static/employees/` directory. Leave `Picture` empty/NULL for people with no photo; the UI falls back to initials. ## Two ways to provide the directory **Option A - map an existing HR/directory database** (see the view recipe below). Use this when the site already has a system of record for people. **Option B - self-hosted directory (managed in-app)** for sites with no HR database. Set `employee_directory_mode` to `selfhosted` and manage people under **Settings > Employee Directory** (add / edit / delete + CSV import) - no SQL needed. The app owns a `directoryemployees` table (migration 7d16); the lookup APIs read it automatically in this mode. If you would rather load it with SQL, the canonical table is equivalent to: ```sql CREATE DATABASE shopdb_directory CHARACTER SET utf8mb4; USE shopdb_directory; CREATE TABLE employees ( SSO INT NOT NULL PRIMARY KEY, First_Name VARCHAR(100) NOT NULL, Last_Name VARCHAR(100) NOT NULL, Team VARCHAR(100) NULL, Role VARCHAR(100) NULL, Picture VARCHAR(255) NULL ); -- add people (or bulk-load from CSV with LOAD DATA INFILE) INSERT INTO employees (SSO, First_Name, Last_Name, Team, Role, Picture) VALUES (123456, 'Jane', 'Doe', 'Inspection', 'Quality Tech', '123456.jpg'); ``` Put photo files (named as in `Picture`) under the app's `static/employees/`. In self-hosted mode the `directoryemployees` table lives in the main app DB, so no separate database is required. ## Adapting a different site schema (recommended: a view) Sites whose HR/directory database uses different table or column names should **not** be forced to rename anything. Instead, create a read-only **view** named `employees` that maps local columns to the names above: ```sql CREATE VIEW employees AS SELECT person_id AS SSO, given_name AS First_Name, surname AS Last_Name, department AS Team, job_title AS Role, photo_filename AS Picture FROM hr_people WHERE active = 1; ``` Grant the app's read-only user `SELECT` on the view. No app code changes - point `employee_db_*` at that database and the plugin works. Notes: - `SSO` must be numeric (single/batch lookup validate `isdigit()`). - Column-name case follows your database's identifier casing; match the names above exactly on case-sensitive platforms. - If the directory is unreachable or the `employees` object is missing, lookups return a 500 and the rest of the app keeps working (the feature degrades, it does not crash the app).