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) })