Prefácio: Isso serve como uma observação escrita da arquitetura do Magento para a comunidade (e para mim), bem como uma pergunta real. Estamos trabalhando com uma experiência fortemente modificada de carrinho e caixa, mas a raiz desse problema está na lógica principal do Magento.
fundo
Criamos um cupom de frete grátis usando a funcionalidade de regras de preço do carrinho de compras padrão. Não há condições no cupom e a única ação Free Shipping
é definida como For matching items only
. Uma vez que não há condições, isto irá definir free_shipping
para 1
para todos os itens de cotação de vendas.
Como é habitual, também ativamos o método de envio Frete grátis. O Freeshipping
modelo da transportadora fornecerá taxas sempre que a solicitação tiver frete grátis ou o subtotal corresponder ou exceder o limite (mas não estamos usando a opção de limite). Veja 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 */
}
E Mage_Shipping_Model_Carrier_Freeshipping::_updateFreeMethodQuote
fica assim:
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);
}
}
Portanto, desde que todos os itens tenham sido free_shipping
definidos como um valor verdadeiro (que será devido ao cupom), devemos receber frete grátis. E nós fazemos!
O problema
No entanto, há um efeito colateral importante: quaisquer métodos de envio que dependam de um item row_weight
(como é o caso da nossa versão personalizada da transportadora FedEx) não conseguirão calcular as taxas de envio adequadas, pois cada item row_weight
é definido 0
quando o frete grátis está ativo.
Curiosamente, nenhuma das transportadoras padrão do Magento realmente depende row_weight
, mas vamos chegar a isso depois de descobrirmos por que / quando row_weight
está definido 0
.
Descobrir por que row_weight
está definido como0
Esta parte foi realmente muito fácil de desenterrar. Uma grande parte dos cálculos de remessa ocorre Mage_Sales_Model_Quote_Address_Total_Shipping::collect
, incluindo a configuração row_weight
para 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);
}
}
Por que isso não afeta as operadoras padrão do Magento
Se você fizer uma pesquisa regex para /row_?weight/i
(por exemplo getRowWeight
, setRowWeight
, setData('row_weight')
, etc.) em Mage_Shipping
(portadores simples) e Mage_Usa
(FedEx, UPS, e algumas outras operadoras), nada aparece. Por quê? Como as operadoras padrão usam o peso total do endereço, não os pesos dos itens individuais.
Por exemplo, vejamos 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());
}
E de onde a solicitação obtém o peso do pacote? A resposta está em 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());
Podemos ignorar o uso $item->getRowWeight()
daqui porque requestShippingRates
é chamado sem fornecer um item específico como parâmetro em 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();
Isso deve parecer familiar, pois é o mesmo local em que cada item row_weight
é definido 0
se o frete grátis estiver em vigor. Observe como $addressWeight
resume cada item do $rowWeight
, mas isso é feito antes row_weight
está definido para0
.
Basicamente, o peso do endereço sempre será o peso total de todos os itens, independentemente do free_shipping
valor de cada item. Como as operadoras padrão do Magento dependem apenas do peso do endereço, o problema row_weight
não aparece.
Então, por que precisamos row_weight
Precisamos row_weight
porque personalizamos a transportadora FedEx da Magento para calcular taxas separadas para itens provenientes de origens diferentes, mesmo que eles estejam indo para o mesmo destino (e, portanto, fazendo parte do mesmo endereço). Por exemplo, se você mora em NJ, é mais barato (e mais rápido) enviar um item de NJ do que de CA - e se você tiver itens de NJ e CA em seu pedido, poderá ver o custo (e as estimativas data de entrega) de cada remessa.
Em suma, parece que podemos solucionar esse problema facilmente ignorando row_weight
e usando weight * qty
diretamente. Mas isso nos leva a:
A questão
Por que o Shipping
total define os row_weight
itens da cotação de venda 0
se o frete grátis está em vigor? Isso não parece ser utilizado em lugar algum.
Observações adicionais
Eu esqueci de mencionar que, row_weight
na verdade, pode ser diferente de zero, mas ainda menor que weight * qty
, se free_shipping
for um número, em vez de true
. Presumo que o objetivo disso seja fornecer uma solução para um cenário como este:
Eu tenho 3 itens do mesmo produto no meu carrinho, cada item pesando 2 libras. Aplico um cupom de frete grátis, mas ele é limitado a uma quantidade de 2, portanto se aplica somente a 2 itens. Agora, quando eu olhar as taxas de envio, estarei analisando as taxas de envio por 2 libras (2 + 0 + 0) em vez de 6 libras (2 + 2 + 2).
Isso parece fazer sentido, mas há dois grandes problemas:
Nenhuma das operadoras Magento padrão funciona assim (elas usam o peso total do endereço, veja acima).
Mesmo que algumas das transportadoras funcionassem dessa maneira, isso significaria que eu poderia escolher qualquer método de envio (por exemplo, remessa noturna) e pagar apenas pelo peso de 1 item - ou seja, o comerciante teria que cobrir o custo dos outros 2 itens . Caberia ao comerciante descobrir, de alguma forma, que paguei apenas o peso de 1 item e depois enviei os outros 2 itens usando um método mais econômico, criando uma incompatibilidade entre o que o Magento exibe e como os itens eram realmente enviado para fora.
Respostas:
Achei que eu iria dar uma facada nisso ...;)
Uma pergunta bastante interessante que você colocou aqui, então aqui está o porquê eu acho que eles fizeram isso, mas ainda estou trabalhando para rastrear quando esse caso em particular entra em cena.
Indo pelo método de transportadora USPS, parece que os Pedidos Internacionais da API fornecem pesos detalhados para cada produto. Esta é a única operadora que posso encontrar que faz isso. Encontre o método completo abaixo e a seção destacada abaixo.
Seção particular:
Para mim, parece que o Magento está recalculando o peso do pacote com base no peso real do item e não aproveitando o peso do endereço. Gostaria de saber se os itens do pacote que estão sendo passados não têm seu peso zerado, porque isso levaria a pesos de itens falsos, considerando que a resposta das taxas seria baseada em dados de peso incorretos.
Disparado no escuro embora.
fonte
Eu também tive uma facada nisso e achei algo interessante nessa linha de código
$item->setRowWeight($rowWeight);
foi introduzido na versão 1.1.5 antes que a função fosse assim.
Magento Mirror Import Magento Versão 1.1.5 - Shipping.php
Magento Mirror Import Magento Versão 1.1.1 - Shipping.php
Meu entendimento é que Mage_Sales_Model_Quote_Address_Total_Shipping :: collect precisa calcular / atualizar $ addressWeight e $ freeMethodWeight
e $ item-> setRowWeight não devem ser usados aqui porque estão relacionados ao item e não à cotação e endereço.
Minha aposta é que isso é um erro. Provavelmente, row_total nunca foi projetado para ser usado em métodos de envio, por isso os módulos padrão não o estão usando.
Não consegui rastrear nenhum log de alterações que possa explicar por que isso foi introduzido na v 1.1.5.
fonte