Examinemos el canShip
método para ver cómo se calcula:
/**
* 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;
}
Los métodos de pedido se pueden sustituir de la siguiente manera
canUnhold ()
order->state === 'holded'
isPaymentReview ()
order->state === 'payment_review'
getIsVirtual ()
order->is_virtual === 1
está cancelado()
order->state === 'canceled'
getActionFlag ()
Las marcas de acción se establecen durante los procesos de venta, no son relevantes para recuperar pedidos de la base de datos
getAllItems ()
Aquí necesitamos unirnos sobre los artículos del pedido. is_virtual
y locked_do_ship
son columnas de la sale_flat_order_item
tabla.
getQtyToShip ()
Esto nuevamente se calcula en base a otros atributos
/**
* Retrieve item qty available for ship
*
* @return float|integer
*/
public function getQtyToShip()
{
if ($this->isDummy(true)) {
return 0;
}
return $this->getSimpleQtyToShip();
}
isDummy
las devoluciones son verdaderas si parent_id === null
y el producto tiene la opción "enviar por separado" O si parent_id !== null
y el producto no tiene la opción "enviar por separado".
getSimpleQtyToShip
vuelve qty_ordered - qty_shipped - qty_refunded - qty_canceled
.
El código
Con esta información podemos preparar una colección:
$collection = Mage::getModel('sales/order')->getCollection();
Primero, unimos los artículos que pertenecen a cada pedido:
$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');
Luego, filtramos los estados de los pedidos que no se pueden enviar ("nin" = "not in"):
$collection
->addFieldToFilter('status', array('nin' => array(
'holded', 'payment_review', 'canceled'
)))
->addFieldToFilter('main_table.is_virtual', '0');
Luego, creamos una expresión SQL para la cantidad de artículos que se pueden enviar:
- Sumamos la cantidad de envío sobre los artículos de pedido
- para elementos virtuales el resultado es 0
- para los elementos "bloqueados" el resultado es 0
- para todos los demás, el resultado es igual
qty_ordered - qty_shipped - qty_refunded - qty_canceled
TODO: tenga en cuenta que la opción de producto "se envía por separado. Esta consulta contará todos los elementos primarios y secundarios, por lo que habrá falsos positivos. Lo dejaré como un ejercicio para que el lector también calcule el resultado isDummy()
en SQL.
La suma estará disponible con el 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'));
Finalmente, filtramos solo los pedidos con un número positivo de artículos enviables. Tenemos que usar "TENER" en lugar de "DONDE" porque la columna se calcula con una función agregada:
$collection->getSelect()->having('shippable_items > 0'));