# PB Service — project notes for Claude Code

**PB Service** is a fingerprint **employee attendance** desktop app for the client
*خزمەتگوزاری پشتیوان بڵباس (PB Service)* — a car-service shop. Kurdish Sorani ONLY, RTL,
**Rudaw font** (public/fonts), premium light design (Finexy-inspired: cream background,
white cards, PB orange #f26522 + black). Brand logo: public/logo.jpg.

Stack copied from Rasty Service: **Electron + React + TypeScript + Vite**, JSON storage in a
portable **"PB Data"** folder (`C:\PB Data` on Windows, `~/PB Data` on Mac dev), atomic writes,
optional Google-Drive mirror folder, silent printing + save-PDF, hardware machine-id (for the
Zagros licensing system later — NOT wired yet, no LicenseGate in v1).

Target machine: client's Chinese all-in-one PC (N5095, 8GB) running Windows, 16h/day, this app
only. The exe is built **on Yusf's Windows laptop** with `npm run dist` (same recipe as Rasty).

## Dev commands
- `npm run dev` — Vite (port **5175**) + Electron live.
- `npm run dist` — Windows .exe via electron-builder (run on Windows).
- `npx tsc --noEmit -p tsconfig.json` — type-check the renderer.

## v1 scope (built 2026-08-29)
- **داشبۆرد** — live board: stat cards (in shop now / came / late / absent), employee cards with
  manual check-in/out buttons (until the fingerprint device arrives), weekly on-time-vs-late bar
  chart, today's activity feed.
- **کارمەندان** — employee CRUD, per-employee optional shift times, active/inactive.
  Modal uses Yusf's «a side» layout (fields one side, stacked buttons the other).
- **ڕاپۆرتەکان** — monthly per-employee: present days, lates, absences, total hours;
  silent print + save PDF (Desktop).
- **ڕێکخستنەکان** — shop hours, late-grace minutes, weekend days, printer, device IP/port
  (placeholder), data folder + mirror, machine ID display.

## Data model (collections in PB Data)
- `employees` — {id, name, phone, role, startTime?, endTime?, active, createdAt}
- `attendance` — {id, empId, date YYYY-MM-DD, in HH:MM, out? HH:MM, source 'manual'|'device'}
  One record per employee per day (reopen clears `out`).
- `settings` — {shopName, startTime, endTime, lateAfterMin, weekend[getDay numbers, 5=Friday],
  deviceIp, devicePort, printer}

## Punch windows & money rules (2026-08-30 — REPLACE the older multi-punch rules)
All in `src/renderer/lib.ts` (constants REST_FROM 11:00, REST_TO 15:00, REST_MINUTES 60,
OVERTIME_CAP_MIN 10; logic in `applyPunch`, aggregated by `dayMoney`; scenario-tested).
- Scan 1 = هاتن; late after shift start cuts per minute (frozen, unchanged).
- Scan between **11:00–15:00** = rest out; the rest hour is 60 min: return early →
  saved minutes PAID (`restExtra`), return late → exceeded minutes CUT (`restCut`) —
  frozen at the return punch, minute price = hourlyRate/60. Rest-out punched OUTSIDE
  the window earns/cuts nothing.
- Scan after **15:00** as 2nd punch = final leave; 4th punch = final leave. Final leave
  past the shift end pays overtime, **capped at 10 min** (`overtime`, frozen).
- Day complete = 4 punches, or 2 punches with the 2nd ≥ 15:00 → further scans refused
  (kiosk shows «ڕۆژەکەت تەواوە», dashboard shows «ڕۆژەکە تەواوە» badge instead of buttons).
- **Friday = NO rest hour** (fixed 2026-08-30): on Fridays the 2nd scan is ALWAYS the
  final leave (any time), day completes at 2 punches, rest money never settles, overtime
  past the Friday end applies (capped 10). `isFridayKey` in lib; punchActionLabel/punchCols
  take a `friday` param defaulting to "today is Friday".
- No-rest bonus KEPT: finished non-Friday 2-punch day (2nd ≥ 15:00) → +1 hour
  (`noRestBonusIQD`, computed live). Extra column renamed «کاتی زیادە» in مووچە/Reports.
- **Per-employee rest days**: Settings row «پشووی کارمەندان» → modal: pick employee +
  one day or from→to range (`settings.leaves`, `EmployeeLeave`). Those days never count
  absent for HIM (`isOnLeave` in lib): Reports skips them, Dashboard shows «پشوو» badge
  and excludes him from the absent stat. Used for the monthly rest day / sickness.

## Face recognition (built 2026-08-30)
- **InsightFace on the CPU, fully offline**: `models/det_500m.onnx` (SCRFD detector) +
  `models/w600k_mbf.onnx` (ArcFace/MobileFaceNet recognizer) run via `onnxruntime-node`
  in the MAIN process (`src/main/face.ts`, decode math in `src/main/scrfd.ts` — pure,
  tested by `node scripts/test-face.cjs <photo.jpg>`). Renderer half in
  `src/renderer/face.ts`: frame→tensor, 5-point alignment (least-squares similarity),
  cosine matching, threshold 0.42. Packaged via extraResources + asarUnpack (win x64 only).
- **Enrollment**: employee modal has fingerprint slots (up to 4, Touch-ID-style
  `FingerEnroll.tsx` — UI only until a scanner SDK exists) + one face
  (`FaceEnroll.tsx`: 4 averaged samples + head photo). `Employee.fingers[]`, `Employee.face`.
- **Avatars everywhere are the enrolled face photo** (`Avatar.tsx`; gray when outside),
  icon fallback when no face.
- **Kiosk**: manual punch buttons REMOVED from the bottom table. Punching is by camera:
  waking the screensaver with a touch opens `FaceScan.tsx` (also a floating ScanFace
  button) → recognize → auto `withPunch` (labels from punch count), 60s per-employee
  cooldown, 12s give-up. Camera only runs while the scan overlay is open (cool-PC rule).
  Admin Dashboard keeps manual buttons as the fallback.
- ⚠️ InsightFace model files are research-license (not for commercial sale) — Yusf chose
  them knowingly (2026-08-30).

## Fingerprint scanner (built 2026-08-30) ✅
- **Windows Biometric Framework** — works with the shop's ChipSailing/CS9711 USB reader
  (VID_2541, preferred over any built-in laptop sensor) and any other WBF scanner.
- `fingerbridge/` — C# helper exe (compiled by `fingerbridge/build.ps1` with the in-box
  .NET Framework csc, C# 5 syntax only). Speaks line-JSON on stdout, word commands on stdin
  (status/enroll/identify/stop/delete/quit). Packaged via extraResources.
- `fingerbridge.exe --setup` (elevated, ONE TIME PER PC): registers PB Service's private
  WinBio database (GUID 7B8E4C21-9A3D-4F5B-8C6E-2D1A0F9B3E57) by cloning the sensor's
  system db format under WbioSrvc\Databases, clones sensor config 0 → next slot with
  SystemSensor=0 + our DatabaseId, restarts WbioSrvc. Re-running is harmless.
  UI: Settings row «ئامێری پەنجەمۆر» (status + setup button) and FingerEnroll offers it too.
- `src/main/finger.ts` keeps the bridge alive (auto-restart) and relays events on
  channel 'finger-event'. Enrollment: FingerEnroll.tsx drives the real scanner
  (ripple per good press, shake on bad, engine decides press count); template GUID
  stored as `EmployeeFinger.wbId` (+`subFactor`). Kiosk runs an identify loop the
  whole time it's mounted — finger touch = auto punch (same cooldown/day-complete
  rules as face), restarts itself if the listener dies. Removing a finger chip in
  Employees deletes the Windows template on save. Mac/browser keep the old
  click-simulation.
