Aufträge: Sichten auf die Auftragsbestätigung (Team, öffentlich, Frühbeginn)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
47a92811a3
commit
98eceaf14c
2 files changed
+240
No files matched your search
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Sichten auf eine Auftragsbestätigung (Übersicht V3/V4): Kundensnapshot, früheste Leistung bei Privatpersonen,
|
||||
* Zahlungsplan- und Aufwandstext, Ansicht fürs Team und öffentliche Ansicht für Token- und Kundenrouten.
|
||||
* Alles rein (kein Appwrite, keine Konfiguration); die Wortlaute kommen aus texte.js.
|
||||
*/
|
||||
import { withdrawalDeadline } from '../deadlines.js'
|
||||
import { addDays, berlinDate } from '../finance/dates.js'
|
||||
import { euroDe } from '../finance/format.js'
|
||||
import { AGB_FASSUNG, KUNDENARTEN, LIMITS, STUNDENSATZ_STANDARD_CENTS } from './konstanten.js'
|
||||
import { AB_TEXTE, BESTAETIGUNG_TEXT, FRUEHBEGINN_TEXT } from './texte.js'
|
||||
|
||||
// Kürzungen, damit der Snapshot samt JSON in die Spalte kundeJson (2000 Zeichen) passt
|
||||
const SNAPSHOT_FELDER = [
|
||||
['name', 'name', 120], ['firma', 'companyName', 120], ['strasse', 'street', 120], ['plz', 'postalCode', 20],
|
||||
['ort', 'city', 80], ['land', 'country', 60], ['email', 'email', 200], ['kundennummer', 'code', 40],
|
||||
]
|
||||
|
||||
const text = (wert, max) => (typeof wert === 'string' ? wert.trim().slice(0, max).trim() : '')
|
||||
|
||||
export function kundeSnapshot(customerDoc) {
|
||||
const doc = customerDoc || {}
|
||||
const snapshot = {}
|
||||
for (const [ziel, quelle, max] of SNAPSHOT_FELDER) snapshot[ziel] = text(doc[quelle], max)
|
||||
snapshot.kundenart = KUNDENARTEN.includes(doc.kundenart) ? doc.kundenart : ''
|
||||
// feste Reihenfolge der Schlüssel wie KundeSnapshot in V3
|
||||
const { name, firma, strasse, plz, ort, land, email, kundenart, kundennummer } = snapshot
|
||||
return { name, firma, strasse, plz, ort, land, email, kundenart, kundennummer }
|
||||
}
|
||||
|
||||
/** Früheste Leistung bei Privatpersonen ohne Frühbeginn: Tag nach Ablauf der Widerrufsfrist. */
|
||||
export function fruehesterBeginn(auftrag) {
|
||||
if (auftrag.kunde?.kundenart !== 'privat' || auftrag.fruehbeginn || !auftrag.gesendetAm) return null
|
||||
const gesendet = new Date(auftrag.gesendetAm)
|
||||
if (Number.isNaN(gesendet.getTime())) return null
|
||||
return addDays(withdrawalDeadline(gesendet).lastDay, 1)
|
||||
}
|
||||
|
||||
export function zahlungsplanText(auftrag) {
|
||||
const ziel = Number.isSafeInteger(auftrag.zahlungszielTage) ? auftrag.zahlungszielTage : LIMITS.standardZahlungszielTage
|
||||
const plan = auftrag.zahlungsplan || { art: 'nach_abnahme' }
|
||||
const bausteine = AB_TEXTE.zahlungsplan
|
||||
if (plan.art === 'monatlich') return bausteine.monatlich({ ziel })
|
||||
if (plan.art === 'frei') return bausteine.frei({ text: plan.text || '', ziel })
|
||||
if (plan.art === 'anzahlung' && Number.isSafeInteger(auftrag.summeCents) && typeof plan.prozent === 'number' && Number.isFinite(plan.prozent)) {
|
||||
const anzahlung = Math.round((auftrag.summeCents * plan.prozent) / 100)
|
||||
return bausteine.anzahlung({ prozent: plan.prozent, anzahlung: euroDe(anzahlung), rest: euroDe(auftrag.summeCents - anzahlung), ziel })
|
||||
}
|
||||
return bausteine.nach_abnahme({ ziel })
|
||||
}
|
||||
|
||||
/** Stunden als Text mit Dezimalkomma ohne überflüssige Nullen: 600 → '10', 135 → '2,25', 90 → '1,5'. */
|
||||
const stundenText = (minuten) => String(Math.round((minuten / 60) * 100) / 100).replace('.', ',')
|
||||
|
||||
export function aufwandText(auftrag) {
|
||||
if (auftrag.art !== 'aufwand') return null
|
||||
const minuten = auftrag.schaetzungMinuten
|
||||
return AB_TEXTE.aufwand({
|
||||
satz: euroDe(auftrag.stundensatzCents ?? STUNDENSATZ_STANDARD_CENTS),
|
||||
schaetzung: Number.isSafeInteger(minuten) && minuten > 0 ? stundenText(minuten) : null,
|
||||
})
|
||||
}
|
||||
|
||||
/** Auftrag ohne tokenHash, pdfDatei und bestaetigtPdfSha256, plus die abgeleiteten Felder. */
|
||||
export function auftragFuerTeam(auftrag) {
|
||||
const { tokenHash, pdfDatei, bestaetigtPdfSha256, ...rest } = auftrag
|
||||
return {
|
||||
...rest,
|
||||
pdfVorhanden: Boolean(pdfDatei),
|
||||
fruehesterBeginn: fruehesterBeginn(auftrag),
|
||||
zahlungsplanText: zahlungsplanText(auftrag),
|
||||
aufwandText: aufwandText(auftrag),
|
||||
}
|
||||
}
|
||||
|
||||
/** Nur für Token- und Kundenrouten: keine Kundendaten, kein Token, keine Pfade. */
|
||||
export function oeffentlicheAnsicht(auftrag) {
|
||||
const kundenart = auftrag.kunde?.kundenart || ''
|
||||
return {
|
||||
nummer: auftrag.nummer,
|
||||
datum: auftrag.gesendetAm ? berlinDate(new Date(auftrag.gesendetAm)) : null,
|
||||
titel: auftrag.titel,
|
||||
art: auftrag.art,
|
||||
positionen: auftrag.positionen.map((p) => ({
|
||||
beschreibung: p.beschreibung, menge: p.menge, einzelpreisCents: p.einzelpreisCents, summeCents: p.menge * p.einzelpreisCents,
|
||||
})),
|
||||
summeCents: auftrag.summeCents,
|
||||
stundensatzCents: auftrag.stundensatzCents,
|
||||
schaetzungMinuten: auftrag.schaetzungMinuten,
|
||||
aufwandText: aufwandText(auftrag),
|
||||
zahlungsplanText: zahlungsplanText(auftrag),
|
||||
startAm: auftrag.startAm,
|
||||
fertigBis: auftrag.fertigBis,
|
||||
kundenart,
|
||||
status: auftrag.status,
|
||||
bestaetigtAm: auftrag.bestaetigtAm,
|
||||
fruehbeginn: auftrag.fruehbeginn,
|
||||
fruehesterBeginn: fruehesterBeginn(auftrag),
|
||||
agbUrl: `/rechtliches/agb-${auftrag.agbFassung || AGB_FASSUNG}.pdf`,
|
||||
texte: {
|
||||
bestaetigung: BESTAETIGUNG_TEXT,
|
||||
fruehbeginn: kundenart === 'privat' ? FRUEHBEGINN_TEXT : null,
|
||||
kleinunternehmer: AB_TEXTE.kleinunternehmer,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user