is_version_220(); $mwp_action = MainWP_System::instance()->validate_params( 'mwp_action' ); if ( ! empty( $mwp_action ) ) { switch ( $mwp_action ) { case 'sync_data': $information = ! $is_ver220 ? $this->sync_data() : $this->sync_data_two(); break; case 'report_data': $information = ! $is_ver220 ? $this->report_data() : $this->report_data_two(); break; case 'update_wc_db': $information = $this->update_wc_db(); break; default: break; } } MainWP_Helper::write( $information ); } /** * Compare woocommerce versions. * * By default, version_compare returns -1 if the first version is lower than the second, * 0 if they are equal, and 1 if the second is lower. * When using the optional operator argument, the function will return true if the relationship is * the one specified by the operator, false otherwise. * * @return bool|int Comparison response. */ public function is_version_220() { return version_compare( WC()->version, '2.2.0', '>=' ); } /** * Sync Woocommerce data. * * @return array $information Woocommerce data grabed. */ public function sync_data() { /** * Object, providing access to the WordPress database. * * @global object $wpdb WordPress Database instance. */ global $wpdb; $file = WP_PLUGIN_DIR . '/woocommerce/includes/admin/reports/class-wc-admin-report.php'; if ( file_exists( $file ) ) { include_once $file; // NOSONAR -- WP compatible. } else { return false; } // Define allowed WooCommerce order statuses. $allowed_statuses = array( 'completed', 'processing', 'on-hold', 'refunded', 'cancelled', 'failed', 'pending' ); // Apply the filter and validate against whitelist. $filtered_statuses = apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ); $safe_statuses = array_intersect( $filtered_statuses, $allowed_statuses ); // Ensure at least one status exists, otherwise use default. if ( empty( $safe_statuses ) ) { $safe_statuses = array( 'completed' ); } // Build dynamic placeholders for IN clause. $placeholders = implode( ',', array_fill( 0, count( $safe_statuses ), '%s' ) ); // Prepare dates. $month_start = date( 'Y-m-01' ); // phpcs:ignore -- local time. $month_end = date( 'Y-m-d H:i:s' ); // phpcs:ignore -- local time. // Generate cache key. $cache_key = 'wc_sales_' . md5( implode( '_', $safe_statuses ) . '_' . $month_start . '_' . $month_end ); //phpcs:ignore --NOSONAR --safe for key. // Try to get cached value. $sales = wp_cache_get( $cache_key, 'mainwp_woocommerce' ); // If cache miss, execute the query and cache result. if ( false === $sales ) { $sales = $wpdb->get_var( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prepare( "SELECT SUM( postmeta.meta_value ) FROM {$wpdb->posts} as posts LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id ) LEFT JOIN {$wpdb->terms} AS term USING( term_id ) LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id WHERE posts.post_type = 'shop_order' AND posts.post_status = 'publish' AND tax.taxonomy = 'shop_order_status' AND term.slug IN ( {$placeholders} ) AND postmeta.meta_key = '_order_total' AND posts.post_date >= %s AND posts.post_date <= %s", array_merge( $safe_statuses, array( $month_start, $month_end ) ) ) ); wp_cache_set( $cache_key, $sales, 'mainwp_woocommerce', HOUR_IN_SECONDS ); } // Generate cache key for top seller. $cache_key_top = 'wc_top_seller_' . md5( implode( '_', $safe_statuses ) . '_' . $month_start . '_' . $month_end ); //phpcs:ignore --NOSONAR --safe for key. // Try to get cached value. $top_seller = wp_cache_get( $cache_key_top, 'mainwp_woocommerce' ); // If cache miss, execute the query and cache result. if ( false === $top_seller ) { $top_seller = $wpdb->get_row( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prepare( "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id FROM {$wpdb->posts} as posts LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id ) LEFT JOIN {$wpdb->terms} AS term USING( term_id ) LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id WHERE posts.post_type = 'shop_order' AND posts.post_status = 'publish' AND tax.taxonomy = 'shop_order_status' AND term.slug IN ( {$placeholders} ) AND order_item_meta.meta_key = '_qty' AND order_item_meta_2.meta_key = '_product_id' AND posts.post_date >= %s AND posts.post_date <= %s GROUP BY product_id ORDER BY qty DESC LIMIT 1", array_merge( $safe_statuses, array( $month_start, $month_end ) ) ) ); wp_cache_set( $cache_key_top, $top_seller, 'mainwp_woocommerce', HOUR_IN_SECONDS ); } if ( ! empty( $top_seller ) ) { $top_seller->name = get_the_title( $top_seller->product_id ); } // Counts. $on_hold_count = get_term_by( 'slug', 'on-hold', 'shop_order_status' )->count; $processing_count = get_term_by( 'slug', 'processing', 'shop_order_status' )->count; // Get products using a query. $stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) ); $nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) ); $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )"; $lowinstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query. $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )"; $outofstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query. $data = array( 'sales' => $sales, 'formated_sales' => wc_price( $sales ), 'top_seller' => $top_seller, 'onhold' => $on_hold_count, 'awaiting' => $processing_count, 'stock' => $stock, 'nostock' => $nostock, 'lowstock' => $lowinstock_count, 'outstock' => $outofstock_count, ); $data = apply_filters( 'mainwp_child_woocom_sync_data', $data ); $information['data'] = $data; return $information; } /** * Woocommerce report data. * * @return array $information Woocommerce data grabed. */ public function report_data() { /** * Object, providing access to the WordPress database. * * @global object $wpdb WordPress Database instance. */ global $wpdb; $file = WP_PLUGIN_DIR . '/woocommerce/includes/admin/reports/class-wc-admin-report.php'; if ( file_exists( $file ) ) { include_once $file; // NOSONAR -- WP compatible. } else { return false; } // phpcs:disable WordPress.Security.NonceVerification $start_date = isset( $_POST['start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['start_date'] ) ) : ''; $end_date = isset( $_POST['end_date'] ) ? sanitize_text_field( wp_unslash( $_POST['end_date'] ) ) : ''; $start_date = date( 'Y-m-d H:i:s', $start_date ); // phpcs:ignore -- local time. $end_date = date( 'Y-m-d H:i:s', $end_date ); // phpcs:ignore -- local time. // phpcs:enable // Define allowed WooCommerce order statuses. $allowed_statuses = array( 'completed', 'processing', 'on-hold', 'refunded', 'cancelled', 'failed', 'pending' ); // Apply the filter and validate against whitelist. $filtered_statuses = apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ); $safe_statuses = array_intersect( $filtered_statuses, $allowed_statuses ); // Ensure at least one status exists, otherwise use default. if ( empty( $safe_statuses ) ) { $safe_statuses = array( 'completed' ); } // Build dynamic placeholders for IN clause. $placeholders = implode( ',', array_fill( 0, count( $safe_statuses ), '%s' ) ); // Generate cache key. $cache_key = 'wc_sales_' . md5( implode( '_', $safe_statuses ) . '_' . $start_date . '_' . $end_date ); //phpcs:ignore --NOSONAR --safe for key. // Try to get cached value. $sales = wp_cache_get( $cache_key, 'mainwp_woocommerce' ); // If cache miss, execute the query and cache result. if ( false === $sales ) { $sales = $wpdb->get_var( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prepare( "SELECT SUM( postmeta.meta_value ) FROM {$wpdb->posts} as posts LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id ) LEFT JOIN {$wpdb->terms} AS term USING( term_id ) LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id WHERE posts.post_type = 'shop_order' AND posts.post_status = 'publish' AND tax.taxonomy = 'shop_order_status' AND term.slug IN ( {$placeholders} ) AND postmeta.meta_key = '_order_total' AND posts.post_date >= %s AND posts.post_date <= %s", array_merge( $safe_statuses, array( $start_date, $end_date ) ) ) ); wp_cache_set( $cache_key, $sales, 'mainwp_woocommerce', HOUR_IN_SECONDS ); } // Generate cache key for top seller. $cache_key_top = 'wc_top_seller_' . md5( implode( '_', $safe_statuses ) . '_' . $start_date . '_' . $end_date ); //phpcs:ignore --NOSONAR --safe for key. // Try to get cached value. $top_seller = wp_cache_get( $cache_key_top, 'mainwp_woocommerce' ); // If cache miss, execute the query and cache result. if ( false === $top_seller ) { $top_seller = $wpdb->get_row( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prepare( "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id FROM {$wpdb->posts} as posts LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id ) LEFT JOIN {$wpdb->terms} AS term USING( term_id ) LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id WHERE posts.post_type = 'shop_order' AND posts.post_status = 'publish' AND tax.taxonomy = 'shop_order_status' AND term.slug IN ( {$placeholders} ) AND order_item_meta.meta_key = '_qty' AND order_item_meta_2.meta_key = '_product_id' AND posts.post_date >= %s AND posts.post_date <= %s GROUP BY product_id ORDER BY qty DESC LIMIT 1", array_merge( $safe_statuses, array( $start_date, $end_date ) ) ) ); wp_cache_set( $cache_key_top, $top_seller, 'mainwp_woocommerce', HOUR_IN_SECONDS ); } if ( ! empty( $top_seller ) ) { $top_seller->name = get_the_title( $top_seller->product_id ); } // Counts. $on_hold_count = get_term_by( 'slug', 'on-hold', 'shop_order_status' )->count; $processing_count = get_term_by( 'slug', 'processing', 'shop_order_status' )->count; // Get products using a query. $stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) ); $nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) ); $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )"; $lowinstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query. $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )"; $outofstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query. $data = array( 'sales' => $sales, 'formated_sales' => wc_price( $sales ), 'top_seller' => $top_seller, 'onhold' => $on_hold_count, 'awaiting' => $processing_count, 'stock' => $stock, 'nostock' => $nostock, 'lowstock' => $lowinstock_count, 'outstock' => $outofstock_count, ); $data = apply_filters( 'mainwp_child_woocom_report_data', $data ); $information['data'] = $data; return $information; } /** * Sync Woocommerce data for current month. */ public function sync_data_two() { $start_date = date( 'Y-m-01 00:00:00', time() ); // phpcs:ignore -- local time. $end_date = date( 'Y-m-d H:i:s', time() ); // phpcs:ignore -- local time. $start_date = strtotime( $start_date ); $end_date = strtotime( $end_date ); return $this->get_woocom_data( $start_date, $end_date ); } /** * Sync Woocomerce data for specific date range. */ public function report_data_two() { // phpcs:disable WordPress.Security.NonceVerification $start_date = isset( $_POST['start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['start_date'] ) ) : ''; $end_date = isset( $_POST['end_date'] ) ? sanitize_text_field( wp_unslash( $_POST['end_date'] ) ) : ''; // phpcs:enable return $this->get_woocom_data( $start_date, $end_date ); } /** * Check if woocomerce DB needs to be updated. * * @return bool true|false. */ public function check_db_update() { if ( version_compare( get_option( 'woocommerce_db_version' ), WC_VERSION, '<' ) ) { return true; } return false; } /** * Get top seller. * * @param string $start_date Start Date. * @param string $end_date End Date. * * @return array $information Woocommerce data grabed. */ public function get_top_seller( $start_date, $end_date ) { //phpcs:ignore -- NOSONAR - ignore complex. $top_seller = false; $page = 0; $total_page = 1; $top_count = 0; while ( $page < $total_page ) { ++$page; $args = array( 'before' => $end_date, 'after' => $start_date, 'page' => $page, 'per_page' => 1000, ); $product_data = false; $compat_ver_after_93 = false; if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '9.3.0', '>=' ) ) { $compat_ver_after_93 = true; } if ( $compat_ver_after_93 ) { if ( class_exists( '\Automattic\WooCommerce\Admin\API\Reports\Products\DataStore' ) ) { $data_store = new \Automattic\WooCommerce\Admin\API\Reports\Products\DataStore(); $product_data = $data_store->get_data( $args ); } } elseif ( class_exists( '\Automattic\WooCommerce\Admin\API\Reports\Products\Query' ) ) { $report = new \Automattic\WooCommerce\Admin\API\Reports\Products\Query( $args ); $product_data = $report->get_data(); } $products = array(); if ( is_object( $product_data ) ) { $products = ! empty( $product_data->data ) ? $product_data->data : array(); if ( ! is_array( $products ) ) { $products = array(); } foreach ( $products as $prod_sel ) { if ( is_array( $prod_sel ) && isset( $prod_sel['items_sold'] ) && $prod_sel['items_sold'] > $top_count ) { $top_seller = $prod_sel; $top_count = $prod_sel['items_sold']; } } if ( ! empty( $product_data->pages ) && $product_data->pages > $total_page ) { $total_page = $product_data->pages; } } else { break; } } $top_data = array(); if ( ! empty( $top_seller ) ) { $top_data = array( 'product_id' => $top_seller['product_id'], 'qty' => $top_seller['items_sold'], ); $product = wc_get_product( $top_seller['product_id'] ); $top_data['name'] = ! empty( $product ) ? $product->get_name() : 'N/A'; } return $top_data; } /** * Get Woocommerce 8 reports. * * @param string $start_date Start Date. * @param string $end_date End Date. * * @return array $information Woocommerce data grabed. */ public function get_woocom_reports( $start_date, $end_date ) { if ( class_exists( '\Automattic\WooCommerce\Admin\Features\Features' ) && \Automattic\WooCommerce\Admin\Features\Features::is_enabled( 'analytics' ) ) { return $this->get_woocom_analytics( $start_date, $end_date ); } $on_hold_count = 0; if ( function_exists( 'wc_orders_count' ) ) { $status_counts = array_map( 'wc_orders_count', array( 'on-hold' ) ); $on_hold_count = array_sum( $status_counts ); } $processing_count = 0; if ( function_exists( 'wc_processing_order_count' ) ) { $processing_count = wc_processing_order_count(); } $total_sales = $this->get_total_sales( $start_date, $end_date ); $top_seller = $this->get_top_sellers_report( $start_date, $end_date ); $report = new \Automattic\WooCommerce\Admin\API\Reports\Stock\Stats\Query(); $stock_data = $report->get_data(); return array( 'sales' => $total_sales, 'formated_sales' => wc_price( $total_sales ), 'top_seller' => ! empty( $top_seller ) ? (object) $top_seller : false, 'onhold' => $on_hold_count, 'awaiting' => $processing_count, 'lowstock' => is_array( $stock_data ) && isset( $stock_data['lowstock'] ) ? intval( $stock_data['lowstock'] ) : 0, 'outstock' => is_array( $stock_data ) && isset( $stock_data['outofstock'] ) ? intval( $stock_data['outofstock'] ) : 0, ); } /** * Get Woocommerce data. * * @param string $start_date Start Date. * @param string $end_date End Date. * * @return array $information Woocommerce data grabed. */ public function get_woocom_data( $start_date, $end_date ) { if ( class_exists( '\Automattic\WooCommerce\Admin\API\Reports\Orders\Stats\Query' ) ) { $data = $this->get_woocom_reports( $start_date, $end_date ); } else { $data = $this->get_woocom_reports_old( $start_date, $end_date ); } $information['data'] = $data; $information['need_db_update'] = $this->check_db_update(); return $information; } /** * Get Woocommerce reports old. * * @param string $start_date Start Date. * @param string $end_date End Date. * * @return array $information Woocommerce data grabed. */ public function get_woocom_reports_old( $start_date, $end_date ) { /** * Object, providing access to the WordPress database. * * @global object $wpdb WordPress Database instance. */ global $wpdb; $file = WP_PLUGIN_DIR . '/woocommerce/includes/admin/reports/class-wc-admin-report.php'; if ( file_exists( $file ) ) { include_once $file; // NOSONAR -- WP compatible. } else { return false; } $start_date = date( 'Y-m-d H:i:s', $start_date ); // phpcs:ignore -- local time. Required to achieve desired results, pull request solutions appreciated. $end_date = date( 'Y-m-d H:i:s', $end_date ); // phpcs:ignore -- local time. Required to achieve desired results, pull request solutions appreciated. // Sales. $query = array(); $query['fields'] = "SELECT SUM( postmeta.meta_value ) FROM {$wpdb->posts} as posts"; $query['join'] = "INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id "; $query['where'] = "WHERE posts.post_type IN ( '" . implode( "','", wc_get_order_types( 'reports' ) ) . "' ) "; $query['where'] .= "AND posts.post_status IN ( 'wc-" . implode( "','wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) "; $query['where'] .= "AND postmeta.meta_key = '_order_total' "; $query['where'] .= 'AND posts.post_date >= STR_TO_DATE(' . $wpdb->prepare( '%s', $start_date ) . ", '%Y-%m-%d %H:%i:%s' ) "; $query['where'] .= 'AND posts.post_date <= STR_TO_DATE(' . $wpdb->prepare( '%s', $end_date ) . ", '%Y-%m-%d %H:%i:%s' ) "; $sales = $wpdb->get_var( implode( ' ', apply_filters( 'woocommerce_dashboard_status_widget_sales_query', $query ) ) ); // phpcs:ignore -- safe query. // Get top seller. $query = array(); $query['fields'] = "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id FROM {$wpdb->posts} as posts"; $query['join'] = "INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id "; $query['join'] .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id "; $query['join'] .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id "; $query['where'] = "WHERE posts.post_type IN ( '" . implode( "','", wc_get_order_types( 'order-count' ) ) . "' ) "; $query['where'] .= "AND posts.post_status IN ( 'wc-" . implode( "','wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) "; $query['where'] .= "AND order_item_meta.meta_key = '_qty' "; $query['where'] .= "AND order_item_meta_2.meta_key = '_product_id' "; $query['where'] .= 'AND posts.post_date >= STR_TO_DATE(' . $wpdb->prepare( '%s', $start_date ) . ", '%Y-%m-%d %H:%i:%s') "; $query['where'] .= 'AND posts.post_date <= STR_TO_DATE(' . $wpdb->prepare( '%s', $end_date ) . ", '%Y-%m-%d %H:%i:%s') "; $query['groupby'] = 'GROUP BY product_id'; $query['orderby'] = 'ORDER BY qty DESC'; $query['limits'] = 'LIMIT 1'; $top_seller = $wpdb->get_row( implode( ' ', $query ) ); // phpcs:ignore -- safe query. if ( ! empty( $top_seller ) ) { $top_seller->name = get_the_title( $top_seller->product_id ); } // Counts. $on_hold_count = 0; $processing_count = 0; foreach ( wc_get_order_types( 'order-count' ) as $type ) { $counts = (array) wp_count_posts( $type ); $on_hold_count += isset( $counts['wc-on-hold'] ) ? $counts['wc-on-hold'] : 0; $processing_count += isset( $counts['wc-processing'] ) ? $counts['wc-processing'] : 0; } // Get products using a query. $stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) ); $nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) ); $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )"; $lowinstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query. $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) ) "; $outofstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query. $data = array( 'sales' => $sales, 'formated_sales' => wc_price( $sales ), 'top_seller' => $top_seller, 'onhold' => $on_hold_count, 'awaiting' => $processing_count, 'stock' => $stock, 'nostock' => $nostock, 'lowstock' => $lowinstock_count, 'outstock' => $outofstock_count, ); $data = apply_filters( 'mainwp_child_woocom_get_data', $data ); return $data; } /** * Update Woocommerce Database. * * @return string[] Success. */ private static function update_wc_db() { include_once WC()->plugin_path() . '/includes/class-wc-background-updater.php'; // NOSONAR -- WP compatible. $background_updater = new \WC_Background_Updater(); $current_db_version = get_option( 'woocommerce_db_version' ); $logger = wc_get_logger(); $update_queued = false; foreach ( \WC_Install::get_db_update_callbacks() as $version => $update_callbacks ) { if ( version_compare( $current_db_version, $version, '<' ) ) { foreach ( $update_callbacks as $update_callback ) { $logger->info( sprintf( 'Queuing %s - %s', $version, $update_callback ), array( 'source' => 'wc_db_updates' ) ); $background_updater->push_to_queue( $update_callback ); $update_queued = true; } } } if ( $update_queued ) { $background_updater->save()->dispatch(); } return array( 'result' => 'success' ); } /** * Get top seller. * * @param string $start_date Start Date. * @param string $end_date End Date. * * @return array $information Woocommerce data grabed. */ public function get_top_sellers_report( $start_date, $end_date ) { include_once WC()->plugin_path() . '/includes/admin/reports/class-wc-admin-report.php'; // NOSONAR -- WP compatible. $report = new \WC_Admin_Report(); $_GET['start_date'] = gmdate( 'Y-m-d H:i:s', $start_date ); $_GET['end_date'] = gmdate( 'Y-m-d H:i:s', $end_date ); $report->calculate_current_range( 'custom' ); $top_sellers = $report->get_order_report_data( array( 'data' => array( '_product_id' => array( 'type' => 'order_item_meta', 'order_item_type' => 'line_item', 'function' => '', 'name' => 'product_id', ), '_qty' => array( 'type' => 'order_item_meta', 'order_item_type' => 'line_item', 'function' => 'SUM', 'name' => 'order_item_qty', ), ), 'order_by' => 'order_item_qty DESC', 'group_by' => 'product_id', 'limit' => 1000, 'query_type' => 'get_results', 'filter_range' => true, ) ); $top_product = false; $top_count = 0; foreach ( $top_sellers as $top_seller ) { if ( is_object( $top_seller ) && isset( $top_seller->order_item_qty ) && $top_seller->order_item_qty > $top_count ) { $top_product = $top_seller; $top_count = $top_seller->order_item_qty; } } $top_data = array(); if ( ! empty( $top_product ) ) { $top_data = array( 'product_id' => $top_product->product_id, 'qty' => $top_product->order_item_qty, ); $product = wc_get_product( $top_product->product_id ); $top_data['name'] = ! empty( $product ) ? $product->get_name() : 'N/A'; } return $top_data; } /** * Get total sales. * * @param string $start_date Start Date. * @param string $end_date End Date. * * @return int $total_sales Total sales. */ public function get_total_sales( $start_date, $end_date ) { include_once WC()->plugin_path() . '/includes/admin/reports/class-wc-admin-report.php'; // NOSONAR -- WP compatible. include_once WC()->plugin_path() . '/includes/admin/reports/class-wc-report-sales-by-date.php'; // NOSONAR -- WP compatible. $total_sales = 0; $_GET['start_date'] = gmdate( 'Y-m-d H:i:s', $start_date ); $_GET['end_date'] = gmdate( 'Y-m-d H:i:s', $end_date ); $report = new \WC_Report_Sales_By_Date(); $report->calculate_current_range( 'custom' ); $report_data = $report->get_report_data(); if ( is_object( $report_data ) && ! empty( $report_data->total_sales ) ) { $total_sales = $report_data->total_sales; } return $total_sales; } /** * Get Woocommerce 8 analytics. * * @param string $start_date Start Date. * @param string $end_date End Date. * * @return array $information Woocommerce data grabed. */ public function get_woocom_analytics( $start_date, $end_date ) { $on_hold_count = 0; if ( function_exists( 'wc_orders_count' ) ) { $status_counts = array_map( 'wc_orders_count', array( 'on-hold' ) ); $on_hold_count = array_sum( $status_counts ); } $processing_count = 0; if ( function_exists( 'wc_processing_order_count' ) ) { $processing_count = wc_processing_order_count(); } $sales_data = $this->get_sales_data( $start_date, $end_date ); $total_sales = $sales_data['total_sales']; $top_seller = $sales_data['top_seller']; $report = new \Automattic\WooCommerce\Admin\API\Reports\Stock\Stats\Query(); $stock_data = $report->get_data(); return array( 'sales' => $total_sales, 'formated_sales' => wc_price( $total_sales ), 'top_seller' => ! empty( $top_seller ) ? (object) $top_seller : false, 'onhold' => $on_hold_count, 'awaiting' => $processing_count, 'lowstock' => is_array( $stock_data ) && isset( $stock_data['lowstock'] ) ? intval( $stock_data['lowstock'] ) : 0, 'outstock' => is_array( $stock_data ) && isset( $stock_data['outofstock'] ) ? intval( $stock_data['outofstock'] ) : 0, ); } /** * Get sales data. * * @param string $start_date Start Date. * @param string $end_date End Date. * * @return array Sales data. */ public function get_sales_data( $start_date, $end_date ) { $start_date = gmdate( 'Y-m-d H:i:s', $start_date ); // phpcs:ignore $end_date = gmdate( 'Y-m-d H:i:s', $end_date ); // phpcs:ignore $args = array( 'before' => $end_date, 'after' => $start_date, 'status_is' => array( 'on-hold', 'processing' ), 'per_page' => 1000, ); $report = new \Automattic\WooCommerce\Admin\API\Reports\Orders\Stats\Query( $args ); $order_data = $report->get_data(); $on_hold_count = is_object( $order_data ) && ! empty( $order_data->totals->orders_count ) ? $order_data->totals->orders_count : 0; $args = array( 'before' => $end_date, 'after' => $start_date, 'fields' => array( 'total_sales' ), 'per_page' => 1000, ); $report = new \Automattic\WooCommerce\Admin\API\Reports\Revenue\Query( $args ); $revenue_data = $report->get_data(); $total_sales = is_object( $revenue_data ) && ! empty( $revenue_data->totals->total_sales ) ? $revenue_data->totals->total_sales : 0; $top_seller = $this->get_top_seller( $start_date, $end_date ); return array( 'top_seller' => $top_seller, 'on_hold_count' => $on_hold_count, 'total_sales' => $total_sales, ); } }