Files
ClaudeandClaude Opus 5.5 1f56b86791 Shop Review B I-4: Steuerart und Lieferzeit ohne Vorbelegung
kleinunternehmer ist ab jetzt null (nicht gewaehlt), lieferzeit leer. istVollstaendig verlangt eine
bewusste Wahl (true oder false), der Shop kann sonst nicht geoeffnet werden. Im Reiter
Einstellungen gibt es zwei Pflicht-Radiobuttons ohne Vorauswahl; Preise, Artikel und Muster
rechnen bei null wie bei Regelbesteuerung. Tests und Fixtures angepasst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:50:54 +00:00

404 lines
21 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.
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { berechne } from '../server/services/shop/preise.js'
import { euro } from '../server/services/shop/geld.js'
const basis = {
aktiv: true, kurz: '', beschreibung: '', steuersatz: 19, grundpreis: null, bestand: null, varianten: null,
bilder: [], lieferart: 'beides', widerruf: 'gesetzlich',
}
const produkt = (id, extra = {}) => ({ ...basis, id, name: `Artikel ${id}`, preisCent: 1000, ...extra })
const karte = (...liste) => new Map(liste.map((p) => [p.id, p]))
const shirt = produkt('shirt', {
name: 'T-Shirt',
preisCent: null,
varianten: {
name: 'Größe',
werte: [
{ id: 'sizeaaaa', name: 'M', preisCent: 2000, bestand: 5 },
{ id: 'sizebbbb', name: 'L', preisCent: 2200, bestand: null },
],
},
})
const einstellungen = (extra = {}) => ({
kleinunternehmer: false,
versand: { aktiv: true, laender: ['DE', 'AT'], kostenCent: { DE: 490, AT: 990 }, freiAbCent: null },
abholung: { aktiv: true, adresse: 'Weg 1', hinweis: '' },
mindestbestellCent: null,
...extra,
})
const rechne = (args) => berechne({ lieferart: 'versand', land: 'DE', einstellungen: einstellungen(), ...args })
const eine = (p, menge = 1, varianteId) => [{ produktId: p.id, menge, ...(varianteId ? { varianteId } : {}) }]
test('einfacher Warenkorb: Summen, Texte, Versand und Steuer', () => {
const a = produkt('a', { preisCent: 1190 })
const r = rechne({ produkte: karte(a), positionen: eine(a, 2) })
assert.equal(r.ok, true)
assert.equal(r.zwischensummeCent, 2380)
assert.equal(r.versandCent, 490)
assert.equal(r.gesamtCent, 2870)
assert.deepEqual(r.positionen[0], {
produktId: 'a', varianteId: null, name: 'Artikel a', varianteText: '', menge: 2, einzelCent: 1190, summeCent: 2380,
steuersatz: 19, widerruf: 'gesetzlich', einzelText: euro(1190), summeText: euro(2380),
})
assert.equal(r.texte.zwischensumme, euro(2380))
assert.equal(r.texte.versand, euro(490))
assert.equal(r.texte.gesamt, euro(2870))
// 2870 * 19 / 119 = 458,23 => 458
assert.deepEqual(r.steuer, [{ satz: 19, betragCent: 458, text: `inkl. 19 % USt: ${euro(458)}` }])
assert.equal(r.texte.steuerHinweis, `inkl. 19 % USt: ${euro(458)}`)
assert.equal(r.lieferart, 'versand')
assert.equal(r.land, 'DE')
})
test('gemischte Steuersaetze: Text mit absteigenden Saetzen, 0 % liefert keinen Eintrag', () => {
const a = produkt('a', { preisCent: 2000, steuersatz: 19 })
const b = produkt('b', { preisCent: 700, steuersatz: 7 })
const c = produkt('c', { preisCent: 500, steuersatz: 0 })
const e = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: 1 } })
const r = berechne({ produkte: karte(a, b, c), positionen: [...eine(b), ...eine(c), ...eine(a)], lieferart: 'versand', land: 'DE', einstellungen: e })
assert.equal(r.versandCent, 0)
assert.equal(r.texte.versand, 'kostenlos')
assert.deepEqual(r.steuer.map((s) => [s.satz, s.betragCent]), [[19, 319], [7, 46]])
assert.equal(r.texte.steuerHinweis, `inkl. 19 % USt: ${euro(319)}; inkl. 7 % USt: ${euro(46)}`)
assert.deepEqual(r.positionen.map((p) => p.produktId), ['b', 'c', 'a'], 'Reihenfolge der Eingabe bleibt')
})
test('Steuer wird je Satz ueber die Summe gerundet, nicht je Position', () => {
// Einzeln: 3 * round(0,119 * ... ) waere 3 * 0 ; ueber die Summe: 3 * 10 = 30 Cent brutto => 30*19/119 = 4,79 => 5
const a = produkt('a', { preisCent: 10 })
const fueller = produkt('fueller', { preisCent: 100, steuersatz: 0 }) // hebt den Gesamtbetrag ueber 0,50 EUR, ohne Steuer
const e = einstellungen({ abholung: { aktiv: true, adresse: 'x', hinweis: '' } })
const r = berechne({ produkte: karte(a, fueller), positionen: [...eine(a, 3), ...eine(fueller)], lieferart: 'abholung', einstellungen: e })
assert.equal(r.steuer[0].betragCent, 5)
})
test('Rundung halb auf an der Grenze', () => {
// brutto 107 bei 7 % => 107*7/107 = 7 exakt; brutto 50 bei 7 % => 3,271 => 3; brutto 1 bei 19 % => 0,159 => 0 (kein Eintrag)
const fueller = produkt('fueller', { preisCent: 100, steuersatz: 0 }) // Gesamtbetrag mindestens 0,50 EUR, selbst ohne Steuer
const abh = (p, n = 1) => berechne({ produkte: karte(p, fueller), positionen: [...eine(p, n), ...eine(fueller)], lieferart: 'abholung', einstellungen: einstellungen() })
assert.equal(abh(produkt('a', { preisCent: 107, steuersatz: 7 })).steuer[0].betragCent, 7)
assert.equal(abh(produkt('a', { preisCent: 50, steuersatz: 7 })).steuer[0].betragCent, 3)
assert.deepEqual(abh(produkt('a', { preisCent: 1, steuersatz: 19 })).steuer, [])
assert.equal(abh(produkt('a', { preisCent: 1, steuersatz: 19 })).texte.steuerHinweis, '')
// brutto 5 bei 19 % => 0,798 => 1
assert.equal(abh(produkt('a', { preisCent: 5, steuersatz: 19 })).steuer[0].betragCent, 1)
// brutto 30 bei 7 % => 1,962 => 2
assert.equal(abh(produkt('a', { preisCent: 30, steuersatz: 7 })).steuer[0].betragCent, 2)
})
test('Versand wird anteilig nach Bruttowert auf die Saetze verteilt, Rest-Cent auf den hoechsten Satz', () => {
const a = produkt('a', { preisCent: 1000, steuersatz: 19 })
const b = produkt('b', { preisCent: 1000, steuersatz: 7 })
const e = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 501 }, freiAbCent: null } })
const r = berechne({ produkte: karte(b, a), positionen: [...eine(b), ...eine(a)], lieferart: 'versand', land: 'DE', einstellungen: e })
// Anteile: floor(501*1000/2000) = 250 je Satz, Rest 1 => 19 %: 1251, 7 %: 1250
assert.equal(r.gesamtCent, 2501)
assert.deepEqual(r.steuer.map((s) => [s.satz, s.betragCent]), [
[19, Math.floor((2 * 1251 * 19 + 119) / (2 * 119))],
[7, Math.floor((2 * 1250 * 7 + 107) / (2 * 107))],
])
assert.equal(r.steuer[0].betragCent, 200)
assert.equal(r.steuer[1].betragCent, 82)
})
test('Versand bei nur 0-%-Artikeln erzeugt keine Steuer', () => {
const a = produkt('a', { preisCent: 1000, steuersatz: 0 })
const r = rechne({ produkte: karte(a), positionen: eine(a) })
assert.deepEqual(r.steuer, [])
assert.equal(r.gesamtCent, 1490)
})
test('Kleinunternehmer: keine Steuer, Hinweis nach Paragraph 19', () => {
const a = produkt('a', { preisCent: 1000, steuersatz: 0 })
const r = rechne({ produkte: karte(a), positionen: eine(a), einstellungen: einstellungen({ kleinunternehmer: true }) })
assert.deepEqual(r.steuer, [])
assert.equal(r.texte.steuerHinweis, 'Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.')
// auch wenn ein Artikel (fehlerhaft) einen Satz traegt
const b = produkt('b', { preisCent: 1000, steuersatz: 19 })
const r2 = rechne({ produkte: karte(b), positionen: eine(b), einstellungen: einstellungen({ kleinunternehmer: true }) })
assert.deepEqual(r2.steuer, [])
})
test('Steuerart noch nicht gewählt (null) rechnet wie Regelbesteuerung, nie wie Kleinunternehmer', () => {
const a = produkt('a', { preisCent: 1190, steuersatz: 19 })
const r = rechne({ produkte: karte(a), positionen: eine(a), einstellungen: einstellungen({ kleinunternehmer: null }) })
assert.ok(r.steuer.length > 0)
assert.doesNotMatch(r.texte.steuerHinweis, /§ 19/)
})
test('Versandkosten: Oesterreich, Schwelle gleich ist kostenlos, knapp darunter nicht', () => {
const e = einstellungen({ versand: { aktiv: true, laender: ['DE', 'AT'], kostenCent: { DE: 490, AT: 990 }, freiAbCent: 5000 } })
const p = (cent) => produkt('a', { preisCent: cent })
const lauf = (cent, land) => {
const a = p(cent)
return berechne({ produkte: karte(a), positionen: eine(a), lieferart: 'versand', land, einstellungen: e })
}
assert.equal(lauf(2000, 'AT').versandCent, 990)
assert.equal(lauf(2000, 'AT').texte.versand, euro(990))
assert.equal(lauf(4999, 'DE').versandCent, 490)
assert.equal(lauf(5000, 'DE').versandCent, 0)
assert.equal(lauf(5000, 'AT').versandCent, 0)
assert.equal(lauf(5001, 'DE').texte.versand, 'kostenlos')
assert.equal(lauf(5000, 'DE').gesamtCent, 5000)
})
test('freiAbCent 0 heisst immer kostenlos', () => {
const a = produkt('a')
const e = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: 0 } })
assert.equal(berechne({ produkte: karte(a), positionen: eine(a), lieferart: 'versand', land: 'DE', einstellungen: e }).versandCent, 0)
})
test('Abholung: keine Versandkosten, eigener Text, Land ignoriert', () => {
const a = produkt('a', { preisCent: 1190 })
const r = berechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: einstellungen() })
assert.equal(r.ok, true)
assert.equal(r.versandCent, 0)
assert.equal(r.gesamtCent, 1190)
assert.equal(r.texte.versand, 'Abholung – kostenlos')
assert.equal(r.lieferart, 'abholung')
})
test('Abholung nur-Abholung-Artikel sind bei Abholung erlaubt', () => {
const a = produkt('a', { lieferart: 'abholung' })
const r = berechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: einstellungen() })
assert.equal(r.ok, true)
})
test('Varianten: Preis und Text der Variante', () => {
const r = rechne({ produkte: karte(shirt), positionen: eine(shirt, 2, 'sizebbbb') })
assert.equal(r.ok, true)
assert.equal(r.positionen[0].varianteId, 'sizebbbb')
assert.equal(r.positionen[0].varianteText, 'Größe: L')
assert.equal(r.positionen[0].einzelCent, 2200)
assert.equal(r.positionen[0].summeCent, 4400)
})
test('Fehler leer: leere, fehlende oder keine Liste', () => {
const a = produkt('a')
for (const positionen of [[], undefined, null, 'x', {}]) {
const r = rechne({ produkte: karte(a), positionen })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'leer')
assert.match(r.fehler.text, /Warenkorb/)
}
})
test('Fehler zu_viele: 51 verschiedene Positionen, aber 50 sind erlaubt und Duplikate zaehlen einmal', () => {
const liste = Array.from({ length: 51 }, (_, i) => produkt(`p${i}`))
const positionen = (n) => liste.slice(0, n).map((p) => ({ produktId: p.id, menge: 1 }))
const r51 = rechne({ produkte: karte(...liste), positionen: positionen(51) })
assert.equal(r51.ok, false)
assert.equal(r51.fehler.code, 'zu_viele')
assert.equal(rechne({ produkte: karte(...liste), positionen: positionen(50) }).ok, true)
// 51 Eintraege, aber nur 50 verschiedene
const dup = [...positionen(50), { produktId: 'p0', menge: 1 }]
const r = rechne({ produkte: karte(...liste), positionen: dup })
assert.equal(r.ok, true)
assert.equal(r.positionen.length, 50)
assert.equal(r.positionen[0].menge, 2)
})
test('Fehler menge: 0, 100, negativ, Dezimal, Text, NaN, fehlend', () => {
const a = produkt('a')
for (const menge of [0, 100, -1, 1.5, '2', NaN, Infinity, undefined, null]) {
const r = rechne({ produkte: karte(a), positionen: [{ produktId: 'a', menge }] })
assert.equal(r.ok, false, String(menge))
assert.equal(r.fehler.code, 'menge', String(menge))
assert.equal(r.fehler.produktId, 'a')
}
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 99) }).ok, true)
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 1) }).ok, true)
})
test('Zusammengefasste Menge ueber 99 ist ein Mengenfehler', () => {
const a = produkt('a')
const r = rechne({ produkte: karte(a), positionen: [{ produktId: 'a', menge: 60 }, { produktId: 'a', menge: 40 }] })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'menge')
assert.equal(r.fehler.produktId, 'a')
})
test('Duplikate gleicher Artikel werden zusammengefasst, Varianten bleiben getrennt', () => {
const a = produkt('a', { preisCent: 300 })
const r = rechne({
produkte: karte(a, shirt),
positionen: [
{ produktId: 'a', menge: 1 },
{ produktId: 'shirt', varianteId: 'sizeaaaa', menge: 1 },
{ produktId: 'a', menge: 2 },
{ produktId: 'shirt', varianteId: 'sizebbbb', menge: 1 },
{ produktId: 'shirt', varianteId: 'sizeaaaa', menge: 1 },
],
})
assert.equal(r.ok, true)
assert.deepEqual(r.positionen.map((p) => [p.produktId, p.varianteId, p.menge]), [
['a', null, 3], ['shirt', 'sizeaaaa', 2], ['shirt', 'sizebbbb', 1],
])
assert.equal(r.zwischensummeCent, 900 + 4000 + 2200)
})
test('Fehler nicht_verfuegbar: unbekannt, inaktiv, ungueltige Position', () => {
const a = produkt('a')
const aus = produkt('aus', { aktiv: false })
for (const pos of [{ produktId: 'x', menge: 1 }, { produktId: 'aus', menge: 1 }, { produktId: '__proto__', menge: 1 }, { produktId: 'constructor', menge: 1 }]) {
const r = rechne({ produkte: karte(a, aus), positionen: [pos] })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'nicht_verfuegbar')
}
assert.equal(rechne({ produkte: karte(a), positionen: [null] }).fehler.code, 'nicht_verfuegbar')
assert.equal(rechne({ produkte: karte(a), positionen: [{ menge: 1 }] }).fehler.code, 'nicht_verfuegbar')
assert.equal(rechne({ produkte: karte(a), positionen: [{ produktId: 5, menge: 1 }] }).fehler.code, 'nicht_verfuegbar')
assert.equal(rechne({ produkte: karte(aus), positionen: eine(aus) }).fehler.produktId, 'aus')
})
test('Fehler variante: fehlt, unbekannt, bei Artikel ohne Varianten', () => {
const a = produkt('a')
const ohne = rechne({ produkte: karte(shirt), positionen: eine(shirt) })
assert.equal(ohne.fehler.code, 'variante')
assert.match(ohne.fehler.text, /Größe/)
assert.equal(rechne({ produkte: karte(shirt), positionen: eine(shirt, 1, 'gibtsnich') }).fehler.code, 'variante')
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 1, 'sizeaaaa') }).fehler.code, 'variante')
assert.equal(ohne.fehler.produktId, 'shirt')
assert.equal(rechne({ produkte: karte(shirt), positionen: eine(shirt, 1, 'sizeaaaa') }).ok, true)
})
test('Bestand: unbegrenzt, genau ausreichend, zu wenig mit Restmenge, ausverkauft', () => {
const frei = produkt('frei', { bestand: null })
assert.equal(rechne({ produkte: karte(frei), positionen: eine(frei, 99) }).ok, true)
const a = produkt('a', { bestand: 3 })
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 3) }).ok, true)
const zuwenig = rechne({ produkte: karte(a), positionen: eine(a, 4) })
assert.equal(zuwenig.ok, false)
assert.equal(zuwenig.fehler.code, 'ausverkauft')
assert.equal(zuwenig.fehler.produktId, 'a')
assert.match(zuwenig.fehler.text, /Artikel a/)
assert.match(zuwenig.fehler.text, /nur noch 3/)
const null0 = produkt('n', { bestand: 0 })
const aus = rechne({ produkte: karte(null0), positionen: eine(null0) })
assert.equal(aus.fehler.code, 'ausverkauft')
assert.match(aus.fehler.text, /Artikel n/)
assert.match(aus.fehler.text, /ausverkauft/)
})
test('Bestand: gilt fuer die zusammengefasste Menge', () => {
const a = produkt('a', { bestand: 3 })
const r = rechne({ produkte: karte(a), positionen: [{ produktId: 'a', menge: 2 }, { produktId: 'a', menge: 2 }] })
assert.equal(r.fehler.code, 'ausverkauft')
})
test('Reservierungen verringern den Bestand (Artikel und Variante)', () => {
const a = produkt('a', { bestand: 5 })
const res = new Map([['a', 3]])
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 2), reserviert: res }).ok, true)
const r = rechne({ produkte: karte(a), positionen: eine(a, 3), reserviert: res })
assert.equal(r.fehler.code, 'ausverkauft')
assert.match(r.fehler.text, /nur noch 2/)
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 1), reserviert: new Map([['a', 7]]) }).fehler.code, 'ausverkauft')
const vres = new Map([['shirt:sizeaaaa', 4]])
assert.equal(rechne({ produkte: karte(shirt), positionen: eine(shirt, 1, 'sizeaaaa'), reserviert: vres }).ok, true)
const rv = rechne({ produkte: karte(shirt), positionen: eine(shirt, 2, 'sizeaaaa'), reserviert: vres })
assert.equal(rv.fehler.code, 'ausverkauft')
assert.match(rv.fehler.text, /T-Shirt/)
assert.match(rv.fehler.text, /Größe: M/)
// unbegrenzte Variante bleibt unberuehrt, andere Schluessel stoeren nicht
assert.equal(rechne({ produkte: karte(shirt), positionen: eine(shirt, 50, 'sizebbbb'), reserviert: new Map([['shirt:sizebbbb', 99], ['shirt', 99]]) }).ok, true)
})
test('Variantenbestand wird je Variante geprueft', () => {
const r = rechne({ produkte: karte(shirt), positionen: eine(shirt, 6, 'sizeaaaa') })
assert.equal(r.fehler.code, 'ausverkauft')
assert.match(r.fehler.text, /nur noch 5/)
})
test('Fehler lieferart: unbekannt, Versand aus, Abholung aus', () => {
const a = produkt('a')
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'post' }).fehler.code, 'lieferart')
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: undefined }).fehler.code, 'lieferart')
const keinVersand = einstellungen({ versand: { aktiv: false, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: null } })
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), einstellungen: keinVersand }).fehler.code, 'lieferart')
const keineAbholung = einstellungen({ abholung: { aktiv: false, adresse: '', hinweis: '' } })
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: keineAbholung }).fehler.code, 'lieferart')
// Abholung geht, obwohl Versand aus ist
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: einstellungen({ versand: { aktiv: false, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: null } }) }).ok, true)
})
test('Fehler land: nicht geliefert, fehlend, ungueltig', () => {
const a = produkt('a')
const nurDe = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: null } })
for (const land of ['AT', 'CH', '', undefined, null, 'de']) {
const r = rechne({ produkte: karte(a), positionen: eine(a), land, einstellungen: nurDe })
assert.equal(r.fehler.code, 'land', String(land))
}
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), land: 'AT' }).ok, true)
})
test('Fehler nur_abholung: Versand mit Nur-Abholung-Artikel', () => {
const a = produkt('a')
const abholer = produkt('abholer', { lieferart: 'abholung' })
const r = rechne({ produkte: karte(a, abholer), positionen: [...eine(a), ...eine(abholer)] })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'nur_abholung')
assert.equal(r.fehler.produktId, 'abholer')
assert.match(r.fehler.text, /Artikel abholer/)
})
test('Fehler mindestbestellwert: Grenze gleich ist erlaubt, Versandkosten zaehlen nicht mit', () => {
const a = produkt('a', { preisCent: 1000 })
const e = (cent) => einstellungen({ mindestbestellCent: cent })
const r = rechne({ produkte: karte(a), positionen: eine(a), einstellungen: e(1001) })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'mindestbestellwert')
assert.ok(r.fehler.text.includes(euro(1001)))
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), einstellungen: e(1000) }).ok, true)
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), einstellungen: e(null) }).ok, true)
// gilt auch bei Abholung
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: e(1001) }).fehler.code, 'mindestbestellwert')
})
test('Fehler mindestbetrag: Gesamtbetrag unter 0,50 EUR (Stripe-Minimum) wird abgelehnt, 0,50 EUR genau ist erlaubt; Versandkosten zaehlen mit', () => {
const klein = produkt('klein', { preisCent: 30 })
const r = rechne({ produkte: karte(klein), positionen: eine(klein), lieferart: 'abholung' })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'mindestbetrag')
assert.equal(r.fehler.text, 'Der Bestellwert muss mindestens 0,50 € betragen.')
const genau = produkt('genau', { preisCent: 50 })
assert.equal(rechne({ produkte: karte(genau), positionen: eine(genau), lieferart: 'abholung' }).ok, true)
const knapp = produkt('knapp', { preisCent: 49 })
assert.equal(rechne({ produkte: karte(knapp), positionen: eine(knapp), lieferart: 'abholung' }).fehler.code, 'mindestbetrag')
// mit Versandkosten ueber der Grenze
const e = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: null } })
assert.equal(rechne({ produkte: karte(klein), positionen: eine(klein), lieferart: 'versand', land: 'DE', einstellungen: e }).ok, true)
})
test('Reihenfolge der Pruefungen: Artikelfehler vor Lieferart, Lieferart vor Mindestbestellwert', () => {
const a = produkt('a', { bestand: 0 })
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'post' }).fehler.code, 'ausverkauft')
const b = produkt('b')
assert.equal(rechne({ produkte: karte(b), positionen: eine(b), lieferart: 'post', einstellungen: einstellungen({ mindestbestellCent: 99999 }) }).fehler.code, 'lieferart')
})
test('Eingaben werden nicht veraendert und das Ergebnis teilt keine Referenzen', () => {
const a = produkt('a', { bestand: 10 })
const positionen = [{ produktId: 'a', menge: 2 }, { produktId: 'a', menge: 1 }]
const e = einstellungen()
const vorher = JSON.stringify({ a, positionen, e })
const r1 = rechne({ produkte: karte(a), positionen, einstellungen: e })
assert.equal(JSON.stringify({ a, positionen, e }), vorher)
const r2 = rechne({ produkte: karte(a), positionen, einstellungen: e })
assert.deepEqual(r1, r2)
assert.notEqual(r1.positionen, r2.positionen)
})
test('Gesamtsumme ist immer ganzzahlig und gleich Zwischensumme plus Versand', () => {
const a = produkt('a', { preisCent: 333, steuersatz: 7 })
const b = produkt('b', { preisCent: 777, steuersatz: 19 })
const r = rechne({ produkte: karte(a, b), positionen: [{ produktId: 'a', menge: 7 }, { produktId: 'b', menge: 3 }], land: 'AT' })
assert.ok(Number.isInteger(r.gesamtCent))
assert.equal(r.gesamtCent, r.zwischensummeCent + r.versandCent)
assert.equal(r.zwischensummeCent, 333 * 7 + 777 * 3)
const summe = r.steuer.reduce((s, x) => s + x.betragCent, 0)
assert.ok(summe < r.gesamtCent)
})