Commit Graph

18 Commits

Author SHA1 Message Date
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
94030230e7 fix(attendance): timezone-proof print dates; overnight-only time prefixes
Two bugs in the attendance admin print (Tisk on Správa docházky):

1. Viewer-timezone date shift: day rows are built from date-only strings
   ('2026-06-01'), which new Date() parses as UTC midnight per spec; local
   formatting then rendered the PREVIOUS day for any viewer west of UTC —
   a June print opened with '31. 5. Neděle' (a May row holding June 1's
   punches) and every weekday name + weekend tint shifted by one. Czech
   viewers were unaffected (UTC+1/+2), US viewers always hit it.
   formatDate / getCzechWeekday / isWeekendDate now parse the calendar day
   from the string parts — identical output in every timezone.

2. Every time cell carried a bogus date prefix ('1.6. 08:00'): the
   overnight-only guard compared the extracted date part against the raw
   wire shift_date ('2026-06-01T02:00:00') — never equal. Now compares
   normalized date parts; the prefix fires only for true overnight punches.

Verified end-to-end against the production June payload; +8 regression
tests (timezone-proof by construction — parts-based, no UTC parse).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:26:23 +02:00
BOHA
4bb7de7247 feat(app): detect new deploy and prompt refresh (+ lazy-chunk recovery)
When a new version is deployed, open tabs no longer silently run stale code
until a manual F5.

- Server stamps every response with X-App-Version (src/middleware/version.ts,
  onSend hook in server.ts).
- Client bakes its build version via Vite define (__APP_VERSION__); apiFetch
  compares the header to it (no polling — piggybacks existing traffic) and
  latches a mismatch in a small store (appVersion.ts).
- UpdateBanner: a persistent bottom Snackbar "Je k dispozici nová verze
  aplikace. — Obnovit" → location.reload(). User-initiated, so an in-progress
  form is never interrupted.
- lazyWithReload: every React.lazy page reloads once if its hashed chunk 404s
  after a deploy (sessionStorage-guarded against loops), fixing the "old chunk
  gone" error screen.

Approach chosen after researching current SPA practice (version header + banner
beats polling / a service worker for a non-PWA admin app).

Tests: +4 (server header, client store latch/notify). Full suite 669 pass,
tsc -b clean, lint 0. Verified live via Playwright (header emitted; banner
appears on a simulated mismatch). Detection applies to deploys from the NEXT
release onward (current clients lack the detection code).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 21:19:46 +02:00
BOHA
7e4469b29a fix(richtext): blank lines survive into PDFs and read-only previews
Quill 2's semantic HTML saves a blank line as a truly empty <p></p> (no
<br> placeholder) — verified in the DB (invoice_sections hold <p></p>,
zero <p><br>) — which collapses to nothing outside the editor.
cleanQuillHtml (PDF path) and the new restoreQuillBlankLines (order/
invoice read-only previews) restore the placeholder at render time, so
existing stored content is covered retroactively.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 08:35:46 +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
5462fcf944 feat(orders): PDF footer = issuer name+email, 'Zobrazit objednávku' button, per-currency list totals
- Issued-order PDF: drop the Vystavil/Schválil signature row; footer now shows
  'Vystavil: <name>' + 'E-mail: <email>' from the logged-in user (authData).
