feat(dokumente): DOC-004 Original-vs-Kopie-Kennzeichnung
Neues Feld ist_original (Boolean, Migration 0029) - explizit beim Upload und beim Ersetzen (DOC-003) setzbar, kein Default in eine Richtung erraten. Wird beim Ersetzen bewusst NICHT vom Vorgänger übernommen (neue Version könnte ein nachgereichtes Original sein, das eine bisherige Kopie ersetzt, oder umgekehrt). Frontend: Checkbox "Original (nicht nur Kopie/Scan)" im Upload-Formular, "Original"-Badge in der Dokumentenliste. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -69,6 +69,7 @@ export async function ladeDokumentHoch(felder: {
|
||||
entitaet_id: string;
|
||||
dokumenttyp: string;
|
||||
beschreibung?: string;
|
||||
istOriginal: boolean;
|
||||
datei: File;
|
||||
}): Promise<unknown> {
|
||||
const formData = new FormData();
|
||||
@@ -76,6 +77,7 @@ export async function ladeDokumentHoch(felder: {
|
||||
formData.append("entitaet_id", felder.entitaet_id);
|
||||
formData.append("dokumenttyp", felder.dokumenttyp);
|
||||
if (felder.beschreibung) formData.append("beschreibung", felder.beschreibung);
|
||||
formData.append("ist_original", String(felder.istOriginal));
|
||||
formData.append("datei", felder.datei);
|
||||
|
||||
const headers: Record<string, string> = {};
|
||||
@@ -93,10 +95,12 @@ export async function ladeDokumentHoch(felder: {
|
||||
export async function ladeDokumentErsetzen(felder: {
|
||||
dokumentId: string;
|
||||
beschreibung?: string;
|
||||
istOriginal: boolean;
|
||||
datei: File;
|
||||
}): Promise<unknown> {
|
||||
const formData = new FormData();
|
||||
if (felder.beschreibung) formData.append("beschreibung", felder.beschreibung);
|
||||
formData.append("ist_original", String(felder.istOriginal));
|
||||
formData.append("datei", felder.datei);
|
||||
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
@@ -145,6 +145,7 @@ export interface Dokument {
|
||||
hochgeladen_von: number;
|
||||
hochgeladen_am: string;
|
||||
vorgaenger_id: string | null;
|
||||
ist_original: boolean;
|
||||
}
|
||||
|
||||
export interface Lagerbewegung {
|
||||
|
||||
@@ -48,6 +48,7 @@ export function DokumentePanel({ entitaetTyp, entitaetId, onFehler }: Props) {
|
||||
const [dokumente, setDokumente] = useState<Dokument[]>([]);
|
||||
const [laedt, setLaedt] = useState(true);
|
||||
const [beschreibung, setBeschreibung] = useState("");
|
||||
const [istOriginal, setIstOriginal] = useState(false);
|
||||
const [dokumenttyp, setDokumenttyp] = useState<DokumentTyp>("sonstiges");
|
||||
const [filterTyp, setFilterTyp] = useState<DokumentTyp | "">("");
|
||||
const [wirdHochgeladen, setWirdHochgeladen] = useState(false);
|
||||
@@ -79,8 +80,16 @@ export function DokumentePanel({ entitaetTyp, entitaetId, onFehler }: Props) {
|
||||
if (!datei) return;
|
||||
setWirdHochgeladen(true);
|
||||
try {
|
||||
await ladeDokumentHoch({ entitaet_typ: entitaetTyp, entitaet_id: entitaetId, dokumenttyp, beschreibung, datei });
|
||||
await ladeDokumentHoch({
|
||||
entitaet_typ: entitaetTyp,
|
||||
entitaet_id: entitaetId,
|
||||
dokumenttyp,
|
||||
beschreibung,
|
||||
istOriginal,
|
||||
datei,
|
||||
});
|
||||
setBeschreibung("");
|
||||
setIstOriginal(false);
|
||||
e.target.value = "";
|
||||
await laden();
|
||||
} catch (fehler) {
|
||||
@@ -99,7 +108,7 @@ export function DokumentePanel({ entitaetTyp, entitaetId, onFehler }: Props) {
|
||||
const datei = e.target.files?.[0];
|
||||
if (!datei) return;
|
||||
try {
|
||||
await ladeDokumentErsetzen({ dokumentId, datei });
|
||||
await ladeDokumentErsetzen({ dokumentId, istOriginal, datei });
|
||||
e.target.value = "";
|
||||
delete versionenOffen[dokumentId];
|
||||
await laden();
|
||||
@@ -177,6 +186,10 @@ export function DokumentePanel({ entitaetTyp, entitaetId, onFehler }: Props) {
|
||||
placeholder="Beschreibung (optional)"
|
||||
style={{ maxWidth: "16rem" }}
|
||||
/>
|
||||
<label className="row" style={{ gap: "0.3rem", alignItems: "center", fontSize: "0.9rem" }}>
|
||||
<input type="checkbox" checked={istOriginal} onChange={(e) => setIstOriginal(e.target.checked)} />
|
||||
Original (nicht nur Kopie/Scan)
|
||||
</label>
|
||||
<input type="file" accept=".pdf,.jpg,.jpeg,.png,.webp" onChange={hochladen} disabled={wirdHochgeladen} />
|
||||
</div>
|
||||
{dokumente.length > 0 || filterTyp ? (
|
||||
@@ -205,6 +218,15 @@ export function DokumentePanel({ entitaetTyp, entitaetId, onFehler }: Props) {
|
||||
<span className="badge badge-neutral" style={{ marginRight: "0.4rem" }}>
|
||||
{DOKUMENTTYP_LABEL[d.dokumenttyp]}
|
||||
</span>
|
||||
{d.ist_original && (
|
||||
<span
|
||||
className="badge badge-success"
|
||||
style={{ marginRight: "0.4rem" }}
|
||||
title="Als Original gekennzeichnet, nicht nur Kopie/Scan"
|
||||
>
|
||||
Original
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
onClick={() => ansehen(d.id)}
|
||||
title="In neuem Tab ansehen"
|
||||
|
||||
Reference in New Issue
Block a user