Передмова: Це покликане слугувати як письмовим спостереженням архітектури 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 + 0 + 0), а не за 6 фунтів (2 + 2 + 2).
Це, мабуть, має сенс, але в цьому є дві основні проблеми:
Жоден з операторів Magento за замовчуванням не працює так (вони використовують загальну вагу адреси, див. Вище).
Навіть якщо хтось із перевізників працював так, це означатиме, що я можу вибрати будь-який спосіб доставки (наприклад, доставка за ніч) і заплатити лише вагу 1 товару - це означає, що торговець повинен буде покрити вартість інших 2 товарів . Торговець мав би якось зрозуміти, що я заплатив лише вагу 1 предмета, а потім відвантажував інші 2 предмети, використовуючи більш економічний метод, фактично створюючи невідповідність між тим, що відображає Magento, і тим, як фактично були товари. відвантажений.