Предисловие: Это должно служить как письменным наблюдением архитектуры 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, и тем, как предметы были на самом деле. отправлен