Предисловие: Это должно служить как письменным наблюдением архитектуры Magento для сообщества (и меня), так и актуальным вопросом. Мы работаем с сильно измененной корзиной и опытом оформления заказов, но корень этой проблемы лежит в основной логике Magento.
Задний план
Мы создали бесплатный купон на доставку, используя стандартные правила цен для корзины покупок. На купоне нет условий, и единственное действие, Free Shipping
которое установлено в For matching items only
. Поскольку нет никаких условий, это будет установлено free_shipping
в1
течение всех продаж цитаты пунктов.
Как обычно, мы также включили метод бесплатной доставки. Модель Freeshipping
перевозчика будет предоставлять тарифы всякий раз, когда запрос имеет бесплатную доставку или промежуточный итог совпадает или превышает порог (но мы не используем опцию порога). Смотрите 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 */
}
И Mage_Shipping_Model_Carrier_Freeshipping::_updateFreeMethodQuote
выглядит так:
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);
}
}
Таким образом, до тех пор, пока все предметы будут free_shipping
установлены в истинное значение (что они и получат благодаря купону), мы должны получить бесплатную доставку. И мы делаем!
Проблема
Тем не менее, есть серьезный побочный эффект: любые методы доставки, основанные на элементе row_weight
(как в случае с нашей настроенной версией перевозчика FedEx), не смогут рассчитать правильные тарифы на доставку, поскольку для каждого элемента row_weight
установлено значение0
когда активна бесплатная доставка.
Интересно, что ни один из стандартных перевозчиков Magento на самом деле не полагается row_weight
, но мы вернемся к этому после того, как выясним, почему / когда row_weight
установлено 0
.
Выяснить, почему row_weight
установлено0
Эта часть была на самом деле довольно легко выкопать. Большой кусок из судоходных расчетов происходит в Mage_Sales_Model_Quote_Address_Total_Shipping::collect
том числе установки row_weight
на 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);
}
}
Почему это не влияет на носителей Magento по умолчанию
Если вы делаете регулярное выражение для поиска /row_?weight/i
(например getRowWeight
, setRowWeight
, setData('row_weight')
и т.д.) в Mage_Shipping
(простых носителях) иMage_Usa
(FedEx, UPS, и некоторые другие носители), ничего не выскочит. Зачем? Поскольку операторы по умолчанию используют общий вес адреса, а не вес отдельных предметов.
Например, давайте посмотрим на 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());
}
И откуда запрос получает вес посылки? Ответ в 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());
Мы можем игнорировать использование $item->getRowWeight()
здесь, потому что requestShippingRates
вызывается без предоставления конкретного элемента в качестве параметра в 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();
Это должно выглядеть знакомым, так как это то же самое место , что каждый элемент row_weight
устанавливаются в 0
случае бесплатная доставка действует. Обратите внимание, как $addressWeight
подводятся итоги каждого элемента $rowWeight
, но это делается до того, row_weight
как установлено значение0
.
По сути, вес адреса всегда будет общим весом всех предметов, независимо от free_shipping
стоимости каждого предмета. Так как операторы по умолчанию Magento полагаются только на вес адреса, проблема с row_weight
не появляется.
Так зачем нам row_weight
Нам нужно row_weight
потому что мы настроили носитель FedEx в Magento для расчета отдельных тарифов на товары, которые прибывают из разных стран, даже если они отправляются в один и тот же пункт назначения (и, следовательно, являются частью одного и того же адреса). Например, если вы живете в Нью-Джерси, дешевле (и быстрее) доставить товар из Нью-Джерси, чем из Калифорнии, а если в вашем заказе есть товары из Нью-Джерси и Калифорнии, вы сможете увидеть стоимость (и оценку). дата доставки) каждой партии.
В общем, похоже, что мы можем легко обойти эту проблему, игнорируя row_weight
и используя weight * qty
напрямую. Но это приводит нас к:
Вопрос
Почему Shipping
итоговое значение устанавливает row_weight
количество предложений, 0
если действует бесплатная доставка? Это, кажется, нигде не используется.
Дальнейшие наблюдения
Я забыл упомянуть, что на row_weight
самом деле может быть ненулевым, но все же меньше, чем weight * qty
, если free_shipping
это число вместо true
. Я предполагаю, что цель этого состоит в том, чтобы обеспечить решение сценария как это:
В моей корзине 3 предмета одного и того же товара, каждый весит 2 фунта. Я применяю бесплатный купон на доставку, но его количество ограничено 2, поэтому оно распространяется только на 2 товара. Теперь, когда я смотрю на стоимость доставки, я буду смотреть на стоимость доставки за 2 фунта (2 + 0 + 0), а не за 6 фунтов (2 + 2 + 2).
Кажется, это имеет смысл, но есть две основные проблемы:
Ни один из стандартных носителей Magento не работает так (они используют общий вес адреса, см. Выше).
Даже если бы некоторые из перевозчиков работали так, это означало бы, что я мог бы выбрать любой способ доставки (например, ночная доставка) и заплатить только за вес 1 предмета , то есть продавец должен был бы покрыть стоимость двух других предметов. , Продавец должен каким-то образом выяснить, что я заплатил только за вес 1 предмета, а затем отправить 2 других товара, используя более экономичный метод, эффективно создавая несоответствие между тем, что отображает Magento, и тем, как предметы были на самом деле. отправлен