Files
Webklar-Kundenbereich/test/auftraegeRechnungenHilfe.js

101 lines
5.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Gemeinsame Testdaten für die Rechnungstests des Strangs L2. Erst nach useTestEnv() importieren (zieht config.js).
import { config } from '../server/config.js'
import { deleteDocument } from '../server/services/appwriteAdmin.js'
import { createAuftrag, updateAuftrag } from '../server/services/auftraege/auftragStore.js'
import { createProjektRechnung } from '../server/services/auftraege/projektRechnungenStore.js'
/** n Arbeitstage (Montag bis Freitag) ab start (YYYY-MM-DD, einschließlich). */
export function werktage(start, n) {
const tage = []
for (let ms = Date.parse(`${start}T00:00:00Z`); tage.length < n; ms += 86_400_000) {
const tag = new Date(ms)
if (tag.getUTCDay() % 6 !== 0) tage.push(tag.toISOString().slice(0, 10))
}
return tage
}
const deutsch = (tag) => tag.split('-').reverse().join('.')
/**
* Kunden k1 (fest, mit Anschrift: Frau Wolf), k2 (fest), lead1 (Interessent); Projekte p1 (k1, Ticket t1), p2 (k2, Ticket t2),
* p3 (k1, Ticket t4); Tickets t1 bis t4 (t2 gehört k2); Arbeitsblätter ws1 bis wsT3 (Oktober) und wl01 bis wl20 (Großprojekt).
*/
export function seedRechnungsDaten(aw) {
const kunde = (id, over = {}) =>
aw.seed('customers', { $id: id, email: `${id}@example.invalid`, name: `Kunde ${id}`, customerStatus: 'active', status: 'active', street: 'Hauptstr. 1', postalCode: '10115', city: 'Berlin', ...over })
kunde('k1', { name: 'Anelia Wolf', email: 'wolf@example.invalid' })
kunde('k2')
kunde('lead1', { customerStatus: 'lead' })
const projekt = (id, customerId, projectName, ticketId) => aw.seed('websiteProjects', { $id: id, customerId, projectName, ticketId })
projekt('p1', 'k1', 'Website anelia-wolf.com', 't1')
projekt('p2', 'k2', 'Fremdes Projekt', 't2')
projekt('p3', 'k1', 'Großprojekt', 't4')
for (const [id, customerId, woid] of [['t1', 'k1', 'WO-100'], ['t2', 'k2', 'WO-200'], ['t3', 'k1', 'WO-101'], ['t4', 'k1', 'WO-102']]) {
aw.seed('workorders', { $id: id, customerId, woid, topic: `Ticket ${woid}`, status: 'occupied' })
}
const blatt = (id, workorderId, startDate, totalTime, details, over = {}) =>
aw.seed('worksheets', { $id: id, workorderId, woid: 'WO', startDate, totalTime, details, serviceType: 'remote', countsToPlan: false, isComment: false, ...over })
blatt('ws1', 't1', '06.10.2026', 90, 'Startseite')
blatt('ws2', 't1', '06.10.2026', 45, 'Kontaktformular')
blatt('ws3', 't1', '07.10.2026', 60, 'Impressum')
blatt('wsAbo', 't1', '07.10.2026', 60, 'Wartung', { countsToPlan: true })
blatt('wsKomm', 't1', '07.10.2026', 30, 'Notiz', { isComment: true })
blatt('wsNull', 't1', '07.10.2026', 0, 'Leer')
blatt('wsFremd', 't2', '07.10.2026', 60, 'Fremd')
blatt('wsT3', 't3', '08.10.2026', 30, 'Zusatzticket')
// Großprojekt: 20 Arbeitstage (vier Wochen, KW 45 bis 48) zu je 60 Minuten
werktage('2026-11-02', 20).forEach((tag, i) => blatt(`wl${String(i + 1).padStart(2, '0')}`, 't4', deutsch(tag), 60, `Etappe ${i + 1}`))
}
/**
* Eine versendete Festpreis-AB des Kunden k1 zu p1 (80.000 Cent: Website 600 €, Texte 2 × 100 €; Anzahlung 50 %; AB-2026-0002,
* gesendet am 06.10.2026) über die L0-Speicher anlegen. over gilt für createAuftrag, patch für das anschließende updateAuftrag
* (Status, Nummer, bestaetigtAm, …).
*/
export async function legeAuftragAn(over = {}, patch = {}) {
const roh = await createAuftrag({
kundenId: 'k1', projektId: 'p1', titel: 'Website anelia-wolf.com', zeitquelle: [], art: 'festpreis',
positionen: [{ beschreibung: 'Website', menge: 1, einzelpreisCents: 60000 }, { beschreibung: 'Texte', menge: 2, einzelpreisCents: 10000 }],
stundensatzCents: null, schaetzungMinuten: null, summeCents: 80000,
zahlungsplan: { art: 'anzahlung', prozent: 50, text: '' }, zahlungszielTage: 14,
auftragVom: '2026-10-05', auftragWeg: 'telefon', startAm: '', fertigBis: '', erstelltVon: 'kenso@webklar.com',
...over,
})
return updateAuftrag(roh.id, { status: 'versendet', nummer: 'AB-2026-0002', jahr: 2026, laufnummer: 2, gesendetAm: '2026-10-06T08:15:00.000Z', ...patch })
}
const unix = (iso) => Math.floor(new Date(iso).getTime() / 1000)
/**
* Eine Projektrechnung wie nach createProjectInvoice: Stripe-Rechnung im Nachbau (status draft, open, paid oder void; ohneStripe: die
* Rechnung gibt es bei Stripe nicht, z. B. im Dashboard gelöscht) plus aktiver Projektdatensatz. docNummer ist die Nummer im Datensatz
* (Vorgabe: die der Rechnung, bei einem Entwurf leer).
*/
export async function legeRechnungAn(st, id, {
status = 'draft', nummer = 'WEBKLAR-0007', total = 40000, art = 'aufwand', auftragId = '', projektId = 'p1',
arbeitsblattIds = [], finalisiert = '2026-10-08T10:00:00.000Z', ohneStripe = false, docNummer,
} = {}) {
const entwurf = status === 'draft'
if (!ohneStripe) {
st.invoices.set(id, {
id, object: 'invoice', number: entwurf ? null : nummer, status, customer: 'cus_x', created: unix(finalisiert), total, amount_due: total,
amount_paid: status === 'paid' ? total : 0, amount_remaining: status === 'paid' ? 0 : total,
status_transitions: { finalized_at: entwurf ? null : unix(finalisiert) }, metadata: { art }, lines: { object: 'list', data: [], has_more: false },
})
}
await createProjektRechnung(id, {
kundenId: 'k1', projektId, auftragId, art, betragCents: total, arbeitsblattIds,
rechnungsnummer: docNummer ?? (entwurf ? '' : nummer), aktiv: true, erstelltVon: 'k', erstelltAm: new Date().toISOString(),
})
}
/** Aufträge und Projektdatensätze löschen, Stripe-Nachbau (Rechnungen, Kunden, Aufrufe) leeren. */
export async function leereRechnungsDaten(aw, st) {
for (const sammlung of [config.collections.auftraege, config.collections.projektRechnungen]) {
for (const id of aw.docs(sammlung).keys()) await deleteDocument(sammlung, id)
}
st.invoices.clear()
st.customers.clear()
st.calls.length = 0
}