- 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>
Queried status "converted"/"expired" but actual DB values are
"ordered"/"invalidated". Updated label "Prošlé" → "Zneplatněné".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mobile browsers block window.open() after async operations. Changed all
file viewers to open a blank window synchronously in the click handler,
then set location.href after fetch completes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The useEffect checked prev.currency === "EUR" but initial default was
changed to "CZK", so the settings default was never applied.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
Switching months quickly on received invoices triggered rate limit
due to multiple API calls per navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend expected flat customer_name and responsible_user_name but API
returned nested customers/users objects.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Project detail API returned nested orders/quotations objects but frontend
expected flat order_number, order_status, quotation_number fields.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Daily cron (8:00 AM) checks created and received invoices
- Alerts 3 days before due date and on due date
- Summary email to INVOICE_ALERT_EMAIL with grouped tables
- Tracks sent alerts in invoice_alert_log to prevent duplicates
- node-cron scheduler runs inside the app process
- Favicon files copied from PHP project
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
Critical:
- InvoiceDetail: sanitize notes HTML with DOMPurify
- OrderDetail: use proper DOMPurify import instead of window fallback
Important:
- AttendanceBalances: add fund_to_date to interface, remove as-any casts
- All schemas: replace z.any() with z.preprocess for boolean fields
- Routes: simplify boolean coercion (Zod handles it now)
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>
Was showing only worked hours, but +/- was calculated against covered.
Now both columns are consistent — Odpracováno includes vacation, sick,
and holiday hours so the numbers make sense with the +/- column.
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>
Card header, per-user +/-, progress bar, and yearly table all now
use the same prorated fund_to_date for the current month.
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>
API returns scope_template_sections (Prisma relation name) but
frontend was reading sections. Now checks both field names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
RichEditor now supports readOnly prop — hides toolbar and disables
editing via ReactQuill's built-in readOnly. Content renders with
proper Quill CSS (list margins, indentation, fonts) instead of
broken browser defaults from dangerouslySetInnerHTML.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>