- Add created_at to offer create/update schemas so user-selected date is stored instead of always defaulting to now()
- Translate payment method in invoice PDF based on language (Příkazem → Bank transfer, etc.)
- Add order_date and cnb_rate translation keys, replace hardcoded inline strings
- Remove duplicate has_order filter from offers backend route
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove exchange_rate and exchange_rate_date from quotations (schema, service, PDF, form)
- Add item_description field to invoice_items (schema, migration, service, form, PDF)
- Add offer status/customer/order filters with tab-based UI
- Clean up offer statuses to active/ordered/invalidated
- Allow invalidate action for non-ordered offers (not just expired)
- Add fade animations on offers and invoices table data changes
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 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>
- Remove ProjectCreate page, POST /projects endpoint, and next-number endpoint
- Projects can only be created through orders (shared numbering sequence)
- Remove dead CreateProjectSchema and createProject service function
- Delete 'order' row from number_sequences (unused; code uses 'shared')
- Smart sequence release: decrement last_number only when deleting the highest number
- Fix received-invoices stats referencing non-existent is_deleted and amount_czk columns
- Update deploy instructions in CLAUDE.md (npm install, prisma migrate deploy)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove ref-mirror useEffect in AuthContext (cachedUserRef already written at mutation sites)
- Replace useEffect slide direction in ReceivedInvoices with render-time computation
- Fix Login.tsx useEffect dependency array (mount-only alert should have [] deps)
- Move "project created" alert to navigation source in ProjectCreate, remove useEffect in ProjectDetail
- Move companySettings defaults into fetch callbacks in InvoiceDetail and OfferDetail
- Replace due_date useEffect with useMemo in InvoiceDetail
- Capture initial snapshots from API data instead of useEffect in InvoiceDetail, OfferDetail, OrderDetail
- Replace localStorage draft useEffect with lazy useState initializer in OfferDetail
- Fix attendance dropdown to filter by attendance.record permission only
- Fix clock-out 404 on update-address (remove departure_time filter for departure action)
- Fix received-invoices stats endpoint referencing non-existent is_deleted and amount_czk columns
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
$queryRaw on MySQL returns BigInt for integer columns and 0/1 for booleans.
Passing these raw values back to Prisma client methods causes validation errors:
- Expected Int, provided BigInt
- Expected Boolean, provided Int
Fixed in auth refresh, TOTP login, and TOTP backup code flows by wrapping
storedToken.id, storedToken.user_id with Number() and remember_me with Boolean().
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- feat: order confirmation PDF generation with VAT support
- feat: order confirmation modal with custom item editing
- fix: attendance negative duration clamping and switchProject timing
- fix: Quill editor locked to Tahoma 14px, PDF heading sizes
- fix: invoice/offer PDF font consistency (Tahoma enforcement)
- fix: invoice alert cron improvements
- fix: NAS financials manager edge cases
- refactor: numbering service with unique sequence constraints
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Due date uses days selector in edit mode (same as create)
- Overdue invoices fully editable (same as issued)
- Overdue status reversed to issued when due date moved to future
- Invoice list: edit icon for issued/overdue, eye for paid
- Invoice list: PDF opens blob from NAS (removed lang modal)
- NAS cleanup: properly scans directories when cleaning old PDFs
- Fixed syntax error from leftover else block
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Invoice edit mode now uses the same form as create mode (all fields editable)
- Bank account pre-selected by matching IBAN/account number
- Invoice number read-only in edit mode
- Paid invoices remain read-only
- NAS: old PDF deleted when invoice date changes to different month
- Buttons: Zobrazit fakturu, Uložit, Smazat + status transitions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Schema now accepts both string and number user_ids (frontend sends strings)
- Bulk fill now skips Czech public holidays in addition to weekends
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
- 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>
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>
- 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>