Abo: Fix Checkout-Rücksprung bei Einmalprodukten in der Bestellstrecke
Die Submit-Validierung checkSendYardForPayment() prüfte jede Cart-Zeile gegen das Abo-Sortiment (12/13) – Einmal-Zeilen (show_on 3) fielen durch und der Checkout-Klick sprang per back() kommentarlos auf die Bestellseite zurück. Einmal-Zeilen werden jetzt gegen das Einmal-Sortiment validiert (manipulierte Zeilen weiterhin abgewiesen). Zusätzlich zeigt list.blade.php jetzt alle Validierungsfehler an (vorher nur Comp-Produkt-Fehler → Fehler blieb unsichtbar). Tests: 2 neue Regressionstests in AboCreateOneTimeCartTest (19 grün). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
cdb121fde0
commit
657d4bd5b7
4 changed files with 93 additions and 1 deletions
|
|
@ -476,6 +476,14 @@ class OrderController extends Controller
|
|||
if (! $product) {
|
||||
continue;
|
||||
}
|
||||
if ($isAbo && ($row->options->abo_addon ?? false)) {
|
||||
// Einmal-Zeilen (nur erste Lieferung) gehören zum Einmal-Sortiment, nicht zum Abo-Sortiment
|
||||
if (! ProductOrderContext::productMatchesShowOn($product, ProductOrderContext::oneTimeShowOnIds())) {
|
||||
throw new \Exception(__('msg.cart_product_not_allowed_for_order_type'));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
if (! ProductOrderContext::isProductAllowedInContext($product, $isAbo, $data['shipping_is_for'])) {
|
||||
throw new \Exception(__('msg.cart_product_not_allowed_for_order_type'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,6 +201,23 @@ Kein neues Datenmodell, keine Migration, kein Purge nötig.
|
|||
|
||||
---
|
||||
|
||||
## 8a. Nachtrag — Bugfix Checkout-Rücksprung (2026-07-22)
|
||||
|
||||
**Status:** ERLEDIGT
|
||||
|
||||
**Gemeldet:** Auf `/user/order/my/list/abo-me` sprang der Klick auf „Checkout" kommentarlos an den Seitenanfang zurück, sobald ein Einmalprodukt im Warenkorb lag.
|
||||
|
||||
**Ursache:** `OrderController::checkSendYardForPayment()` (Z. ~473) re-validiert beim Absenden JEDE Cart-Zeile gegen das Abo-Sortiment (`isProductAllowedInContext` → `['12','13']`). Einmal-Zeilen (`show_on '3'`) fielen durch → Exception → `back()->withErrors(['error' => …])`. Zweites Problem: `list.blade.php` renderte nur `switchers-comp-product`-Fehler — der eigentliche Fehler blieb unsichtbar (daher „kommentarloser" Rücksprung).
|
||||
|
||||
**Fix:**
|
||||
- `checkSendYardForPayment()`: Einmal-Zeilen (`abo_addon`) werden gegen `ProductOrderContext::oneTimeShowOnIds()` validiert statt gegen das Abo-Sortiment (manipulierte Addon-Zeilen mit reinen Abo-Produkten werden weiterhin abgewiesen).
|
||||
- `list.blade.php`: Fehlerblock zeigt jetzt `$errors->any()` statt nur Comp-Produkt-Fehler — künftige Validierungsfehler sind sichtbar.
|
||||
- Tests: 2 neue in `AboCreateOneTimeCartTest` (Passieren mit Addon-Zeile, Abweisen manipulierter Addon-Zeile), gesamt 19 Feature-Tests grün.
|
||||
|
||||
**Lesson learned:** Die Checkout-Submit-Validierung war ein zweites, beim initialen Grep übersehenes Sortiment-Gate (neben `handleUpdateCart`); alle `isProductAllowedInContext`-Aufrufer wurden jetzt vollständig geprüft (keine weiteren Gates).
|
||||
|
||||
---
|
||||
|
||||
## 9. Umsetzungs-Zusammenfassung (2026-07-21)
|
||||
|
||||
Alle 7 Phasen umgesetzt. Kern-Mechanik: Cart-Option `abo_addon` → `shopping_order_items.is_abo_addon` (Checkout persistiert jetzt) → PDF-Gruppierung automatisch; `createAboItems`/`ensureUserAboItemsFromLatestOrder` überspringen Addons; `user_abos.amount` ohne Addon-Anteil. Kein neues Datenmodell, keine Migration. Neue Tests: `AboCreateOneTimeItemsTest` (6), `AboCreateOneTimeCartTest` (6), `AboPortalCreateOneTimeTest` (5).
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
}
|
||||
}
|
||||
</style>
|
||||
@if($errors->has('switchers-comp-product'))
|
||||
@if($errors->any())
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="alert alert-danger" id="gotocomp">
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ use App\Models\Country;
|
|||
use App\Models\Shipping;
|
||||
use App\Models\ShippingCountry;
|
||||
use App\Models\ShippingPrice;
|
||||
use App\Models\UserAccount;
|
||||
use App\Services\ProductOrderContext;
|
||||
use App\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Collection;
|
||||
use Tests\TestCase;
|
||||
|
|
@ -122,6 +124,71 @@ it('lehnt ein reines Abo-Produkt als Einmal-Artikel ab', function () {
|
|||
->and(Yard::instance('shopping')->content())->toHaveCount(0);
|
||||
});
|
||||
|
||||
/**
|
||||
* Eingeloggter Berater mit Versandland-Account (Voraussetzung für checkSendYardForPayment).
|
||||
*/
|
||||
function makeCheckoutValidationUser(ShippingCountry $shippingCountry): User
|
||||
{
|
||||
$account = UserAccount::create([
|
||||
'salutation' => 'Herr',
|
||||
'first_name' => 'Max',
|
||||
'last_name' => 'Muster',
|
||||
'address' => 'Musterstr. 1',
|
||||
'zipcode' => '12345',
|
||||
'city' => 'Musterstadt',
|
||||
'country_id' => $shippingCountry->country_id,
|
||||
'phone' => '123456',
|
||||
'same_as_billing' => 1,
|
||||
'language' => 'de',
|
||||
]);
|
||||
|
||||
$user = User::forceCreate([
|
||||
'email' => 'checkout-'.uniqid('', true).'@example.com',
|
||||
'password' => bcrypt('secret'),
|
||||
'lang' => 'de',
|
||||
'account_id' => $account->id,
|
||||
]);
|
||||
|
||||
test()->be($user);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
function invokeCheckSendYardForPayment(): void
|
||||
{
|
||||
$controller = app(OrderController::class);
|
||||
$reflection = new ReflectionMethod($controller, 'checkSendYardForPayment');
|
||||
$reflection->invoke($controller, ['shipping_is_for' => 'abo-me'], null);
|
||||
}
|
||||
|
||||
it('lässt die Checkout-Validierung Einmal-Zeilen im Abo-Cart passieren', function () {
|
||||
makeCheckoutValidationUser($this->shippingCountry);
|
||||
$aboProduct = makeProduct(['12'], 90, 19);
|
||||
$addonProduct = makeProduct(['3'], 25.50, 19);
|
||||
|
||||
postCartUpdate(['product_id' => $aboProduct->id, 'qty' => 1, 'shipping_is_for' => 'abo-me']);
|
||||
postCartUpdate(['product_id' => $addonProduct->id, 'qty' => 2, 'abo_addon' => 1, 'shipping_is_for' => 'abo-me']);
|
||||
|
||||
// Vor dem Fix warf die Sortiment-Prüfung hier "cart_product_not_allowed_for_order_type"
|
||||
// und der Checkout-Klick sprang kommentarlos auf die Bestellseite zurück.
|
||||
invokeCheckSendYardForPayment();
|
||||
|
||||
expect(findYardRows($addonProduct->id, true))->toHaveCount(1);
|
||||
});
|
||||
|
||||
it('weist manipulierte Einmal-Zeilen mit Abo-Produkt bei der Checkout-Validierung ab', function () {
|
||||
makeCheckoutValidationUser($this->shippingCountry);
|
||||
$aboProduct = makeProduct(['12'], 90, 19);
|
||||
|
||||
postCartUpdate(['product_id' => $aboProduct->id, 'qty' => 1, 'shipping_is_for' => 'abo-me']);
|
||||
Yard::instance('shopping')->add($aboProduct->id, 'Manipuliert', 1, 90.0, false, false, [
|
||||
'slug' => $aboProduct->slug, 'weight' => 500, 'points' => 10, 'show_on' => ['12'],
|
||||
'abo_addon' => true,
|
||||
]);
|
||||
|
||||
invokeCheckSendYardForPayment();
|
||||
})->throws(Exception::class);
|
||||
|
||||
it('entfernt mit qty=0 nur die Einmal-Zeile, die Abo-Zeile bleibt', function () {
|
||||
$product = makeProduct(['3', '12'], 90, 19);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue