Case file 003Attendance & payroll
The problem
“Our fingerprint machine knows who came late. Payroll doesn’t. Every month, someone works it out by hand.”
Solved
- For
- A company running shifts across branches
- Built
- HR & payroll app: web admin, phone app, machine bridge
- Stack
- Next.js · Expo · Prisma · Postgres · ZKTeco ADMS
- Result
- Number to confirm
01 · The pain
The machine knew who was late. Payroll didn’t.
The fingerprint machine keeps a log of codes and times. Turning that into salaries meant matching every line to a person, a shift, a late rule, an overtime rate and any advance. By hand. Every month.
- Punches to match
- 0
- Example team
- 40 people · 26 days
And one wrong line means someone’s salary is wrong.
1024 2026-08-01 08:31:59 1027 2026-08-01 18:54:04 1040 2026-08-01 08:11:59 1003 2026-08-01 20:04:40 1037 2026-08-01 08:43:03 1025 2026-08-01 18:52:04 1033 2026-08-01 09:16:42 1030 2026-08-01 18:49:44 1002 2026-08-01 08:08:39 1016 2026-08-01 18:18:52 1023 2026-08-01 08:27:34 1017 2026-08-01 18:35:21 1029 2026-08-02 08:23:34 1010 2026-08-02 18:38:27 1012 2026-08-02 08:04:46 1009 2026-08-02 18:42:37 1038 2026-08-02 08:28:23 1026 2026-08-02 18:11:37 1001 2026-08-02 08:45:45 1018 2026-08-02 18:14:17 1034 2026-08-02 08:35:03 1036 2026-08-02 18:10:49 1012 2026-08-02 08:26:46 1015 2026-08-02 18:19:59 1012 2026-08-03 08:34:53 1029 2026-08-03 18:13:29 1016 2026-08-03 08:29:56 1032 2026-08-03 18:22:35 1036 2026-08-03 08:19:44 1036 2026-08-03 20:14:20 1002 2026-08-03 09:44:48 1010 2026-08-03 18:10:36 1026 2026-08-03 08:53:14 1028 2026-08-03 18:30:18 1028 2026-08-03 09:53:21 1012 2026-08-03 18:32:55 1018 2026-08-04 08:04:35 1027 2026-08-04 18:49:35 1003 2026-08-04 08:28:26 1034 2026-08-04 20:53:19 1004 2026-08-04 08:03:45 1031 2026-08-04 20:23:48 1018 2026-08-04 08:12:09 1024 2026-08-04 18:38:07 1035 2026-08-04 09:59:08 1011 2026-08-04 18:34:40 1002 2026-08-04 09:15:07 1020 2026-08-04 18:29:09 1009 2026-08-05 08:27:30 1040 2026-08-05 18:09:16 1032 2026-08-05 08:29:05 1030 2026-08-05 20:06:19 1009 2026-08-05 08:37:33 1035 2026-08-05 18:29:07 1026 2026-08-05 08:31:46 1040 2026-08-05 18:51:19 1037 2026-08-05 08:51:57 1038 2026-08-05 18:02:42 1004 2026-08-05 08:02:57 1001 2026-08-05 18:02:26 1005 2026-08-06 08:03:20 1009 2026-08-06 18:45:55 1015 2026-08-06 08:31:30 1006 2026-08-06 18:05:16 1001 2026-08-06 09:29:18 1022 2026-08-06 18:34:54 1038 2026-08-06 08:33:44 1004 2026-08-06 18:56:39 1001 2026-08-06 08:44:47 1030 2026-08-06 18:02:13 1027 2026-08-06 08:26:37 1010 2026-08-06 20:51:56 1004 2026-08-07 08:19:46 1036 2026-08-07 18:19:40 1013 2026-08-07 08:57:14 1028 2026-08-07 18:38:41 1024 2026-08-07 08:37:32 1032 2026-08-07 20:16:59 1035 2026-08-07 08:38:08 1027 2026-08-07 18:37:11 1038 2026-08-07 08:10:45 1002 2026-08-07 18:45:48 1006 2026-08-07 08:35:32 1007 2026-08-07 18:09:39 1012 2026-08-08 09:19:50 1021 2026-08-08 18:55:11 1039 2026-08-08 08:27:18 1024 2026-08-08 20:34:10 1039 2026-08-08 08:40:03 1025 2026-08-08 20:07:38 1003 2026-08-08 08:50:39 1023 2026-08-08 18:19:06 1001 2026-08-08 09:40:06 1019 2026-08-08 18:21:20 1005 2026-08-08 08:44:00 1018 2026-08-08 20:11:55 1039 2026-08-09 08:44:36 1034 2026-08-09 18:21:47 1006 2026-08-09 08:57:55 1012 2026-08-09 18:40:41 1016 2026-08-09 08:52:36 1036 2026-08-09 18:07:54 1017 2026-08-09 08:07:16 1031 2026-08-09 18:38:46 1011 2026-08-09 08:32:24 1001 2026-08-09 18:21:40 1038 2026-08-09 08:01:23 1001 2026-08-09 20:08:44 1007 2026-08-10 09:41:09 1026 2026-08-10 18:43:22 1025 2026-08-10 08:38:41 1030 2026-08-10 20:16:23 1011 2026-08-10 08:16:54 1036 2026-08-10 18:11:19 1037 2026-08-10 08:32:37 1021 2026-08-10 18:20:59 1002 2026-08-10 08:45:01 1038 2026-08-10 18:23:04 1025 2026-08-10 08:07:29 1006 2026-08-10 18:54:17 1007 2026-08-11 08:04:42 1024 2026-08-11 18:32:02 1020 2026-08-11 08:20:00 1030 2026-08-11 20:22:29 1014 2026-08-11 09:30:22 1030 2026-08-11 18:26:18 1038 2026-08-11 09:22:45 1023 2026-08-11 20:13:40 1013 2026-08-11 08:36:48 1010 2026-08-11 18:54:48 1028 2026-08-11 08:21:37 1018 2026-08-11 20:16:24 1030 2026-08-12 08:18:55 1032 2026-08-12 18:22:36 1026 2026-08-12 09:49:25 1012 2026-08-12 18:07:02 1013 2026-08-12 08:22:10 1005 2026-08-12 20:18:23 1016 2026-08-12 08:40:24 1010 2026-08-12 18:45:12
02 · The fix
Punches in. Payslips out.
One sample employee’s month, and the payslip the system builds from it. Change any day and watch the salary follow.
Sample employee · Rs 60,000 / month · shift 09:00–18:00
Late rule: 15+ min late costs 1 hour · overtime × 1.5
Day 4 · what the machine sent
1042 2026-08-04 09:27:12 → in
1042 2026-08-04 18:02:40 → outClick any day to make it late, overtime, absent or leave. The payslip redoes itself.
Payslip · AugustSample
- Base salary26 working days
- Rs 60,000
- Overtime9.3 h × Rs 256 × 1.5
- Rs 3,577
- Late deduction3 late × 1 h
- − Rs 769
- Unpaid days1 × Rs 2,308
- − Rs 2,308
- Advance installmentmonthly repayment
- − Rs 5,000
Net payRs 55,500
03 · How it works
Three steps, and machines that all speak differently.
Every punch lands in one place
Fingerprint machines send punches straight to the app. Staff away from a machine clock in from the phone app, but only inside their branch's geofence.
Punches become attendance
Each punch is matched to an employee by their device code. The first of the day is clock-in, the next is clock-out, extras are ignored. Shifts, late rules and leave are applied on top.
Payroll runs itself, HR approves
At month end the engine builds every payslip: overtime, late deductions, unpaid days, advances. HR reviews and approves; staff see their own slip in the app.
The hard part
Three ways in, one record out.
Newer ZKTeco machines call the server themselves and send plain text lines. Older ones never call: a small agent on site reads their log and remembers the last punch it saw, so nothing is counted twice. Phones send a location that has to be inside the branch’s radius.
All three are turned into the same shape: device code, date, time. Only then does the app decide whether it is a clock-in, a clock-out, or noise.
Newer machines push
POST /iclock/cdata?SN=…&table=ATTLOG
1042 2026-08-04 09:27:12Older machines get pulled
agent reads the device log
resumes after the last punch it sawPhones check distance
You are 450m from Head Office
(allowed radius: 200m)Becomes
code 1042 · 2026-08-04 · 09:27 → clock-in, 27 min late04 · What changed
Payroll became something HR approves, not something HR builds.
- People on payroll
- —
- Month-end payroll time
- —
- Salary disputes
- —
Metric to confirm · staff paid through it
Metric to confirm · days by hand → minutes
Metric to confirm · before vs after
