HRM — Org Structure & Employees

Departments, job titles, and the employee record that everything else hangs off

The foundation of HRM

Every other HR feature — attendance, shifts, leave, payroll, self-service — points back to an Employee record. Set this up first. The recommended order is: Departments → Designations → Employees.

What This Module Does (Plain Language)

This is your staff directory. It tracks everyone who works at the business — not only the people who log into the POS. A delivery driver who never touches a screen, a cleaner, and a cashier are all "employees" here. The module stores their personal details, job title, department, reporting manager, employment dates, uploaded documents (visas, certificates), and — for authorized HR staff only — sensitive data like national ID and bank details (stored encrypted).

Three Kinds of People

A login account (User) and an employment record (Employee) are deliberately separate things. That separation is what lets POSVelo handle real-world staffing cleanly.

PersonaHas a login (User)?Has an Employee record?Example
System-onlyYesNoSUPER_ADMIN / platform operator — never on payroll
Worker-onlyNoYesCleaner or driver who is paid & scheduled but never logs in
Full staffYesYesCashier, manager, HR manager — logs in AND is on payroll
Why this matters

You can suspend someone from working without deleting their login, or give a worker-only employee a login later. When creating an employee you may optionally provision a login at the same time (see below).

Departments & Designations

EntityWhat it isKey fields
DepartmentAn organizational unit. Can nest (a parent department).name, parentDepartmentId, headEmployeeId
DesignationA job title / role. Carries a seniority <code>level</code> used for leave-policy defaults.name, level

Both support soft delete (archive) and restore, and both are blocked from deletion while employees still reference them — you'll get a 409 Conflict telling you how many employees are attached.

In the app

1

Departments

Go to HR › Departments (/hr/departments). Add units like "Sales Floor", "Kitchen", "Back Office".

2

Designations

Go to HR › Designations (/hr/designations). Add titles like "Cashier", "Shift Lead", "Store Manager" and set a seniority level.

The Employee Record

Field groupFieldsNotes
IdentityemployeeCode (unique per business), firstName, lastName, dateOfBirth, genderemployeeCode disambiguates two people with the same name.
Contactemail, phone, emergencyContactemergencyContact is a small object: name, relationship, phone.
PlacementstoreId, departmentId, designationId, reportsToIdreportsToId is the person's manager — drives team-scoped approvals.
EmploymentemploymentType, employmentStatus, employmentStartDateSee the enums below.
Login linkuserId (nullable)Set when the employee also has a login account.
Sensitive (encrypted)nationalId, passportNumber, taxId, bankDetailsHidden unless the caller has the sensitive read permission.

Employment type & status

EnumAllowed values
employmentTypeFULL_TIME · PART_TIME · CONTRACT · INTERN
employmentStatusACTIVE · PROBATION · ON_LEAVE · SUSPENDED · INACTIVE · TERMINATED · RESIGNED · RETIRED · DECEASED · CONTRACT_ENDED
Only active people can be scheduled or punch in

Attendance, shifts, and new leave requests are blocked for employees whose status is TERMINATED, RESIGNED, or SUSPENDED. Rosters can only be built for ACTIVE and PROBATION staff. This is enforced server-side across modules.

Creating an Employee

In the app

Go to HR › Employees › New (/hr/employees/new), fill in identity, placement, and employment details, then save. The list at /hr/employees supports search, filters, and pagination; each row opens a detail page at /hr/employees/[id].

Via the API

Dev Only Requires permission hr.employees.create.

http
json
Create a login at the same time

Add a createUser object ({ email, password, role, storeId }) to provision a login user atomically. This needs the users.create permission, and the role is limited to MANAGER, HR_MANAGER, CASHIER, or EMPLOYEE. A SUPER_ADMIN can never be an employee.

Sensitive Data & Salary — Handled Specially

Two rules protect personal and pay data:

RuleHow it works
Sensitive fields are filtered, not refusedIf you read an employee without the sensitive-read permission, you get a normal 200 response with the sensitive fields simply omitted — you can see the person exists, just not their NID/bank.
Salary is edited via a dedicated endpointPay is never edited inline. Updating salary creates a new effective-dated EmployeeSalary row; the old one is closed off (it gets an effectiveTo and is marked superseded). This preserves a full salary history — see Payroll.

The detail view exposes these as separate cards/endpoints:

ActionEndpointPermission
Update basic fieldsPATCH /api/v2/hr/employees/:idhr.employees.update
Update sensitive PIIPATCH /api/v2/hr/employees/:id/sensitivehr.employees.update.sensitive
Update salaryPATCH /api/v2/hr/employees/:id/salaryhr.employees.update.salary

Contracts & Documents

WhatEndpointsNotes
Employment contractsGET / POST /api/v2/hr/employees/:id/contractsA history of employment periods (useful for rehires and fixed-term staff).
DocumentsGET / POST / DELETE /api/v2/hr/employees/:id/documents[/:docId]Uploaded files like passports or certificates; a document can carry an expiry date.

Termination (Separation)

Ending employment is a workflow, not a delete. The record is never destroyed — history is preserved for audit and compliance.

Dev Only POST /api/v2/hr/employees/:id/terminate · permission hr.employees.terminate

When you terminate, the system:

1

Sets the separation outcome

Status becomes one of RESIGNED, TERMINATED, RETIRED, DECEASED, or CONTRACT_ENDED, and records employmentEndDate + a reason.

2

Optionally deactivates the login

If you set deactivateUser: true, the linked login is soft-deactivated (the User row is kept for audit).

3

Cleans up downstream

All pending leave requests are auto-cancelled, future shift schedules are cancelled, and a final settlement can be flagged for the next payroll run.

Soft delete vs terminate

DELETE /api/v2/hr/employees/:id is a soft delete (archive) — reversible via PATCH .../:id/restore. Termination is the proper way to end an employment relationship while keeping the person in history. Neither removes data.

Who Can Do What

CapabilityADMINHR_MANAGERMANAGERACCOUNTANTEMPLOYEE
Read employees (basic)YesYesTeam onlyLimitedOwn (via ESS)
Read sensitive (NID/bank)YesYesNoNoNo
Read salaryYesYesNoYesOwn (via ESS)
Create / update / terminateYesYesNoNoNo
Manage departments & designationsYesYesNoNoNo

"Team" for a MANAGER means employees whose reportsToId points at that manager's own employee record. The backend always enforces these rules; the UI just hides controls you can't use.

Common Gotchas

SituationWhat happens
Duplicate employeeCode409 Conflict — codes are unique per business.
Manager loop (A reports to B reports to A)Rejected with a validation error.
employmentEndDate before start dateValidation error.
Editing a TERMINATED/RESIGNED employeeBlocked except for separation fields.
Deleting a department with staff in it409 Conflict with the count of attached employees.
Rehiring someoneCreate a fresh employment cycle (new contract & start date); old data is retained; leave balances start fresh by default.
Next step

With employees in place, set up Attendance & Shifts so staff can clock in and be rostered.