Prefazione: serve sia come osservazione scritta dell'architettura di Magento per la comunità (e per me), sia come domanda reale. Stiamo lavorando con un'esperienza di carrello ed acquisto fortemente modificata, ma la radice di questo problema è nella logica di base di Magento.
sfondo
Abbiamo creato un coupon di spedizione gratuito utilizzando la funzionalità standard delle regole di prezzo del carrello. Non ci sono condizioni sul coupon e l'unica azione è quella Free Shipping
impostata For matching items only
. Poiché non ci sono condizioni, questo verrà impostato free_shipping
su 1
per tutti gli articoli di preventivo di vendita.
Come di consueto, abbiamo anche abilitato il metodo di spedizione Spedizione gratuita. Il Freeshipping
modello del corriere fornirà le tariffe ogni volta che la richiesta ha la spedizione gratuita o il totale parziale corrisponde o supera la soglia (ma non stiamo usando l'opzione soglia). Vedi 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 si Mage_Shipping_Model_Carrier_Freeshipping::_updateFreeMethodQuote
presenta così:
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);
}
}
Quindi, purché tutti gli articoli abbiano free_shipping
impostato un valore di verità (che a causa del coupon), dovremmo ottenere la spedizione gratuita. E lo facciamo!
Il problema
Tuttavia, c'è un grande effetto collaterale: tutti i metodi di spedizione che si basano su un articolo row_weight
(come nel caso della nostra versione personalizzata del corriere FedEx) non calcoleranno le tariffe di spedizione appropriate perché ogni articolo row_weight
è impostato su 0
quando è attiva la spedizione gratuita.
È interessante notare che nessuno dei corrieri di default di Magento si affida effettivamente row_weight
, ma ci arriveremo dopo aver capito perché / quando row_weight
è impostato su0
.
Capire perché row_weight
è impostato su0
Questa parte è stata in realtà abbastanza facile da scavare. Si verifica una grande parte dei calcoli di spedizione Mage_Sales_Model_Quote_Address_Total_Shipping::collect
, inclusa l'impostazione row_weight
di 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);
}
}
Perché questo non influisce sui gestori predefiniti di Magento
Se fate una ricerca regex per /row_?weight/i
(ad esempio getRowWeight
, setRowWeight
, setData('row_weight')
, etc.) Mage_Shipping
(vettori semplici) eMage_Usa
(FedEx, UPS, e di alcuni altri vettori), non si apre. Perché? Poiché i corrieri predefiniti utilizzano il peso totale dell'indirizzo, non i pesi dei singoli articoli.
Ad esempio, diamo un'occhiata a 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 da dove viene la richiesta ottenere il peso del pacchetto? La risposta è in 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());
Possiamo ignorare l'uso di $item->getRowWeight()
qui perché requestShippingRates
viene chiamato senza fornire un elemento specifico come parametro in 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();
Questo dovrebbe sembrare familiare, poiché si tratta dello stesso posto in cui row_weight
è impostato ogni articolo 0
se è attiva la spedizione gratuita. Si noti come $addressWeight
somma di ogni elemento $rowWeight
, ma ciò che è stato fatto prima row_weight
è impostato su0
.
Fondamentalmente, il peso dell'indirizzo sarà sempre il peso totale di tutti gli articoli, indipendentemente dal free_shipping
valore di ciascun articolo. Poiché i gestori predefiniti di Magento si basano solo sul peso dell'indirizzo, il problema con row_weight
non si presenta.
Allora perché ne abbiamo bisogno row_weight
Abbiamo bisogno row_weight
perché abbiamo personalizzato il corriere FedEx di Magento per calcolare tariffe separate per articoli che provengono da origini diverse, anche se vanno alla stessa destinazione (e fanno quindi parte dello stesso indirizzo). Ad esempio, se vivi in NJ, è più economico (e più veloce) spedire un articolo da NJ che da CA - e se hai articoli sia di NJ che di CA nel tuo ordine, puoi vedere il costo (e la stima data di consegna) di ogni spedizione.
Tutto sommato, sembra che possiamo facilmente aggirare questo problema ignorando row_weight
e utilizzando weight * qty
direttamente. Ma ci porta a:
La domanda
Perché il Shipping
totale imposta gli row_weight
articoli del preventivo di vendita su 0
se è attiva la spedizione gratuita? Questo non sembra essere utilizzato da nessuna parte.
Ulteriori osservazioni
Ho trascurato di menzionare che in row_weight
realtà potrebbe essere diverso da zero, ma comunque inferiore a weight * qty
, se free_shipping
è un numero anziché true
. Presumo che lo scopo sia quello di fornire una soluzione a uno scenario come questo:
Ho 3 articoli dello stesso prodotto nel mio carrello, ogni articolo pesa 2 libbre. Applico un coupon di spedizione gratuito, ma è limitato a una quantità di 2, quindi si applica solo a 2 degli articoli. Ora, quando guarderò le tariffe di spedizione, guarderò le tariffe di spedizione per 2 + 0 + 0 libbre invece che 2 + 2 + 2 libbre.
Questo sembra avere senso, ma ci sono due problemi principali:
Nessuno dei vettori Magento predefiniti funziona in questo modo (usano l'indirizzo peso totale, vedi sopra).
Anche se alcuni dei corrieri lavorassero in questo modo, ciò significherebbe che potrei scegliere qualsiasi metodo di spedizione (ad es. Spedizione durante la notte) e pagare solo il peso di 1 articolo - il che significa che il commerciante dovrebbe coprire il costo degli altri 2 articoli . Spetterebbe al commerciante in qualche modo capire che ho pagato solo il peso di 1 articolo, quindi spedire gli altri 2 articoli utilizzando un metodo più economico, creando in modo efficace una discrepanza tra ciò che Magento visualizza e il modo in cui gli articoli erano effettivamente spedito fuori.