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
@@ -15,7 +15,7 @@ import { uploadSlot } from './chat.js'
|
||||
import { istVollstaendig, speichereEinstellungen } from '../services/shop/einstellungen.js'
|
||||
import { bereitschaft, istGeoeffnet, ladeShop, leereCache } from '../services/shop/kontext.js'
|
||||
import {
|
||||
bildIdsInGebrauch, holeProdukt, listeProdukte, loescheProdukt, normalisiereProdukt, parseProdukt, sortiere, speichereProdukt,
|
||||
bildIdsInGebrauch, holeProdukt, leseBestandGeladen, listeProdukte, loescheProdukt, normalisiereProdukt, parseProdukt, sortiere, speichereProdukt,
|
||||
} from '../services/shop/produkte.js'
|
||||
import {
|
||||
BESTAETIGUNG_TEXT, GEWAEHRLEISTUNG_BILD, gewaehrleistungsHinweis, musterAgb, musterWiderrufsbelehrung, musterWiderrufsformular,
|
||||
@@ -333,7 +333,7 @@ async function speichereArtikel(req, res, id) {
|
||||
const meta = await bildMeta(shop.siteKey, bildId)
|
||||
if (!meta || meta.shopId !== shop.id) throw eingabeFehler({ bilder: 'Eines der Bilder gibt es nicht (mehr). Bitte lade es neu hoch.' })
|
||||
}
|
||||
const gespeichert = await speichereProdukt({ shopId: shop.id, customerId }, id, produkt)
|
||||
const gespeichert = await speichereProdukt({ shopId: shop.id, customerId }, id, produkt, { geladen: leseBestandGeladen(req.body?.produkt) })
|
||||
await beanspruche(shop.siteKey, neueBilder)
|
||||
await raeumeBilderAuf(shop, vorher.filter((b) => !neueBilder.includes(b)))
|
||||
return gespeichert
|
||||
|
||||
@@ -424,19 +424,74 @@ export async function holeProdukt(shopId, id) {
|
||||
return doc && doc.shopId === shopId ? doc : null
|
||||
}
|
||||
|
||||
const bestandRoh = (wert) => {
|
||||
if (wert === null || wert === undefined || (typeof wert === 'string' && wert.trim() === '')) return { ok: true, wert: null }
|
||||
const n = ganzeZahl(wert)
|
||||
return n !== null && n >= 0 ? { ok: true, wert: n } : { ok: false }
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest aus dem rohen Artikel des Browsers, welchen Bestand er beim Laden gesehen hat (bestandGeladen am Artikel, je Variantenwert
|
||||
* mit id am Wert). hat: false, wenn der Browser nichts (Brauchbares) gesendet hat. Das Ergebnis geht an speichereProdukt.
|
||||
* @returns {{ hat: boolean, bestand?: number|null, werte: Map<string, number|null> }}
|
||||
*/
|
||||
export function leseBestandGeladen(roh) {
|
||||
const werte = new Map()
|
||||
const liste = hole(hole(roh, 'varianten'), 'werte')
|
||||
if (Array.isArray(liste)) {
|
||||
for (const w of liste) {
|
||||
const id = hole(w, 'id')
|
||||
if (typeof id !== 'string' || !WERT_ID_RE.test(id) || !Object.hasOwn(w, 'bestandGeladen')) continue
|
||||
const b = bestandRoh(w.bestandGeladen)
|
||||
if (b.ok) werte.set(id, b.wert)
|
||||
}
|
||||
}
|
||||
if (!istObjekt(roh) || !Object.hasOwn(roh, 'bestandGeladen')) return { hat: false, werte }
|
||||
const b = bestandRoh(roh.bestandGeladen)
|
||||
return b.ok ? { hat: true, bestand: b.wert, werte } : { hat: false, werte }
|
||||
}
|
||||
|
||||
/** Bestand uebernehmen: hat der Haendler den Wert nicht angefasst (gesendet == geladen), bleibt der aktuell gespeicherte Stand. */
|
||||
function uebernehmeBestand(neu, alt, geladen) {
|
||||
if (!istObjekt(alt) || !istObjekt(geladen)) return neu
|
||||
const kopie = { ...neu }
|
||||
if (geladen.hat && !neu.varianten && !alt.varianten && (neu.bestand ?? null) === geladen.bestand) kopie.bestand = alt.bestand ?? null
|
||||
if (neu.varianten && Array.isArray(neu.varianten.werte) && alt.varianten && Array.isArray(alt.varianten.werte) && geladen.werte instanceof Map) {
|
||||
const aktuell = new Map(alt.varianten.werte.map((w) => [w.id, w]))
|
||||
kopie.varianten = {
|
||||
...neu.varianten,
|
||||
werte: neu.varianten.werte.map((w) => {
|
||||
const vorher = aktuell.get(w.id)
|
||||
return vorher && geladen.werte.has(w.id) && (w.bestand ?? null) === geladen.werte.get(w.id) ? { ...w, bestand: vorher.bestand ?? null } : w
|
||||
}),
|
||||
}
|
||||
}
|
||||
return kopie
|
||||
}
|
||||
|
||||
/**
|
||||
* Legt einen Artikel an (id = null, ans Ende sortiert, hoechstens config.shop.maxArtikel) oder aktualisiert ihn (Sortierung bleibt).
|
||||
* produkt ist das Ergebnis von normalisiereProdukt. Fehler tragen .status (400/404/409) und einen deutschen Text.
|
||||
* opt.geladen (leseBestandGeladen): Beim Aendern behalten Bestaende, die der Haendler nicht angefasst hat (gesendet == geladen),
|
||||
* den aktuell gespeicherten Stand, damit zwischenzeitliche Verkaeufe nicht ueberschrieben werden. Ohne geladen gilt der gesendete Bestand.
|
||||
*/
|
||||
export async function speichereProdukt({ shopId, customerId }, id, produkt) {
|
||||
export async function speichereProdukt({ shopId, customerId }, id, produkt, { geladen } = {}) {
|
||||
if (!istObjekt(produkt) || typeof produkt.name !== 'string' || produkt.name === '') throw fehlerMit(400, 'Der Artikel ist unvollständig.')
|
||||
const json = JSON.stringify(produkt)
|
||||
if (json.length > MAX_JSON) throw fehlerMit(400, 'Der Artikel ist zu umfangreich. Bitte kürze die Beschreibung oder lege weniger Varianten an.')
|
||||
const pruefeGroesse = (obj) => {
|
||||
const json = JSON.stringify(obj)
|
||||
if (json.length > MAX_JSON) throw fehlerMit(400, 'Der Artikel ist zu umfangreich. Bitte kürze die Beschreibung oder lege weniger Varianten an.')
|
||||
return json
|
||||
}
|
||||
const json = pruefeGroesse(produkt)
|
||||
const daten = { name: produkt.name, aktiv: produkt.aktiv !== false, produkt: json }
|
||||
return withLocks([sperre(shopId)], async () => {
|
||||
if (id) {
|
||||
const vorhanden = await holeProdukt(shopId, id)
|
||||
if (!vorhanden) throw fehlerMit(404, 'Diesen Artikel gibt es nicht (mehr).')
|
||||
if (geladen) {
|
||||
const zusammen = uebernehmeBestand(produkt, parseProdukt(vorhanden), geladen)
|
||||
return updateDocument(col(), id, { ...daten, produkt: pruefeGroesse(zusammen) })
|
||||
}
|
||||
return updateDocument(col(), id, daten)
|
||||
}
|
||||
const alle = await listeProdukte(shopId)
|
||||
|
||||
Reference in new issue
Block a user