DeparturesAsia/Kolkata
Everything the company owes someone, and when.
Zenith HR runs the people operations of one company — payroll, leave, attendance, hiring, reviews — on a schedule it keeps itself. Below is that schedule. Not a diagram of one: the eight expressions that actually fire, read in the timezone they fire in.
| Departs | Operation | Cadence | Next run |
|---|---|---|---|
| 06:00 | MARK ABSENTYesterday's un-punched working days closed as absentEvery day | Every day30 0 * * * | |
| 06:30 | ACCRUE LEAVEThe month's accrual credited to every balance1st of the month | 1st of the month0 1 1 * * | |
| 07:00 | CARRY FORWARDBalances rolled into the new leave year, capped per policy1 January | 1 January30 1 1 1 * | |
| 11:30 | SEND REPORTSSaved reports mailed to the people who asked for themEvery day | Every day0 6 * * * | |
| 12:30 | OPEN SURVEYSAny recurring survey whose next occurrence is dueEvery day | Every day0 7 * * * | |
| 14:30 | CHASE APPROVALSApprovers sitting on pending leave requestsEvery day | Every day0 9 * * * | |
| 15:30 | CHASE REVIEWSReviews not yet submitted in an open cycleEvery day | Every day0 10 * * * | |
| 15:30 | NUDGE GOALSOwners of goals whose progress has gone staleMondays | Mondays0 10 * * 1 |
RunningEmail queue drains every 10 minutes*/10 * * * *
Schedules live in Postgres under pg_cron, stored in UTC and shown here in IST. The times are the organization’s, not yours.
The reporting lineSample
One request, moving
Leave, expense claims and attendance corrections all travel up the reporting line. Each state change below is one Postgres function, not a sequence of application writes — so a request cannot be approved without its balance moving, and it cannot come to rest half-approved.
| At | Event | By | State |
|---|---|---|---|
| 09:14 | Casual leave applied for, 3 daysR. Iyer, Engineering | R. Iyer, Engineering | Pending |
| 09:14 | Balance checked: 11.5 days availableapply_leave_request() | apply_leave_request() | Held |
| 11:02 | Approved at level 1S. Rao, reporting manager | S. Rao, reporting manager | Approved |
| 11:02 | Balance debited: 8.5 days remainingapprove_leave_request() | approve_leave_request() | Posted |
| 11:02 | Written to audit_logs with actor, before and afteraudit trigger | audit trigger | Recorded |
| 11:10 | Notification deliveredemail-queue | email-queue | Sent |
Sample rows, real mechanism. apply_leave_request and approve_leave_request are security definer functions: the balance write, the status write and the audit row are one transaction. Extending the workflow means extending the function, not the screen.
PayrollSample
The arithmetic, shown
A payslip is the one document an employee will check by hand, so it has to survive being checked. Every statutory line below is read from statutory_config with an effective_from date. None of it is hardcoded: a rate change is a row, not a deploy.
Earnings
Deductions
Money is stored as numeric(14,2) with its currency code beside it, never as a float. The seeded statutory values are plausible, not authoritative — they carry an effective_from and have to be verified against current law before anyone is paid from them.
Scope
Twelve modules, one register
Every module is the same object: a table you can scan, a row you can open, and a state that means the same thing in all twelve. Nothing here is a separate product bolted on with its own vocabulary.
Row-level security
What it refuses
Most of the work in an HR system is the access it does not grant. Each line below is a question the database is asked and answers with nothing — asserted by a test that runs against a real Postgres instance with real sessions, not by a comment promising it would.
| Question | Answer | Asserted by |
|---|---|---|
| A manager opens their report's payslippayroll-rls.test.ts | Denied | payroll-rls.test.ts |
| An employee opens a colleague's published payslippayroll-rls.test.ts | Denied | payroll-rls.test.ts |
| Anyone edits a payslip inside a locked run, service role includedpayroll-rls.test.ts | Denied | payroll-rls.test.ts |
| A compensation row is amended instead of supersededpayroll-rls.test.ts | Denied | payroll-rls.test.ts |
| Org A's admin reads an Org B employee by direct idrls.test.ts | Denied | rls.test.ts |
| An employee grants themselves a rolerls.test.ts | Denied | rls.test.ts |
| A review subject reads the individual responses about themperformance-privacy.test.ts | Denied | performance-privacy.test.ts |
| An anonymous survey response is linked back to a personperformance-privacy.test.ts | Denied | performance-privacy.test.ts |
- 84
- tables
- 84
- with RLS enabled
- 166
- policies
- 19
- views, all security_invoker
Arrivals
There is nothing to buy.
Zenith HR is an internal tool for one company. No plan, no trial, no sales call. If you have an account, sign in. If you are the one setting the company up, create the organization and you become its first administrator.