UX: Rechnungs-Edit-Modal status-bewusst + Storno-Button, SW by-v1075
- _openNeueRechnungModal akzeptiert jetzt status+invoice_number; je nach Status anderes Verhalten: • draft → bearbeitbar, Stornieren + Abbrechen + Speichern • sent → readonly Banner, Stornieren + Schließen • paid → readonly Banner, Stornieren + Schließen • neu → Abbrechen + Erstellen (kein Storno-Slot) - Footer-Layout: Stornieren links, Abbrechen/Speichern rechts (justify-content:space-between → symmetrisch) - Stornieren öffnet den existierenden _openStornoModal; das Edit- Modal wird durch UI.modal.open() automatisch geschlossen - Submit-Handler ignoriert locked-Status (Schutz auch wenn Button irgendwie sichtbar wäre) - Upgrades-Tab + Rechnungen-Tab geben jetzt inv.status+invoice_number beim Öffnen mit; Reload-Callback aus Upgrades-Tab rendert den Tab neu, damit nach Stornierung der gelbe Button zurück auf orange geht
This commit is contained in:
parent
5886e1b269
commit
c4a82e96fd
5 changed files with 66 additions and 18 deletions
|
|
@ -3715,7 +3715,7 @@ window.Page_admin = (() => {
|
|||
try {
|
||||
const inv = await API.get(`/admin/invoices/${btn.dataset.invoiceId}`);
|
||||
_openNeueRechnungModal(() => {
|
||||
_tab = 'rechnungen';
|
||||
// Nach Speichern/Stornieren: zurück auf Upgrades-Tab, damit der Button neu rendert
|
||||
_renderTab();
|
||||
}, {
|
||||
recipient_name: inv.recipient_name,
|
||||
|
|
@ -3725,7 +3725,7 @@ window.Page_admin = (() => {
|
|||
discount_pct: inv.discount_pct || 0,
|
||||
notes: inv.notes || '',
|
||||
items: inv.items.map(it => ({ description: it.description, quantity: it.quantity, unit_price: it.unit_price })),
|
||||
}, inv.id);
|
||||
}, inv.id, inv.status, inv.invoice_number);
|
||||
} catch (e) {
|
||||
UI.toast.error(e.message || 'Rechnung konnte nicht geladen werden.');
|
||||
}
|
||||
|
|
@ -3923,7 +3923,7 @@ window.Page_admin = (() => {
|
|||
discount_pct: inv.discount_pct || 0,
|
||||
notes: inv.notes || '',
|
||||
items: inv.items.map(it => ({ description: it.description, quantity: it.quantity, unit_price: it.unit_price })),
|
||||
}, inv.id);
|
||||
}, inv.id, inv.status, inv.invoice_number);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -3943,16 +3943,31 @@ window.Page_admin = (() => {
|
|||
});
|
||||
}
|
||||
|
||||
function _openNeueRechnungModal(reload, prefill = null, invoiceId = null) {
|
||||
function _openNeueRechnungModal(reload, prefill = null, invoiceId = null, status = null, invoiceNumber = null) {
|
||||
const id = `inv-new-${Date.now()}`;
|
||||
const p = prefill || {};
|
||||
const isEdit = !!invoiceId;
|
||||
const isEdit = !!invoiceId;
|
||||
const isLocked = isEdit && status && status !== 'draft'; // sent / paid / cancelled → nicht änderbar
|
||||
const canCancel = isEdit && status !== 'cancelled'; // alles außer schon storniert
|
||||
const lockedBanner = isLocked ? (() => {
|
||||
const map = {
|
||||
sent: ['#fff8f0', '#f0a060', '#c05000', 'Diese Rechnung wurde bereits versendet — Inhalt nicht mehr änderbar. Korrekturen nur per Stornierung.'],
|
||||
paid: ['#f0fdf4', '#86efac', '#15803d', 'Diese Rechnung ist bezahlt — Inhalt nicht änderbar.'],
|
||||
cancelled: ['#fef2f2', '#fca5a5', '#b91c1c', 'Diese Rechnung wurde storniert — Inhalt nicht änderbar.'],
|
||||
};
|
||||
const [bg, br, fg, msg] = map[status] || ['#f5f5f5', '#ccc', '#444', `Status: ${status}`];
|
||||
return `<div style="padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);
|
||||
background:${bg};border:1px solid ${br};
|
||||
font-size:var(--text-xs);color:${fg};line-height:1.6">${msg}</div>`;
|
||||
})() : '';
|
||||
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('receipt')} ${isEdit ? 'Rechnung bearbeiten' : 'Neue Rechnung erstellen'}`,
|
||||
title: `${UI.icon('receipt')} ${isEdit ? (isLocked ? 'Rechnung ansehen' : 'Rechnung bearbeiten') : 'Neue Rechnung erstellen'}`,
|
||||
body: `
|
||||
<form id="${id}" style="display:flex;flex-direction:column;gap:var(--space-3)">
|
||||
|
||||
${lockedBanner}
|
||||
|
||||
${!isEdit && !p.recipient_name ? `
|
||||
<div style="padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);
|
||||
background:#fff8f0;border:1px solid #f0a060;
|
||||
|
|
@ -4031,11 +4046,43 @@ window.Page_admin = (() => {
|
|||
</form>
|
||||
`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" form="${id}" type="submit">${UI.icon('receipt')} ${isEdit ? 'Änderungen speichern' : 'Rechnung erstellen'}</button>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);width:100%;flex-wrap:wrap">
|
||||
<div>
|
||||
${canCancel ? `
|
||||
<button class="btn btn-ghost" id="${id}-cancel-invoice"
|
||||
style="color:var(--c-danger);border:1px solid var(--c-danger)">
|
||||
${UI.icon('x-circle')} Stornieren
|
||||
</button>` : ''}
|
||||
</div>
|
||||
<div style="display:flex;gap:var(--space-2)">
|
||||
<button class="btn btn-secondary" data-modal-close>${isLocked ? 'Schließen' : 'Abbrechen'}</button>
|
||||
${!isLocked ? `<button class="btn btn-primary" form="${id}" type="submit">
|
||||
${UI.icon('receipt')} ${isEdit ? 'Änderungen speichern' : 'Rechnung erstellen'}
|
||||
</button>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
// Bei gesperrten Rechnungen (sent/paid/cancelled) alle Eingaben & Action-Buttons readonly
|
||||
if (isLocked) {
|
||||
setTimeout(() => {
|
||||
const form = document.getElementById(id);
|
||||
if (!form) return;
|
||||
form.querySelectorAll('input, textarea').forEach(inp => { inp.disabled = true; });
|
||||
// "+ Position hinzufügen" und Item-Lösch-Buttons verstecken
|
||||
const addBtn = document.getElementById(`${id}-add-item`);
|
||||
if (addBtn) addBtn.style.display = 'none';
|
||||
form.querySelectorAll('.inv-item-remove').forEach(b => { b.style.display = 'none'; });
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Stornieren — schließt dieses Modal, öffnet den Storno-Dialog
|
||||
document.getElementById(`${id}-cancel-invoice`)?.addEventListener('click', () => {
|
||||
// _openStornoModal ruft intern UI.modal.open() → schließt dieses Modal automatisch
|
||||
_openStornoModal(invoiceId, invoiceNumber || `#${invoiceId}`, reload);
|
||||
});
|
||||
|
||||
// Items-Container und Hilfsfunktionen
|
||||
const itemsContainer = document.getElementById(`${id}-items`);
|
||||
const previewEl = document.getElementById(`${id}-preview`);
|
||||
|
|
@ -4100,6 +4147,7 @@ window.Page_admin = (() => {
|
|||
// Form Submit
|
||||
document.getElementById(id)?.addEventListener('submit', async e => {
|
||||
e.preventDefault();
|
||||
if (isLocked) return; // gesperrte Rechnung — Submit ignorieren (Button ist eh ausgeblendet)
|
||||
const fd = new FormData(e.target);
|
||||
const items = [];
|
||||
itemsContainer.querySelectorAll('.adm-inv-item-row').forEach(row => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue