Давайте рассмотрим canShip
метод, чтобы увидеть, как он рассчитывается:
/**
* 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;
}
Методы заказа могут быть заменены следующим образом
canUnhold ()
order->state === 'holded'
isPaymentReview ()
order->state === 'payment_review'
getIsVirtual ()
order->is_virtual === 1
isCanceled ()
order->state === 'canceled'
getActionFlag ()
Флаги действий устанавливаются в процессе продаж и не имеют отношения к получению заказов из базы данных.
getAllItems ()
Здесь нам нужно объединить элементы заказа. is_virtual
и locked_do_ship
являются столбцами sale_flat_order_item
таблицы.
getQtyToShip ()
Это снова рассчитывается на основе других атрибутов
/**
* Retrieve item qty available for ship
*
* @return float|integer
*/
public function getQtyToShip()
{
if ($this->isDummy(true)) {
return 0;
}
return $this->getSimpleQtyToShip();
}
isDummy
return is true, если parent_id === null
и у продукта есть опция «отправить отдельно» ИЛИ если parent_id !== null
у продукта нет опции «отправить отдельно».
getSimpleQtyToShip
возвращается qty_ordered - qty_shipped - qty_refunded - qty_canceled
.
Код
С помощью этой информации мы можем подготовить коллекцию:
$collection = Mage::getModel('sales/order')->getCollection();
Сначала мы объединяем элементы, которые принадлежат каждому заказу:
$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');
Затем мы фильтруем статусы заказов, которые не могут быть отправлены ("nin" = "not in"):
$collection
->addFieldToFilter('status', array('nin' => array(
'holded', 'payment_review', 'canceled'
)))
->addFieldToFilter('main_table.is_virtual', '0');
Затем мы создаем выражение SQL для количества отправляемых товаров:
- мы суммируем кол-во отправляемого товара
- для виртуальных предметов результат равен 0
- для "заблокированных" элементов результат равен 0
- для всех остальных результат равен
qty_ordered - qty_shipped - qty_refunded - qty_canceled
TODO: принять опцию продукта "поставляется отдельно во внимание. Этот запрос будет подсчитывать все родительские и дочерние элементы, поэтому будут ложные срабатывания. Я оставлю это в качестве упражнения для читателя, чтобы также вычислить результат isDummy()
в SQL.
Сумма будет доступна с псевдонимом "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'));
Наконец, мы фильтруем только заказы с положительным количеством отправляемых товаров. Мы должны использовать «HAVING» вместо «WHERE», потому что столбец рассчитывается с помощью статистической функции:
$collection->getSelect()->having('shippable_items > 0'));