Commit Graph

18 Commits

Author SHA1 Message Date
BOHA
996012e3d3 feat(attendance): 'Chybějící hodiny' (manko) row — real hours below the fond
manko = max(0, fond − (worked_total + dovolená + nemoc + neplacené volno))
using REAL clock time, not the paušál. Supplement next to Přesčas — the
verified binding formulas are untouched.

Why: the paušál Odpracováno can nominally cover the fond while dozens of
real hours are missing (Dominik 6/2026: 21 days × 8 + 8h dovolená = 176 =
fond exactly, thanks to weekend shifts compensating 3 recordless weekdays —
yet real worked time was 136,5h). Přesčas = 0 correctly, and the 31,5h gap
vanished from every total. Now it's a permanent summary line, red+bold when
positive, plus a 'Chybí: Xh' chip on the admin screen cards.

+4 tests incl. the real June dataset as a pinned acceptance case (also
pins noc 6,75 — early shifts starting 05:30/05:45 legitimately count
night minutes before 06:00).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 00:12:20 +02:00
BOHA
e3b414d22c fix(attendance): payroll recap per accountant spec; multi-shift day rows; shared computation
Rekapitulace (accountant notes, confirmed by owner 2026-07):
- Odpracováno = all worked hours EXCEPT hours worked on holidays
  (was floor(worked/8)×8 — a 3h day printed as 0h)
- Vč. svátků a přesčasů = ALL worked hours; line2 − line1 = Svátek
  (was worked + vacation − 8h×worked-holidays — could print less than
  actually worked, and vacation inflated it)
- Přesčas = max(0, worked − fond), month-level, never negative
  (was the sub-8h remainder + vacation — printed 3h overtime for a
  3h month, 8h overtime for pure vacation, and −6h when a holiday
  was worked)
- Svátek = hours actually WORKED on holiday dates
  (was 8h per UNworked holiday — opposite meaning)
- So/Ne, Práce v noci, Dovolená, Fond unchanged (verified correct)

Single shared computeMzdaSummary now feeds BOTH the print and the
admin screen (they had drifted: capped vs uncapped holiday adjustment).

Print day table: one row PER RECORD — days with two shifts (real in
prod data) silently dropped all but the last record from the table
while the totals counted them all.

