Kundenansicht neu P2: Reiter Rechnungen mit Aufträgen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8c8d29657d
commit
cf391eb117
2 files changed
+102
No files matched your search
@@ -0,0 +1,65 @@
|
||||
/* Kundenansicht neu (P2): Reiter Rechnungen in Verträge. Oben der Abschnitt "Aufträge" (Alt-Modul auftrag-portal.js, blendet sich selbst ein,
|
||||
wenn Aufträge aktiv und vorhanden sind), darunter die Rechnungen als Tabelle. Fehler der Aufträge stören die Rechnungen nie. */
|
||||
|
||||
import { api } from './api.js'
|
||||
import { h, leeren } from './dom.js'
|
||||
import { K, legacyRequest, safeUrl, fmtEuro, fmtDatum } from './ansicht-hilfen.js'
|
||||
import { initAuftraege } from '../auftrag-portal.js'
|
||||
|
||||
const TEXT_LEER = 'Es liegen noch keine Rechnungen vor.'
|
||||
const TEXT_LUECKE = 'Deine Rechnungen konnten gerade nicht vollständig geladen werden. Bitte versuche es später noch einmal.'
|
||||
const laeufe = new WeakMap() // Reiter-Inhalt → Nummer des jüngsten Aufrufs
|
||||
|
||||
const pille = (r) => (r.overdue ? K.pille.rot : r.paid ? K.pille.gruen : r.cancelled ? K.pille.grau : K.pille.orange)
|
||||
|
||||
function rechnungsZeile(r) {
|
||||
const offen = Number(r.balance) > 0
|
||||
const url = safeUrl(r.portalUrl)
|
||||
const links = [
|
||||
url ? h('a', { class: 'kn-zeilenlink', href: url, target: '_blank', rel: 'noopener noreferrer' }, offen ? 'Zahlen' : 'Ansehen') : null,
|
||||
r.hasPdf !== false ? h('a', { class: 'kn-zeilenlink', href: `/api/invoices/${encodeURIComponent(r.id)}/pdf` }, 'PDF') : null,
|
||||
].filter(Boolean)
|
||||
return h('tr', { class: offen ? 'is-offen' : 'is-bezahlt' },
|
||||
h('td', { 'data-label': 'Nr.', class: 'kn-rechnung-nr' }, r.number, r.projekt ? h('span', { class: 'kn-rechnung-projekt' }, r.projekt) : null),
|
||||
h('td', { 'data-label': 'Datum', class: 'kn-num' }, fmtDatum(r.date)),
|
||||
h('td', { 'data-label': 'Betrag', class: 'kn-num' }, fmtEuro(r.amount)),
|
||||
h('td', { 'data-label': 'Offen', class: 'kn-num' }, fmtEuro(r.balance)),
|
||||
h('td', { 'data-label': 'Status' }, h('span', { class: pille(r) }, r.status)),
|
||||
h('td', { class: 'kn-rechnung-aktionen' }, links))
|
||||
}
|
||||
|
||||
function tabelle(rechnungen) {
|
||||
return h('div', { class: 'kn-tabelle kn-rechnungen' }, h('table', null,
|
||||
h('thead', null, h('tr', null, ['Nr.', 'Datum', 'Betrag', 'Offen', 'Status'].map((t) => h('th', null, t)), h('th', { 'aria-label': 'Aktionen' }))),
|
||||
h('tbody', null, rechnungen.map(rechnungsZeile))))
|
||||
}
|
||||
|
||||
/** Reiter-Inhalt. antwort ist optional die schon geladene Antwort von GET /api/invoices; ohne sie lädt die Funktion selbst. */
|
||||
export async function zeigeRechnungen(box, { me, antwort } = {}) {
|
||||
const nr = (laeufe.get(box) ?? 0) + 1
|
||||
laeufe.set(box, nr)
|
||||
const aktuell = () => laeufe.get(box) === nr
|
||||
|
||||
const auftraegeAbschnitt = h('section', { class: 'ab-portal hidden', id: 'kn-auftraege' }, h('h2', { class: K.abschnitt }, 'Aufträge'), h('div', { class: 'kn-auftraege-rumpf' }))
|
||||
const rechnungenTitel = h('h2', { class: `${K.abschnitt} hidden` }, 'Rechnungen')
|
||||
const liste = h('div', { class: 'kn-rechnungen-liste' }, h('p', { class: K.leer }, 'Wird geladen …'))
|
||||
leeren(box).append(auftraegeAbschnitt, rechnungenTitel, liste)
|
||||
|
||||
let daten = antwort
|
||||
if (!daten) {
|
||||
try { daten = await api('/api/invoices') } catch (f) {
|
||||
if (!aktuell()) return
|
||||
leeren(liste).append(h('p', { class: K.fehler }, `Rechnungen konnten nicht geladen werden: ${f.message}`))
|
||||
}
|
||||
}
|
||||
if (!aktuell()) return
|
||||
if (daten) {
|
||||
const rechnungen = Array.isArray(daten.invoices) ? daten.invoices : []
|
||||
const luecke = daten.incomplete ? h('p', { class: K.meldung }, TEXT_LUECKE) : null
|
||||
leeren(liste).append(...(rechnungen.length ? [tabelle(rechnungen), luecke] : [luecke ?? h('p', { class: 'kn-hinweis' }, TEXT_LEER)]).filter(Boolean))
|
||||
}
|
||||
// Aufträge: ein Fehler lässt den Abschnitt weg und lässt die Rechnungen stehen
|
||||
try {
|
||||
await initAuftraege({ section: auftraegeAbschnitt, body: auftraegeAbschnitt.querySelector('.kn-auftraege-rumpf'), invoicesTitle: rechnungenTitel, request: legacyRequest(api), viewAs: me?.viewAs === true })
|
||||
} catch { /* ohne Aufträge bleiben die Rechnungen */ }
|
||||
}
|
||||
Reference in new issue
Block a user