From 30bc3c4b9628c7fde287bd81b97f9dbccf7f8f06 Mon Sep 17 00:00:00 2001 From: Md Mahbub Rabbani Date: Fri, 30 Aug 2024 21:35:07 +0600 Subject: [PATCH] Implement stock stats report to filter by seller --- .../Analytics/Reports/BaseQueryFilter.php | 2 +- .../Reports/Stock/Stats/WcDataStore.php | 166 ++++++++++++++++++ .../Reports/Stock/Stats/QueryFilterTest.php | 53 ++++++ 3 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 includes/Analytics/Reports/Stock/Stats/WcDataStore.php create mode 100644 tests/php/src/Analytics/Reports/Stock/Stats/QueryFilterTest.php diff --git a/includes/Analytics/Reports/BaseQueryFilter.php b/includes/Analytics/Reports/BaseQueryFilter.php index 042fa00be..dbb7de129 100644 --- a/includes/Analytics/Reports/BaseQueryFilter.php +++ b/includes/Analytics/Reports/BaseQueryFilter.php @@ -165,7 +165,7 @@ protected function add_where_subquery_for_seller_filter( array $clauses ): array * * @return int */ - protected function get_seller_id() { + public function get_seller_id() { if ( ! is_user_logged_in() ) { return 0; } diff --git a/includes/Analytics/Reports/Stock/Stats/WcDataStore.php b/includes/Analytics/Reports/Stock/Stats/WcDataStore.php new file mode 100644 index 000000000..a9920e63f --- /dev/null +++ b/includes/Analytics/Reports/Stock/Stats/WcDataStore.php @@ -0,0 +1,166 @@ +get_seller_id(); + + $report_data = array(); + $cache_expire = DAY_IN_SECONDS * 30; + // Set seller specific key. + $low_stock_transient_name = 'wc_admin_stock_count_lowstock' . $seller_id; + $low_stock_count = get_transient( $low_stock_transient_name ); + + if ( false === $low_stock_count ) { + $low_stock_count = $this->get_low_stock_count(); + set_transient( $low_stock_transient_name, $low_stock_count, $cache_expire ); + } else { + $low_stock_count = intval( $low_stock_count ); + } + + $report_data['lowstock'] = $low_stock_count; + + $status_options = wc_get_product_stock_status_options(); + foreach ( $status_options as $status => $label ) { + // Set seller specific key. + $transient_name = 'wc_admin_stock_count_' . $status . $seller_id; + $count = get_transient( $transient_name ); + if ( false === $count ) { + $count = $this->get_count( $status ); + set_transient( $transient_name, $count, $cache_expire ); + } else { + $count = intval( $count ); + } + $report_data[ $status ] = $count; + } + + // Set seller specific key. + $product_count_transient_name = 'wc_admin_product_count' . $seller_id; + $product_count = get_transient( $product_count_transient_name ); + if ( false === $product_count ) { + $product_count = $this->get_product_count(); + set_transient( $product_count_transient_name, $product_count, $cache_expire ); + } else { + $product_count = intval( $product_count ); + } + $report_data['products'] = $product_count; + return $report_data; + } + + /** + * Get low stock count (products with stock < low stock amount, but greater than no stock amount). + * + * @return int Low stock count. + */ + protected function get_low_stock_count() { + global $wpdb; + + $no_stock_amount = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) ); + $low_stock_amount = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) ); + $seller_where = $this->get_seller_where_query(); + + return (int) $wpdb->get_var( + $wpdb->prepare( + " + SELECT count( DISTINCT posts.ID ) FROM {$wpdb->posts} posts + LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup ON posts.ID = wc_product_meta_lookup.product_id + LEFT JOIN {$wpdb->postmeta} low_stock_amount_meta ON posts.ID = low_stock_amount_meta.post_id AND low_stock_amount_meta.meta_key = '_low_stock_amount' + WHERE posts.post_type IN ( 'product', 'product_variation' ) + AND wc_product_meta_lookup.stock_quantity IS NOT NULL + AND wc_product_meta_lookup.stock_status = 'instock' + AND ( + ( + low_stock_amount_meta.meta_value > '' + AND wc_product_meta_lookup.stock_quantity <= CAST(low_stock_amount_meta.meta_value AS SIGNED) + AND wc_product_meta_lookup.stock_quantity > %d + ) + OR ( + ( + low_stock_amount_meta.meta_value IS NULL OR low_stock_amount_meta.meta_value <= '' + ) + AND wc_product_meta_lookup.stock_quantity <= %d + AND wc_product_meta_lookup.stock_quantity > %d + ) + ) + {$seller_where} + ", + $no_stock_amount, + $low_stock_amount, + $no_stock_amount + ) + ); + } + + /** + * Get count for the passed in stock status. + * + * @param string $status Status slug. + * @return int Count. + */ + protected function get_count( $status ) { + global $wpdb; + + $seller_where = $this->get_seller_where_query(); + + return (int) $wpdb->get_var( + $wpdb->prepare(// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + " + SELECT count( DISTINCT posts.ID ) FROM {$wpdb->posts} posts + LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup ON posts.ID = wc_product_meta_lookup.product_id + WHERE posts.post_type IN ( 'product', 'product_variation' ) + AND wc_product_meta_lookup.stock_status = %s {$seller_where} + ", + $status + ) + ); + } + + /** + * Get product count for the store. + * + * @return int Product count. + */ + protected function get_product_count() { + $query_args = array(); + $query_args['post_type'] = array( 'product', 'product_variation' ); + $seller_id = $this->get_seller_id(); + + if ( $seller_id ) { + $query_args['author'] = $seller_id; + } + + $query = new \WP_Query(); + $query->query( $query_args ); + + return intval( $query->found_posts ); + } + + protected function get_seller_id(): int { + return (int) dokan()->get_container()->get( \WeDevs\Dokan\Analytics\Reports\Stock\QueryFilter::class )->get_seller_id(); + } + + protected function get_seller_where_query() { + $seller_id = $this->get_seller_id(); + $where = ''; + + if ( $seller_id ) { + global $wpdb; + + $where = $wpdb->prepare( + ' AND posts.post_author = %d ', + $seller_id + ); + } + + return $where; + } +} diff --git a/tests/php/src/Analytics/Reports/Stock/Stats/QueryFilterTest.php b/tests/php/src/Analytics/Reports/Stock/Stats/QueryFilterTest.php new file mode 100644 index 000000000..c6f7df4d5 --- /dev/null +++ b/tests/php/src/Analytics/Reports/Stock/Stats/QueryFilterTest.php @@ -0,0 +1,53 @@ +factory()->product + ->set_seller_id( $this->seller_id1 ) + ->create_many( 5 ); + + $seller2_prod_ids = $this->factory()->product + ->set_seller_id( $this->seller_id2 ) + ->create_many( 5 ); + + wp_set_current_user( $this->admin_id ); + + $query = new \Automattic\WooCommerce\Admin\API\Reports\Stock\Stats\Query(); + + $data = $query->get_data(); + $total = count( $seller1_prod_ids ) + count( $seller2_prod_ids ); + + $this->assertEquals( $total, $data['instock'] ); + $this->assertEquals( $total, $data['products'] ); + } + + public function tests_stock_stats_report_by_seller_filter() { + $seller1_prod_ids = $this->factory()->product + ->set_seller_id( $this->seller_id1 ) + ->create_many( 5 ); + + $seller2_prod_ids = $this->factory()->product + ->set_seller_id( $this->seller_id2 ) + ->create_many( 5 ); + + wp_set_current_user( $this->admin_id ); + + $_GET['sellers'] = $this->seller_id1; + + $query = new \Automattic\WooCommerce\Admin\API\Reports\Stock\Stats\Query(); + + $data = $query->get_data(); + $total = count( $seller1_prod_ids ); + + $this->assertEquals( $total, $data['instock'] ); + $this->assertEquals( $total, $data['products'] ); + } +}