- 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>
Bug #1: completed shifts in today_shifts had no project names,
showing "undefined" in the UI. Now includes attendance_project_logs
relation and enriches with project names from projects table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Route handlers: add exhaustive return after error checks so TypeScript
narrows the union and result properties are accessible without casts
- attendance.service: use Prisma.attendanceGetPayload for included relations
- projects.service: remove unnecessary cast on orders relation
- Dashboard.tsx: replace Record<string,any> with proper DashData interface
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Created czech-holidays.ts with 11 fixed + 2 Easter-based holidays
- Fund now automatically excludes public holidays (no manual records needed)
- covered = worked + vacation + sick (NOT holidays — already in fund)
- Renamed "Odpracováno" to "Pokryto" (worked + leave = what counts)
- Removed dependency on holiday attendance records per employee
Matches PHP CzechHolidays::getMonthlyWorkFund() logic exactly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously: holidays reduced the fund (fund = bizDays - holidays).
This caused a mismatch — frontend compared covered against full
month fund, but backend used reduced fund.
Now: holidays count as covered hours (like vacation/sick). Fund stays
at full working days. So worked + vacation + sick + holidays = covered,
and covered >= fund means fulfilled.
Example: Jan has 22 days (176h), 1 holiday. Haas worked 168h.
Before: fund=168, covered=168, OK but frontend saw fund=176, not OK.
After: fund=176, covered=168+8=176, OK everywhere.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend: per-user missing/overtime in current month now calculated
against bizDaysToDate (working days up to today), not full month.
Frontend: monthly card percentage and fulfilled check also use
fund_to_date for current month.
Now both the yearly summary table and the monthly cards agree.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Monthly cards show full month fund (e.g., 168h for 21 days).
Yearly summary table uses fund_to_date (prorated to today for
current month) so the +/- column is accurate mid-month.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Past months use full month working days. Current month counts
working days only up to today (e.g., March 24 = 16 working days
out of 21), so the +/- column shows an accurate difference
instead of always showing a deficit mid-month.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Matches PHP: past year shows all 12, current year shows up to current
month, future year shows nothing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On logout, finds all refresh tokens matching the same user + IP +
user-agent (same browser session) and deletes them all. This cleans
up zombie tokens from previous logins and token rotations that
were showing as stale sessions on the dashboard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Deletes current token AND tokens replaced by it
- Cleans up all expired tokens on logout
- Prevents stale sessions from showing on dashboard after re-login
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
loadAuthData() didn't include totp_enabled or require_2fa in the
AuthData response. The frontend always saw undefined → false.
Now includes both fields from the database.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added billing_text column to invoices table (VARCHAR 500)
- Prisma migration: 20260323_add_billing_text
- Form field on invoice create page with placeholder
- PDF uses billing_text, falls back to default translation
- Stored on create and editable on draft invoices
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- mailer.ts: nodemailer transport via local sendmail
- leave-notification.ts: HTML email matching PHP template
- Sends notification to LEAVE_NOTIFY_EMAIL on new leave request
- Non-blocking: errors logged but don't fail the request
- Added LEAVE_NOTIFY_EMAIL and APP_URL env vars
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TypeScript port of PHP NasFileManager with symlink rejection,
path traversal protection, MIME validation via file-type, and
blocked extension checking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>