public function reserve_stock_for_order( $order, $minutes = 0 ) {
$minutes = $minutes ? $minutes : (int) get_option( 'woocommerce_hold_stock_minutes', 60 );
/**
* Filters the number of minutes an order should reserve stock for.
*
* This hook allows the number of minutes that stock in an order should be reserved for to be filtered, useful for third party developers to increase/reduce the number of minutes if the order meets certain criteria, or to exclude an order from stock reservation using a zero value.
*
* @since 8.8.0
*
* @param int $minutes How long to reserve stock for the order in minutes. Defaults to woocommerce_hold_stock_minutes or 10 if block checkout entry.
* @param \WC_Order $order Order object.
*/
$minutes = (int) apply_filters( 'woocommerce_order_hold_stock_minutes', $minutes, $order );
if ( ! $minutes || ! $this->is_enabled() ) {
return;
}
$held_stock_notes = array();
try {
$items = array_filter(
$order->get_items(),
function ( $item ) {
return $item->is_type( 'line_item' ) && $item->get_product() instanceof \WC_Product && $item->get_quantity() > 0;
}
);
$rows = array();
foreach ( $items as $item ) {
$product = $item->get_product();
if ( ! $product->is_in_stock() ) {
throw new ReserveStockException(
'woocommerce_product_out_of_stock',
sprintf(
/* translators: %s: product name */
__( '"%s" is out of stock and cannot be purchased.', 'woocommerce' ),
$product->get_name()
),
403
);
}
// If stock management is off, no need to reserve any stock here.
if ( ! $product->managing_stock() || $product->backorders_allowed() ) {
continue;
}
$managed_by_id = $product->get_stock_managed_by_id();
/**
* Filter order item quantity.
*
* @param int|float $quantity Quantity.
* @param WC_Order $order Order data.
* @param WC_Order_Item_Product $item Order item data.
*/
$item_quantity = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $order, $item );
$rows[ $managed_by_id ] = isset( $rows[ $managed_by_id ] ) ? $rows[ $managed_by_id ] + $item_quantity : $item_quantity;
if ( count( $held_stock_notes ) < 5 ) {
// translators: %1$s is a product's formatted name, %2$d: is the quantity of said product to which the stock hold applied.
$held_stock_notes[] = sprintf( _x( '- %1$s × %2$d', 'held stock note', 'woocommerce' ), $product->get_formatted_name(), $rows[ $managed_by_id ] );
}
}
if ( ! empty( $rows ) ) {
foreach ( $rows as $product_id => $quantity ) {
$this->reserve_stock_for_product( $product_id, $quantity, $order, $minutes );
}
}
} catch ( ReserveStockException $e ) {
$this->release_stock_for_order( $order );
throw $e;
}
// Add order note after successfully holding the stock.
if ( ! empty( $held_stock_notes ) ) {
$remaining_count = count( $rows ) - count( $held_stock_notes );
if ( $remaining_count > 0 ) {
$held_stock_notes[] = sprintf(
// translators: %d is the remaining order items count.
_nx( '- ...and %d more item.', '- ... and %d more items.', $remaining_count, 'held stock note', 'woocommerce' ),
$remaining_count
);
}
$order->add_order_note(
sprintf(
// translators: %1$s is a time in minutes, %2$s is a list of products and quantities.
_x( 'Stock hold of %1$s minutes applied to: %2$s', 'held stock note', 'woocommerce' ),
$minutes,
'<br>' . implode( '<br>', $held_stock_notes )
),
false,
false,
array(
'note_group' => OrderNoteGroup::PRODUCT_STOCK,
)
);
}
}