Rechnungsdialog: Zustand mit Reducer, Appwrite-Lesezugriffe und Hooks für Projektkonto, Vorschau und weitere Tickets
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0f1dbabd98
commit
6a5f943034
4 files changed
+418
No files matched your search
@@ -0,0 +1,115 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { loadModule } from './load.mjs'
|
||||
|
||||
const { anfangszustand, reduce } = await loadModule('src/lib/rechnungDialogState.js')
|
||||
|
||||
test('anfangszustand: nichts gewählt, eine leere Position, Zahlungsziel 14', () => {
|
||||
assert.deepEqual(anfangszustand({ projektId: 'p1' }), {
|
||||
projektId: 'p1', art: '', auftragId: '',
|
||||
items: [{ uid: 1, description: '', quantity: '1', price: '' }], zusatz: [],
|
||||
abgewaehlt: [], weitereTickets: [], anzahlung: {}, zeilentexte: {},
|
||||
zeitraum: '', zeitraumGeaendert: false, days: '14', memo: '',
|
||||
})
|
||||
assert.equal(anfangszustand().projektId, null)
|
||||
assert.equal(anfangszustand({ projektId: '' }).projektId, '')
|
||||
})
|
||||
|
||||
test('projekt: setzt alles zurück, behält aber Zahlungsziel und Notiz', () => {
|
||||
let s = anfangszustand({ projektId: 'p1' })
|
||||
s = reduce(s, { type: 'tage', wert: '30' })
|
||||
s = reduce(s, { type: 'notiz', wert: 'Danke' })
|
||||
s = reduce(s, { type: 'art', art: 'aufwand' })
|
||||
s = reduce(s, { type: 'blatt', id: 'b1' })
|
||||
s = reduce(s, { type: 'zeilentext', schluessel: '2026-10-06', text: 'Neu', original: 'Alt' })
|
||||
s = reduce(s, { type: 'projekt', projektId: 'p2' })
|
||||
assert.equal(s.projektId, 'p2')
|
||||
assert.equal(s.art, '')
|
||||
assert.deepEqual(s.abgewaehlt, [])
|
||||
assert.deepEqual(s.zeilentexte, {})
|
||||
assert.equal(s.days, '30')
|
||||
assert.equal(s.memo, 'Danke')
|
||||
assert.equal(reduce(s, { type: 'projekt', projektId: null }).projektId, null)
|
||||
})
|
||||
|
||||
test('art: Positionen für Festbetrag und Zusatzpositionen bleiben getrennt und erhalten', () => {
|
||||
let s = anfangszustand({ projektId: 'p1' })
|
||||
s = reduce(s, { type: 'position', liste: 'items', uid: 1, feld: 'description', wert: 'Pauschale' })
|
||||
s = reduce(s, { type: 'position+', liste: 'zusatz' })
|
||||
s = reduce(s, { type: 'position', liste: 'zusatz', uid: 2, feld: 'price', wert: '12,00' })
|
||||
s = reduce(s, { type: 'art', art: 'aufwand' })
|
||||
assert.equal(s.art, 'aufwand')
|
||||
assert.equal(s.items[0].description, 'Pauschale')
|
||||
assert.deepEqual(s.zusatz, [{ uid: 2, description: '', quantity: '1', price: '12,00' }])
|
||||
// eine unbekannte Liste oder ein unbekanntes Feld ändert nichts
|
||||
assert.equal(reduce(s, { type: 'position', liste: 'x', uid: 1, feld: 'description', wert: 'y' }), s)
|
||||
assert.equal(reduce(s, { type: 'position', liste: 'items', uid: 1, feld: 'uid', wert: 9 }), s)
|
||||
})
|
||||
|
||||
test('position+ und position-: Positionen mindestens 1, Zusatzpositionen mindestens 0, höchstens 20', () => {
|
||||
let s = anfangszustand({ projektId: 'p1' })
|
||||
assert.equal(reduce(s, { type: 'position-', liste: 'items', uid: 1 }), s)
|
||||
s = reduce(s, { type: 'position+', liste: 'zusatz' })
|
||||
assert.equal(s.zusatz[0].uid, 2)
|
||||
s = reduce(s, { type: 'position-', liste: 'zusatz', uid: 2 })
|
||||
assert.deepEqual(s.zusatz, [])
|
||||
for (let i = 0; i < 25; i += 1) s = reduce(s, { type: 'position+', liste: 'zusatz' })
|
||||
assert.equal(s.zusatz.length, 20)
|
||||
})
|
||||
|
||||
test('blatt: Abwählen und Wiederanwählen', () => {
|
||||
let s = anfangszustand({ projektId: 'p1' })
|
||||
s = reduce(s, { type: 'blatt', id: 'b2' })
|
||||
assert.deepEqual(s.abgewaehlt, ['b2'])
|
||||
s = reduce(s, { type: 'blatt', id: 'b2' })
|
||||
assert.deepEqual(s.abgewaehlt, [])
|
||||
})
|
||||
|
||||
test('ticket+ und ticket-: ein Ticket nur einmal, beim Entfernen verschwindet die Abwahl seiner Blätter', () => {
|
||||
const ticket = { ticketId: 't2', woid: '100140', titel: 'Kontaktformular' }
|
||||
let s = anfangszustand({ projektId: 'p1' })
|
||||
s = reduce(s, { type: 'ticket+', ticket })
|
||||
s = reduce(s, { type: 'ticket+', ticket })
|
||||
assert.deepEqual(s.weitereTickets, [ticket])
|
||||
s = reduce(s, { type: 'blatt', id: 'w1' })
|
||||
s = reduce(s, { type: 'blatt', id: 'b9' })
|
||||
s = reduce(s, { type: 'ticket-', ticketId: 't2', blattIds: ['w1'] })
|
||||
assert.deepEqual(s.weitereTickets, [])
|
||||
assert.deepEqual(s.abgewaehlt, ['b9'])
|
||||
})
|
||||
|
||||
test('zeitraum-vorschlag gilt nur, solange der Nutzer das Feld nicht selbst geändert hat', () => {
|
||||
let s = anfangszustand({ projektId: 'p1' })
|
||||
s = reduce(s, { type: 'zeitraum-vorschlag', text: '01.10.–20.10.2026' })
|
||||
assert.deepEqual([s.zeitraum, s.zeitraumGeaendert], ['01.10.–20.10.2026', false])
|
||||
assert.equal(reduce(s, { type: 'zeitraum-vorschlag', text: '01.10.–20.10.2026' }), s) // gleicher Text: kein neuer Zustand
|
||||
s = reduce(s, { type: 'zeitraum', text: 'Oktober 2026' })
|
||||
assert.deepEqual([s.zeitraum, s.zeitraumGeaendert], ['Oktober 2026', true])
|
||||
assert.equal(reduce(s, { type: 'zeitraum-vorschlag', text: '02.10.–21.10.2026' }).zeitraum, 'Oktober 2026')
|
||||
// ein Wechsel der Art verwirft einen Vorschlag, nicht aber die Eingabe des Nutzers
|
||||
const vorschlag = reduce(anfangszustand({ projektId: 'p1' }), { type: 'zeitraum-vorschlag', text: '01.10.–20.10.2026' })
|
||||
assert.equal(reduce(vorschlag, { type: 'art', art: 'festbetrag' }).zeitraum, '')
|
||||
assert.equal(reduce(s, { type: 'art', art: 'festbetrag' }).zeitraum, 'Oktober 2026')
|
||||
})
|
||||
|
||||
test('zeilentext: ein unveränderter oder leerer Text entfernt den Eintrag', () => {
|
||||
let s = anfangszustand({ projektId: 'p1' })
|
||||
const setze = (text) => reduce(s, { type: 'zeilentext', schluessel: '2026-10-06', text, original: 'Alt' })
|
||||
s = setze('Neu')
|
||||
assert.deepEqual(s.zeilentexte, { '2026-10-06': 'Neu' })
|
||||
s = setze('Alt')
|
||||
assert.deepEqual(s.zeilentexte, {})
|
||||
s = setze('Neu')
|
||||
s = setze(' ')
|
||||
assert.deepEqual(s.zeilentexte, {})
|
||||
})
|
||||
|
||||
test('anzahlung und auftrag: Eingaben ergänzen sich, eine andere AB setzt sie zurück', () => {
|
||||
let s = anfangszustand({ projektId: 'p1' })
|
||||
s = reduce(s, { type: 'anzahlung', patch: { modus: 'betrag' } })
|
||||
s = reduce(s, { type: 'anzahlung', patch: { wert: '400,00' } })
|
||||
assert.deepEqual(s.anzahlung, { modus: 'betrag', wert: '400,00' })
|
||||
s = reduce(s, { type: 'auftrag', auftragId: 'a2' })
|
||||
assert.deepEqual([s.auftragId, s.anzahlung], ['a2', {}])
|
||||
assert.equal(reduce(s, { type: 'gibtsnicht' }), s)
|
||||
})
|
||||
Reference in new issue
Block a user