Uzávěrky výkazů
Uzávěrka je proces, kdy schválené výkazy přejdou do stavu "fakturováno" — tedy uzavřené pro další úpravy. Po Sprint 75 byla uzávěrka centralizována do dedikovaného flow (separate od fakturace samotné).
Typický uživatel: CFO / účetní (Michala). Uzavírá výkazy ve dvou scénářích:
- Uzavírka přes vystavení faktury v MůjVýkaz (default workflow) — automatický flip statusu při vytvoření faktury.
- Uzavírka externě — pokud agentura fakturuje přes externí systém (Fakturoid, iDoklad), MůjVýkaz uzavře výkazy s referencí na externí číslo faktury.
Closure flow — od schválení k uzavírce
draft (zápis)
↓ submitEntries
submitted (čeká na schválení)
↓ approveEntries (manager)
approved (schváleno)
↓ markEntriesInvoiced (closure)
invoiced (uzavřeno) ← END STATE
Sprint 75 přidal markEntriesInvoiced a unmarkEntriesInvoiced jako primary closure mutations. Sprint 91 oddělil closure od reportu (před Sprint 91 bylo odeslání reportu auto-trigger closure — to už neplatí).
Off-mode (invoicingMode='off') — externí fakturace
Pokud vaše agentura fakturuje externě (Fakturoid, iDoklad, vlastní účetní systém), MůjVýkaz může pracovat v "off-mode" — modul Fakturace je vypnutý, ale /uzavirky zůstává funkční pro audit/closure.
V off-mode se na /uzavirky zobrazí pro každý (klient × období) aggregate primary CTA:
"Uzavřít externě"
- Klikněte Uzavřít externě.
- Vyplňte External reference — povinné pole (např. "Fakturoid FV-2026-042" nebo "iDoklad #INV-123").
- Volitelný popisek/poznámka pro audit.
- Klikněte Potvrdit uzavírku.
Co se stane:
- Schválené výkazy v období se flipnou na
status='invoiced'. - Audit log entry s
closure_context = {trigger:'manual_external', external_ref:'...', closed_by:userId}. - V
/timesheetvýkazy dostanou amber badge "Externě: Fakturoid FV-2026-042".
External ref umožňuje Michala v budoucnu dohledat: "Tento výkaz byl uzavřen kdy a kterou fakturou?" Bez ref by nešlo prokázat, že peníze opravdu přišly přes externí systém.
Module mode (invoicingMode='module') — vystavení faktury
Pokud máte modul Fakturace zapnutý (default), uzávěrka probíhá automaticky při vystavení faktury:
- Otevřete
/invoicing→ + Nová faktura. - Vyberte Z času + nákladů (Sprint 75 path) nebo Z klientského reportu (Sprint 92 path).
- Submit faktury → automatický flip
time_entries.status='invoiced'+invoice_idFK.
Audit log entry: closure_context = {trigger:'invoice_created', invoice_id, invoice_number}.
V /timesheet se výkaz označí modrým badge "Faktura FV-2026-XXX".
Manual closure (jemnější rozlišení)
Pokud je modul Fakturace zapnutý, ale potřebujete uzavřít výkazy bez vystavení faktury, klikněte na /uzavirky Uzavřít manuálně:
- Vyberte řádek
(klient × období)v seznamu uzávěrek. - Klik Uzavřít manuálně.
- Vyplňte volitelnou poznámku.
Audit log: closure_context = {trigger:'manual_ui_normal', closed_by:userId}. Badge: cyan "Uzavřeno: <reason>".
Provenance badges (Sprint 90.5)
Sprint 90.5 zavedl jednotné badges pro provenance v /timesheet + /uzavirky:
| Badge | Trigger | Popis |
|---|---|---|
| Modrý "Faktura FV-XXX" | invoice_created | Uzavřeno přes vystavení faktury v MůjVýkazu |
| Amber "Externě: <ref>" | manual_external | Uzavřeno přes "Uzavřít externě" CTA |
| Cyan "Uzavřeno: <reason>" | manual_ui_normal | Manuální uzavírka bez faktury |
Source of truth: audit_logs.changes.new.closure_context — žádná duplikace v separate column. Forensics pomocí SQL query nad audit_logs JSONB.
Reverzace closure (storno)
Pokud potřebujete vrátit výkaz zpět do stavu "approved" (např. po stornu faktury):
- Storno faktury automaticky vrátí
time_entries.status='approved'+invoice_id=NULL. - Manuální revert přes
/uzavirky: klik na uzavřený řádek → Vrátit uzavírku (jen pro manual closure, ne pro invoice closure). - Externě: pokud externí faktura byla stornována, manager+ může klik Vrátit uzavírku s důvodem.
Audit log obsahuje reverse entry s closure_context.trigger = 'unmark_manual' (Sprint 77 + Sprint 92.9 P1-1 reverse audit pattern).
Existing invoice detection (Sprint 92.8)
Sprint 92 zavedl invoice from client report workflow. Sprint 75 path (Z času + nákladů) a Sprint 92 path (Z reportu) coexist na /uzavirky:
- Pokud existuje sent report pro období → zobrazí se Sprint 92 CTA "Vystavit fakturu z reportu".
- Pokud existuje invoice s
created_from_report_id→ info badge "Faktura FV-XXX již existuje" + read-only (Sprint 92.8 fix proti duplikaci CTA buttons).
Manager scoping
Manager vidí jen (klient × období) řádky pro klienty z client_assignments. Sprint 77 closed Client Reports IDOR bug — manager bez assignmentu na klient nemůže fetchnout closure period nebo report (FORBIDDEN error).
Prague TZ aggregation (Sprint 78)
Closure aggregation používá AT TIME ZONE 'Europe/Prague' — výkaz zapsaný v Prague summer-time 1.10. CEST 02:00 je correctly aggregated do října, ne do září (jak by stalo s UTC aggregation).
Double-close protection (Sprint 78)
Strict eligibility check zabraňuje:
- Closure of already-closed entries (idempotent — žádný error, ale i žádné UPDATE).
- Closure se mixed-status batch (některé approved + některé invoiced) → vrátí warning.
- Concurrent close během jiné mutation → 409 Conflict toast s instrukcí refresh.
Přístup
/uzavirky modul je dostupný pro role: Owner / Admin / Accountant / Manager. HR + Worker nemají přístup.
V off-mode (invoicingMode='off') je /uzavirky jediný způsob, jak označit výkazy jako fakturované — proto je accessible i bez Sprint 92.5 P1-1 promotion z managerProcedure → accountantProcedure.