fix(odin): gate Uložit on invoices.create + correct invoice invalidation key

Final review caught two minor issues:
- saveInvoice invalidated ["received-invoices"], which matches no query (the
  received list/stats are keyed ["invoices","received",…]); use ["invoices"]
  so the list/stats actually refresh after an import.
- The Save button hit /received-invoices (requires invoices.create) while Odin
  is gated only on ai.use; disable Uložit (with a hint) when the user lacks
  invoices.create so the UI never offers a 403 action. Dormant today (ai.use is
  admin-only and admin bypasses checks) but correct going forward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-08 19:46:49 +02:00
parent b7ebf04001
commit ac7c220bb1
2 changed files with 16 additions and 2 deletions

View File

@@ -4,6 +4,7 @@ import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import apiFetch from "../../utils/api";
import { useAlert } from "../../context/AlertContext";
import { useAuth } from "../../context/AuthContext";
import {
aiUsageOptions,
aiConversationsOptions,
@@ -61,6 +62,10 @@ function summaryNote(reviews: ReviewInvoice[]): string {
export default function OdinChat() {
const alert = useAlert();
const qc = useQueryClient();
const { hasPermission } = useAuth();
// Saving an extracted invoice hits /received-invoices (needs invoices.create);
// Odin itself only needs ai.use, so the Save affordance is gated separately.
const canSave = hasPermission("invoices.create");
const { data: usage } = useQuery(aiUsageOptions());
const { data: convData, isPending: convPending } = useQuery(
aiConversationsOptions(),
@@ -304,7 +309,7 @@ export default function OdinChat() {
if (!res.ok) throw new Error(body?.error || "Uložení selhalo");
alert.success("Faktura uložena");
setReview((r) => r.filter((x) => x.uid !== inv.uid));
qc.invalidateQueries({ queryKey: ["received-invoices"] });
qc.invalidateQueries({ queryKey: ["invoices"] });
} catch (e) {
alert.error(e instanceof Error ? e.message : "Uložení selhalo");
} finally {
@@ -445,6 +450,7 @@ export default function OdinChat() {
key={inv.uid}
inv={inv}
busy={busy}
canSave={canSave}
onChange={patch}
onSave={saveInvoice}
onDiscard={(uid) =>