38217-vm/wp-content/plugins/mainwp-child/class/class-mainwp-child-woocommerce-status.php
2026-02-05 17:08:59 +03:00

888 lines
39 KiB
PHP

<?php
/**
* MainWP Child Woocomerce Status
*
* MainWP WooCommerce Status Extension handler.
*
* @link https://mainwp.com/extension/woocommerce-status/
*
* @package MainWP\Child
*
* Credits
*
* Plugin-Name: WooCommerce
* Plugin URI: https://woocommerce.com/
* Author: Automattic
* Author URI: https://woocommerce.com
*/
namespace MainWP\Child;
// phpcs:disable PSR1.Classes.ClassDeclaration, WordPress.WP.AlternativeFunctions -- Required to achieve desired results, pull request solutions appreciated.
/**
* Class MainWP_Child_WooCommerce_Status
*
* MainWP WooCommerce Status Extension handler.
*/
class MainWP_Child_WooCommerce_Status {
/**
* Public static variable to hold the single instance of the class.
*
* @var mixed Default null
*/
public static $instance = null;
/**
* Method instance()
*
* Create a public static instance.
*
* @return mixed Class instance.
*/
public static function instance() {
if ( null === static::$instance ) {
static::$instance = new self();
}
return static::$instance;
}
/**
* MainWP_Child_WooCommerce_Status constructor.
*
* Run any time class is called.
*/
public function __construct() {
add_action( 'mainwp_child_deactivation', array( $this, 'child_deactivation' ) );
}
/**
* MainWP Child Plugin deactivation hooks.
*/
public function child_deactivation() {
}
/**
* MainWP Child Woocommerce actions: sync_data, report_data, update_wc_db.
*
* @uses \MainWP\Child\MainWP_Helper::write()
*/
public function action() {
$information = array();
if ( ! class_exists( '\WooCommerce' ) || ! defined( 'WC_VERSION' ) ) {
$information['error'] = 'NO_WOOCOMMERCE';
MainWP_Helper::write( $information );
}
$is_ver220 = $this->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,
);
}
}