66 lines
3.8 KiB
JavaScript
66 lines
3.8 KiB
JavaScript
/* 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, sichereAdresse, 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 = sichereAdresse(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 */ }
|
|
}
|