Shop Review A I3: Artikel-Editor ueberschreibt den Bestand nicht mehr (bestandGeladen, Bestand nur bei echter Aenderung schreiben)
Der Editor sendet neben bestand den beim Laden gesehenen Wert (bestandGeladen, je Variantenwert mit id). speichereProdukt behaelt unter der Artikelsperre den aktuell gespeicherten Stand, wenn gesendet gleich geladen ist, und schreibt nur vom Haendler geaenderte Bestaende. bestandGeladen wird nie gespeichert; ohne den Wert (alter Browser) gilt der gesendete Bestand wie bisher. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9c2897a0b0
commit
55fe1c567f
6 files changed
+215
-10
No files matched your search
@@ -396,3 +396,73 @@ test('parallele Anlage vergibt verschiedene sort-Werte', async () => {
|
||||
const docs = await Promise.all(Array.from({ length: 5 }, (_, i) => speichereProdukt(shop, null, ok(basis({ name: `P${i}` })))))
|
||||
assert.deepEqual(docs.map((d) => d.sort).sort(), [0, 1, 2, 3, 4])
|
||||
})
|
||||
|
||||
// ---- Bestand beim Bearbeiten (Review A, I3): nur ein vom Haendler geaenderter Bestand wird geschrieben ------------------
|
||||
|
||||
const bestandVon = async (id) => JSON.parse((await holeProdukt('shop-a', id)).produkt)
|
||||
|
||||
async function mitGeladen(id, roh, normalisiert = ok(roh)) {
|
||||
return speichereProdukt(SHOP, id, normalisiert, { geladen: P.leseBestandGeladen(roh) })
|
||||
}
|
||||
|
||||
test('leseBestandGeladen: Zahl, null und leer werden gelesen, Unbrauchbares gilt als nicht gesendet', () => {
|
||||
assert.deepEqual(P.leseBestandGeladen({ bestandGeladen: 5 }), { hat: true, bestand: 5, werte: new Map() })
|
||||
assert.deepEqual(P.leseBestandGeladen({ bestandGeladen: null }), { hat: true, bestand: null, werte: new Map() })
|
||||
assert.equal(P.leseBestandGeladen({ bestandGeladen: '3' }).bestand, 3)
|
||||
assert.equal(P.leseBestandGeladen({ bestandGeladen: '' }).bestand, null)
|
||||
assert.equal(P.leseBestandGeladen({ bestandGeladen: 'abc' }).hat, false)
|
||||
assert.equal(P.leseBestandGeladen({}).hat, false)
|
||||
assert.equal(P.leseBestandGeladen(null).hat, false)
|
||||
const w = P.leseBestandGeladen({ varianten: { werte: [{ id: 'aaaaaaaa', bestandGeladen: 2 }, { id: 'bbbbbbbb', bestandGeladen: null }, { id: 'cccccccc' }, { bestandGeladen: 1 }] } })
|
||||
assert.deepEqual([...w.werte], [['aaaaaaaa', 2], ['bbbbbbbb', null]])
|
||||
})
|
||||
|
||||
test('speichern I3: unveraenderter Bestand ueberschreibt nicht den inzwischen verkauften Stand', async () => {
|
||||
const a = await speichereProdukt(SHOP, null, ok(basis({ name: 'Kerze', bestand: 5 })))
|
||||
// Es wurden 3 Stueck verkauft
|
||||
await speichereProdukt(SHOP, a.$id, { ...(await bestandVon(a.$id)), bestand: 2 })
|
||||
const g = await mitGeladen(a.$id, basis({ name: 'Kerze neu', bestand: 5, bestandGeladen: 5 }))
|
||||
assert.equal(JSON.parse(g.produkt).name, 'Kerze neu')
|
||||
assert.equal((await bestandVon(a.$id)).bestand, 2)
|
||||
})
|
||||
|
||||
test('speichern I3: geaenderter Bestand wird geschrieben (auch auf unbegrenzt und auf 0)', async () => {
|
||||
const a = await speichereProdukt(SHOP, null, ok(basis({ name: 'Kerze', bestand: 5 })))
|
||||
await speichereProdukt(SHOP, a.$id, { ...(await bestandVon(a.$id)), bestand: 2 })
|
||||
await mitGeladen(a.$id, basis({ name: 'Kerze', bestand: 9, bestandGeladen: 5 }))
|
||||
assert.equal((await bestandVon(a.$id)).bestand, 9)
|
||||
await mitGeladen(a.$id, basis({ name: 'Kerze', bestand: '', bestandGeladen: 9 }))
|
||||
assert.equal((await bestandVon(a.$id)).bestand, null)
|
||||
await mitGeladen(a.$id, basis({ name: 'Kerze', bestand: 0, bestandGeladen: null }))
|
||||
assert.equal((await bestandVon(a.$id)).bestand, 0)
|
||||
})
|
||||
|
||||
test('speichern I3: ohne bestandGeladen gilt der gesendete Bestand wie bisher', async () => {
|
||||
const a = await speichereProdukt(SHOP, null, ok(basis({ name: 'Kerze', bestand: 5 })))
|
||||
await speichereProdukt(SHOP, a.$id, ok(basis({ name: 'Kerze', bestand: 7 })))
|
||||
assert.equal((await bestandVon(a.$id)).bestand, 7)
|
||||
})
|
||||
|
||||
test('speichern I3: Variantenbestand je Wert - unveraenderte Werte behalten den aktuellen Stand, geaenderte und neue Werte nicht', async () => {
|
||||
const roh = (w) => basis({ name: 'Shirt', preisCent: undefined, varianten: { name: 'Größe', werte: w } })
|
||||
const a = await speichereProdukt(SHOP, null, ok(roh([{ id: 'aaaaaaaa', name: 'S', preisCent: 800, bestand: 5 }, { id: 'bbbbbbbb', name: 'M', preisCent: 900, bestand: 5 }, { id: 'cccccccc', name: 'L', preisCent: 900, bestand: null }])))
|
||||
// S: 2 verkauft, M: 5 verkauft
|
||||
const stand = await bestandVon(a.$id)
|
||||
stand.varianten.werte[0].bestand = 3
|
||||
stand.varianten.werte[1].bestand = 0
|
||||
await speichereProdukt(SHOP, a.$id, stand)
|
||||
await mitGeladen(a.$id, roh([
|
||||
{ id: 'aaaaaaaa', name: 'S', preisCent: 800, bestand: 5, bestandGeladen: 5 }, // unberuehrt
|
||||
{ id: 'bbbbbbbb', name: 'M', preisCent: 900, bestand: 20, bestandGeladen: 5 }, // Haendler hat aufgestockt
|
||||
{ id: 'cccccccc', name: 'L', preisCent: 900, bestand: null, bestandGeladen: null }, // unbegrenzt bleibt
|
||||
{ name: 'XL', preisCent: 900, bestand: 4 }, // neuer Wert
|
||||
]))
|
||||
const nachher = (await bestandVon(a.$id)).varianten.werte
|
||||
assert.deepEqual(nachher.map((w) => [w.name, w.bestand]), [['S', 3], ['M', 20], ['L', null], ['XL', 4]])
|
||||
})
|
||||
|
||||
test('speichern I3: Bestand beim Anlegen bleibt wie gesendet; bestandGeladen landet nie im gespeicherten Artikel', async () => {
|
||||
const a = await mitGeladen(null, basis({ name: 'Neu', bestand: 4, bestandGeladen: 4 }))
|
||||
assert.equal((await bestandVon(a.$id)).bestand, 4)
|
||||
assert.ok(!JSON.stringify(await bestandVon(a.$id)).includes('Geladen'))
|
||||
})
|
||||
@@ -241,6 +241,29 @@ test('Einstellungen speichern leert den 30-s-Speicher von shopPerSiteKey', async
|
||||
|
||||
// ---------------------------------------------------------------- Artikel
|
||||
|
||||
test('Artikel I3: Speichern ohne Bestandsaenderung behaelt den inzwischen verkauften Bestand, geaenderter Bestand wird geschrieben', async () => {
|
||||
await aktiviere()
|
||||
await schreiben('PUT', 'einstellungen', { einstellungen: EINSTELLUNGEN })
|
||||
const neu = await schreiben('POST', 'produkte', { produkt: { ...ARTIKEL, bestand: 5, bestandGeladen: 5 } })
|
||||
assert.equal(neu.status, 201)
|
||||
const id = neu.body.produkt.id
|
||||
assert.equal(neu.body.produkt.bestand, 5)
|
||||
assert.equal(neu.body.produkt.bestandGeladen, undefined)
|
||||
// Zwischenzeitlich wurden 3 Stueck verkauft
|
||||
const doc = aw.docs('shopProducts').get(id)
|
||||
await updateDocument(config.collections.shopProducts, id, { produkt: JSON.stringify({ ...JSON.parse(doc.produkt), bestand: 2 }) })
|
||||
|
||||
const nurText = await schreiben('PUT', `produkte/${id}`, { produkt: { ...ARTIKEL, name: 'Tasse 2', bestand: 5, bestandGeladen: 5 } })
|
||||
assert.equal(nurText.status, 200)
|
||||
assert.deepEqual([nurText.body.produkt.name, nurText.body.produkt.bestand], ['Tasse 2', 2])
|
||||
assert.ok(!aw.docs('shopProducts').get(id).produkt.includes('bestandGeladen'))
|
||||
|
||||
const aufgestockt = await schreiben('PUT', `produkte/${id}`, { produkt: { ...ARTIKEL, bestand: 12, bestandGeladen: 5 } })
|
||||
assert.equal(aufgestockt.body.produkt.bestand, 12)
|
||||
const ohneMarker = await schreiben('PUT', `produkte/${id}`, { produkt: { ...ARTIKEL, bestand: 7 } })
|
||||
assert.equal(ohneMarker.body.produkt.bestand, 7, 'alter Browser ohne bestandGeladen: wie bisher')
|
||||
})
|
||||
|
||||
test('Artikel: anlegen 201, lesen, aendern, Fehler 400 je Feld, unbekannte ID 404, Kleinunternehmer-Steuer', async () => {
|
||||
await aktiviere()
|
||||
await schreiben('PUT', 'einstellungen', { einstellungen: EINSTELLUNGEN }) // Kleinunternehmer => 0 %
|
||||
|
||||
@@ -38,6 +38,14 @@ const artikel = (id, name, extra = {}) => {
|
||||
return a
|
||||
}
|
||||
|
||||
/** Wie koerper, dazu der beim Laden gesehene Bestand (bestandGeladen, je Variantenwert mit id) - so geht ein bestehender Artikel zum Server. */
|
||||
const geladenKoerper = (a, extra = {}) => {
|
||||
const k = koerper(a)
|
||||
k.bestandGeladen = a.bestand ?? null
|
||||
if (k.varianten) k.varianten = { ...k.varianten, werte: k.varianten.werte.map((w) => ({ ...w, bestandGeladen: w.bestand ?? null })) }
|
||||
return { ...k, ...extra }
|
||||
}
|
||||
|
||||
/** Der Artikel so, wie er beim Speichern zum Server geht (ohne id, sort, bildUrls). */
|
||||
const koerper = (a, extra = {}) => {
|
||||
const { id, sort, bildUrls, ...rest } = a
|
||||
@@ -168,7 +176,7 @@ test('Schalter „aktiv“: PUT mit dem ganzen Artikel und umgedrehtem aktiv, da
|
||||
assert.equal(rest.length, 0)
|
||||
assert.equal(put.method, 'PUT')
|
||||
assert.equal(put.url, '/api/shop/produkte/a1')
|
||||
assert.deepEqual(put.body, { projectId: P, produkt: koerper(DREI()[0], { aktiv: false }) })
|
||||
assert.deepEqual(put.body, { projectId: P, produkt: geladenKoerper(DREI()[0], { aktiv: false }) })
|
||||
assert.equal(knopf(fx, zeile(fx, panel, 'a1'), 'aktiv').checked, false)
|
||||
assert.match(meldungen.at(-1).text, /Mütze.*inaktiv/)
|
||||
}))
|
||||
@@ -365,7 +373,7 @@ test('Bearbeiten: Werte-IDs der Varianten bleiben im Körper erhalten', mit({
|
||||
await fx.klick(knopf(fx, zeile(fx, panel, 'a3'), 'bearbeiten'))
|
||||
await fx.klick(knopf(fx, fx.finde(panel, 'form'), 'speichern'))
|
||||
assert.deepEqual(aenderungen(fx)[0].body.produkt.varianten.werte.map((w) => w.id), ['aaaaaaaa', 'bbbbbbbb'])
|
||||
assert.deepEqual(aenderungen(fx)[0].body.produkt, koerper(DREI()[2]))
|
||||
assert.deepEqual(aenderungen(fx)[0].body.produkt, geladenKoerper(DREI()[2]))
|
||||
}))
|
||||
|
||||
/* --------------------------------------------------------------------- Bilder --- */
|
||||
@@ -576,3 +584,32 @@ test('Statik: kein innerHTML, outerHTML, insertAdjacentHTML oder document.write;
|
||||
}
|
||||
assert.doesNotMatch(code, /from '\.\/termine/, 'keine Importe aus termine*.js')
|
||||
})
|
||||
|
||||
test('Bearbeiten I3: der Körper trägt den beim Laden gesehenen Bestand (bestandGeladen) neben dem Feldwert, je Variantenwert; neue Werte und neue Artikel nicht', mit({
|
||||
produkte: DREI(),
|
||||
sonst: (a) => (a.method === 'PUT' ? { body: { produkt: artikel('a3', 'Handschuhe') } } : null),
|
||||
}, async ({ fx, panel }) => {
|
||||
await fx.klick(knopf(fx, zeile(fx, panel, 'a3'), 'bearbeiten'))
|
||||
const form = fx.finde(panel, 'form')
|
||||
await fx.setze(feldVon(fx, form, 'varianten.werte.0.bestand'), '9')
|
||||
await fx.klick(knopf(fx, form, 'wert-neu'))
|
||||
await fx.setze(feldVon(fx, form, 'varianten.werte.2.name'), 'XL')
|
||||
await fx.setze(feldVon(fx, form, 'varianten.werte.2.preisCent'), '11')
|
||||
await fx.setze(feldVon(fx, form, 'varianten.werte.2.bestand'), '3')
|
||||
await fx.klick(knopf(fx, form, 'speichern'))
|
||||
const { produkt } = aenderungen(fx)[0].body
|
||||
assert.equal(produkt.bestandGeladen, null)
|
||||
assert.deepEqual(produkt.varianten.werte.map((w) => [w.id, w.bestand, w.bestandGeladen]), [['aaaaaaaa', 9, 2], ['bbbbbbbb', null, null], [undefined, 3, undefined]])
|
||||
}))
|
||||
|
||||
test('Bearbeiten I3: Einzelbestand - geändert wird der neue Wert gesendet, geladen bleibt der alte', mit({
|
||||
produkte: DREI(),
|
||||
sonst: (a) => (a.method === 'PUT' ? { body: { produkt: artikel('a1', 'Mütze') } } : null),
|
||||
}, async ({ fx, panel }) => {
|
||||
await fx.klick(knopf(fx, zeile(fx, panel, 'a1'), 'bearbeiten'))
|
||||
const form = fx.finde(panel, 'form')
|
||||
await fx.setze(feldVon(fx, form, 'bestand'), '8')
|
||||
await fx.klick(knopf(fx, form, 'speichern'))
|
||||
const { produkt } = aenderungen(fx)[0].body
|
||||
assert.deepEqual([produkt.bestand, produkt.bestandGeladen], [8, 5])
|
||||
}))
|
||||
Reference in new issue
Block a user