Files
shopdb-flask/plugins/employees
cproudlock bad7aa29bb Employee directory: note it varies per site + add self-hosted option
- Flag the employee directory as the integration most likely to differ per
  site; USB (cmmc_usb) is standardized and rarely needs adaptation.
- Add "Option B: stand up a self-hosted directory" with the canonical employees
  table DDL, for sites with no HR database. In-app management (CRUD/CSV import)
  noted as a possible future enhancement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 08:39:53 -04:00
..

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:

-- 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/<Picture>, 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 - stand up a self-hosted directory for sites with no HR database. Create the canonical table and point employee_db_* at it:

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/. This can be a dedicated MySQL database or another schema on the same server as the main app DB - the plugin connects to it independently.

A future enhancement could manage this self-hosted directory in-app (add / edit people, CSV import) so a site needs no direct SQL. Not built yet.

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:

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).