added changes for low stock notifications per product

This commit is contained in:
Joey 2018-05-27 05:40:58 +01:00
parent 127e5f0213
commit b08b55a651
18 changed files with 148 additions and 9 deletions

View File

@ -74,6 +74,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
'stock_quantity' => null,
'stock_status' => 'instock',
'backorders' => 'no',
'low_stock_amount' => -1,
'sold_individually' => false,
'weight' => '',
'length' => '',
@ -389,6 +390,17 @@ class WC_Product extends WC_Abstract_Legacy_Product {
return $this->get_prop( 'backorders', $context );
}
/**
* Get low stock amount.
*
* @param string $context What the value is for. Valid values are view and edit.
* @since 3.4.0
* @return int|null
*/
public function get_low_stock_amount( $context = 'view' ) {
return $this->get_prop( 'low_stock_amount', $context );
}
/**
* Return if should be sold individually.
*
@ -963,6 +975,15 @@ class WC_Product extends WC_Abstract_Legacy_Product {
$this->set_prop( 'backorders', $backorders );
}
/**
* Set low stock amount.
*
* @param $amount
*/
public function set_low_stock_amount( $amount ) {
$this->set_prop( 'low_stock_amount', $amount );
}
/**
* Set if should be sold individually.
*
@ -1300,10 +1321,11 @@ class WC_Product extends WC_Abstract_Legacy_Product {
* @since 3.0.0
*/
public function validate_props() {
// Before updating, ensure stock props are all aligned. Qty and backorders are not needed if not stock managed.
// Before updating, ensure stock props are all aligned. Qty, backorders and low stock amount are not needed if not stock managed.
if ( ! $this->get_manage_stock() ) {
$this->set_stock_quantity( '' );
$this->set_backorders( 'no' );
$this->set_low_stock_amount( '' );
// If we are stock managing and we don't have stock, force out of stock status.
} elseif ( $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount', 0 ) && 'no' === $this->get_backorders() ) {

View File

@ -640,6 +640,7 @@ class WC_Product_CSV_Importer_Controller {
'stock_status' => __( 'In stock?', 'woocommerce' ),
'stock_quantity' => _x( 'Stock', 'Quantity in stock', 'woocommerce' ),
'backorders' => __( 'Backorders allowed?', 'woocommerce' ),
'low_stock_amount' => __( 'Low Stock Amount', 'woocommerce' ),
'sold_individually' => __( 'Sold individually?', 'woocommerce' ),
/* translators: %s: weight unit */
'weight' => sprintf( __( 'Weight (%s)', 'woocommerce' ), $weight_unit ),

View File

@ -55,6 +55,7 @@ function wc_importer_default_english_mappings( $mappings ) {
'In stock?' => 'stock_status',
'Stock' => 'stock_quantity',
'Backorders allowed?' => 'backorders',
'Low Stock Amount' => 'low_stock_amount',
'Sold individually?' => 'sold_individually',
sprintf( 'Weight (%s)', $weight_unit ) => 'weight',
sprintf( 'Length (%s)', $dimension_unit ) => 'length',

View File

@ -187,6 +187,7 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
<div class="tax_status">' . esc_html( $this->object->get_tax_status() ) . '</div>
<div class="tax_class">' . esc_html( $this->object->get_tax_class() ) . '</div>
<div class="backorders">' . esc_html( $this->object->get_backorders() ) . '</div>
<div class="low_stock_amount">' . esc_html( $this->object->get_low_stock_amount() ) . '</div>
</div>
';
}

View File

@ -365,6 +365,7 @@ class WC_Meta_Box_Product_Data {
'backorders' => isset( $_POST['_backorders'] ) ? wc_clean( wp_unslash( $_POST['_backorders'] ) ) : null,
'stock_status' => wc_clean( wp_unslash( $_POST['_stock_status'] ) ),
'stock_quantity' => $stock,
'low_stock_amount' => '' === $_POST['_low_stock_amount'] ? '' : absint( wp_unslash( $_POST['_low_stock_amount'] ) ),
'download_limit' => '' === $_POST['_download_limit'] ? '' : absint( wp_unslash( $_POST['_download_limit'] ) ),
'download_expiry' => '' === $_POST['_download_expiry'] ? '' : absint( wp_unslash( $_POST['_download_expiry'] ) ),
'downloads' => self::prepare_downloads(
@ -372,12 +373,12 @@ class WC_Meta_Box_Product_Data {
isset( $_POST['_wc_file_urls'] ) ? wp_unslash( $_POST['_wc_file_urls'] ) : array(),
isset( $_POST['_wc_file_hashes'] ) ? wp_unslash( $_POST['_wc_file_hashes'] ) : array()
),
'product_url' => esc_url_raw( wp_unslash( $_POST['_product_url'] ) ),
'button_text' => wc_clean( wp_unslash( $_POST['_button_text'] ) ),
'children' => 'grouped' === $product_type ? self::prepare_children() : null,
'reviews_allowed' => ! empty( $_POST['comment_status'] ) && 'open' === $_POST['comment_status'],
'attributes' => $attributes,
'default_attributes' => self::prepare_set_attributes( $attributes, 'default_attribute_' ),
'product_url' => esc_url_raw( wp_unslash( $_POST['_product_url'] ) ),
'button_text' => wc_clean( wp_unslash( $_POST['_button_text'] ) ),
'children' => 'grouped' === $product_type ? self::prepare_children() : null,
'reviews_allowed' => ! empty( $_POST['comment_status'] ) && 'open' === $_POST['comment_status'],
'attributes' => $attributes,
'default_attributes' => self::prepare_set_attributes( $attributes, 'default_attribute_' ),
)
);
@ -455,6 +456,7 @@ class WC_Meta_Box_Product_Data {
'manage_stock' => isset( $_POST['variable_manage_stock'][ $i ] ),
'stock_quantity' => $stock,
'backorders' => isset( $_POST['variable_backorders'], $_POST['variable_backorders'][ $i ] ) ? wc_clean( $_POST['variable_backorders'][ $i ] ) : null,
'low_stock_amount' => wc_clean( $_POST['variable_low_stock_amount'][ $i ] ),
'stock_status' => wc_clean( $_POST['variable_stock_status'][ $i ] ),
'image_id' => wc_clean( $_POST['upload_image_id'][ $i ] ),
'attributes' => self::prepare_set_attributes( $parent->get_attributes(), 'attribute_', $i ),

View File

@ -65,6 +65,20 @@ if ( ! defined( 'ABSPATH' ) ) {
)
);
woocommerce_wp_text_input( array(
'id' => '_low_stock_amount',
'value' => -1 === $product_object->get_low_stock_amount( 'edit' ) ? '' : $product_object->get_low_stock_amount( 'edit' ),
'label' => __( 'Low stock threshold', 'woocommerce' ),
'desc_tip' => true,
'description' => __( 'When product stock reaches this amount you will be notified by email', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'min' => 0,
'step' => 1,
),
'data_type' => 'stock',
) );
do_action( 'woocommerce_product_options_stock_fields' );
echo '</div>';

View File

@ -195,6 +195,24 @@ if ( ! defined( 'ABSPATH' ) ) {
)
);
woocommerce_wp_text_input(
array(
'id' => "variable_low_stock_amount{$loop}",
'name' => "variable_low_stock_amount[{$loop}]",
'value' => wc_stock_amount( $variation_object->get_low_stock_amount( 'edit' ) ),
'label' => __( 'Low stock threshold', 'woocommerce' ),
'desc_tip' => true,
'description' => __( 'When product stock reaches this amount you will be notified by email', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'min' => 0,
'step' => 1,
),
'data_type' => 'stock',
'wrapper_class' => 'form-row form-row-first',
)
);
/**
* woocommerce_variation_options_inventory action.
*

View File

@ -208,6 +208,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Products_Controller
'manage_stock' => $object->managing_stock(),
'stock_quantity' => $object->get_stock_quantity(),
'in_stock' => $object->is_in_stock(),
'low_stock_amount' => $object->get_low_stock_amount(),
'backorders' => $object->get_backorders(),
'backorders_allowed' => $object->backorders_allowed(),
'backordered' => $object->is_on_backorder(),
@ -346,6 +347,10 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Products_Controller
$variation->set_stock_status( true === $request['in_stock'] ? 'instock' : 'outofstock' );
}
if ( isset( $request['low_stock_amount'] ) ) {
$variation->set_low_stock_amount( $request['low_stock_amount'] );
}
if ( isset( $request['backorders'] ) ) {
$variation->set_backorders( $request['backorders'] );
}
@ -821,6 +826,12 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Products_Controller
'default' => true,
'context' => array( 'view', 'edit' ),
),
'low_stock_amount' => array(
'description' => __( 'If managing stock, this controls when low stock notifications are sent.', 'woocommerce' ),
'type' => 'integer',
'default' => -1,
'context' => array( 'view', 'edit' ),
),
'backorders' => array(
'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ),
'type' => 'string',

View File

@ -608,6 +608,7 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
'manage_stock' => $product->managing_stock(),
'stock_quantity' => $product->get_stock_quantity( $context ),
'in_stock' => $product->is_in_stock(),
'low_stock_amount' => $product->get_low_stock_amount( $context ),
'backorders' => $product->get_backorders( $context ),
'backorders_allowed' => $product->backorders_allowed(),
'backordered' => $product->is_on_backorder(),
@ -901,6 +902,11 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
$product->set_manage_stock( $request['manage_stock'] );
}
// Low stock amount.
if ( isset( $request['low_stock_amount'] ) ) {
$product->set_low_stock_amount( $request['low_stock_amount'] );
}
// Backorders.
if ( isset( $request['backorders'] ) ) {
$product->set_backorders( $request['backorders'] );
@ -911,11 +917,13 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
$product->set_backorders( 'no' );
$product->set_stock_quantity( '' );
$product->set_stock_status( $stock_status );
$product->set_low_stock_amount( '' );
} elseif ( $product->is_type( 'external' ) ) {
$product->set_manage_stock( 'no' );
$product->set_backorders( 'no' );
$product->set_stock_quantity( '' );
$product->set_stock_status( 'instock' );
$product->set_low_stock_amount( '' );
} elseif ( $product->get_manage_stock() ) {
// Stock status is always determined by children so sync later.
if ( ! $product->is_type( 'variable' ) ) {
@ -1664,6 +1672,12 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
'default' => true,
'context' => array( 'view', 'edit' ),
),
'low_stock_amount' => array(
'description' => __( 'If managing stock, this controls when low stock notifications are sent.', 'woocommerce' ),
'type' => 'integer',
'default' => -1,
'context' => array( 'view', 'edit' ),
),
'backorders' => array(
'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ),
'type' => 'string',

View File

@ -494,6 +494,7 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller {
'manage_stock' => $product->managing_stock(),
'stock_quantity' => $product->get_stock_quantity(),
'in_stock' => $product->is_in_stock(),
'low_stock_amount' => $product->get_low_stock_amount(),
'backorders' => $product->get_backorders(),
'backorders_allowed' => $product->backorders_allowed(),
'backordered' => $product->is_on_backorder(),
@ -568,6 +569,7 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller {
'manage_stock' => $variation->managing_stock(),
'stock_quantity' => $variation->get_stock_quantity(),
'in_stock' => $variation->is_in_stock(),
'low_stock_amount' => $variation->get_low_stock_amount(),
'backorders' => $variation->get_backorders(),
'backorders_allowed' => $variation->backorders_allowed(),
'backordered' => $variation->is_on_backorder(),
@ -1230,6 +1232,11 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller {
$product->set_manage_stock( $request['manage_stock'] );
}
// Low stock amount.
if ( isset( $request['low_stock_amount'] ) ) {
$product->set_low_stock_amount( $request['low_stock_amount'] );
}
// Backorders.
if ( isset( $request['backorders'] ) ) {
$product->set_backorders( $request['backorders'] );
@ -1240,11 +1247,13 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller {
$product->set_backorders( 'no' );
$product->set_stock_quantity( '' );
$product->set_stock_status( $stock_status );
$product->set_low_stock_amount( '' );
} elseif ( $product->is_type( 'external' ) ) {
$product->set_manage_stock( 'no' );
$product->set_backorders( 'no' );
$product->set_stock_quantity( '' );
$product->set_stock_status( 'instock' );
$product->set_low_stock_amount( '' );
} elseif ( $product->get_manage_stock() ) {
// Stock status is always determined by children so sync later.
if ( ! $product->is_type( 'variable' ) ) {
@ -1440,6 +1449,10 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller {
$variation->set_stock_status( true === $data['in_stock'] ? 'instock' : 'outofstock' );
}
if ( isset( $data['low_stock_amount'] ) ) {
$variation->set_low_stock_amount( $data['low_stock_amount'] );
}
if ( isset( $data['backorders'] ) ) {
$variation->set_backorders( $data['backorders'] );
}
@ -1954,6 +1967,12 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller {
'default' => true,
'context' => array( 'view', 'edit' ),
),
'low_stock_amount' => array(
'description' => __( 'If managing stock, this controls when low stock notifications are sent.', 'woocommerce' ),
'type' => 'integer',
'default' => -1,
'context' => array( 'view', 'edit' ),
),
'backorders' => array(
'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ),
'type' => 'string',
@ -2414,6 +2433,12 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller {
'default' => true,
'context' => array( 'view', 'edit' ),
),
'low_stock_amount' => array(
'description' => __( 'If managing stock, this controls when low stock notifications are sent.', 'woocommerce' ),
'type' => 'integer',
'default' => -1,
'context' => array( 'view', 'edit' ),
),
'backorders' => array(
'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ),
'type' => 'string',

View File

@ -46,6 +46,7 @@ class WC_Product_Query extends WC_Object_Query {
'stock_quantity' => '',
'stock_status' => '',
'backorders' => '',
'low_stock_amount' => '',
'sold_individually' => '',
'weight' => '',
'length' => '',

View File

@ -406,6 +406,7 @@ class WC_Product_Variable extends WC_Product {
if ( ! $this->get_manage_stock() ) {
$this->set_stock_quantity( '' );
$this->set_backorders( 'no' );
$this->set_low_stock_amount( '' );
$this->data_store->sync_stock_status( $this );
// If we are stock managing, backorders are allowed, and we don't have stock, force on backorder status.

View File

@ -37,6 +37,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'_stock',
'_stock_status',
'_backorders',
'_low_stock_amount',
'_sold_individually',
'_weight',
'_length',
@ -333,6 +334,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'stock_quantity' => get_post_meta( $id, '_stock', true ),
'stock_status' => get_post_meta( $id, '_stock_status', true ),
'backorders' => get_post_meta( $id, '_backorders', true ),
'low_stock_amount' => get_post_meta( $id, '_low_stock_amount', true ),
'sold_individually' => get_post_meta( $id, '_sold_individually', true ),
'weight' => get_post_meta( $id, '_weight', true ),
'length' => get_post_meta( $id, '_length', true ),
@ -499,6 +501,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'_tax_class' => 'tax_class',
'_manage_stock' => 'manage_stock',
'_backorders' => 'backorders',
'_low_stock_amount' => 'low_stock_amount',
'_sold_individually' => 'sold_individually',
'_weight' => 'weight',
'_length' => 'length',

View File

@ -314,6 +314,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
'download_expiry' => get_post_meta( $id, '_download_expiry', true ),
'image_id' => get_post_thumbnail_id( $id ),
'backorders' => get_post_meta( $id, '_backorders', true ),
'low_stock_amount' => get_post_meta( $id, '_low_stock_amount', true ),
'sku' => get_post_meta( $id, '_sku', true ),
'stock_quantity' => get_post_meta( $id, '_stock', true ),
'weight' => get_post_meta( $id, '_weight', true ),
@ -353,6 +354,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
'sku' => get_post_meta( $product->get_parent_id(), '_sku', true ),
'manage_stock' => get_post_meta( $product->get_parent_id(), '_manage_stock', true ),
'backorders' => get_post_meta( $product->get_parent_id(), '_backorders', true ),
'low_stock_amount' => get_post_meta( $product->get_parent_id(), '_low_stock_amount', true ),
'stock_quantity' => wc_stock_amount( get_post_meta( $product->get_parent_id(), '_stock', true ) ),
'weight' => get_post_meta( $product->get_parent_id(), '_weight', true ),
'length' => get_post_meta( $product->get_parent_id(), '_length', true ),

View File

@ -94,6 +94,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
'tax_class' => __( 'Tax class', 'woocommerce' ),
'stock_status' => __( 'In stock?', 'woocommerce' ),
'stock' => __( 'Stock', 'woocommerce' ),
'low_stock_amount' => __( 'Low stock amount', 'woocommerce' ),
'backorders' => __( 'Backorders allowed?', 'woocommerce' ),
'sold_individually' => __( 'Sold individually?', 'woocommerce' ),
/* translators: %s: weight */

View File

@ -234,7 +234,7 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
}
if ( 'external' === $object->get_type() ) {
unset( $data['manage_stock'], $data['stock_status'], $data['backorders'] );
unset( $data['manage_stock'], $data['stock_status'], $data['backorders'], $data['low_stock_amount'] );
}
if ( 'importing' === $object->get_status() ) {

View File

@ -571,6 +571,7 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
'short_description' => array( $this, 'parse_skip_field' ),
'description' => array( $this, 'parse_skip_field' ),
'manage_stock' => array( $this, 'parse_bool_field' ),
'low_stock_amount' => array( $this, 'parse_int_field' ),
'backorders' => array( $this, 'parse_backorders_field' ),
'stock_status' => array( $this, 'parse_bool_field' ),
'sold_individually' => array( $this, 'parse_bool_field' ),

View File

@ -52,6 +52,11 @@ function wc_update_product_stock( $product, $stock_quantity = null, $operation =
do_action( 'woocommerce_product_set_stock', $product_with_stock );
}
// Send low stock notification for variable products
if ( ( $product->get_id() !== $product_id_with_stock ) && absint( $product_with_stock->get_stock_quantity() ) <= wc_get_low_stock_amount( $product_with_stock ) ) {
do_action( 'woocommerce_low_stock', $product_with_stock );
}
return $product_with_stock->get_stock_quantity();
}
return $product->get_stock_quantity();
@ -120,7 +125,7 @@ function wc_reduce_stock_levels( $order_id ) {
if ( '' !== get_option( 'woocommerce_notify_no_stock_amount' ) && $new_stock <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
do_action( 'woocommerce_no_stock', $product );
} elseif ( '' !== get_option( 'woocommerce_notify_low_stock_amount' ) && $new_stock <= get_option( 'woocommerce_notify_low_stock_amount' ) ) {
} elseif ( $new_stock <= wc_get_low_stock_amount( $product ) ) {
do_action( 'woocommerce_low_stock', $product );
}
@ -143,3 +148,19 @@ function wc_reduce_stock_levels( $order_id ) {
do_action( 'woocommerce_reduce_order_stock', $order );
}
}
/**
* Return low stock amount to determine if notification needs to be sent
*
* @param WC_Product $product
*
* @return int
*/
function wc_get_low_stock_amount( WC_Product $product ) {
$low_stock_amount = absint( $product->get_low_stock_amount() );
if ( ! is_int( $low_stock_amount ) ) {
$low_stock_amount = absint( get_option( 'woocommerce_notify_low_stock_amount', 2 ) );
}
return $low_stock_amount;
}