+7 spec tests (accountant's own examples); print snapshot updated to
the new spec values.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:00:17 +02:00
BOHA
1ee59b54bc chore(release): v2.4.35 — lint cleanup (68→0) + duplicate-Zavřít modal fix
UI fix:
- Close-only modals showing a redundant second close button now use
  hideCancel: ReceivedInvoices paid-detail modal (was two identical "Zavřít"
  buttons) and PlanCellModal "Den je součástí rozsahu".
- Add modal-duplicate-close test enforcing close-only modals set hideCancel.

Lint: cleared all 68 warnings → 0.
- preserve-caught-error: attach { cause } in ai.service / exchange-rates.
- no-require-imports: package.json version read via fs (APP_VERSION) instead
  of require(), avoiding a rootDir-expanding static JSON import.
- react-hooks/exhaustive-deps (11): ref-in-cleanup copies, derived-value
  useMemo wrapping, PlanGrid field extraction, stable nextKey useCallback,
  AuthContext documented cycle-break.
- no-explicit-any (53): precise route param/Prisma types, generic enrich*()
  preserving payload shape, minimal vite module type, frontend body/query-key
  types, SystemInfo for Settings.

Refactor (test enablement): shift-form types moved to dependency-free
shiftFormTypes.ts so the print-HTML builders are unit-testable without the
component graph; characterization test pins their output.

Gates: 649 tests pass, tsc -b clean, lint 0. Verified touched flows live
via Playwright (PlanWork CRUD + optimistic cache, warehouse form keys,
Settings system info, invoice detail).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 18:35:31 +02:00
BOHA
907ee574e6 chore(lint): remove all 34 dead-code warnings (verified site by site)
unused-vars (25): leftover imports/types across pages, routes and services;
write-only sickHours accumulator; unused holidayCount pair; test helpers
authPatch/authDelete; TOut dropped from ApiMutationOptions (the hook keeps
its own generic — no call-site changes); requireAuth no longer importable
in customers/warehouse routes (matches the no-bare-auth-reads convention).

useless-assignment (5): initializers proven overwritten on every path
(systemQty x2, hours, writtenSize -> let x: number) and the seed's dead
adminUser reassignment (create kept, assignment dropped).

useless-escape (4): [eE+\-] -> [eE+-] and [\/...] -> [/...] — identical
semantics.

Behavior-neutral; lint 102 -> 68 warnings (0 errors), suite 641 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 00:13:52 +02:00
BOHA
c2746d78c9 fix(dashboard): stale punch button/KPIs after attendance changes on other pages
Verified flow: clock in on dashboard -> delete the record in /attendance/admin
-> navigate back: 'Zaznamenat odchod', the Pritomni KPI and the presence card
still showed the pre-delete state until F5. Root cause: every attendance
mutation outside the dashboard invalidated only ["attendance"], never
["dashboard"], so within the dashboard query's 60s staleTime the remount was
served from cache.

- useAttendanceAdmin create/bulk/edit/delete, /attendance punch +
  leave-request, AttendanceCreate, LeaveApproval approve/reject now also
  invalidate ["dashboard"] (CLAUDE.md rule: mutations invalidate every domain
  that embeds their data)
- systemic backstop: dashboardOptions uses refetchOnMount "always" - the
  dashboard aggregates attendance/offers/invoices/orders/projects/leave, and
  domain pages can't all be expected to invalidate it; returning to the
  dashboard must never show pre-mutation data

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 11:40:06 +02:00
BOHA
b3e2abf9b2 fix(attendance): admin month view no longer truncates at 100 records
The KPI cards and on-screen summary totals (computeUserTotals) are computed
client-side from one month fetch. The hook requested limit=1000 but
parsePagination silently clamped it to 100, so once a month exceeded 100
attendance rows (~5 employees x 21 shifts) the newest-first list dropped the
EARLIEST days of the month — screen totals undercounted while the print path
(complete server fetch) stayed correct. This is the data-volume cliff that
made the summary counting look like it changed without any formula change:
git archaeology confirms every counting formula is bit-identical from v1.9.1
through HEAD.

- routes/admin/attendance.ts: list endpoint passes maxLimit 2000 to
  parsePagination (complete-month view; 2000 ~ 64 employees x 31 days)
- useAttendanceAdmin: month fetch requests limit=2000 with the constraint
  documented
- regression test: a 120-row month returns all 120 records

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 10:20:40 +02:00
BOHA
1826fc7976 fix: audit fix pass #1 — all 19 verified HIGH findings + critical dep cleanup
Fixes every CRITICAL/HIGH finding from the 2026-06-09 full-codebase audit
(REVIEW_FINDINGS.md); each fix went through independent spec + code-quality
review. Plan and per-task log: docs/superpowers/plans/2026-06-10-audit-high-fix-pass.md

- attendance: schemas accept the combined local datetimes the forms/service
  use (new dateTimeString helpers in schemas/common.ts), breaks persist on
  create, AttendanceCreate submit rebuilt — every submit 400'd since 519edce
- 2fa: backup codes wired to /totp/backup-verify (+ remember-me parity),
  enrollment QR generated locally via qrcode (CSP-blocked external service
  also leaked the secret), dashboard shows per-user enrollment, not policy
- invoices/orders: per-line VAT survives re-saves (numberOr 0-respecting
  coercion in formatters.ts), billing_text persists on update, issued-order
  status transitions update UI gates
- trips: real pagination on all 3 pages, GET /trips/stats server aggregate
  (shared buildTripsWhere + legacy distance coalesce), vehicle_id applies on
  PUT with both-vehicle odometer recompute, print rebuilt (sync window.open,
  escaped template, server totals)
- orders api: attachment_data PDF blob excluded from all non-binary reads
- warehouse: unit field is a Select over UnitEnum, receipt attachments
  downloadable via new authenticated GET route
- downloads: shared RFC 5987 contentDisposition helper — Czech filenames no
  longer 500 (warehouse, received-invoices, orders endpoints)
- misc: block-env hook actually blocks (exit 2 + stderr), project create
  works with empty dates, NaN filter guards on trips endpoints
- deps: remove unused concurrently (clears both critical advisories), pin
  @hono/node-server >=1.19.13 via overrides (clears the 3 moderates without
  the Prisma 6 downgrade), drop deprecated @types stubs

Gates: tsc -b clean - vitest 30 files / 342 tests (31 new) - eslint 0 errors
- build OK

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 09:59:47 +02:00
BOHA
5233db2002 v1.8.0: warehouse module (16 commits), docházka mzda model, leave_type=holiday removal, api.ts race fix
Highlights:
- Warehouse module: receipts, issues, reservations, inventory, reports, dashboard,
  master data (categories, suppliers, locations), FIFO service, integration tests
- Docházka: mzda PDF counting model (Odpracováno / Vč. svátků / Přesčas / Svátek /
  So/Ne / Noc) with Czech weekday names and decimal hours
- AttendanceAdmin/AttendanceHistory KPI cards unified to mzda formula with
  fund bar colored by delta, badges for Práce/Dov/Nem/Sv/Nep
- Remove leave_type=holiday entirely (auto-computed from Czech public holidays)
- Allow multiple work shifts per day (overlap detection only)
- Pre-flight refresh in api.ts eliminates spurious 401s on fresh page loads
- Prisma: company_settings gets 6 nullable columns for warehouse numbering
  (PRI/VYD/INV prefixes, default patterns); migration seeds defaults
2026-06-03 23:13:10 +02:00
BOHA
cd6d3daf43 v1.6.5: fix attendance unique constraint, schema sync, seed script
- Change attendance idx_attendance_user_date from unique to index (allow multiple shifts per day)
- Reset migrations to single baseline init migration
- Add seed script with admin user (admin/admin)
- Update CLAUDE.md with migration workflow documentation
- Various frontend fixes (queries, pages, hooks)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 16:21:34 +02:00
BOHA
aa6c1b5094 refactor: fix all Low findings from FLAWS_REPORT audit
- Auth: TOTP params from config, JWT error logging, audit log failure
  logging, replaced_by_hash validation on token rotation
- Invoices: remove dead VAT code, consistent PDF permissions,
  WebP magic-byte detection, deduped exchange-rate fetches
- Orders/Offers: multipart limit from config, use paginated() helper,
  payment method from DB in PDF
- Projects: verify project exists before creating note
- Attendance: action_type enum validation, consistent local-time
  shift_date construction, holiday attendance in work fund,
  trips.view permission on last-km query
- Users: paginated() helper usage, remove duplicate dashboard keys,
  parallel currency conversion, single hashToken implementation
- Frontend: memoized customInput, reliable print onload, modal prop
  standardization (isOpen), ConfirmModal type icons, id===0 key
  fallback, Login useCallback, CompanySettings ConfirmModal,
  Attendance timeout cleanup, Dashboard memoization, beforeunload
  dirty-state warnings on Invoice/Offer/Order detail
- Schema: invoice_alert_log timestamp, config/env comment on
  Date.prototype.toJSON override
- Utils: exchange-rate inflight dedup

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 08:45:37 +02:00
BOHA
528e55991b security: fix all Critical and High findings from FLAWS_REPORT audit
- Auth: pessimistic locking on login tokens and refresh token rotation,
  backup code attempt counter, rate limiting verification
- Schema: unique constraints on business numbers, FK relations,
  unsigned/signed alignment, attendance duplicate prevention
- Invoices/PDFs: DOMPurify sanitization, bounded queries in stats
  and alerts, VAT rounding, Puppeteer error handling
- Orders/Offers: transactional parent+child creation, Zod NaN
  refinement, status enums, uniqueness checks
- Projects/Files: path traversal protection, streamed uploads,
  permission guards, query param validation
- Attendance/HR: duplicate checks, ownership validation, GPS
  restrictions, trip distance validation
- Frontend: modal lock reference counting, XSS escaping in print
  HTML, ref mutation fixes, accessibility attributes

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 00:58:35 +02:00
BOHA
af1b41994c fix: attendance shows only users with attendance.record permission
- Filter attendance admin/balances/workfund to users with attendance.record
  permission or admin role
- New attendance_users API action for user dropdown
- Fix missing prisma import in attendance route
- Fix user edit: empty password no longer blocks save (preprocess to undefined)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:32:22 +01:00
BOHA
6b31b2f74b feat: system settings, dynamic logos, template numbering, permission consolidation
- System settings page with tabs: Security, System, Firma
- Configurable attendance rules (break thresholds, rounding) from DB
- Configurable document numbering with template patterns ({YYYY}/{PREFIX}/{NNN})
- Dynamic logo upload (light/dark variants) served from DB instead of static files
- Email settings (SMTP from/name, alert/leave emails) configurable in UI
- Currency and VAT rate lists configurable, used across all modules
- Permissions simplified: offers.settings + settings.roles + settings.security → settings.manage
- Leaflet bundled locally, removed unpkg.com from CSP
- Silent catch blocks fixed with proper logging
- console.log replaced with app.log.info in server.ts
- Schema renamed: company-settings.schema → settings.schema
- App info section: version, Node.js, uptime, memory, DB status, NAS status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 10:15:47 +01:00
BOHA
baceb88347 feat: NAS storage for invoices/offers, code cleanup, date/time fixes
- NAS storage for created invoices (PDF via puppeteer), received invoices,
  and offers with auto-save on create/edit
- Deterministic file paths derived from DB fields (no file_path column needed)
- Separate NAS mount points: NAS_FINANCIALS_PATH, NAS_OFFERS_PATH
- Invoice language field (cs/en) stored per invoice, replaces lang modal
- Invoices list filtered by month/year matching KPI card selection
- Centralized date helpers (src/utils/date.ts) replacing all .toISOString()
  calls that returned UTC instead of local time
- Attendance project switching uses exact time (not rounded)
- Comment cleanup: removed ~100 unnecessary/Czech comments
- Removed as-any casts in orders and attendance
- Prisma migrations: add invoice language, drop received_invoices BLOB columns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 10:36:39 +01:00
BOHA
3c167cf5c4 style: run prettier on entire codebase 2026-03-24 19:59:14 +01:00
BOHA
373ea82279 fix: rewrite attendance print to match PHP design 1:1
- Dark table headers (#333), proper column widths, uppercase labels
- User header bar with gray background and total hours
- Records from userData.records (not filtered from global records)
- Fund row with covered/total and status badge
- Leave summary with vacation remaining
- Print wrapper table for repeating header
- Matching CSS: borders, fonts, spacing, badges

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:39:31 +01:00
BOHA
8c1fd07293 feat: implement attendance admin print functionality
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:33:43 +01:00
BOHA
4608494a3f initial commit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 08:46:51 +01:00