Vorwort: Dies soll sowohl als schriftliche Beobachtung der Magento-Architektur für die Community (und mich) als auch als eigentliche Frage dienen. Wir arbeiten mit einer stark veränderten Cart- und Checkout-Erfahrung, aber die Wurzel dieses Problems liegt in der Kernlogik von Magento.
Hintergrund
Wir haben einen kostenlosen Versandgutschein mit der Standardfunktion für Warenkorb-Preisregeln erstellt. Es gibt keine Bedingungen für den Coupon, und die einzige Aktion, die festgelegt ist, Free Shipping
ist For matching items only
. Da es keine Bedingungen gibt, wird festgelegt dies free_shipping
auf 1
für alle die Angebotspositionen Umsatz.
Wie üblich haben wir auch die Versandart "Kostenloser Versand" aktiviert. Das Freeshipping
Carrier-Modell liefert Preise, wenn die Anfrage versandkostenfrei ist oder die Zwischensumme dem Schwellenwert entspricht oder diesen überschreitet (wir verwenden jedoch nicht die Schwellenwertoption). Siehe Mage_Shipping_Model_Carrier_Freeshipping::collectRates
:
$this->_updateFreeMethodQuote($request);
if (($request->getFreeShipping()) // <-- This is the condition we're relying on
|| ($request->getBaseSubtotalInclTax() >=
$this->getConfigData('free_shipping_subtotal'))
) {
/* Snip: Add $0.00 method to the result */
}
Und Mage_Shipping_Model_Carrier_Freeshipping::_updateFreeMethodQuote
sieht so aus:
protected function _updateFreeMethodQuote($request)
{
$freeShipping = false;
$items = $request->getAllItems();
$c = count($items);
for ($i = 0; $i < $c; $i++) {
if ($items[$i]->getProduct() instanceof Mage_Catalog_Model_Product) {
if ($items[$i]->getFreeShipping()) {
$freeShipping = true;
} else {
return;
}
}
}
if ($freeShipping) {
$request->setFreeShipping(true);
}
}
Solange sich alle Artikel free_shipping
auf einen echten Wert eingestellt haben (der sich aufgrund des Gutscheins ergibt), sollten wir den Versand kostenlos erhalten. Und wir tun es!
Das Problem
Allerdings gibt es einen wichtigen Nebeneffekt: alle Methoden Versand , das auf ein Element der verlassen row_weight
(wie es der Fall mit unserer maßgeschneiderten Version des FedEx Trägers) fehl richtig Versandkosten zu berechnen , da jedes Element row_weight
eingestellt wird , 0
wenn freies Verschiffen aktiv ist .
Interessanterweise verlässt sich keiner von Magentos Standardversendern tatsächlich darauf row_weight
, aber wir werden darauf zurückkommen, nachdem wir herausgefunden haben, warum / wann eingestellt row_weight
ist 0
.
Herausfinden, warum auf row_weight
gesetzt ist0
Dieser Teil war eigentlich ziemlich einfach zu graben. Ein großer Teil der Versandberechnungen findet statt Mage_Sales_Model_Quote_Address_Total_Shipping::collect
, einschließlich der Einstellung row_weight
auf 0
:
public function collect(Mage_Sales_Model_Quote_Address $address)
{
parent::collect($address);
foreach ($items as $item) {
/* Snip: Handling virtual items and parent items */
if ($item->getHasChildren() && $item->isShipSeparately()) {
/* Snip: Handling items with children */
}
else {
if (!$item->getProduct()->isVirtual()) {
$addressQty += $item->getQty();
}
$itemWeight = $item->getWeight();
$rowWeight = $itemWeight*$item->getQty();
$addressWeight+= $rowWeight;
if ($freeAddress || $item->getFreeShipping()===true) {
$rowWeight = 0;
} elseif (is_numeric($item->getFreeShipping())) {
$freeQty = $item->getFreeShipping();
if ($item->getQty()>$freeQty) {
$rowWeight = $itemWeight*($item->getQty()-$freeQty);
}
else {
$rowWeight = 0;
}
}
$freeMethodWeight+= $rowWeight;
$item->setRowWeight($rowWeight);
}
}
Warum dies nicht die Standardanbieter von Magento betrifft
Wenn Sie eine regex Suche nach tun /row_?weight/i
(zB getRowWeight
, setRowWeight
, setData('row_weight')
, etc.) in Mage_Shipping
(einfachen Träger) und Mage_Usa
(FedEx, UPS, und einige andere Träger), erscheint nichts auf. Warum? Da die Standardanbieter das Gesamtadressgewicht verwenden, nicht die einzelnen Artikelgewichte.
Schauen wir uns zum Beispiel an Mage_Usa_Model_Shipping_Carrier_Fedex::setRequest
:
public function setRequest(Mage_Shipping_Model_Rate_Request $request)
{
$this->_request = $request;
$r = new Varien_Object();
/* Snip */
$weight = $this->getTotalNumOfBoxes($request->getPackageWeight());
$r->setWeight($weight);
if ($request->getFreeMethodWeight()!= $request->getPackageWeight()) {
$r->setFreeMethodWeight($request->getFreeMethodWeight());
}
Und woher bezieht die Anfrage das Paketgewicht? Die Antwort ist in Mage_Sales_Model_Quote_Address::requestShippingRates
:
public function requestShippingRates(Mage_Sales_Model_Quote_Item_Abstract $item = null)
{
/** @var $request Mage_Shipping_Model_Rate_Request */
$request = Mage::getModel('shipping/rate_request');
/* Snip */
$request->setPackageWeight($item ? $item->getRowWeight() : $this->getWeight());
Wir können die Verwendung von $item->getRowWeight()
here ignorieren , da requestShippingRates
aufgerufen wird, ohne ein bestimmtes Element als Parameter anzugeben in Mage_Sales_Model_Quote_Address_Total_Shipping::collect
:
public function collect(Mage_Sales_Model_Quote_Address $address)
{
parent::collect($address);
foreach ($items as $item) {
/* Snip: Handling virtual items and parent items */
if ($item->getHasChildren() && $item->isShipSeparately()) {
/* Snip: Handling items with children */
}
else {
if (!$item->getProduct()->isVirtual()) {
$addressQty += $item->getQty();
}
$itemWeight = $item->getWeight();
$rowWeight = $itemWeight*$item->getQty();
$addressWeight+= $rowWeight;
if ($freeAddress || $item->getFreeShipping()===true) {
$rowWeight = 0;
} elseif (is_numeric($item->getFreeShipping())) {
$freeQty = $item->getFreeShipping();
if ($item->getQty()>$freeQty) {
$rowWeight = $itemWeight*($item->getQty()-$freeQty);
}
else {
$rowWeight = 0;
}
}
$freeMethodWeight+= $rowWeight;
$item->setRowWeight($rowWeight);
}
}
$address->setWeight($addressWeight);
$address->setFreeMethodWeight($freeMethodWeight);
$address->collectShippingRates();
Dies sollte Ihnen bekannt vorkommen, da dies derselbe Ort ist, auf den jeder Artikel row_weight
eingestellt ist, 0
wenn der Versand kostenlos ist. Beachten Sie, wie $addressWeight
die einzelnen Elemente zusammengefasst werden $rowWeight
, dies wird jedoch vor dem row_weight
Festlegen von festgelegt0
.
Grundsätzlich ist das Adressgewicht immer das Gesamtgewicht aller Artikel, unabhängig vom free_shipping
Wert jedes Artikels. Da die Standardanbieter von Magento nur auf das Adressgewicht angewiesen sind, tritt das Problem mit row_weight
nicht auf.
Also warum brauchen wir row_weight
Dies ist erforderlich, row_weight
da wir die FedEx-Spedition von Magento so angepasst haben, dass separate Preise für Artikel mit unterschiedlicher Herkunft berechnet werden, auch wenn sie zum selben Ziel (und damit zur selben Adresse gehören) gehen. Wenn Sie beispielsweise in NJ leben, ist es billiger (und schneller), einen Artikel von NJ aus zu versenden als von CA aus. Wenn Ihre Bestellung sowohl Artikel von NJ als auch von CA enthält, können Sie die Kosten (und geschätzten Kosten) anzeigen Liefertermin) jeder Sendung.
Alles in allem sieht es so aus, als könnten wir dieses Problem leicht umgehen, indem wir es ignorieren row_weight
und weight * qty
direkt verwenden. Aber es führt uns zu:
Die Frage
Warum setzt die Shipping
Gesamtsumme den row_weight
Wert der Angebotspositionen auf, 0
wenn der Versand kostenlos ist? Dies scheint nirgendwo genutzt zu werden.
Weitere Beobachtungen
Ich habe versäumt zu erwähnen, dass row_weight
das tatsächlich nicht Null sein könnte, aber immer noch kleiner als weight * qty
, wenn free_shipping
stattdessen eine Zahl ist true
. Ich gehe davon aus, dass der Zweck darin besteht, eine Lösung für ein Szenario wie das folgende bereitzustellen:
Ich habe 3 Artikel desselben Produkts in meinem Warenkorb, wobei jeder Artikel 2 Pfund wiegt. Ich wende einen kostenlosen Versandgutschein an, der jedoch auf eine Menge von 2 Stück begrenzt ist. Daher gilt er nur für 2 Artikel. Wenn ich mir jetzt die Versandkosten anschaue, sehe ich mir die Versandkosten für 2 lbs (2 + 0 + 0) anstatt für 6 lbs (2 + 2 + 2) an.
Dies scheint sinnvoll zu sein, es gibt jedoch zwei Hauptprobleme:
Keiner der Standard-Magento-Carrier funktioniert so (sie verwenden das Gesamtgewicht der Adresse, siehe oben).
Selbst wenn einige der Spediteure so arbeiten würden, würde das bedeuten, dass ich eine beliebige Versandart (z. B. Nachtversand) auswählen und nur für das Gewicht von 1 Artikel bezahlen könnte - das heißt, der Händler müsste die Kosten für die anderen 2 Artikel tragen . Es wäre Sache des Händlers, irgendwie herauszufinden, dass ich nur für das Gewicht von einem Artikel bezahlt und dann die anderen 2 Artikel mit einer kostengünstigeren Methode versandt habe, wodurch effektiv eine Diskrepanz zwischen dem, was Magento anzeigt, und dem, was die Artikel tatsächlich sind, entsteht verschifft.