Update stock amounts with DB queries Closes #5367

@claudiosmweb and @coenjacobs please review
This commit is contained in:
Mike Jolley 2014-04-25 15:27:58 +01:00
parent bf553381b9
commit ca9955fa9a
4 changed files with 140 additions and 111 deletions

View File

@ -149,63 +149,81 @@ class WC_Product {
return $this->get_stock_quantity();
}
/**
* Check if the stock status needs changing
*/
private function check_stock_status() {
// Update stock status
if ( ! $this->backorders_allowed() && $this->get_total_stock() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
$this->set_stock_status( 'outofstock' );
} elseif ( $this->backorders_allowed() || $this->get_total_stock() > get_option( 'woocommerce_notify_no_stock_amount' ) ) {
$this->set_stock_status( 'instock' );
}
}
/**
* Set stock level of the product.
*
* @param mixed $amount (default: null)
* @return int Stock
* Uses queries rather than update_post_meta so we can do this in one query (to avoid stock issues).
* We cannot rely on the original loaded value in case another order was made since then.
*
* @param int $amount (default: null)
* @param string $mode can be set, add, or subtract
* @return int new stock level
*/
public function set_stock( $amount = null ) {
if ( is_null( $amount ) ) {
return 0;
}
public function set_stock( $amount = null, $mode = 'set' ) {
global $wpdb;
if ( $this->managing_stock() ) {
if ( ! is_null( $amount ) && $this->managing_stock() ) {
// Update stock amount
$this->stock = apply_filters( 'woocommerce_stock_amount', $amount );
// Update meta
update_post_meta( $this->id, '_stock', $this->stock );
// Update stock status
if ( ! $this->backorders_allowed() && $this->get_total_stock() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
$this->set_stock_status( 'outofstock' );
} elseif ( $this->backorders_allowed() || $this->get_total_stock() > get_option( 'woocommerce_notify_no_stock_amount' ) ) {
$this->set_stock_status( 'instock' );
// Update stock in DB directly
switch ( $mode ) {
case 'add' :
$wpdb->query( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + {$amount} WHERE post_id = {$this->id} AND meta_key='_stock'" );
break;
case 'subtract' :
$wpdb->query( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - {$amount} WHERE post_id = {$this->id} AND meta_key='_stock'" );
break;
default :
$wpdb->query( "UPDATE {$wpdb->postmeta} SET meta_value = {$amount} WHERE post_id = {$this->id} AND meta_key='_stock'" );
break;
}
// Clear caches
wp_cache_delete( $this->id, 'post_meta' );
// Clear total stock transient
delete_transient( 'wc_product_total_stock_' . $this->id );
// Stock status
$this->check_stock_status();
// Trigger action
do_action( 'woocommerce_product_set_stock', $this );
return $this->get_stock_quantity();
}
return 0;
return $this->get_stock_quantity();
}
/**
* Reduce stock level of the product.
* Reduce stock level of the product.
*
* @param int $by (default: 1) Amount to reduce by.
* @return int Stock
* @param int $amount (default: 1) Amount to reduce by.
* @return int new stock level
*/
public function reduce_stock( $by = 1 ) {
return $this->set_stock( $this->stock - $by );
public function reduce_stock( $amount = 1 ) {
return $this->set_stock( $amount, 'subtract' );
}
/**
* Increase stock level of the product.
*
* @param int $by (default: 1) Amount to increase by
* @return int Stock
* @param int $amount (default: 1) Amount to increase by
* @return int new stock level
*/
public function increase_stock( $by = 1 ) {
return $this->set_stock( $this->stock + $by );
public function increase_stock( $amount = 1 ) {
return $this->set_stock( $amount, 'add' );
}
/**

View File

@ -1005,14 +1005,12 @@ class WC_AJAX {
$_product = $order->get_product_from_item( $order_item );
if ( $_product->exists() && $_product->managing_stock() && isset( $order_item_qty[ $item_id ] ) && $order_item_qty[ $item_id ] > 0 ) {
$stock_change = apply_filters( 'woocommerce_reduce_order_stock_quantity', $order_item_qty[ $item_id ], $item_id );
$new_stock = $_product->reduce_stock( $stock_change );
$old_stock = $_product->stock;
$stock_change = apply_filters( 'woocommerce_reduce_order_stock_quantity', $order_item_qty[ $item_id ], $item_id );
$new_quantity = $_product->reduce_stock( $stock_change );
$return[] = sprintf( __( 'Item #%s stock reduced from %s to %s.', 'woocommerce' ), $order_item['product_id'], $old_stock, $new_quantity );
$order->add_order_note( sprintf( __( 'Item #%s stock reduced from %s to %s.', 'woocommerce' ), $order_item['product_id'], $old_stock, $new_quantity) );
$order->send_stock_notifications( $_product, $new_quantity, $order_item_qty[ $item_id ] );
$return[] = sprintf( __( 'Item #%s stock reduced from %s to %s.', 'woocommerce' ), $order_item['product_id'], $new_stock + $stock_change, $new_stock );
$order->add_order_note( sprintf( __( 'Item #%s stock reduced from %s to %s.', 'woocommerce' ), $order_item['product_id'], $new_stock + $stock_change, $new_stock ) );
$order->send_stock_notifications( $_product, $new_stock, $order_item_qty[ $item_id ] );
}
}

View File

@ -1568,17 +1568,11 @@ class WC_Order {
$_product = $this->get_product_from_item( $item );
if ( $_product && $_product->exists() && $_product->managing_stock() ) {
$qty = apply_filters( 'woocommerce_order_item_quantity', $item['qty'], $this, $item );
$new_stock = $_product->reduce_stock( $qty );
$old_stock = $_product->stock;
$qty = apply_filters( 'woocommerce_order_item_quantity', $item['qty'], $this, $item );
$new_quantity = $_product->reduce_stock( $qty );
$this->add_order_note( sprintf( __( 'Item #%s stock reduced from %s to %s.', 'woocommerce' ), $item['product_id'], $old_stock, $new_quantity) );
$this->send_stock_notifications( $_product, $new_quantity, $item['qty'] );
$this->add_order_note( sprintf( __( 'Item #%s stock reduced from %s to %s.', 'woocommerce' ), $item['product_id'], $new_stock + $qty, $new_stock) );
$this->send_stock_notifications( $_product, $new_stock, $item['qty'] );
}
}

View File

@ -361,111 +361,130 @@ class WC_Product_Variation extends WC_Product {
/**
* Set stock level of the product variation.
* @param int $amount
*
* Uses queries rather than update_post_meta so we can do this in one query (to avoid stock issues).
* We cannot rely on the original loaded value in case another order was made since then.
*
* @param int $amount
* @param bool $force_variation_stock If true, the variation's stock will be updated and not the parents.
* @return int
* @todo Need to return 0 if is_null? Or something. Should not be just return.
* @param string $mode can be set, add, or subtract
* @return int new stock level
*/
function set_stock( $amount = null, $force_variation_stock = false ) {
if ( is_null( $amount ) )
return;
public function set_stock( $amount = null, $force_variation_stock = false, $mode = 'set' ) {
global $wpdb;
if ( $amount === '' && $force_variation_stock ) {
if ( ! is_null( $amount ) ) {
// If amount is an empty string, stock management is being turned off at variation level
$this->variation_has_stock = false;
$this->stock = '';
unset( $this->manage_stock );
if ( '' === $amount && $force_variation_stock ) {
// Update meta
update_post_meta( $this->variation_id, '_stock', '' );
// If amount is an empty string, stock management is being turned off at variation level
$this->variation_has_stock = false;
$this->stock = '';
unset( $this->manage_stock );
// Refresh parent prices
WC_Product_Variable::sync( $this->id );
// Update meta
update_post_meta( $this->variation_id, '_stock', '' );
} elseif ( $this->variation_has_stock || $force_variation_stock ) {
// Refresh parent prices
WC_Product_Variable::sync( $this->id );
// Update stock amount
$this->stock = intval( $amount );
$this->variation_has_stock = true;
$this->manage_stock = 'yes';
} elseif ( $this->variation_has_stock || $force_variation_stock ) {
// Update meta
update_post_meta( $this->variation_id, '_stock', $this->stock );
// Update stock values
$this->variation_has_stock = true;
$this->manage_stock = 'yes';
// Clear total stock transient
delete_transient( 'wc_product_total_stock_' . $this->id );
// Update stock in DB directly
switch ( $mode ) {
case 'add' :
$wpdb->query( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + {$amount} WHERE post_id = {$this->variation_id} AND meta_key='_stock'" );
break;
case 'subtract' :
$wpdb->query( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - {$amount} WHERE post_id = {$this->variation_id} AND meta_key='_stock'" );
break;
default :
$wpdb->query( "UPDATE {$wpdb->postmeta} SET meta_value = {$amount} WHERE post_id = {$this->variation_id} AND meta_key='_stock'" );
break;
}
// Check parents out of stock attribute
if ( ! $this->is_in_stock() ) {
// Clear caches
wp_cache_delete( $this->variation_id, 'post_meta' );
// Check parent
$parent_product = get_product( $this->id );
// Update stock amount in class
$this->stock = get_post_meta( $this->variation_id, '_stock', true );
// Only continue if the parent has backorders off and all children are stock managed and out of stock
if ( ! $parent_product->backorders_allowed() && $parent_product->get_total_stock() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
// Clear total stock transient
delete_transient( 'wc_product_total_stock_' . $this->id );
$all_managed = true;
// Check parents out of stock attribute
if ( ! $this->is_in_stock() ) {
if ( sizeof( $parent_product->get_children() ) > 0 ) {
foreach ( $parent_product->get_children() as $child_id ) {
$stock = get_post_meta( $child_id, '_stock', true );
if ( $stock == '' ) {
$all_managed = false;
break;
// Check parent
$parent_product = get_product( $this->id );
// Only continue if the parent has backorders off and all children are stock managed and out of stock
if ( ! $parent_product->backorders_allowed() && $parent_product->get_total_stock() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
$all_managed = true;
if ( sizeof( $parent_product->get_children() ) > 0 ) {
foreach ( $parent_product->get_children() as $child_id ) {
$stock = get_post_meta( $child_id, '_stock', true );
if ( $stock == '' ) {
$all_managed = false;
break;
}
}
}
if ( $all_managed ) {
$this->set_stock_status( 'outofstock' );
}
}
if ( $all_managed ) {
$this->set_stock_status( 'outofstock' );
}
} elseif ( $this->is_in_stock() ) {
$this->set_stock_status( 'instock' );
}
} elseif ( $this->is_in_stock() ) {
$this->set_stock_status( 'instock' );
// Refresh parent prices
WC_Product_Variable::sync( $this->id );
// Trigger action
do_action( 'woocommerce_product_set_stock', $this );
} else {
return parent::set_stock( $amount, $mode );
}
// Refresh parent prices
WC_Product_Variable::sync( $this->id );
// Trigger action
do_action( 'woocommerce_product_set_stock', $this );
return $this->get_stock_quantity();
} else {
return parent::set_stock( $amount );
}
return $this->get_stock_quantity();
}
/**
* Reduce stock level of the product.
*
* @param int $by (default: 1) Amount to reduce by
* @param int $amount (default: 1) Amount to reduce by
* @return int stock level
*/
public function reduce_stock( $by = 1 ) {
public function reduce_stock( $amount = 1 ) {
if ( $this->variation_has_stock ) {
return $this->set_stock( $this->stock - $by );
return $this->set_stock( $amount, false, 'subtract' );
} else {
return parent::reduce_stock( $by );
return parent::reduce_stock( $amount );
}
}
/**
* Increase stock level of the product.
*
* @param int $by (default: 1) Amount to increase by
* @param int $amount (default: 1) Amount to increase by
* @return int stock level
*/
public function increase_stock( $by = 1 ) {
public function increase_stock( $amount = 1 ) {
if ( $this->variation_has_stock ) {
return $this->set_stock( $this->stock + $by );
return $this->set_stock( $amount, false, 'add' );
} else {
return parent::increase_stock( $by );
return parent::increase_stock( $amount );
}
}