Přeskočit na hlavní obsah

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é).

Michala persona

Typický uživatel: CFO / účetní (Michala). Uzavírá výkazy ve dvou scénářích:

  1. Uzavírka přes vystavení faktury v MůjVýkaz (default workflow) — automatický flip statusu při vytvoření faktury.
  2. 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ě"

  1. Klikněte Uzavřít externě.
  2. Vyplňte External reference — povinné pole (např. "Fakturoid FV-2026-042" nebo "iDoklad #INV-123").
  3. Volitelný popisek/poznámka pro audit.
  4. 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 /timesheet výkazy dostanou amber badge "Externě: Fakturoid FV-2026-042".
Proč external reference povinný?

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:

  1. Otevřete /invoicing+ Nová faktura.
  2. Vyberte Z času + nákladů (Sprint 75 path) nebo Z klientského reportu (Sprint 92 path).
  3. Submit faktury → automatický flip time_entries.status='invoiced' + invoice_id FK.

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ě:

  1. Vyberte řádek (klient × období) v seznamu uzávěrek.
  2. Klik Uzavřít manuálně.
  3. 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:

BadgeTriggerPopis
Modrý "Faktura FV-XXX"invoice_createdUzavřeno přes vystavení faktury v MůjVýkazu
Amber "Externě: <ref>"manual_externalUzavřeno přes "Uzavřít externě" CTA
Cyan "Uzavřeno: <reason>"manual_ui_normalManuá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):

  1. Storno faktury automaticky vrátí time_entries.status='approved' + invoice_id=NULL.
  2. Manuální revert přes /uzavirky: klik na uzavřený řádek → Vrátit uzavírku (jen pro manual closure, ne pro invoice closure).
  3. 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.