Examinons la canShip
méthode pour voir comment elle est calculée:
/**
* Retrieve order shipment availability
*
* @return bool
*/
public function canShip()
{
if ($this->canUnhold() || $this->isPaymentReview()) {
return false;
}
if ($this->getIsVirtual() || $this->isCanceled()) {
return false;
}
if ($this->getActionFlag(self::ACTION_FLAG_SHIP) === false) {
return false;
}
foreach ($this->getAllItems() as $item) {
if ($item->getQtyToShip()>0 && !$item->getIsVirtual()
&& !$item->getLockedDoShip())
{
return true;
}
}
return false;
}
Les méthodes de commande peuvent être remplacées comme suit
canUnhold ()
order->state === 'holded'
isPaymentReview ()
order->state === 'payment_review'
getIsVirtual ()
order->is_virtual === 1
est annulé()
order->state === 'canceled'
getActionFlag ()
Les indicateurs d'action sont définis pendant les processus de vente, non pertinents pour récupérer les commandes de la base de données
getAllItems ()
Ici, nous devons faire une jointure sur les articles de la commande. is_virtual
et locked_do_ship
sont des colonnes du sale_flat_order_item
tableau.
getQtyToShip ()
Ceci est à nouveau calculé sur la base d'autres attributs
/**
* Retrieve item qty available for ship
*
* @return float|integer
*/
public function getQtyToShip()
{
if ($this->isDummy(true)) {
return 0;
}
return $this->getSimpleQtyToShip();
}
isDummy
renvoie est vrai si parent_id === null
et le produit a l'option «expédier séparément» OU si parent_id !== null
et le produit n'a pas l'option «expédier séparément».
getSimpleQtyToShip
retourne qty_ordered - qty_shipped - qty_refunded - qty_canceled
.
Le code
Avec ces informations, nous pouvons préparer une collection:
$collection = Mage::getModel('sales/order')->getCollection();
Tout d'abord, nous joignons les articles qui appartiennent à chaque commande:
$collection->getSelect()
->joinLeft(
array('order_item' => $collection->getTable('sales/order_item')),
'main_table.entity_id=order_item.order_id', array('qty_ordered', 'qty_shipped', 'qty_refunded', 'qty_canceled', 'is_virtual', 'locked_do_ship'))
->group('main_table.entity_id');
Ensuite, nous filtrons les statuts de commande qui ne peuvent pas être expédiés ("nin" = "not in"):
$collection
->addFieldToFilter('status', array('nin' => array(
'holded', 'payment_review', 'canceled'
)))
->addFieldToFilter('main_table.is_virtual', '0');
Ensuite, nous créons une expression SQL pour le nombre d'articles qui peuvent être expédiés:
- nous additionnons la quantité expédiable sur les articles de la commande
- pour les objets virtuels, le résultat est 0
- pour les éléments "verrouillés", le résultat est 0
- pour tous les autres, le résultat est égal à
qty_ordered - qty_shipped - qty_refunded - qty_canceled
TODO: prendre en compte l'option de produit "expédier séparément. Cette requête comptera tous les éléments parents et enfants, donc il y aura des faux positifs. Je laisserai au lecteur un exercice pour calculer également le résultat de isDummy()
SQL.
La somme sera disponible avec l'alias "shippable_items"
$collection->addExpressionFieldToSelect(
'shippable_items',
'SUM(({{qty_ordered}} - {{qty_shipped}} - {{qty_refunded}} - {{qty_canceled}}) * !{{is_virtual}} * {{locked_do_ship}} IS NOT NULL)',
array(
'qty_ordered' => 'order_item.qty_ordered',
'qty_shipped' => 'order_item.qty_shipped',
'qty_refunded' => 'order_item.qty_refunded',
'qty_canceled' => 'order_item.qty_canceled',
'is_virtual' => 'order_item.is_virtual',
'locked_do_ship' => 'order_item.locked_do_ship'));
Enfin, nous filtrons uniquement les commandes avec un nombre positif d'articles expédiables. Nous devons utiliser "HAVING" au lieu de "WHERE" car la colonne est calculée avec une fonction d'agrégation:
$collection->getSelect()->having('shippable_items > 0'));