Stato degli ordini di WooCommerce

Recentemente mi è stato chiesto come impostare automaticamente lo stato degli ordini di WooCommerce su “completato” anziché su “in lavorazione”, quando il pagamento di un ordine avvenuto tramite uno dei miei plugin è stato completato correttamente.
Per rispondere a questa domanda bisogna sapere che WooCommerce consente già di far questo se tutti i prodotti di un ordine sono di tipo virtuale (virtual) e contemporaneamente scaricabili (downloadable).
Nel rispetto di questa scelta architetturale, presa dal team di WooCommerce, i miei plugin (che estendono i gateway di pagamento di WooCommerce) si comportano nella stessa maniera.

Quello che avviene in WooCommerce è quanto segue (lo si trova nel file class-wc-order.php):

public function payment_complete() {

  [...]
  
	$order_needs_processing = true;

	if ( sizeof( $this->get_items() ) > 0 ) {
		foreach( $this->get_items() as $item ) {
			if ( $item['product_id'] > 0 ) {
				$_product = $this->get_product_from_item( $item );

				if ( ( $_product->is_downloadable() && $_product->is_virtual() )
						|| ! apply_filters( 'woocommerce_order_item_needs_processing', true, $_product, $this->id ) ) {
					$order_needs_processing = false;
					continue;
				}

			}
			$order_needs_processing = true;
			break;
		}
	}

	$new_order_status = $order_needs_processing ? 'processing' : 'completed';

	$new_order_status = apply_filters( 'woocommerce_payment_complete_order_status', $new_order_status, $this->id );
  [...]

Se però si volesse aggirare questa impostazione, si ha a disposizione una semplice strada che consiste nell’aggiungere un po’ di codice alla fine del file functions.php del tema attivo (preferibilmente in un child theme), sfruttando il filtro “woocommerce_payment_complete_order_status“.

In questo articolo How to set WooCommerce Virtual Order Status to Complete after Payment troviamo il codice che risolve questo “problema” in modo molto semplice:

add_filter( 'woocommerce_payment_complete_order_status', 'virtual_order_payment_complete_order_status', 10, 2 );
function virtual_order_payment_complete_order_status( $order_status, $order_id ) {
  $order = new WC_Order( $order_id );
 
  if ( 'processing' == $order_status && ( 'on-hold' == $order->status || 'pending' == $order->status || 'failed' == $order->status ) ) {
    $virtual_order = null;
    if ( count( $order->get_items() ) > 0 ) {
      foreach( $order->get_items() as $item ) {
        if ( 'line_item' == $item['type'] ) {
          $_product = $order->get_product_from_item( $item );
          if ( ! $_product->is_virtual() ) {
            // once we've found one non-virtual product we know we're done, break out of the loop
            $virtual_order = false;
            break;
          }
          else {
            $virtual_order = true;
          }
        }
      }
    }
 
    // virtual order, mark as completed
    if ( $virtual_order ) {
      return 'completed';
    }
  }
 
  // non-virtual order, return original status
  return $order_status;
}

In questo modo basterà impostare i prodotti come “virtuali” (senza per forza essere “scaricabili”) per avere automaticamente lo stato degli ordini su “completato”.

Come potete notare, per ogni prodotto presente nell’ordine verifichiamo che il prodotto sia virtuale. Se nell’ordine troviamo anche un solo prodotto non virtuale non possiamo (o meglio vogliamo) che l’ordine venga automaticamente contrassegnato come completato (perché magari potremmo voler gestire le scorte).

Se proprio volessimo, potremmo fare la cosa per qualsiasi prodotto (senza neppur dover specificare “virtuale”) inserendo il seguente codice sempre alla fine del file functions.php del tema attivo:

add_filter('woocommerce_payment_complete_order_status', 'all_orders_payment_complete_order_status', 10, 2);
function all_orders_payment_complete_order_status($order_status, $order_id) {
	$order = new WC_Order($order_id);
	if ('processing' == $order_status && ('on-hold' == $order->status || 'pending' == $order->status || 'failed' == $order->status)) {
		if (count($order->get_items()) > 0) { return 'completed'; }
	}
	return $order_status;
}

In questo caso non facciamo alcun controllo sul fatto che un prodotto sia virtuale o meno: semplicemente se l’ordine è valido vogliamo impostare lo stato su “completed”.

Per poter utilizzare il form dei commenti di Disqus è necessario accettare l'utilizzo dei cookie di terze parti.