Vamos examinar o canShip
método para ver como é calculado:
/**
* 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;
}
Os métodos de pedido podem ser substituídos da seguinte maneira
canUnhold ()
order->state === 'holded'
isPaymentReview ()
order->state === 'payment_review'
getIsVirtual ()
order->is_virtual === 1
está cancelado()
order->state === 'canceled'
getActionFlag ()
Os sinalizadores de ação são definidos durante os processos de vendas, não relevantes para recuperar pedidos do banco de dados
getAllItems ()
Aqui precisamos fazer uma junção sobre os itens do pedido. is_virtual
e locked_do_ship
são colunas da sale_flat_order_item
tabela.
getQtyToShip ()
Isso novamente é calculado com base em outros atributos
/**
* Retrieve item qty available for ship
*
* @return float|integer
*/
public function getQtyToShip()
{
if ($this->isDummy(true)) {
return 0;
}
return $this->getSimpleQtyToShip();
}
isDummy
retorna é verdadeiro se parent_id === null
e o produto tiver a opção "enviar separadamente" OU se parent_id !== null
e o produto não tiver a opção "enviar separadamente".
getSimpleQtyToShip
retorna qty_ordered - qty_shipped - qty_refunded - qty_canceled
.
O código
Com essas informações, podemos preparar uma coleção:
$collection = Mage::getModel('sales/order')->getCollection();
Primeiro, juntamos os itens que pertencem 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');
Em seguida, filtramos os status dos pedidos que não podem ser enviados ("nin" = "not in"):
$collection
->addFieldToFilter('status', array('nin' => array(
'holded', 'payment_review', 'canceled'
)))
->addFieldToFilter('main_table.is_virtual', '0');
Em seguida, criamos uma expressão SQL para o número de itens que podem ser enviados:
- somamos o qty entregável sobre os itens do pedido
- para itens virtuais, o resultado é 0
- para itens "bloqueados", o resultado é 0
- para todos os outros, o resultado é igual a
qty_ordered - qty_shipped - qty_refunded - qty_canceled
TODO: leve a opção do produto "em consideração separadamente. Essa consulta contará todos os itens pai e filho, portanto haverá falsos positivos. Vou deixar como um exercício para o leitor também calcular o resultado do isDummy()
SQL.
A soma estará disponível com o 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'));
Por fim, filtramos apenas pedidos com um número positivo de itens que podem ser entregues. Temos que usar "HAVING" em vez de "WHERE" porque a coluna é calculada com uma função agregada:
$collection->getSelect()->having('shippable_items > 0'));