Przedmowa: Ma to służyć zarówno jako pisemna obserwacja architektury Magento dla społeczności (i mnie), jak i jako rzeczywiste pytanie. Pracujemy nad silnie zmodyfikowanym koszykiem i obsługą kasy, ale źródło tego problemu leży w głównej logice Magento.
tło
Stworzyliśmy bezpłatny kupon wysyłkowy, korzystając ze standardowej funkcjonalności reguł ceny w koszyku. Kupon nie ma żadnych warunków, a jedyną czynnością Free Shipping
jest ustawienie For matching items only
. Ponieważ nie istnieją żadne warunki, to ustawić free_shipping
aby 1
dla wszystkich elementów cytując sprzedaży.
Jak zwykle, włączyliśmy również metodę bezpłatnej wysyłki. Model Freeshipping
przewoźnika zapewnia stawki, ilekroć żądanie ma bezpłatną wysyłkę lub suma częściowa odpowiada lub przekracza próg (ale nie korzystamy z opcji progu). Zobacz 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 */
}
I Mage_Shipping_Model_Carrier_Freeshipping::_updateFreeMethodQuote
wygląda tak:
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);
}
}
Tak więc, o ile wszystkie elementy mają free_shipping
ustawioną wartość zgodną z prawdą (którą będą, ze względu na kupon), powinniśmy otrzymać bezpłatną wysyłkę. I my robimy!
Problem
Istnieje jednak poważny efekt uboczny: wszelkie metody wysyłki, które opierają się na elemencie produktu row_weight
(jak ma to miejsce w przypadku naszej dostosowanej wersji przewoźnika FedEx), nie obliczą prawidłowych stawek wysyłki, ponieważ dla każdego elementu row_weight
jest ustawiona opcja, 0
gdy aktywna jest bezpłatna wysyłka.
Co ciekawe, żaden z domyślnych przewoźników Magento tak naprawdę nie polega row_weight
, ale przejdziemy do tego po tym, jak ustalimy, dlaczego / kiedy row_weight
jest ustawiony 0
.
Zrozumienie, dlaczego row_weight
jest ustawione0
Ta część była dość łatwa do wykopania. Duży fragment obliczeń żeglugi wydarzy się w Mage_Sales_Model_Quote_Address_Total_Shipping::collect
tym otoczeniu row_weight
do 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);
}
}
Dlaczego nie wpływa to na domyślnych operatorów Magento
Jeśli nie szukaj regex /row_?weight/i
(np getRowWeight
, setRowWeight
, setData('row_weight')
, itd.) Na Mage_Shipping
(proste) i nośniki Mage_Usa
(FedEx, UPS, i kilka innych przewoźników), nic nie wyskakuje. Czemu? Ponieważ domyślni przewoźnicy używają całkowitej masy adresu, a nie poszczególnych wag.
Na przykład spójrzmy na 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());
}
A skąd zapytanie bierze wagę paczki? Odpowiedź jest w 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());
Możemy zignorować użycie $item->getRowWeight()
tutaj, ponieważ requestShippingRates
jest wywoływane bez podania określonego elementu jako parametru w 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();
Powinno to wyglądać znajomo, ponieważ jest to to samo miejsce, w którym row_weight
ustawiony jest każdy element, 0
jeśli obowiązuje bezpłatna wysyłka. Wskazówki jak $addressWeight
podsumowuje każdego elementu $rowWeight
, ale to zrobione przed row_weight
jest ustawiona0
.
Zasadniczo waga adresu zawsze będzie całkowitą wagą wszystkich pozycji, niezależnie od free_shipping
wartości każdej pozycji. Ponieważ domyślni operatorzy Magento polegają tylko na wadze adresu, problem z row_weight
nie pojawia się.
Dlaczego więc potrzebujemy row_weight
Potrzebujemy, row_weight
ponieważ dostosowaliśmy przewoźnika FedEx Magento do obliczania oddzielnych stawek dla produktów pochodzących z różnych źródeł, nawet jeśli idą do tego samego miejsca docelowego (a zatem są częścią tego samego adresu). Na przykład, jeśli mieszkasz w NJ, tańsze (i szybsze) jest wysłanie przedmiotu z NJ niż z CA - a jeśli masz w zamówieniu przedmioty zarówno z NJ, jak i CA, możesz zobaczyć koszt (i oszacować data dostawy) każdej przesyłki.
Podsumowując, wygląda na to, że możemy łatwo obejść ten problem, ignorując row_weight
i używając weight * qty
bezpośrednio. Ale prowadzi nas to do:
Pytanie
Dlaczego Shipping
suma ustawia row_weight
pozycje z ofert sprzedaży, 0
jeśli obowiązuje bezpłatna wysyłka? To nie wydaje się być nigdzie wykorzystywane.
Dalsze obserwacje
Zlekceważyłem wspomnienie, że to row_weight
może być niezerowe, ale wciąż mniejsze niż weight * qty
, jeśli free_shipping
zamiast jest liczba true
. Zakładam, że celem tego jest zapewnienie rozwiązania takiego scenariusza:
Mam w koszyku 3 produkty tego samego produktu, każdy waży 2 funty. Stosuję kupon na bezpłatną wysyłkę, ale jest on ograniczony do ilości 2, więc dotyczy tylko 2 produktów. Teraz, gdy spojrzę na stawki wysyłki, będę patrzeć na stawki wysyłki 2 funtów (2 + 0 + 0) zamiast 6 funtów (2 + 2 + 2).
Wydaje się to mieć sens, ale istnieją dwa główne problemy:
Żaden z domyślnych nośników Magento nie działa w ten sposób (używają masy całkowitej adresu, patrz wyżej).
Nawet jeśli niektórzy przewoźnicy działali w ten sposób, oznaczałoby to, że mogłem wybrać dowolną metodę wysyłki (np. Przesyłkę na noc) i zapłacić tylko za wagę 1 sztuki - co oznacza, że sprzedawca musiałby pokryć koszt pozostałych 2 produktów . Do kupca należałoby dowiedzieć się, że zapłaciłem tylko za wagę 1 przedmiotu, a następnie wysłałem pozostałe 2 przedmioty przy użyciu bardziej opłacalnej metody, skutecznie tworząc rozbieżność między tym, co wyświetla Magento, a tym, jak rzeczy były w rzeczywistości wysłane.