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>
201 lines
7.3 KiB
PHP
201 lines
7.3 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Einmalprodukte in der Sales-Center-Abo-Bestellstrecke (Anlage, Variante A):
|
|
* updateCart mit abo_addon=1 legt eine getrennte Cart-Zeile (Option abo_addon) an,
|
|
* Sortiment-Prüfung läuft gegen das Einmal-Sortiment (show_on '3').
|
|
*/
|
|
|
|
use App\Http\Controllers\User\OrderController;
|
|
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;
|
|
|
|
uses(TestCase::class, RefreshDatabase::class);
|
|
|
|
function makeOneTimeCartEnv(): ShippingCountry
|
|
{
|
|
$country = Country::create([
|
|
'code' => 'DE', 'phone' => '49', 'en' => 'Germany', 'de' => 'Deutschland',
|
|
'es' => 'Alemania', 'fr' => 'Allemagne', 'it' => 'Germania', 'ru' => 'Германия',
|
|
]);
|
|
$shipping = Shipping::create(['name' => 'Standard', 'active' => true]);
|
|
ShippingPrice::create([
|
|
'shipping_id' => $shipping->id, 'price' => 4.90, 'price_comp' => 0, 'num_comp' => 0,
|
|
'tax_rate' => 19, 'weight_from' => 1, 'weight_to' => 100000,
|
|
]);
|
|
|
|
return ShippingCountry::create(['shipping_id' => $shipping->id, 'country_id' => $country->id]);
|
|
}
|
|
|
|
/**
|
|
* Ruft OrderController::performRequest direkt auf (Domain-Routing hat einen Catch-All,
|
|
* daher kein HTTP-Roundtrip). Liefert das dekodierte JSON-Payload zurück.
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
function postCartUpdate(array $payload): array
|
|
{
|
|
$request = request();
|
|
$request->replace(array_merge([
|
|
'action' => 'updateCart',
|
|
'shipping_is_for' => 'abo-ot-customer',
|
|
'is_abo' => '1',
|
|
], $payload));
|
|
$request->headers->set('X-Requested-With', 'XMLHttpRequest');
|
|
|
|
$response = app(OrderController::class)->performRequest();
|
|
|
|
return $response->getData(true);
|
|
}
|
|
|
|
function findYardRows(int $productId, bool $addon): Collection
|
|
{
|
|
return Yard::instance('shopping')->search(function ($item) use ($productId, $addon) {
|
|
return (int) $item->id === $productId && (bool) ($item->options->abo_addon ?? false) === $addon;
|
|
});
|
|
}
|
|
|
|
beforeEach(function () {
|
|
$this->shippingCountry = makeOneTimeCartEnv();
|
|
$yard = Yard::instance('shopping');
|
|
$yard->destroy();
|
|
$yard->setShippingCountryWithPrice($this->shippingCountry->id, 'abo-ot-customer');
|
|
});
|
|
|
|
afterEach(function () {
|
|
Yard::instance('shopping')->destroy();
|
|
});
|
|
|
|
it('liefert das Einmal-Sortiment (show_on 3) für Abo-Zusatzartikel', function () {
|
|
expect(ProductOrderContext::oneTimeShowOnIds())->toBe(['3']);
|
|
});
|
|
|
|
it('legt mit abo_addon=1 eine getrennte Einmal-Cart-Zeile an', function () {
|
|
$product = makeProduct(['3'], 25.50, 19);
|
|
|
|
$result = postCartUpdate(['product_id' => $product->id, 'qty' => 2, 'abo_addon' => 1]);
|
|
expect($result['response'])->toBeTrue();
|
|
|
|
$rows = findYardRows($product->id, true);
|
|
expect($rows)->toHaveCount(1)
|
|
->and((int) $rows->first()->qty)->toBe(2)
|
|
->and((bool) $rows->first()->options->abo_addon)->toBeTrue();
|
|
});
|
|
|
|
it('hält Abo-Zeile und Einmal-Zeile desselben Produkts getrennt', function () {
|
|
$product = makeProduct(['3', '12'], 90, 19);
|
|
|
|
postCartUpdate(['product_id' => $product->id, 'qty' => 1]);
|
|
postCartUpdate(['product_id' => $product->id, 'qty' => 3, 'abo_addon' => 1]);
|
|
|
|
$aboRows = findYardRows($product->id, false);
|
|
$addonRows = findYardRows($product->id, true);
|
|
|
|
expect($aboRows)->toHaveCount(1)
|
|
->and((int) $aboRows->first()->qty)->toBe(1)
|
|
->and($addonRows)->toHaveCount(1)
|
|
->and((int) $addonRows->first()->qty)->toBe(3)
|
|
->and($aboRows->first()->rowId)->not->toBe($addonRows->first()->rowId);
|
|
});
|
|
|
|
it('lehnt ein Normalsortiment-Produkt ohne abo_addon-Flag im Abo-Cart weiter ab', function () {
|
|
$product = makeProduct(['3'], 25.50, 19);
|
|
|
|
$result = postCartUpdate(['product_id' => $product->id, 'qty' => 1]);
|
|
|
|
expect($result['message'] ?? null)->toBe(__('msg.cart_product_not_allowed_for_order_type'))
|
|
->and(Yard::instance('shopping')->content())->toHaveCount(0);
|
|
});
|
|
|
|
it('lehnt ein reines Abo-Produkt als Einmal-Artikel ab', function () {
|
|
$product = makeProduct(['12'], 90, 19);
|
|
|
|
$result = postCartUpdate(['product_id' => $product->id, 'qty' => 1, 'abo_addon' => 1]);
|
|
|
|
expect($result['message'] ?? null)->toBe(__('msg.cart_product_not_allowed_for_order_type'))
|
|
->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);
|
|
|
|
postCartUpdate(['product_id' => $product->id, 'qty' => 1]);
|
|
postCartUpdate(['product_id' => $product->id, 'qty' => 2, 'abo_addon' => 1]);
|
|
postCartUpdate(['product_id' => $product->id, 'qty' => 0, 'abo_addon' => 1]);
|
|
|
|
expect(findYardRows($product->id, true))->toHaveCount(0)
|
|
->and(findYardRows($product->id, false))->toHaveCount(1);
|
|
});
|