Remove lot document comments

This commit is contained in:
gitea_admin committed 2026-09-08 12:17:49 +02:00
1 parent f130092d40
commit 3a3de9da22
4 files changed
+10 -48

No files matched your search

-7
View File
@@ -36,13 +36,11 @@ try {
$pdo,
(int) ($_POST['lotId'] ?? 0),
$file,
(string) ($_POST['comment'] ?? ''),
(string) ($currentUser['username'] ?? $currentUser['email'] ?? 'unknown')
);
json_response(201, ['ok' => true, 'document' => [
'id' => (int) $document['id'],
'original_filename' => (string) $document['original_filename'],
'comment' => $document['comment'] === null ? '' : (string) $document['comment'],
]]);
}
@@ -103,11 +101,6 @@ try {
json_response(200, ['ok' => true, 'productId' => $productId, 'availableQty' => get_lager_product_available_qty($pdo, $productId)]);
}
if ($action === 'update_document_comment') {
update_stock_lot_document_comment($pdo, (int) ($data['documentId'] ?? 0), (string) ($data['comment'] ?? ''));
json_response(200, ['ok' => true]);
}
if ($action === 'delete_document') {
delete_stock_lot_document($pdo, (int) ($data['documentId'] ?? 0));
json_response(200, ['ok' => true]);
+5 -23
View File
@@ -286,7 +286,7 @@ function get_stock_lot_documents_by_lot_ids(PDO $pdo, array $lotIds): array
$placeholders = implode(',', array_fill(0, count($lotIds), '?'));
$stmt = $pdo->prepare(
"SELECT id, stock_lot_id, original_filename, mime_type, size_bytes, comment, uploaded_at
"SELECT id, stock_lot_id, original_filename, mime_type, size_bytes, uploaded_at
FROM stock_lot_document
WHERE stock_lot_id IN ({$placeholders})
ORDER BY stock_lot_id, id"
@@ -299,7 +299,6 @@ function get_stock_lot_documents_by_lot_ids(PDO $pdo, array $lotIds): array
'original_filename' => (string) $row['original_filename'],
'mime_type' => (string) $row['mime_type'],
'size_bytes' => (int) $row['size_bytes'],
'comment' => $row['comment'] === null ? '' : (string) $row['comment'],
'uploaded_at' => (string) $row['uploaded_at'],
];
}
@@ -309,7 +308,7 @@ function get_stock_lot_documents_by_lot_ids(PDO $pdo, array $lotIds): array
function get_stock_lot_document(PDO $pdo, int $documentId): array
{
$stmt = $pdo->prepare(
'SELECT id, stock_lot_id, original_filename, stored_filename, mime_type, size_bytes, comment
'SELECT id, stock_lot_id, original_filename, stored_filename, mime_type, size_bytes
FROM stock_lot_document WHERE id = :id'
);
$stmt->execute([':id' => $documentId]);
@@ -320,22 +319,6 @@ function get_stock_lot_document(PDO $pdo, int $documentId): array
return $document;
}
function update_stock_lot_document_comment(PDO $pdo, int $documentId, string $comment): void
{
$document = get_stock_lot_document($pdo, $documentId);
$statusStmt = $pdo->prepare('SELECT status FROM stock_lot WHERE id = :id');
$statusStmt->execute([':id' => (int) $document['stock_lot_id']]);
$status = $statusStmt->fetchColumn();
if ($status !== 'current' && $status !== 'open') {
throw new InvalidArgumentException('Dokumente vergangener Chargen können nicht bearbeitet werden');
}
$stmt = $pdo->prepare('UPDATE stock_lot_document SET comment = :comment WHERE id = :id');
$stmt->execute([':comment' => trim($comment) === '' ? null : trim($comment), ':id' => $documentId]);
if ($stmt->rowCount() !== 1) {
throw new RuntimeException('Dokument nicht gefunden');
}
}
function delete_stock_lot_document(PDO $pdo, int $documentId): void
{
$document = get_stock_lot_document($pdo, $documentId);
@@ -353,7 +336,7 @@ function delete_stock_lot_document(PDO $pdo, int $documentId): void
}
}
function upload_stock_lot_document(PDO $pdo, int $lotId, array $file, string $comment, string $uploadedBy): array
function upload_stock_lot_document(PDO $pdo, int $lotId, array $file, string $uploadedBy): array
{
if ($lotId <= 0 || !is_uploaded_file((string) ($file['tmp_name'] ?? ''))) {
throw new InvalidArgumentException('Ungültige Dokumentdatei');
@@ -389,8 +372,8 @@ function upload_stock_lot_document(PDO $pdo, int $lotId, array $file, string $co
try {
$stmt = $pdo->prepare(
'INSERT INTO stock_lot_document
(stock_lot_id, original_filename, stored_filename, mime_type, size_bytes, comment, uploaded_by)
VALUES (:lot_id, :original_filename, :stored_filename, :mime_type, :size_bytes, :comment, :uploaded_by)
(stock_lot_id, original_filename, stored_filename, mime_type, size_bytes, uploaded_by)
VALUES (:lot_id, :original_filename, :stored_filename, :mime_type, :size_bytes, :uploaded_by)
RETURNING id'
);
$stmt->execute([
@@ -399,7 +382,6 @@ function upload_stock_lot_document(PDO $pdo, int $lotId, array $file, string $co
':stored_filename' => $storedFilename,
':mime_type' => $mimeType,
':size_bytes' => $size,
':comment' => trim($comment) === '' ? null : trim($comment),
':uploaded_by' => $uploadedBy,
]);
return get_stock_lot_document($pdo, (int) $stmt->fetchColumn());
+5 -12
View File
@@ -311,12 +311,7 @@ function auth_render_lager_documents(array $documents, bool $editable, string $s
foreach ($documents as $document) {
$documentId = (int) ($document['id'] ?? 0);
$filename = (string) ($document['original_filename'] ?? 'Dokument');
$comment = trim((string) ($document['comment'] ?? ''));
$html .= '<p class="sg-body" data-lager-document-row="' . $documentId . '"><a class="sg-hyperlink" href="/api/lager-document.php?id=' . $documentId . '" data-component="hyperlink">' . auth_escape_html($filename) . '</a>';
if ($comment !== '') {
$html .= '<br><span data-lager-document-comment>Kommentar: ' . auth_escape_html($comment) . '</span>';
}
$html .= '</p>';
$html .= '<p class="sg-body" data-lager-document-row="' . $documentId . '"><a class="sg-hyperlink" href="/api/lager-document.php?id=' . $documentId . '" data-component="hyperlink">' . auth_escape_html($filename) . '</a></p>';
}
}
$html .= '</div>';
@@ -326,10 +321,9 @@ function auth_render_lager_documents(array $documents, bool $editable, string $s
foreach ($documents as $document) {
$documentId = (int) ($document['id'] ?? 0);
$filename = (string) ($document['original_filename'] ?? 'Dokument');
$comment = (string) ($document['comment'] ?? '');
$html .= '<div class="sg-form-sections-card__field-group" data-lager-document-edit-row="' . $documentId . '"><label class="sg-labeled-input-row"><span class="sg-label">' . auth_escape_html($filename) . '</span><input class="sg-interaction-element sg-input-single-line" type="text" value="' . auth_escape_html($comment) . '" data-lager-document-comment-input="' . $documentId . '"></label><button class="sg-interaction-element sg-button sg-button--warning" type="button" data-lager-document-delete="' . $documentId . '">Löschen</button><article class="sg-card sg-card--overlay-host sg-delete-confirmation-pattern__floating-card" role="dialog" aria-modal="true" aria-labelledby="lager-document-delete-title-' . $documentId . '" data-lager-document-confirmation="' . $documentId . '" hidden><div class="sg-card-segment sg-card-segment--body sg-delete-confirmation-pattern__body"><p class="sg-body sg-delete-confirmation-pattern__text" id="lager-document-delete-title-' . $documentId . '"><strong>Dokument löschen?</strong></p><p class="sg-body sg-delete-confirmation-pattern__text">Bestätige durch Eingabe von <span class="sg-delete-confirmation-pattern__code">DELETE</span>.</p><label class="sg-labeled-input-row"><span class="sg-label">Bestätigung</span><input class="sg-interaction-element sg-input-single-line" type="text" data-lager-document-delete-input="' . $documentId . '"></label><div class="sg-delete-confirmation-pattern__actions"><button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-document-delete-cancel="' . $documentId . '">Abbrechen</button><button class="sg-interaction-element sg-button sg-button--process sg-button--process-inactive" type="button" disabled data-lager-document-delete-confirm="' . $documentId . '">Löschen</button></div></div></article></div>';
$html .= '<div class="sg-form-sections-card__field-group" data-lager-document-edit-row="' . $documentId . '"><span class="sg-body">' . auth_escape_html($filename) . '</span><button class="sg-interaction-element sg-button sg-button--warning" type="button" data-lager-document-delete="' . $documentId . '">Löschen</button><article class="sg-card sg-card--overlay-host sg-delete-confirmation-pattern__floating-card" role="dialog" aria-modal="true" aria-labelledby="lager-document-delete-title-' . $documentId . '" data-lager-document-confirmation="' . $documentId . '" hidden><div class="sg-card-segment sg-card-segment--body sg-delete-confirmation-pattern__body"><p class="sg-body sg-delete-confirmation-pattern__text" id="lager-document-delete-title-' . $documentId . '"><strong>Dokument löschen?</strong></p><p class="sg-body sg-delete-confirmation-pattern__text">Bestätige durch Eingabe von <span class="sg-delete-confirmation-pattern__code">DELETE</span>.</p><label class="sg-labeled-input-row"><span class="sg-label">Bestätigung</span><input class="sg-interaction-element sg-input-single-line" type="text" data-lager-document-delete-input="' . $documentId . '"></label><div class="sg-delete-confirmation-pattern__actions"><button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-document-delete-cancel="' . $documentId . '">Abbrechen</button><button class="sg-interaction-element sg-button sg-button--process sg-button--process-inactive" type="button" disabled data-lager-document-delete-confirm="' . $documentId . '">Löschen</button></div></div></article></div>';
}
$html .= '<form class="sg-file-upload" data-pattern="file-upload" data-lager-document-upload-form="' . auth_escape_html($scope) . '"><div class="sg-file-input" data-component="file-input"><div class="sg-file-upload__dropzone sg-labeled-input-row"><label class="sg-label" for="lager-document-file-' . auth_escape_html($scope) . '">Dateien auswählen</label><input class="sg-file-input__control" id="lager-document-file-' . auth_escape_html($scope) . '" type="file" multiple aria-describedby="lager-document-file-status-' . auth_escape_html($scope) . '" data-lager-document-file><button class="sg-interaction-element sg-button sg-button--active sg-file-upload__trigger" type="button">Dateien auswählen</button></div></div><p class="sg-body sg-file-upload__status" id="lager-document-file-status-' . auth_escape_html($scope) . '" data-lager-document-status aria-live="polite">Noch keine Dateien ausgewählt.</p><div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Kommentar</span><input class="sg-interaction-element sg-input-single-line" type="text" data-lager-document-comment></label></div><button class="sg-interaction-element sg-button sg-button--process sg-button--process-inactive" type="submit" disabled aria-disabled="true">Hochladen</button></form>';
$html .= '<form class="sg-file-upload" data-pattern="file-upload" data-lager-document-upload-form="' . auth_escape_html($scope) . '"><div class="sg-file-input" data-component="file-input"><div class="sg-file-upload__dropzone sg-labeled-input-row"><label class="sg-label" for="lager-document-file-' . auth_escape_html($scope) . '">Dateien auswählen</label><input class="sg-file-input__control" id="lager-document-file-' . auth_escape_html($scope) . '" type="file" multiple aria-describedby="lager-document-file-status-' . auth_escape_html($scope) . '" data-lager-document-file><button class="sg-interaction-element sg-button sg-button--active sg-file-upload__trigger" type="button">Dateien auswählen</button></div></div><p class="sg-body sg-file-upload__status" id="lager-document-file-status-' . auth_escape_html($scope) . '" data-lager-document-status aria-live="polite">Noch keine Dateien ausgewählt.</p><button class="sg-interaction-element sg-button sg-button--process sg-button--process-inactive" type="submit" disabled aria-disabled="true">Hochladen</button></form>';
$html .= '</div>';
}
return $html . '</section></div>';
@@ -853,7 +847,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " };";
echo " const getLagerWarningTitle = (trigger) => trigger.dataset.lagerDrawerForecastStatus === 'depleted' ? 'Warnung: Charge ist aufgebraucht' : (trigger.dataset.lagerDrawerForecastMhdWarning === 'true' ? 'Warnung: MHD wird vor Chargenende erreicht' : (trigger.dataset.lagerDrawerForecastWarning === 'true' ? 'Warnung: Charge wird innerhalb von drei Monaten aufgebraucht sein' : 'Keine Warnung'));";
echo " const escapeLagerHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (character) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[character] || character));";
echo " const renderLagerDocumentRows = (documents, editable) => { if (!documents.length) { return '<p class=\"sg-body\">Keine Dokumente vorhanden.</p>'; } return documents.map((document) => { const id = Number(document.id || 0); const filename = escapeLagerHtml(document.original_filename || 'Dokument'); const comment = String(document.comment || '').trim(); if (!editable) { return '<p class=\"sg-body\" data-lager-document-row=\"' + id + '\"><a class=\"sg-hyperlink\" href=\"/api/lager-document.php?id=' + id + '\" data-component=\"hyperlink\">' + filename + '</a>' + (comment ? '<br><span data-lager-document-comment>Kommentar: ' + escapeLagerHtml(comment) + '</span>' : '') + '</p>'; } return '<div class=\"sg-form-sections-card__field-group\" data-lager-document-edit-row=\"' + id + '\"><label class=\"sg-labeled-input-row\"><span class=\"sg-label\">' + filename + '</span><input class=\"sg-interaction-element sg-input-single-line\" type=\"text\" value=\"' + escapeLagerHtml(comment) + '\" data-lager-document-comment-input=\"' + id + '\"></label><button class=\"sg-interaction-element sg-button sg-button--warning\" type=\"button\" data-lager-document-delete=\"' + id + '\">Löschen</button></div>'; }).join(''); };";
echo " const renderLagerDocumentRows = (documents, editable) => { if (!documents.length) { return '<p class=\"sg-body\">Keine Dokumente vorhanden.</p>'; } return documents.map((document) => { const id = Number(document.id || 0); const filename = escapeLagerHtml(document.original_filename || 'Dokument'); if (!editable) { return '<p class=\"sg-body\" data-lager-document-row=\"' + id + '\"><a class=\"sg-hyperlink\" href=\"/api/lager-document.php?id=' + id + '\" data-component=\"hyperlink\">' + filename + '</a></p>'; } return '<div class=\"sg-form-sections-card__field-group\" data-lager-document-edit-row=\"' + id + '\"><span class=\"sg-body\">' + filename + '</span><button class=\"sg-interaction-element sg-button sg-button--warning\" type=\"button\" data-lager-document-delete=\"' + id + '\">Löschen</button></div>'; }).join(''); };";
echo " const syncLagerCurrentDocuments = (trigger) => { const drawer = getDrawer(); if (!drawer) { return; } let documents = []; try { documents = JSON.parse(trigger.dataset.lagerDrawerDocuments || '[]'); } catch (error) { documents = []; } const read = drawer.querySelector('[data-lager-documents-read=\"current-read\"]'); const edit = drawer.querySelector('[data-lager-documents-edit=\"current-edit\"]'); if (read) { read.innerHTML = renderLagerDocumentRows(documents, false); } if (edit) { edit.innerHTML = renderLagerDocumentRows(documents, true) + edit.querySelector('[data-lager-document-upload-form]')?.outerHTML || ''; } };";
echo " const closeDrawer = () => { setDrawerState(false); contentRoot.querySelectorAll('[data-lager-drawer-open]').forEach((trigger) => trigger.setAttribute('aria-expanded', 'false')); };";
echo " document.addEventListener('click', (event) => { const trigger = event.target.closest('[data-lager-drawer-open]'); if (!trigger || trigger.dataset.lagerDrawerType !== 'Produkt') { return; } const drawer = getDrawer(); const forecast = drawer?.querySelector('[data-lager-drawer-forecast-value]'); if (forecast) { forecast.textContent = trigger.dataset.lagerDrawerForecast || 'Keine Prognose'; } syncLagerCurrentDocuments(trigger); });";
@@ -873,8 +867,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " const getLagerDocumentLotId = (form) => form.closest('[data-lager-open-card-wrapper]')?.dataset.lagerOpenCardWrapper || getDrawer()?.dataset.lagerActiveLotId || '';";
echo " contentRoot.querySelectorAll('[data-lager-document-upload-form]').forEach((form) => { const input = form.querySelector('[data-lager-document-file]'); const trigger = form.querySelector('.sg-file-input__trigger'); if (input && trigger) { trigger.addEventListener('click', () => input.click()); } });";
echo " document.addEventListener('change', (event) => { const input = event.target.closest('[data-lager-document-file]'); if (!input) { return; } const form = input.closest('[data-lager-document-upload-form]'); const submit = form?.querySelector('[type=\"submit\"]'); const status = form?.querySelector('[data-lager-document-status]'); const count = input.files?.length || 0; if (status) { status.textContent = count ? count + ' Datei(en) ausgewählt.' : 'Noch keine Dateien ausgewählt.'; } if (submit) { submit.disabled = count === 0; submit.setAttribute('aria-disabled', String(count === 0)); submit.classList.toggle('sg-button--process-inactive', count === 0); } });";
echo " document.addEventListener('submit', async (event) => { const form = event.target.closest('[data-lager-document-upload-form]'); if (!form) { return; } event.preventDefault(); const input = form.querySelector('[data-lager-document-file]'); const submit = form.querySelector('[type=\"submit\"]'); const status = form.querySelector('[data-lager-document-status]'); const files = Array.from(input?.files || []); if (!files.length) { return; } if (files.some((file) => file.size > 52428800)) { if (status) { status.textContent = 'Dokument darf maximal 50 MB groß sein.'; } return; } if (submit) { submit.disabled = true; } if (status) { status.textContent = 'Upload läuft …'; } try { for (const file of files) { const data = new FormData(); data.append('action', 'upload_document'); data.append('csrfToken', contentRoot.querySelector('[data-lager-csrf-token]')?.dataset.lagerCsrfToken || ''); data.append('lotId', getLagerDocumentLotId(form)); data.append('comment', form.querySelector('[data-lager-document-comment]')?.value || ''); data.append('document', file); const response = await fetch('/api/lager-charge.php', { method: 'POST', body: data }); const result = await response.json(); if (!response.ok || result.ok !== true) { throw new Error(result.error || 'Upload fehlgeschlagen'); } } window.location.reload(); } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Upload fehlgeschlagen'; } if (submit) { submit.disabled = false; } } });";
echo " document.addEventListener('blur', async (event) => { const input = event.target.closest('[data-lager-document-comment-input]'); if (!input) { return; } try { await postLagerChange({ action: 'update_document_comment', documentId: input.dataset.lagerDocumentCommentInput, comment: input.value }); } catch (error) { input.setCustomValidity(error instanceof Error ? error.message : 'Kommentar konnte nicht gespeichert werden'); input.reportValidity(); } }, true);";
echo " document.addEventListener('submit', async (event) => { const form = event.target.closest('[data-lager-document-upload-form]'); if (!form) { return; } event.preventDefault(); const input = form.querySelector('[data-lager-document-file]'); const submit = form.querySelector('[type=\"submit\"]'); const status = form.querySelector('[data-lager-document-status]'); const files = Array.from(input?.files || []); if (!files.length) { return; } if (files.some((file) => file.size > 52428800)) { if (status) { status.textContent = 'Dokument darf maximal 50 MB groß sein.'; } return; } if (submit) { submit.disabled = true; } if (status) { status.textContent = 'Upload läuft …'; } try { for (const file of files) { const data = new FormData(); data.append('action', 'upload_document'); data.append('csrfToken', contentRoot.querySelector('[data-lager-csrf-token]')?.dataset.lagerCsrfToken || ''); data.append('lotId', getLagerDocumentLotId(form)); data.append('document', file); const response = await fetch('/api/lager-charge.php', { method: 'POST', body: data }); const result = await response.json(); if (!response.ok || result.ok !== true) { throw new Error(result.error || 'Upload fehlgeschlagen'); } } window.location.reload(); } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Upload fehlgeschlagen'; } if (submit) { submit.disabled = false; } } });";
echo " document.addEventListener('click', (event) => { const open = event.target.closest('[data-lager-document-delete]'); if (!open) { return; } const row = open.closest('[data-lager-document-edit-row]'); const id = open.dataset.lagerDocumentDelete; if (row?.querySelector('[data-lager-document-confirmation]')) { return; } const confirmation = document.createElement('article'); confirmation.className = 'sg-card sg-card--overlay-host sg-delete-confirmation-pattern__floating-card'; confirmation.setAttribute('role', 'dialog'); confirmation.setAttribute('aria-modal', 'true'); confirmation.dataset.lagerDocumentConfirmation = id; confirmation.innerHTML = '<div class=\"sg-card-segment sg-card-segment--body sg-delete-confirmation-pattern__body\"><p class=\"sg-body sg-delete-confirmation-pattern__text\"><strong>Dokument löschen?</strong></p><p class=\"sg-body sg-delete-confirmation-pattern__text\">Bestätige durch Eingabe von <span class=\"sg-delete-confirmation-pattern__code\">DELETE</span>.</p><label class=\"sg-labeled-input-row\"><span class=\"sg-label\">Bestätigung</span><input class=\"sg-interaction-element sg-input-single-line\" type=\"text\" data-lager-document-delete-input=\"' + id + '\"></label><div class=\"sg-delete-confirmation-pattern__actions\"><button class=\"sg-interaction-element sg-button sg-button--active\" type=\"button\" data-lager-document-delete-cancel=\"' + id + '\">Abbrechen</button><button class=\"sg-interaction-element sg-button sg-button--process sg-button--process-inactive\" type=\"button\" disabled data-lager-document-delete-confirm=\"' + id + '\">Löschen</button></div></div>'; row.appendChild(confirmation); });";
echo " document.addEventListener('input', (event) => { const input = event.target.closest('[data-lager-document-delete-input]'); if (!input) { return; } const button = input.closest('[data-lager-document-confirmation]')?.querySelector('[data-lager-document-delete-confirm]'); const valid = input.value === 'DELETE'; if (button) { button.disabled = !valid; button.setAttribute('aria-disabled', String(!valid)); button.classList.toggle('sg-button--process-inactive', !valid); } });";
echo " document.addEventListener('click', async (event) => { const open = event.target.closest('[data-lager-document-delete]'); if (open) { const id = open.dataset.lagerDocumentDelete; const confirmation = open.closest('[data-lager-document-edit-row]')?.querySelector('[data-lager-document-confirmation=\"' + id + '\"]'); if (confirmation) { confirmation.hidden = false; } return; } const cancel = event.target.closest('[data-lager-document-delete-cancel]'); if (cancel) { const confirmation = cancel.closest('[data-lager-document-confirmation]'); if (confirmation) { confirmation.hidden = true; } return; } const confirm = event.target.closest('[data-lager-document-delete-confirm]'); if (!confirm || confirm.disabled) { return; } const confirmation = confirm.closest('[data-lager-document-confirmation]'); const id = confirm.dataset.lagerDocumentDeleteConfirm; confirm.disabled = true; try { await postLagerChange({ action: 'delete_document', documentId: id }); window.location.reload(); } catch (error) { confirm.disabled = false; const input = confirmation?.querySelector('[data-lager-document-delete-input]'); if (input) { input.setCustomValidity(error instanceof Error ? error.message : 'Dokument konnte nicht gelöscht werden'); input.reportValidity(); } } });";