|
|
|
@@ -407,14 +407,14 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="damage-invoice-modal" style="display:none; position:fixed; inset:0; background:rgba(15,23,42,0.72); z-index:9999; padding:20px; overflow:auto;">
|
|
|
|
|
<div id="damage-invoice-modal" role="dialog" aria-modal="true" aria-labelledby="modal-title" style="display:none; position:fixed; inset:0; background:rgba(15,23,42,0.72); z-index:9999; padding:20px; overflow:auto;">
|
|
|
|
|
<div style="max-width:760px; margin:40px auto; background: var(--ui-surface); border-radius:12px; padding:24px; box-shadow:0 20px 60px rgba(0,0,0,0.3);">
|
|
|
|
|
<div style="display:flex; justify-content:space-between; align-items:center; gap:12px; margin-bottom:18px;">
|
|
|
|
|
<div>
|
|
|
|
|
<h2 style="margin:0;">Rechnung erstellen</h2>
|
|
|
|
|
<h2 id="modal-title" style="margin:0;">Rechnung erstellen</h2>
|
|
|
|
|
<p style="margin:6px 0 0; color:#666;">Die Rechnung nutzt das bestehende Rechnungssystem und kann direkt nach der Schadensmeldung erstellt werden.</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button type="button" class="btn btn-secondary" onclick="closeDamageInvoiceModal()">Schließen</button>
|
|
|
|
|
<button type="button" class="btn btn-secondary" onclick="closeDamageInvoiceModal()" aria-label="Modal schließen">Schließen</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<form id="damage-invoice-form" method="post" action="">
|
|
|
|
@@ -432,7 +432,10 @@
|
|
|
|
|
<input id="damage-invoice-code" type="text" readonly style="width:100%; padding:10px; border:1px solid #ddd; border-radius:6px; background: var(--ui-surface-soft);">
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label for="damage-invoice-amount" style="display:block; font-weight:700; margin-bottom:6px;">Preis</label>
|
|
|
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:6px;">
|
|
|
|
|
<label for="damage-invoice-amount" style="font-weight:700; margin:0;">Preis</label>
|
|
|
|
|
<button type="button" id="damage-invoice-replace-btn" class="btn btn-outline-secondary btn-sm" style="padding: 2px 8px; font-size: 0.75rem;">Komplett ersetzen</button>
|
|
|
|
|
</div>
|
|
|
|
|
<input id="damage-invoice-amount" name="invoice_amount" type="text" required style="width:100%; padding:10px; border:1px solid #ddd; border-radius:6px;" placeholder="z.B. 12,50">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
@@ -477,6 +480,7 @@
|
|
|
|
|
const damageInvoiceCode = document.getElementById('damage-invoice-code');
|
|
|
|
|
const damageInvoiceAmount = document.getElementById('damage-invoice-amount');
|
|
|
|
|
const damageInvoiceReason = document.getElementById('damage-invoice-reason');
|
|
|
|
|
const damageInvoiceReplaceBtn = document.getElementById('damage-invoice-replace-btn');
|
|
|
|
|
|
|
|
|
|
function openDamageReportPrompt(button) {
|
|
|
|
|
const row = button.closest('.loan-row');
|
|
|
|
@@ -494,41 +498,48 @@
|
|
|
|
|
|
|
|
|
|
const description = noteInput.trim() || 'Schaden erneut gemeldet';
|
|
|
|
|
|
|
|
|
|
// Visuelles Feedback: Button deaktivieren und Text ändern
|
|
|
|
|
const originalText = button.textContent;
|
|
|
|
|
button.disabled = true;
|
|
|
|
|
button.textContent = 'Speichere...';
|
|
|
|
|
|
|
|
|
|
fetch(`/report_damage/${itemId}`, {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
body: JSON.stringify({ description })
|
|
|
|
|
})
|
|
|
|
|
.then(response => response.json().then(data => ({ ok: response.ok, data })))
|
|
|
|
|
.then(({ ok, data }) => {
|
|
|
|
|
if (!ok || !data.success) {
|
|
|
|
|
.then(async response => {
|
|
|
|
|
// Robustes JSON-Parsing (verhindert Absturz, falls der Server kein JSON zurückgibt)
|
|
|
|
|
const data = await response.json().catch(() => ({}));
|
|
|
|
|
if (!response.ok || !data.success) {
|
|
|
|
|
throw new Error(data.message || 'Fehler beim Speichern der Schadensmeldung.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
})
|
|
|
|
|
.then(data => {
|
|
|
|
|
if (confirm('Schaden gespeichert. Soll direkt eine Rechnung erstellt werden?')) {
|
|
|
|
|
openDamageInvoiceModal(row, description);
|
|
|
|
|
// Button wieder zurücksetzen, da die Seite nicht neu geladen wird
|
|
|
|
|
button.disabled = false;
|
|
|
|
|
button.textContent = originalText;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bei "Abbrechen" im Confirm -> Neuladen der Tabelle
|
|
|
|
|
window.location.reload();
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
alert(error.message || 'Fehler beim Speichern der Schadensmeldung.');
|
|
|
|
|
alert(error.message || 'Ein unbekannter Fehler ist aufgetreten.');
|
|
|
|
|
// Fehlerbehandlung: Button wieder aktiv schalten
|
|
|
|
|
button.disabled = false;
|
|
|
|
|
button.textContent = originalText;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.openDamageReportPrompt = openDamageReportPrompt;
|
|
|
|
|
|
|
|
|
|
function openDamageInvoiceModal(row, description) {
|
|
|
|
|
const modal = document.getElementById('damage-invoice-modal');
|
|
|
|
|
const form = document.getElementById('damage-invoice-form');
|
|
|
|
|
const inputItem = document.getElementById('damage-invoice-item');
|
|
|
|
|
const inputBorrower = document.getElementById('damage-invoice-borrower');
|
|
|
|
|
const inputCode = document.getElementById('damage-invoice-code');
|
|
|
|
|
const inputAmount = document.getElementById('damage-invoice-amount');
|
|
|
|
|
const inputReason = document.getElementById('damage-invoice-reason');
|
|
|
|
|
|
|
|
|
|
if (!modal || !form) {
|
|
|
|
|
if (!damageInvoiceModal || !damageInvoiceForm) {
|
|
|
|
|
console.error("Modal oder Formular nicht gefunden.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@@ -537,29 +548,47 @@
|
|
|
|
|
const itemName = row.dataset.itemName || '';
|
|
|
|
|
const borrower = row.dataset.userName || '';
|
|
|
|
|
const itemCode = row.dataset.itemCode || '';
|
|
|
|
|
|
|
|
|
|
// KORREKTUR: Jetzt greifen wir auf das richtige dataset-Attribut zu
|
|
|
|
|
const itemCost = row.dataset.itemCost || '';
|
|
|
|
|
|
|
|
|
|
form.action = "{{ url_for('admin_create_invoice', borrow_id='__BORROW_ID__') }}".replace('__BORROW_ID__', borrowId);
|
|
|
|
|
damageInvoiceForm.action = "{{ url_for('admin_create_invoice', borrow_id='__BORROW_ID__') }}".replace('__BORROW_ID__', borrowId);
|
|
|
|
|
|
|
|
|
|
inputItem.value = itemName;
|
|
|
|
|
inputBorrower.value = borrower;
|
|
|
|
|
inputCode.value = itemCode;
|
|
|
|
|
damageInvoiceItem.value = itemName;
|
|
|
|
|
damageInvoiceBorrower.value = borrower;
|
|
|
|
|
damageInvoiceCode.value = itemCode;
|
|
|
|
|
|
|
|
|
|
inputAmount.value = String(itemCost).replace(' EUR', '').trim();
|
|
|
|
|
// Feld zunächst leeren, damit der Ersetzen-Button genutzt werden kann
|
|
|
|
|
damageInvoiceAmount.value = '';
|
|
|
|
|
|
|
|
|
|
inputReason.value = description || `Schaden gemeldet für ${itemName}`;
|
|
|
|
|
// Original-Preis im Button als data-Attribut hinterlegen
|
|
|
|
|
if (damageInvoiceReplaceBtn) {
|
|
|
|
|
damageInvoiceReplaceBtn.dataset.acquisition_costs = String(itemCost).replace(' EUR', '').trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modal.style.display = 'block';
|
|
|
|
|
inputAmount.focus();
|
|
|
|
|
damageInvoiceReason.value = description || `Schaden gemeldet für ${itemName}`;
|
|
|
|
|
|
|
|
|
|
damageInvoiceModal.style.display = 'block';
|
|
|
|
|
|
|
|
|
|
// Accessibility: Fokus ins erste aktivierbare Feld setzen
|
|
|
|
|
damageInvoiceAmount.focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeDamageInvoiceModal() {
|
|
|
|
|
const modal = document.getElementById('damage-invoice-modal');
|
|
|
|
|
if (modal) {
|
|
|
|
|
modal.style.display = 'none';
|
|
|
|
|
if (damageInvoiceModal) {
|
|
|
|
|
damageInvoiceModal.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Event-Listener für den Ersetzen-Button
|
|
|
|
|
if (damageInvoiceReplaceBtn) {
|
|
|
|
|
damageInvoiceReplaceBtn.addEventListener('click', function() {
|
|
|
|
|
if (this.dataset.acquisition_costs) {
|
|
|
|
|
damageInvoiceAmount.value = this.dataset.acquisition_costs;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.openDamageInvoiceModal = openDamageInvoiceModal;
|
|
|
|
|
window.closeDamageInvoiceModal = closeDamageInvoiceModal;
|
|
|
|
|
|
|
|
|
@@ -571,8 +600,6 @@
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.closeDamageInvoiceModal = closeDamageInvoiceModal;
|
|
|
|
|
|
|
|
|
|
function applyFilters() {
|
|
|
|
|
const search = (searchInput.value || '').trim().toLowerCase();
|
|
|
|
|
const status = statusFilter.value;
|
|
|
|
@@ -613,4 +640,4 @@
|
|
|
|
|
applyFilters();
|
|
|
|
|
})();
|
|
|
|
|
</script>
|
|
|
|
|
{% endblock %}
|
|
|
|
|
{% endblock %}
|