前言:这旨在作为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
(联邦快递,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
要这样做是因为我们已经定制了Magento的FedEx承运商,以计算来自不同来源的商品的单独费率,即使这些商品要运往同一目的地(因此属于同一地址)也是如此。例如,如果您住在新泽西州,那么从新泽西州发货的商品比从加利福尼亚州发货的商品便宜(快捷)-并且如果您的订单中同时包含新泽西州和加利福尼亚州的商品,则可以看到费用(并估算出交货日期)。
总而言之,看起来我们可以通过忽略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载体都没有这样工作(它们使用地址总重量,请参见上文)。
即使某些承运人是这样工作的,这也意味着我可以选择任何运输方式(例如,隔夜运输),而只需支付一件商品的重量 -这意味着,商人将不得不支付其他2件商品的费用。由商人决定,我只支付了一件商品的重量,然后使用一种更具成本效益的方法运送了另外两件商品,从而有效地在Magento的显示内容与实际商品之间造成了不匹配运了出去。