Warenkorb: nicht-numerische Mengen brechen die Gewichtsberechnung nicht mehr

Yard::weight() multiplizierte Gewicht und Menge ohne Cast. Beide Werte stammen
aus Session- bzw. Request-Daten; ein fuehrend-numerischer String wie "2,5" loeste
unter PHP 8 die ErrorException "A non-numeric value encountered" aus. Danach war
die Position dauerhaft defekt, da reCalculateShippingPrice() bei jedem Aufruf
erneut fehlschlug.

Cart::update() setzt die Menge ohne Validierung (anders als setQuantity()).
Eintrittspunkte waren das quantity-Feld im Shop-POST sowie das Textfeld fuer die
Menge im Backoffice-Warenkorb.

- Yard: neue cartItemWeight() mit explizitem int-Cast fuer Gewicht und Menge
- CardController: nicht-numerische Mengen werden uebersprungen, Schutz gegen ein
  quantity-Feld, das kein Array ist
- OrderController: nutzt das bereits vorhandene $qty (int) statt $data['qty'] roh

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin 2026-09-09 07:16:30 +00:00
parent 2e71173c66
commit 384b2dcff1
4 changed files with 82 additions and 5 deletions

View file

@ -899,8 +899,8 @@ class OrderController extends Controller
Yard::setTax($cartItem->rowId, $product->getTaxWith(Yard::instance('shopping')->getUserCountry())); Yard::setTax($cartItem->rowId, $product->getTaxWith(Yard::instance('shopping')->getUserCountry()));
} }
if (isset($data['qty']) && $data['qty'] > 0) { if ($qty > 0) {
Yard::instance('shopping')->update($cartItem->rowId, $data['qty']); Yard::instance('shopping')->update($cartItem->rowId, $qty);
} else { } else {
// If 0 get the item by qty:1 and remove it // If 0 get the item by qty:1 and remove it
Yard::instance('shopping')->remove($cartItem->rowId); Yard::instance('shopping')->remove($cartItem->rowId);

View file

@ -127,7 +127,14 @@ class CardController extends Controller
$data = Request::all(); $data = Request::all();
if (isset($data['quantity'])) { if (isset($data['quantity'])) {
if (! is_array($data['quantity'])) {
return back();
}
foreach ($data['quantity'] as $rowId => $qty) { foreach ($data['quantity'] as $rowId => $qty) {
if (! is_numeric($qty)) {
continue;
}
$qty = (int) $qty;
$cartItem = Yard::instance($this->instance)->get($rowId); $cartItem = Yard::instance($this->instance)->get($rowId);
if ($cartItem) { if ($cartItem) {
$product = Product::find($cartItem->id); $product = Product::find($cartItem->id);
@ -135,7 +142,7 @@ class CardController extends Controller
$qty = 1; $qty = 1;
} }
if ($product) { if ($product) {
$additionalWeight = (int) $product->weight * ((int) $qty - (int) $cartItem->qty); $additionalWeight = (int) $product->weight * ($qty - (int) $cartItem->qty);
if ($additionalWeight > 0 && Yard::instance($this->instance)->exceedsMaxWeight($additionalWeight)) { if ($additionalWeight > 0 && Yard::instance($this->instance)->exceedsMaxWeight($additionalWeight)) {
\Session::flash('alert-error', __('msg.cart_max_weight_reached')); \Session::flash('alert-error', __('msg.cart_max_weight_reached'));

View file

@ -425,18 +425,28 @@ class Yard extends Cart
{ {
$content = $this->getContent(); $content = $this->getContent();
$total = $content->reduce(function ($total, CartItem $cartItem) { $total = $content->reduce(function ($total, CartItem $cartItem) {
return $total + ($cartItem->options->weight ? ($cartItem->options->weight * $cartItem->qty) : 0); return $total + $this->cartItemWeight($cartItem);
}, 0); }, 0);
return $total; return $total;
} }
/**
* Gewicht einer Warenkorb-Position in Gramm.
* Gewicht und Menge werden explizit gecastet, da beide Werte aus der Session
* bzw. aus Request-Daten stammen und dort auch nicht-numerische Strings landen können.
*/
private function cartItemWeight(CartItem $cartItem): int
{
return (int) $cartItem->options->weight * (int) $cartItem->qty;
}
public function weightByFreeShipping($decimals = null, $decimalPoint = null, $thousandSeperator = null) public function weightByFreeShipping($decimals = null, $decimalPoint = null, $thousandSeperator = null)
{ {
$content = $this->getContent(); $content = $this->getContent();
$total = $content->reduce(function ($total, CartItem $cartItem) { $total = $content->reduce(function ($total, CartItem $cartItem) {
if ($cartItem->options->no_free_shipping) { if ($cartItem->options->no_free_shipping) {
return $total + ($cartItem->options->weight ? ($cartItem->options->weight * $cartItem->qty) : 0); return $total + $this->cartItemWeight($cartItem);
} }
return $total; return $total;

View file

@ -0,0 +1,60 @@
<?php
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
uses(TestCase::class, RefreshDatabase::class);
/**
* Menge und Gewicht stammen aus Session- bzw. Request-Daten. Landet dort ein
* nicht-numerischer String, warf die Gewichtsberechnung in PHP 8 eine
* ErrorException ("A non-numeric value encountered").
*/
describe('Yard Gewichtsberechnung', function () {
it('summiert Gewicht mal Menge', function () {
$yard = Yard::instance('test-cart-weight');
$yard->destroy();
$yard->add(1, 'A', 2, 10.0, false, false, ['weight' => 500]);
$yard->add(2, 'B', 1, 10.0, false, false, ['weight' => 250]);
expect($yard->weight())->toBe(1250);
$yard->destroy();
});
it('wirft keinen Fehler bei nicht-numerischer Menge im Warenkorb', function () {
$yard = Yard::instance('test-cart-weight');
$yard->destroy();
$cartItem = $yard->add(1, 'A', 1, 10.0, false, false, ['weight' => 500]);
$yard->update($cartItem->rowId, '2,5');
expect($yard->weight())->toBe(1000);
$yard->destroy();
});
it('wirft keinen Fehler bei nicht-numerischem Gewicht im Warenkorb', function () {
$yard = Yard::instance('test-cart-weight');
$yard->destroy();
$yard->add(1, 'A', 2, 10.0, false, false, ['weight' => '500 g']);
expect($yard->weight())->toBe(1000);
$yard->destroy();
});
it('berücksichtigt nur Artikel ohne kostenlosen Versand', function () {
$yard = Yard::instance('test-cart-weight');
$yard->destroy();
$yard->add(1, 'A', 2, 10.0, false, false, ['weight' => 500, 'no_free_shipping' => true]);
$yard->add(2, 'B', 2, 10.0, false, false, ['weight' => 500, 'no_free_shipping' => false]);
expect($yard->weightByFreeShipping())->toBe(1000);
$yard->destroy();
});
});