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 Shippingjest ustawienie For matching items only. Ponieważ nie istnieją żadne warunki, to ustawić free_shippingaby 1dla wszystkich elementów cytując sprzedaży.
Jak zwykle, włączyliśmy również metodę bezpłatnej wysyłki. Model Freeshippingprzewoź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::_updateFreeMethodQuotewyglą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_shippingustawioną 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_weightjest ustawiona opcja, 0gdy 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_weightjest ustawiony 0.
Zrozumienie, dlaczego row_weightjest ustawione0
Ta część była dość łatwa do wykopania. Duży fragment obliczeń żeglugi wydarzy się w Mage_Sales_Model_Quote_Address_Total_Shipping::collecttym otoczeniu row_weightdo 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ż requestShippingRatesjest 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_weightustawiony jest każdy element, 0jeśli obowiązuje bezpłatna wysyłka. Wskazówki jak $addressWeightpodsumowuje każdego elementu $rowWeight, ale to zrobione przed row_weightjest ustawiona0 .
Zasadniczo waga adresu zawsze będzie całkowitą wagą wszystkich pozycji, niezależnie od free_shippingwartości każdej pozycji. Ponieważ domyślni operatorzy Magento polegają tylko na wadze adresu, problem z row_weightnie pojawia się.
Dlaczego więc potrzebujemy row_weight
Potrzebujemy, row_weightponieważ 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_weighti używając weight * qtybezpośrednio. Ale prowadzi nas to do:
Pytanie
Dlaczego Shippingsuma ustawia row_weightpozycje z ofert sprzedaży, 0jeśli obowiązuje bezpłatna wysyłka? To nie wydaje się być nigdzie wykorzystywane.
Dalsze obserwacje
Zlekceważyłem wspomnienie, że to row_weightmoże być niezerowe, ale wciąż mniejsze niż weight * qty, jeśli free_shippingzamiast 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.