helper
2 months ago
importers
1 year ago
list-tables
4 weeks ago
marketplace-suggestions
11 months ago
meta-boxes
4 weeks ago
notes
2 months ago
plugin-updates
2 years ago
reports
3 months ago
settings
5 days ago
views
4 weeks ago
class-wc-admin-addons.php
9 months ago
class-wc-admin-api-keys-table-list.php
2 years ago
class-wc-admin-api-keys.php
11 months ago
class-wc-admin-assets.php
4 weeks ago
class-wc-admin-attributes.php
4 weeks ago
class-wc-admin-brands.php
4 months ago
class-wc-admin-customize.php
5 years ago
class-wc-admin-dashboard-setup.php
4 weeks ago
class-wc-admin-dashboard.php
4 months ago
class-wc-admin-duplicate-product.php
5 months ago
class-wc-admin-exporters.php
1 year ago
class-wc-admin-help.php
2 years ago
class-wc-admin-importers.php
11 months ago
class-wc-admin-log-table-list.php
4 months ago
class-wc-admin-marketplace-promotions.php
4 months ago
class-wc-admin-menus.php
4 weeks ago
class-wc-admin-meta-boxes.php
4 weeks ago
class-wc-admin-notices.php
4 weeks ago
class-wc-admin-permalink-settings.php
5 years ago
class-wc-admin-pointers.php
3 years ago
class-wc-admin-post-types.php
1 year ago
class-wc-admin-profile.php
1 year ago
class-wc-admin-reports.php
4 months ago
class-wc-admin-settings.php
3 months ago
class-wc-admin-setup-wizard.php
4 weeks ago
class-wc-admin-status.php
1 year ago
class-wc-admin-taxonomies.php
4 weeks ago
class-wc-admin-upload-downloadable-product.php
2 years ago
class-wc-admin-webhooks-table-list.php
4 weeks ago
class-wc-admin-webhooks.php
4 weeks ago
class-wc-admin.php
3 months ago
wc-admin-functions.php
7 months ago
wc-meta-box-functions.php
1 year ago
woocommerce-legacy-reports.php
1 year ago
class-wc-admin-reports.php
303 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Admin Reports |
| 4 | * |
| 5 | * Functions used for displaying sales and customer reports in admin. |
| 6 | * |
| 7 | * @author WooThemes |
| 8 | * @category Admin |
| 9 | * @package WooCommerce\Admin\Reports |
| 10 | * @version 2.0.0 |
| 11 | */ |
| 12 | |
| 13 | if ( ! defined( 'ABSPATH' ) ) { |
| 14 | exit; |
| 15 | } |
| 16 | |
| 17 | if ( class_exists( 'WC_Admin_Reports', false ) ) { |
| 18 | return; |
| 19 | } |
| 20 | |
| 21 | /** |
| 22 | * WC_Admin_Reports Class. |
| 23 | */ |
| 24 | class WC_Admin_Reports { |
| 25 | |
| 26 | /** |
| 27 | * Register the hook handlers for integrating with admin. |
| 28 | */ |
| 29 | public static function register_hook_handlers() { |
| 30 | add_filter( 'woocommerce_after_dashboard_status_widget_parameter', array( __CLASS__, 'get_report_instance' ) ); |
| 31 | add_filter( 'woocommerce_dashboard_status_widget_reports', array( __CLASS__, 'replace_dashboard_status_widget_reports' ) ); |
| 32 | } |
| 33 | |
| 34 | /** |
| 35 | * Register the hook handlers for integrating with orders. |
| 36 | * |
| 37 | * @internal |
| 38 | * @since 10.6.0 |
| 39 | */ |
| 40 | public static function register_orders_hook_handlers(): void { |
| 41 | add_action( 'woocommerce_delete_shop_order_transients', array( __CLASS__, 'delete_legacy_reports_transients' ), 10, 1 ); |
| 42 | add_action( 'woocommerce_delete_legacy_report_transients', array( __CLASS__, 'delete_legacy_reports_transients' ), 10, 2 ); |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * Execute legacy reports transient deletion (sync or async depending on the context) |
| 47 | * |
| 48 | * @internal |
| 49 | * @since 10.6.0 |
| 50 | * |
| 51 | * @param int $order_id Order ID (unused, exists for compatibility between the hooks we are integrating with). |
| 52 | * @param bool $defer Whether to defer the deletion or execute. |
| 53 | * @return void |
| 54 | */ |
| 55 | public static function delete_legacy_reports_transients( int $order_id, bool $defer = true ): void { |
| 56 | // Deferring is only making sense on sites without object cache enabled (if enabled, no SQLs being executed). |
| 57 | if ( $defer && ! wp_using_ext_object_cache() ) { |
| 58 | static $skip_consequent; |
| 59 | |
| 60 | // Schedule the deletion, cap the execution to single pending event at any given time. |
| 61 | $schedule = ! $skip_consequent && ! as_has_scheduled_action( 'woocommerce_delete_legacy_report_transients', null, 'woocommerce' ); |
| 62 | if ( $schedule ) { |
| 63 | as_schedule_single_action( time() + MINUTE_IN_SECONDS, 'woocommerce_delete_legacy_report_transients', array( $order_id, false ), 'woocommerce' ); |
| 64 | } |
| 65 | $skip_consequent = true; |
| 66 | |
| 67 | return; |
| 68 | } |
| 69 | |
| 70 | delete_transient( 'wc_admin_report' ); |
| 71 | foreach ( self::get_reports() as $report_group ) { |
| 72 | foreach ( $report_group['reports'] as $report_key => $report ) { |
| 73 | delete_transient( 'wc_report_' . $report_key ); |
| 74 | } |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * Get an instance of WC_Admin_Report. |
| 80 | * |
| 81 | * @return WC_Admin_Report |
| 82 | */ |
| 83 | public static function get_report_instance() { |
| 84 | include_once __DIR__ . '/reports/class-wc-admin-report.php'; |
| 85 | return new WC_Admin_Report(); |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Filter handler for replacing the data of the status widget on the Dashboard page. |
| 90 | * |
| 91 | * @param array $status_widget_reports The data to display in the status widget. |
| 92 | */ |
| 93 | public static function replace_dashboard_status_widget_reports( $status_widget_reports ) { |
| 94 | $report = self::get_report_instance(); |
| 95 | |
| 96 | include_once __DIR__ . '/reports/class-wc-report-sales-by-date.php'; |
| 97 | |
| 98 | $sales_by_date = new WC_Report_Sales_By_Date(); |
| 99 | $sales_by_date->start_date = strtotime( gmdate( 'Y-m-01', current_time( 'timestamp' ) ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested |
| 100 | $sales_by_date->end_date = strtotime( gmdate( 'Y-m-d', current_time( 'timestamp' ) ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested |
| 101 | $sales_by_date->chart_groupby = 'day'; |
| 102 | $sales_by_date->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)'; |
| 103 | |
| 104 | $status_widget_reports['net_sales_link'] = 'admin.php?page=wc-reports&tab=orders&range=month'; |
| 105 | $status_widget_reports['top_seller_link'] = 'admin.php?page=wc-reports&tab=orders&report=sales_by_product&range=month&product_ids='; |
| 106 | $status_widget_reports['lowstock_link'] = 'admin.php?page=wc-reports&tab=stock&report=low_in_stock'; |
| 107 | $status_widget_reports['outofstock_link'] = 'admin.php?page=wc-reports&tab=stock&report=out_of_stock'; |
| 108 | $status_widget_reports['report_data'] = $sales_by_date->get_report_data(); |
| 109 | $status_widget_reports['get_sales_sparkline'] = array( $report, 'get_sales_sparkline' ); |
| 110 | |
| 111 | return $status_widget_reports; |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * Handles output of the reports page in admin. |
| 116 | */ |
| 117 | public static function output() { |
| 118 | $reports = self::get_reports(); |
| 119 | $first_tab = array_keys( $reports ); |
| 120 | $current_tab = ! empty( $_GET['tab'] ) && array_key_exists( $_GET['tab'], $reports ) ? sanitize_title( $_GET['tab'] ) : $first_tab[0]; |
| 121 | $current_report = isset( $_GET['report'] ) ? sanitize_title( $_GET['report'] ) : current( array_keys( $reports[ $current_tab ]['reports'] ) ); |
| 122 | |
| 123 | include_once dirname( __FILE__ ) . '/reports/class-wc-admin-report.php'; |
| 124 | include_once dirname( __FILE__ ) . '/views/html-admin-page-reports.php'; |
| 125 | } |
| 126 | |
| 127 | /** |
| 128 | * Returns the definitions for the reports to show in admin. |
| 129 | * |
| 130 | * @return array |
| 131 | */ |
| 132 | public static function get_reports() { |
| 133 | $reports = array( |
| 134 | 'orders' => array( |
| 135 | 'title' => __( 'Orders', 'woocommerce' ), |
| 136 | 'reports' => array( |
| 137 | 'sales_by_date' => array( |
| 138 | 'title' => __( 'Sales by date', 'woocommerce' ), |
| 139 | 'description' => '', |
| 140 | 'hide_title' => true, |
| 141 | 'callback' => array( __CLASS__, 'get_report' ), |
| 142 | ), |
| 143 | 'sales_by_product' => array( |
| 144 | 'title' => __( 'Sales by product', 'woocommerce' ), |
| 145 | 'description' => '', |
| 146 | 'hide_title' => true, |
| 147 | 'callback' => array( __CLASS__, 'get_report' ), |
| 148 | ), |
| 149 | 'sales_by_category' => array( |
| 150 | 'title' => __( 'Sales by category', 'woocommerce' ), |
| 151 | 'description' => '', |
| 152 | 'hide_title' => true, |
| 153 | 'callback' => array( __CLASS__, 'get_report' ), |
| 154 | ), |
| 155 | 'coupon_usage' => array( |
| 156 | 'title' => __( 'Coupons by date', 'woocommerce' ), |
| 157 | 'description' => '', |
| 158 | 'hide_title' => true, |
| 159 | 'callback' => array( __CLASS__, 'get_report' ), |
| 160 | ), |
| 161 | 'downloads' => array( |
| 162 | 'title' => __( 'Customer downloads', 'woocommerce' ), |
| 163 | 'description' => '', |
| 164 | 'hide_title' => true, |
| 165 | 'callback' => array( __CLASS__, 'get_report' ), |
| 166 | ), |
| 167 | ), |
| 168 | ), |
| 169 | 'customers' => array( |
| 170 | 'title' => __( 'Customers', 'woocommerce' ), |
| 171 | 'reports' => array( |
| 172 | 'customers' => array( |
| 173 | 'title' => __( 'Customers vs. guests', 'woocommerce' ), |
| 174 | 'description' => '', |
| 175 | 'hide_title' => true, |
| 176 | 'callback' => array( __CLASS__, 'get_report' ), |
| 177 | ), |
| 178 | 'customer_list' => array( |
| 179 | 'title' => __( 'Customer list', 'woocommerce' ), |
| 180 | 'description' => '', |
| 181 | 'hide_title' => true, |
| 182 | 'callback' => array( __CLASS__, 'get_report' ), |
| 183 | ), |
| 184 | ), |
| 185 | ), |
| 186 | 'stock' => array( |
| 187 | 'title' => __( 'Stock', 'woocommerce' ), |
| 188 | 'reports' => array( |
| 189 | 'low_in_stock' => array( |
| 190 | 'title' => __( 'Low in stock', 'woocommerce' ), |
| 191 | 'description' => '', |
| 192 | 'hide_title' => true, |
| 193 | 'callback' => array( __CLASS__, 'get_report' ), |
| 194 | ), |
| 195 | 'out_of_stock' => array( |
| 196 | 'title' => __( 'Out of stock', 'woocommerce' ), |
| 197 | 'description' => '', |
| 198 | 'hide_title' => true, |
| 199 | 'callback' => array( __CLASS__, 'get_report' ), |
| 200 | ), |
| 201 | 'most_stocked' => array( |
| 202 | 'title' => __( 'Most stocked', 'woocommerce' ), |
| 203 | 'description' => '', |
| 204 | 'hide_title' => true, |
| 205 | 'callback' => array( __CLASS__, 'get_report' ), |
| 206 | ), |
| 207 | ), |
| 208 | ), |
| 209 | ); |
| 210 | |
| 211 | if ( wc_tax_enabled() ) { |
| 212 | $reports['taxes'] = array( |
| 213 | 'title' => __( 'Taxes', 'woocommerce' ), |
| 214 | 'reports' => array( |
| 215 | 'taxes_by_code' => array( |
| 216 | 'title' => __( 'Taxes by code', 'woocommerce' ), |
| 217 | 'description' => '', |
| 218 | 'hide_title' => true, |
| 219 | 'callback' => array( __CLASS__, 'get_report' ), |
| 220 | ), |
| 221 | 'taxes_by_date' => array( |
| 222 | 'title' => __( 'Taxes by date', 'woocommerce' ), |
| 223 | 'description' => '', |
| 224 | 'hide_title' => true, |
| 225 | 'callback' => array( __CLASS__, 'get_report' ), |
| 226 | ), |
| 227 | ), |
| 228 | ); |
| 229 | } |
| 230 | |
| 231 | /** |
| 232 | * Filter the list and add reports to the legacy _WooCommerce > Reports_. |
| 233 | * |
| 234 | * Array items should be in the format of |
| 235 | * |
| 236 | * $reports['automatewoo'] = array( |
| 237 | * 'title' => 'AutomateWoo', |
| 238 | * 'reports' => array( |
| 239 | * 'runs_by_date' => array( |
| 240 | * 'title' => __( 'Workflow Runs', 'automatewoo' ), |
| 241 | * 'description' => '', |
| 242 | * 'hide_title' => false, |
| 243 | * 'callback' => array( $this, 'get_runs_by_date' ), |
| 244 | * ), |
| 245 | * // ... |
| 246 | * ), |
| 247 | * ); |
| 248 | * |
| 249 | * This filter has a colliding name with the one in Automattic\WooCommerce\Admin\API\Reports\Controller. |
| 250 | * To make sure your code runs in the context of the legacy _WooCommerce > Reports_ screen, and not the REST endpoint, |
| 251 | * use the following: |
| 252 | * |
| 253 | * add_filter( 'woocommerce_admin_reports', |
| 254 | * function( $reports ) { |
| 255 | * if ( is_admin() ) { |
| 256 | * // ... |
| 257 | * |
| 258 | * @param array $reports The associative array of reports. |
| 259 | */ |
| 260 | $reports = apply_filters( 'woocommerce_admin_reports', $reports ); |
| 261 | $reports = apply_filters( 'woocommerce_reports_charts', $reports ); // Backwards compatibility. |
| 262 | |
| 263 | foreach ( $reports as $key => &$report_group ) { |
| 264 | if ( isset( $report_group['charts'] ) ) { |
| 265 | $report_group['reports'] = $report_group['charts']; |
| 266 | } |
| 267 | |
| 268 | // Silently ignore reports given for the filter in Automattic\WooCommerce\Admin\API\Reports\Controller. |
| 269 | if ( ! isset( $report_group['reports'] ) ) { |
| 270 | unset( $reports[ $key ] ); |
| 271 | continue; |
| 272 | } |
| 273 | |
| 274 | foreach ( $report_group['reports'] as &$report ) { |
| 275 | if ( isset( $report['function'] ) ) { |
| 276 | $report['callback'] = $report['function']; |
| 277 | } |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | return $reports; |
| 282 | } |
| 283 | |
| 284 | /** |
| 285 | * Get a report from our reports subfolder. |
| 286 | * |
| 287 | * @param string $name |
| 288 | */ |
| 289 | public static function get_report( $name ) { |
| 290 | $name = sanitize_title( str_replace( '_', '-', $name ) ); |
| 291 | $class = 'WC_Report_' . str_replace( '-', '_', $name ); |
| 292 | |
| 293 | include_once apply_filters( 'wc_admin_reports_path', 'reports/class-wc-report-' . $name . '.php', $name, $class ); |
| 294 | |
| 295 | if ( ! class_exists( $class ) ) { |
| 296 | return; |
| 297 | } |
| 298 | |
| 299 | $report = new $class(); |
| 300 | $report->output_report(); |
| 301 | } |
| 302 | } |
| 303 |