- IssuedOrderDetail PDF button 'Export PDF' -> 'Zobrazit objednávku' (already
  opens inline like invoices' 'Zobrazit fakturu').
- New /orders/stats + /issued-orders/stats endpoints sum order total (incl VAT)
  per currency across the active filter; rendered as 'Celkem: …' beneath both
  the received and issued lists. formatMultiCurrency lifted to a shared util.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:50:05 +02:00
BOHA
519edce373 fix: 2026-06-09 full-codebase audit hardening
Validation: shared NaN-guarded Zod coercion helpers in schemas/common.ts replace
the raw number|string transform idiom across every schema (the root-cause NaN bug
class); emailOrEmpty + lenient isoDateString/timeString.

Security: roles privilege-escalation closed; refresh-token family revocation on
reuse; TOTP uses config params; read endpoints permission-guarded; received-invoices
gross VAT on all paths; orders-pdf custom-items authz.

Concurrency: $queryRaw SELECT...FOR UPDATE locks in ascending-id order (warehouse
confirm/cancel, attendance lockUserRow); uniqueness checks moved into create
transactions (TOCTOU -> 409); deterministic id tiebreak on second-precision
timestamp ordering (plan resolveCell/resolveGrid, warehouse FIFO).

Frontend: Rules-of-Hooks fixed across ~14 pages + PlanCellModal; UTC-date persisted
fields; dashboard invalidation gaps; stale-closure confirm bugs.

Tooling/tests: ESLint flat config (react-hooks/rules-of-hooks = error) + Prettier;
tsconfig.test.json so tsc -b type-checks the tests; removed 3 dead deps; npm audit
fix (8 -> 3). Suite 195 -> 247 (happy-path auth, FIFO oldest-first, flakiness fixes),
isolated on app_test via .env.test with a hard-throw setup guard.

Gates: tsc 0 | build 0 | vitest 247/247 | eslint 0 errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 06:45:26 +02:00
BOHA
8bed920de1 feat(dashboard): show every active employee in "Docházka dnes"
The attendance card listed only employees with a record today (present /
break / clocked-out / on-leave). Now every active employee appears;
those with no record today show as "absent" (Nepřítomen), so the card
reflects the whole team. Loads active users (was a bare count) and
appends the unrecorded ones.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 13:00:09 +02:00
BOHA
1f05ef6a55 fix(dates): use local 'today' for date-input defaults instead of the UTC date
11 form defaults across 6 files used new Date().toISOString().split('T')[0] (the UTC date), which lands on yesterday during the post-midnight Prague window (UTC+1/+2). Added todayLocalStr() (local YYYY-MM-DD) in formatters and replaced them. Future-date expressions (e.g. invoice due_date +14d) left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 02:38:58 +02:00
BOHA
522afefc80 refactor(frontend): fix undefined --danger-color, dedup formatDate & require2FAOptions, drop double plan.css import
- base.css: var(--danger-color) (undefined, no fallback -> inherited color) -> var(--danger)
- attendanceHelpers: re-export formatDate from formatters instead of a byte-identical copy
- dashboard.ts: remove dead duplicate require2FAOptions (canonical copy is in settings.ts)
- settings.ts: remove unused @deprecated systemSettingsOptions alias
- PlanWork: drop redundant per-page plan.css import (loaded globally in AdminApp)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 02:22:51 +02:00
BOHA
c5f986adc1 fix(audit): correct + unify entity_type label map to match server values
Audit Log + dashboard feed used legacy keys (invoices_invoice, offers_quotation, trips, vehicles) that never matched the server's entity_type (invoice, quotation, trip, vehicle, ...) and omitted all warehouse_* entities, so many audit rows showed raw English. Single source of truth in lib/entityTypeLabels.ts, keyed to the actual EntityType values, with Czech labels for every type the server writes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 02:19:10 +02:00
BOHA
aef4ab92ec feat(plan): label plan_category audit rows in Czech
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 01:00:30 +02:00
BOHA
4a9c283789 fix(plan): readable audit descriptions, project labels, dashboard invalidation, view-only & sticky-column UI
Work-plan fixes from this session:
- Czech audit descriptions for plan entries/overrides (was empty '-')
- server-embedded project label on grid cells (was '-' past the 100-project cap)
- dashboard activity + audit-log cache invalidation on plan mutations
- read-only cells: no '+' on empty, keep hover on cells with a record
- view modal: Czech category + formatted dates
- sticky date column made opaque (no bleed-through on horizontal scroll)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 00:24:33 +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
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
3c167cf5c4 style: run prettier on entire codebase 2026-03-24 19:59:14 +01:00
BOHA
0b4b6b24e1 fix: attendance times showing +1 hour due to UTC timezone conversion
Times in the database are stored as local time (CET). JavaScript's
Date constructor treated them as UTC, then toLocaleTimeString added
+1 hour for CET timezone.

Fix: extract hours/minutes directly from the datetime string via regex
instead of going through Date object. No timezone conversion applied.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 13:50:30 +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