Danke fürs probieren – warum das bei dir immer funktioniert, aber bei mir nicht, ist mir weiterhin ein Rätsel. Gestern habe ich zuerst mal das Update auf 6.7.7.1 durchgeführt.
Ich habe auf deine Anregung hin mit dem Browser-Cache als mögliche Fehlerquelle nochmal ein anderes Testszenario durchgeführt.
- Testbestellung am PC im Browser (Browserdaten natürlich vorher gelöscht), 1 Stück von Abverkaufs-Artikel
- Smartphone (Samsung Android, Browser Chrome) benutzt, die Shopseite mit dem Abverkaufs-Artikel geladen, den Mengen-Button betätigt = falsche maximal wählbare Menge. Anwählbar sind 3 Stück, obwohl nur 2 noch verfügbar (in der Datenbank und im Backend ist die Menge korrekt).
- iPad (iOS 26) benutzt, mit Safari die Shopseite mit dem Abverkaufs-Artikel geladen, den Mengen-Button betätigt = falsche maximalmenge. Anwählbar sind 3 Stück, obwohl nur 2 noch verfügbar.
Also Fehler im eigenen Shop immer noch da und nachstellbar. Dann das ganze nochmal mit dem o.g. Testshop durchgeführt (in diesem nichts geändert, Standardinstallation wie über das Portal ohne Plug-Ins, nur der eine von mir angelegte Abverkaufs-Artikel ist in diesem Shop).
Das Ergebnis war der gleiche Fehler = falsche Menge vom Mengenfeld wie bei meinem eigenen Shop, in allen drei Geräten (PC, Android-Smartphone, iPad).
Mir hat zwischenzeitlich jemand versucht zu helfen, der deutlich mehr davon versteht als ich. Was aufgefallen ist, ist, das in der „CacheInvalidationSubscriber.php“ zwar Tags für Kategorien (Listing), Varianten-Logik, Product Streams generiert werden, aber es gibt keine Logik, die auf Änderungen des Lagerbestands (product.stock oder product.available_stock) reagiert. Shopware invalidiert den Cache, wenn ich Namen oder die Beschreibung ändere, aber nicht, wenn ein Kunde etwas kauft und sich nur der Bestand ändert.
Auch das Problem, warum F12 bei der Netzwerkanalyse trotz sichtbarer Änderung einen fortlaufenden Wert für „Age“ anzeigt, konnte aus der CacheStore.php abgeleitet werden.
Mir wurde es so erklärt: In der Methode lookup gibt es einen Mechanismus namens Soft Purge. Wenn ein Cache-Eintrag durch eine Änderung (z. B. Textänderung) als „ungültig“ markiert wird, löscht Shopware die Datei nicht. Stattdessen erkennt Shopware beim nächsten Aufruf: „Oh, die Datei ist alt, aber ich habe soft_purge aktiv“. Shopware liefert die alte Datei aus dem Cache aus, schickt aber im Hintergrund eine Nachricht (RefreshHttpCacheMessage) an den Server, um die Datei im Hintergrund zu aktualisieren. Der Age-Header bezieht sich somit wohl auf das ursprüngliche Erstellungsdatum der Datei, auch wenn der Inhalt im Hintergrund bereits ausgetauscht wurde.
Shopware „flickt“ quasi nun den Cache, anstatt ihn bei einer solchen Änderung frisch beim Seitenaufruf komplett neu zu erstellen (wie früher).
In der „CacheStore.php“ gibt es dann wohl eine Methode „getMinInvalidation(array $tags)“. Shopware prüft hier Zeitstempel für Tags nach dem Muster: http_invalidation_{tag}_timestamp.
Das Problem hier scheint: Wenn ich einen Text im Produkt ändere, feuert Shopware ein Event, das diesen Zeitstempel für das Produkt (product-{id}) hochsetzt. Bei einer Bestandsänderung (durch eine Bestellung) feuert Shopware dieses Event standardmäßig nicht, um den Server vor Lastspitzen zu schützen. Da der Zeitstempel sich nicht ändert, denkt der Cache: „Alles super, der Bestand im Cache ist noch aktuell“.
Dann fanden wir noch die „HttpCacheKeyGenerator.php“. Hier wird der Cache-Key generiert. Shopware nutzt das wohl für die URL, einen globalen cacheHash und Cookies (Währung, etc.)
Leider ist aber der Lagerbestand kein Teil des Cache-Keys. Das bedeutet, egal ob der Bestand 5 oder 0 ist, der Key bleibt http-cache-xyz. Ohne einen expliziten „Invalidate“-Befehl (der beim Bestand fehlt), gibt es für das System keinen Grund, das Dokument neu zu generieren.
Da das Problem bei allen Versionen vor 6.7.x.x von Shopware nicht auftrat, ist das für mich ein Bug. Bei einem Abverkaufs-Artikel sollte über die Cache-Generierung im Hintergrund auch die variable {{ product.calculatedMaxPurchase }} neu in den Cache geschrieben werden, damit der Kunde dann den Button der buy-widget-form.html.twig nur in der richtigen Menge betätigen kann - oder die Seite halt wie bei Änderungen am Text im Backend neu für den Cache generiert werden.
Ich werde jetzt mal versuchen, das für Github für ein Issue zusammenzufassen.