feat(identity): IDENT-009 QR-/Code-Neuvergabe mit Historie
POST /objekte/{id}/code-neu-vergeben und /objektpositionen/{id}/code-neu-
vergeben (admin-only) - z.B. bei beschädigtem/verlorenem Etikett. Alter
Code bleibt über die bestehende Audit-Infrastruktur (Historie-Tabelle,
Prompt 13) nachvollziehbar statt eine eigene Versions-Tabelle zu brauchen:
GET /historie?entitaet_typ=objekt&entitaet_id=... zeigt alter_wert/
neuer_wert des Codes. Duplikat-Prüfung explizit vorab (Projekt-Konvention,
siehe geraet_instanz.py::erstelle).
Frontend: "Code neu vergeben"-Aktion in der Objektakte (nur Admin), inline
statt window.prompt (Accessibility-Vorgabe aus dieser Session).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -17,5 +17,7 @@ export const EREIGNIS_LABEL: Record<string, string> = {
|
||||
mindermenge_genehmigt: "Mindermenge genehmigt",
|
||||
nachfuellung_erfasst: "Nachfüllung erfasst",
|
||||
objekt_geaendert: "Objekt geändert",
|
||||
objekt_code_neu_vergeben: "Objekt-Code neu vergeben",
|
||||
objektposition_code_neu_vergeben: "Geräte-Code neu vergeben",
|
||||
qualifikation_erfasst: "Qualifikation erfasst",
|
||||
};
|
||||
|
||||
@@ -25,10 +25,12 @@ const MANGEL_PRIORITAET_LABEL: Record<string, string> = {
|
||||
|
||||
export function AktePage() {
|
||||
const { objektId } = useParams<{ objektId: string }>();
|
||||
const { istVerantwortlich } = useAuth();
|
||||
const { istVerantwortlich, istAdmin } = useAuth();
|
||||
const [akte, setAkte] = useState<Akte | null>(null);
|
||||
const [fehler, setFehler] = useState<string | null>(null);
|
||||
const [laedt, setLaedt] = useState(true);
|
||||
const [codeNeuVergebenOffen, setCodeNeuVergebenOffen] = useState(false);
|
||||
const [neuerCode, setNeuerCode] = useState("");
|
||||
|
||||
function laden() {
|
||||
if (!objektId) return;
|
||||
@@ -50,6 +52,21 @@ export function AktePage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function codeNeuVergeben() {
|
||||
if (!objektId || !neuerCode.trim()) return;
|
||||
try {
|
||||
await apiRequest(`/objekte/${objektId}/code-neu-vergeben`, {
|
||||
method: "POST",
|
||||
body: { neuer_code: neuerCode.trim(), begruendung: "Etikett neu ausgestellt" },
|
||||
});
|
||||
setCodeNeuVergebenOffen(false);
|
||||
setNeuerCode("");
|
||||
laden();
|
||||
} catch {
|
||||
setFehler("Code konnte nicht neu vergeben werden (evtl. bereits vergeben).");
|
||||
}
|
||||
}
|
||||
|
||||
async function pruefungDurchgefuehrt(geraetId: string) {
|
||||
try {
|
||||
await apiRequest(`/geraete/${geraetId}`, {
|
||||
@@ -110,6 +127,41 @@ export function AktePage() {
|
||||
<div className="akte-stamm card">
|
||||
<h3 style={{ marginTop: 0 }}>Stammdaten</h3>
|
||||
<div className="stack">
|
||||
{istAdmin && (
|
||||
<div>
|
||||
{!codeNeuVergebenOffen ? (
|
||||
<button
|
||||
className="btn btn-secondary"
|
||||
onClick={() => {
|
||||
setNeuerCode(objekt.code);
|
||||
setCodeNeuVergebenOffen(true);
|
||||
}}
|
||||
title="Neuen Code vergeben, z.B. bei beschädigtem Etikett - der alte Code bleibt in der Historie sichtbar"
|
||||
>
|
||||
Code neu vergeben
|
||||
</button>
|
||||
) : (
|
||||
<div className="row" style={{ gap: "0.4rem", alignItems: "center", flexWrap: "wrap" }}>
|
||||
<label htmlFor="neuer-code" className="sr-only">
|
||||
Neuer Code
|
||||
</label>
|
||||
<input
|
||||
id="neuer-code"
|
||||
className="input"
|
||||
style={{ maxWidth: "12rem" }}
|
||||
value={neuerCode}
|
||||
onChange={(e) => setNeuerCode(e.target.value)}
|
||||
/>
|
||||
<button className="btn btn-primary" onClick={codeNeuVergeben}>
|
||||
Speichern
|
||||
</button>
|
||||
<button className="btn btn-secondary" onClick={() => setCodeNeuVergebenOffen(false)}>
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div>Standort: {akte.standort_name}</div>
|
||||
<div>
|
||||
Letzte Kontrolle:{" "}
|
||||
|
||||
Reference in New Issue
Block a user