Files
tickte-system/src/lib/auftraegeAnsicht.js
T
ClaudeandClaude Sonnet 5.5 07d36f64e9 Kontrolle: Abschnitt Aufträge lädt auch bei ausgefallenen Projektkonten
Scheitert getProjektkonten, wird die Auftragsliste trotzdem geholt; kommt sie, bleibt der
Abschnitt bedienbar und der Fehler der Projekte steht eigens daneben.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 15:18:03 +00:00

129 lines
6.4 KiB
JavaScript

// Ansichtslogik des Reiters „Aufträge und Rechnungen“ (rein, ohne React und ohne Appwrite; unter Node testbar).
// Die API-Funktionen kommen als Argument. Der Container (AuftraegeUndRechnungen.jsx) verdrahtet diese Funktionen mit
// useFinanceResource und den Abschnitten des Reiters.
import { invoiceDoneMessage } from './financeView'
export const KONTEN_FEHLER = 'Die Projektübersicht konnte nicht geladen werden.'
export const AUFTRAEGE_FEHLER = 'Die Aufträge konnten nicht geladen werden.'
const istObjekt = (wert) => wert !== null && typeof wert === 'object' && !Array.isArray(wert)
const ohneProjektLeer = () => ({ berechnetCents: 0, bezahltCents: 0, anzahl: 0 })
/**
* Lädt Projektkonten und, nur bei eingeschaltetem Schalter (`auftraegeAktiv === true`), die Aufträge des Kunden.
* Scheitert nur listAuftraege, bleiben die Projektkonten und der Fehler steht eigens da.
* Scheitert getProjektkonten, wird die Auftragsliste trotzdem geholt (der Server prüft den Schalter ohnehin und antwortet bei
* „aus“ mit 404): kommt sie, bleibt der Abschnitt Aufträge bedienbar und der Fehler steht als `kontenFehler` daneben;
* kommt sie nicht, geht der Fehler der Projektkonten weiter.
* -> { konten, auftraege: Liste | null, auftraegeFehler: Text | '', kontenFehler?: Text (nur bei ausgefallenen Projektkonten) }
*/
export async function ladeAuftraegeAnsicht(kundeId, { getProjektkonten, listAuftraege }) {
let antwort
try {
antwort = await getProjektkonten(kundeId)
} catch (kontenErr) {
let liste
try {
liste = await listAuftraege(kundeId)
} catch {
throw kontenErr
}
return {
konten: {},
kontenFehler: (kontenErr && kontenErr.message) || KONTEN_FEHLER,
auftraege: Array.isArray(liste?.auftraege) ? liste.auftraege : [],
auftraegeFehler: '',
}
}
const konten = istObjekt(antwort) ? antwort : {}
if (konten.auftraegeAktiv !== true) return { konten, auftraege: null, auftraegeFehler: '' }
try {
const liste = await listAuftraege(kundeId)
return { konten, auftraege: Array.isArray(liste?.auftraege) ? liste.auftraege : [], auftraegeFehler: '' }
} catch (err) {
return { konten, auftraege: null, auftraegeFehler: (err && err.message) || AUFTRAEGE_FEHLER }
}
}
/**
* Zustand der Ansicht aus dem Ergebnis von useFinanceResource ({ data, error }).
* -> { projekteStatus: 'laden' | 'fehler' | 'bereit', fehler, nachladeFehler,
* auftraegeAktiv, auftraegeStatus: 'aus' | 'bereit' | 'fehler', auftraegeFehler,
* konten, ohneProjekt, stripeOk, kunde, auftraege }
* Ohne Daten gibt es keinen Aufträge-Abschnitt (der Schalter ist noch unbekannt). Scheitert das Neuladen, nachdem Daten
* da sind, bleiben die alten Daten stehen und die Meldung kommt als `nachladeFehler` daneben. Waren die Projektkonten
* ausgefallen und die Aufträge trotzdem da (`data.kontenFehler`), ist `projekteStatus` „fehler“ und die Aufträge sind bereit.
*/
export function ansichtsZustand({ data, error } = {}) {
if (!data) {
return {
projekteStatus: error ? 'fehler' : 'laden',
fehler: error || '',
nachladeFehler: '',
auftraegeAktiv: false,
auftraegeStatus: 'aus',
auftraegeFehler: '',
konten: [],
ohneProjekt: ohneProjektLeer(),
stripeOk: true,
kunde: null,
auftraege: [],
}
}
const k = istObjekt(data.konten) ? data.konten : {}
const kontenFehler = typeof data.kontenFehler === 'string' ? data.kontenFehler : ''
// Mit ausgefallenen Projektkonten gilt der Schalter als an, wenn der Server die Auftragsliste geliefert hat
const auftraegeAktiv = kontenFehler ? Array.isArray(data.auftraege) : k.auftraegeAktiv === true
const auftraegeStatus = !auftraegeAktiv ? 'aus' : data.auftraegeFehler ? 'fehler' : 'bereit'
return {
projekteStatus: kontenFehler ? 'fehler' : 'bereit',
fehler: kontenFehler ? error || kontenFehler : '',
nachladeFehler: kontenFehler ? '' : error || '',
auftraegeAktiv,
auftraegeStatus,
auftraegeFehler: auftraegeStatus === 'fehler' ? data.auftraegeFehler : '',
konten: Array.isArray(k.konten) ? k.konten : [],
ohneProjekt: istObjekt(k.ohneProjekt) ? k.ohneProjekt : ohneProjektLeer(),
stripeOk: k.stripeOk !== false,
kunde: istObjekt(k.kunde) ? k.kunde : null,
auftraege: auftraegeAktiv && Array.isArray(data.auftraege) ? data.auftraege : [],
}
}
// Wertebereich von customers.kundenart (V1); bewusst lokal, auftragView.js gehört dem Strang W2
const KUNDENARTEN = ['unternehmen', 'privat']
/**
* Kundendokument mit der frischen Kundenart des Portals (getProjektkonten -> kunde.kundenart). Gilt nur ein gültiger Wert
* („unternehmen“ oder „privat“), kommt eine Kopie mit diesem Wert zurück, das Dokument der Seite bleibt unverändert.
* Sonst dieselbe Referenz (null bleibt null). So fragt der AB-Dialog nicht erneut, wenn die Kundenart eben gespeichert wurde
* und die Kundenseite noch den alten Datensatz hält.
*/
export function kundeMitKundenart(customer, kundeInfo) {
const art = istObjekt(kundeInfo) ? kundeInfo.kundenart : undefined
if (!customer || !KUNDENARTEN.includes(art)) return customer
return { ...customer, kundenart: art }
}
/**
* Rückmeldung nach dem Anlegen einer Rechnung (wie InvoiceTable.handleCreated): `info.notice` (festgeschrieben, aber nicht
* versendet) gewinnt mit seinem Ton, sonst Entwurf oder gesendet. `info` darf fehlen oder null sein. -> { tone, text }
*/
export function erstelltMeldung(rechnung, info) {
const i = istObjekt(info) ? info : {}
if (typeof i.notice === 'string' && i.notice) return { tone: i.tone || 'ok', text: i.notice }
return { tone: 'ok', text: invoiceDoneMessage(i.senden ? 'create-send' : 'create-draft', rechnung) }
}
/** Rückmeldung nach dem Speichern oder Senden einer Auftragsbestätigung (onSaved des AB-Dialogs). -> { tone, text } */
export function auftragMeldung(auftrag) {
const a = istObjekt(auftrag) ? auftrag : {}
const name = a.nummer ? `Auftragsbestätigung ${a.nummer}` : 'Die Auftragsbestätigung'
if (a.status === 'entwurf') return { tone: 'ok', text: 'Der Entwurf wurde gespeichert.' }
if (a.mailStatus === 'fehler') {
return { tone: 'warn', text: `${name} ist versendet, die Mail an den Kunden wurde aber nicht zugestellt. Bitte in der Liste erneut senden.` }
}
if (a.nummer) return { tone: 'ok', text: `${name} wurde${a.gesendetAn ? ` an ${a.gesendetAn}` : ''} gesendet.` }
return { tone: 'ok', text: 'Die Auftragsbestätigung wurde gespeichert.' }
}