- ⚠️ Client PCs need **VC++ 2015-2022 x64 redistributable** (onnxruntime needs it —
  face features crash without it) + one elevated `--setup` run. Both should go into
  the installer later.
- Camera: `openCamera()` auto-prefers an external USB webcam (Logitech C920 family
  first) over an integrated one.

## License gate (built 2026-08-30) ✅
- Same Zagros Supabase licensing as Rasty: `src/renderer/license.ts` (anon key, slug
  **`pb-service`**, product row ADDED to Supabase, trial_days=0 = key required) +
  `LicenseGate.tsx` copied from Rasty wrapping `<App/>` in main.tsx (3-day offline grace,
  60s block polling). Browser dev page (no window.electron) skips the gate.
- Settings row «مۆڵەتی پرۆگرام»: live status + PC ID + enter/activate a new key.
- Keys are generated in Yusf's Zagros Dashboard (~/Desktop/Zagros Future/Zagros Dashboard).
- E2E verified against Supabase: pending → key → active (test rows deleted after).

## TODO next versions
- **ZKTeco device sync** (client will buy a ZKTeco attendance device, e.g. K40): poll the device
  over TCP (port 4370, zklib-style protocol) from the Electron main process, import punches as
  `source:'device'` records, map device user-id ↔ employee. Settings page already stores IP/port.
- Installer: bundle vc_redist.x64.exe install + run `fingerbridge.exe --setup` so the client
  never sees the manual setup step.
- Salary/deduction features — sold separately as an upgrade.
