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>
129 lines
6.4 KiB
JavaScript
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.' }
|
|
}
|