Shopfloor feed resolves employee names live when none is stored
Some checks failed
CI / backend (push) Successful in 1m45s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 8s
CI / migrations-mysql (push) Failing after 8s

Photos already resolved through the directory at read time, but names
only came from the stored employeename column - empty after a
shopdb-only import, so recertification/recognition cards showed bare
SSOs. New resolve_employee_display_name in the employees plugin
(mode-aware: self-hosted table or external HR) backs a fallback in
both the single-card and split-per-employee paths; stored names still
win when present.
This commit is contained in:
cproudlock
2026-07-17 13:29:34 -04:00
parent bc9159742c
commit ee80d684d4
3 changed files with 77 additions and 2 deletions

View File

@@ -116,6 +116,36 @@ def _hr_picture(sso):
return None
def resolve_employee_display_name(sso):
"""Display name ("First Last") for an SSO in either directory mode.
The shopfloor feed uses this as a live fallback when a notification has
no stored employeename (e.g. imported without the employee source).
None on any miss."""
if sso is None or not str(sso).isdigit():
return None
if _selfhosted():
emp = db.session.get(DirectoryEmployee, int(sso))
if emp:
return f'{emp.firstname} {emp.lastname}'.strip() or None
return None
try:
conn = employee_connection()
with conn.cursor() as cur:
cur.execute(
'SELECT First_Name, Last_Name FROM employees WHERE SSO = %s',
(int(sso),))
row = cur.fetchone()
conn.close()
if row:
first = row.get('First_Name') or ''
last = row.get('Last_Name') or ''
return f'{first.strip()} {last.strip()}'.strip() or None
except Exception:
pass
return None
def resolve_employee_photo_url(sso, external_picture=None):
"""Single resolver both consumers share: the display photo URL for an SSO.

View File

@@ -149,6 +149,16 @@ def _config_version():
return hashlib.md5('||'.join(parts).encode()).hexdigest()[:12]
def _employee_name(sso):
"""Live directory name for an SSO; None on any miss. Used as the fallback
when a notification has no stored employeename (see the shopfloor feed)."""
try:
from plugins.employees.api.routes import resolve_employee_display_name
return resolve_employee_display_name(sso)
except Exception:
return None
def _employee_picture(sso):
"""Resolved display photo URL for an SSO, via the shared employees-plugin
resolver so kiosk cards match EmployeeDetail in both directory modes
@@ -723,7 +733,12 @@ def get_shopfloor_notifications():
result['employeepicture'] = employee_override.get('picture')
else:
result['employeesso'] = n.employeesso
result['employeename'] = n.employeename
# Stored name first (import/manual entry), else resolve live from
# the directory so shopdb-only imports still show names.
name = n.employeename
if not name and n.employeesso and ',' not in n.employeesso:
name = _employee_name(n.employeesso)
result['employeename'] = name
result['employeepicture'] = _employee_picture(n.employeesso) if show_photo else None
return result
@@ -742,7 +757,8 @@ def get_shopfloor_notifications():
return [
notification_to_shopfloor(n, {
'sso': sso,
'name': names[i] if i < len(names) else sso,
'name': (names[i] if i < len(names) and names[i] else None)
or _employee_name(sso) or sso,
'picture': _employee_picture(sso) if show_photo else None,
})
for i, sso in enumerate(ssos)