diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 62cb870d511..bdea516b040 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -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() ) { diff --git a/includes/admin/importers/class-wc-product-csv-importer-controller.php b/includes/admin/importers/class-wc-product-csv-importer-controller.php index 8e164e5015d..2e50ff8805d 100644 --- a/includes/admin/importers/class-wc-product-csv-importer-controller.php +++ b/includes/admin/importers/class-wc-product-csv-importer-controller.php @@ -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 ), diff --git a/includes/admin/importers/mappings/default.php b/includes/admin/importers/mappings/default.php index b22a2a260d5..f07ead9ac2d 100644 --- a/includes/admin/importers/mappings/default.php +++ b/includes/admin/importers/mappings/default.php @@ -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', diff --git a/includes/admin/list-tables/class-wc-admin-list-table-products.php b/includes/admin/list-tables/class-wc-admin-list-table-products.php index 424f4512fd1..28a1a9c2a81 100644 --- a/includes/admin/list-tables/class-wc-admin-list-table-products.php +++ b/includes/admin/list-tables/class-wc-admin-list-table-products.php @@ -187,6 +187,7 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
' . esc_html( $this->object->get_tax_status() ) . '
' . esc_html( $this->object->get_tax_class() ) . '
' . esc_html( $this->object->get_backorders() ) . '
+
' . esc_html( $this->object->get_low_stock_amount() ) . '
'; } diff --git a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php index e3e0a300367..43f21f8161b 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php @@ -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 ), diff --git a/includes/admin/meta-boxes/views/html-product-data-inventory.php b/includes/admin/meta-boxes/views/html-product-data-inventory.php index 30ece552b87..51219ccd38d 100644 --- a/includes/admin/meta-boxes/views/html-product-data-inventory.php +++ b/includes/admin/meta-boxes/views/html-product-data-inventory.php @@ -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 ''; diff --git a/includes/admin/meta-boxes/views/html-variation-admin.php b/includes/admin/meta-boxes/views/html-variation-admin.php index 175fb94baf9..1405375bb2d 100644 --- a/includes/admin/meta-boxes/views/html-variation-admin.php +++ b/includes/admin/meta-boxes/views/html-variation-admin.php @@ -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. * diff --git a/includes/api/class-wc-rest-product-variations-controller.php b/includes/api/class-wc-rest-product-variations-controller.php index 621bffd7e5e..1fd8dec63ab 100644 --- a/includes/api/class-wc-rest-product-variations-controller.php +++ b/includes/api/class-wc-rest-product-variations-controller.php @@ -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', diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index 1f2af73999c..1243288628d 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -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', diff --git a/includes/api/v1/class-wc-rest-products-controller.php b/includes/api/v1/class-wc-rest-products-controller.php index 137ebca40d9..247ab65eaa8 100644 --- a/includes/api/v1/class-wc-rest-products-controller.php +++ b/includes/api/v1/class-wc-rest-products-controller.php @@ -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', diff --git a/includes/class-wc-product-query.php b/includes/class-wc-product-query.php index d6780241cb0..1de8bf210d5 100644 --- a/includes/class-wc-product-query.php +++ b/includes/class-wc-product-query.php @@ -46,6 +46,7 @@ class WC_Product_Query extends WC_Object_Query { 'stock_quantity' => '', 'stock_status' => '', 'backorders' => '', + 'low_stock_amount' => '', 'sold_individually' => '', 'weight' => '', 'length' => '', diff --git a/includes/class-wc-product-variable.php b/includes/class-wc-product-variable.php index 5a148156983..37bf5beedc7 100644 --- a/includes/class-wc-product-variable.php +++ b/includes/class-wc-product-variable.php @@ -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. diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index ef96e39718a..dad2e5a296c 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -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', diff --git a/includes/data-stores/class-wc-product-variation-data-store-cpt.php b/includes/data-stores/class-wc-product-variation-data-store-cpt.php index e98d178711e..9a90b8670df 100644 --- a/includes/data-stores/class-wc-product-variation-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-variation-data-store-cpt.php @@ -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 ), diff --git a/includes/export/class-wc-product-csv-exporter.php b/includes/export/class-wc-product-csv-exporter.php index c9f3f0ed25d..aa583d240cc 100644 --- a/includes/export/class-wc-product-csv-exporter.php +++ b/includes/export/class-wc-product-csv-exporter.php @@ -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 */ diff --git a/includes/import/abstract-wc-product-importer.php b/includes/import/abstract-wc-product-importer.php index 6eb0e562794..54df946f924 100644 --- a/includes/import/abstract-wc-product-importer.php +++ b/includes/import/abstract-wc-product-importer.php @@ -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() ) { diff --git a/includes/import/class-wc-product-csv-importer.php b/includes/import/class-wc-product-csv-importer.php index ade2e756930..b632bc60be5 100644 --- a/includes/import/class-wc-product-csv-importer.php +++ b/includes/import/class-wc-product-csv-importer.php @@ -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' ), diff --git a/includes/wc-stock-functions.php b/includes/wc-stock-functions.php index 9903280971a..4b0dfc52644 100644 --- a/includes/wc-stock-functions.php +++ b/includes/wc-stock-functions.php @@ -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; +} \ No newline at end of file