'edd-reports' ) ); // Redirect if user cannot view reports if ( ! current_user_can( 'view_shop_reports' ) ) { edd_redirect( $redirect_url ); } // Start the Reports API. new Reports\Init(); add_filter( 'edd_admin_is_single_view', '__return_false' ); // Get the section and report $section = Reports\get_current_report(); $report = Reports\get_report( $section ); // Redirect if report is invalid if ( empty( $report ) || is_wp_error( $report ) ) { edd_redirect( $redirect_url ); } // Stash the report in the EDD global for future reference EDD()->report = $report; } add_action( 'load-download_page_edd-reports', 'edd_admin_load_report' ); /** * Contains backwards compat code to shim tabs & views to EDD_Sections() * * @since 3.0 */ function edd_reports_sections() { // Instantiate the Sections class and sections array $sections = new EDD\Admin\Reports_Sections(); $c_sections = array(); // Setup sections variables $sections->use_js = false; $sections->current_section = Reports\get_current_report(); $sections->item = null; // Find persisted filters to append to the base URL. $persisted_filters = Reports\get_persisted_filters(); $persisted_filter_args = array(); foreach ( $persisted_filters as $filter ) { if ( isset( $_GET[ $filter ] ) ) { $persisted_filter_args[ $filter ] = sanitize_text_field( $_GET[ $filter ] ); } } // Build the section base URL. $sections->base_url = edd_get_admin_url( array_merge( array( 'page' => 'edd-reports', ), $persisted_filter_args ) ); // Get all registered tabs & views $tabs = Reports\get_reports(); // Loop through tabs & setup sections if ( ! empty( $tabs ) ) { foreach ( $tabs as $id => $tab ) { // Add to sections array $c_sections[] = array( 'id' => $id, 'label' => $tab['label'], 'icon' => $tab['icon'], 'callback' => array( 'edd_output_report_callback', array( $id ) ) ); } } // Set the customer sections $sections->set_sections( $c_sections ); // Display the sections $sections->display(); } /** * Output a report via a callback * * @since 3.0 * * @param string $report_id */ function edd_output_report_callback( $report_id = '' ) { // Maybe use the already loaded report $report = EDD()->report ? EDD()->report : EDD\Reports\get_report( $report_id ); /** * Fires at the top of the content area of a Reports tab. * * @since 1.0 * @since 3.0 Added the `$report` parameter. * * @param \EDD\Reports\Data\Report|\WP_Error $report The current report object, * or WP_Error if invalid. */ do_action( 'edd_reports_page_top', $report ); if ( ! is_wp_error( $report ) ) { printf( '

%s

', esc_html( $report->label ) ); $report->display(); } else { Reports\default_display_report( $report ); } /** * Fires at the bottom of the content area of a Reports tab. * * @since 1.0 * @since 3.0 Added the `$report` parameter. * * @param \EDD\Reports\Data\Report|\WP_Error $report The current report object, * or WP_Error if invalid. */ do_action( 'edd_reports_page_bottom', $report ); } /** * Reports Page * * Renders the reports page contents. * * @since 1.0 * @return void */ function edd_reports_page() { // Enqueue scripts. wp_enqueue_script( 'edd-admin-reports' ); ?>

report ); ?>
add_report( 'overview', array( 'label' => __( 'Overview', 'easy-digital-downloads' ), 'icon' => 'dashboard', 'priority' => 5, 'endpoints' => array( 'tiles' => array( 'overview_sales', 'overview_earnings', 'overview_average_order_value', 'overview_new_customers', 'overview_refunded_amount', ), 'charts' => array( 'overview_sales_earnings_chart', ), ), 'filters' => array( 'dates', 'taxes', 'currencies', ) ) ); $reports->register_endpoint( 'overview_sales', array( 'label' => __( 'Sales', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $stats = new EDD\Stats( array( 'range' => $dates['range'], 'relative' => true, 'currency' => $currency, 'revenue_type' => 'net', ) ); return $stats->get_order_count(); }, 'display_args' => array( 'comparison_label' => $label . ' — ' . __( 'Net', 'easy-digital-downloads' ), ), ), ), ) ); $reports->register_endpoint( 'overview_earnings', array( 'label' => __( 'Earnings', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) { $stats = new EDD\Stats( array( 'range' => $dates['range'], 'function' => 'SUM', 'exclude_taxes' => $exclude_taxes, 'currency' => $currency, 'relative' => true, 'output' => 'formatted', 'revenue_type' => 'net', ) ); return $stats->get_order_earnings(); }, 'display_args' => array( 'comparison_label' => $label . ' — ' . __( 'Net', 'easy-digital-downloads' ), ), ), ), ) ); $reports->register_endpoint( 'overview_average_order_value', array( 'label' => __( 'Average Order Value', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) { $stats = new EDD\Stats( array( 'function' => 'AVG', 'output' => 'formatted', 'relative' => true, 'range' => $dates['range'], 'exclude_taxes' => $exclude_taxes, 'currency' => $currency, ) ); return $stats->get_order_earnings(); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'overview_new_customers', array( 'label' => __( 'New Customers', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates ) { $stats = new EDD\Stats(); return $stats->get_customer_count( array( 'range' => $dates['range'], 'relative' => true, ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'overview_refunded_amount', array( 'label' => __( 'Total Refund Amount', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) { $stats = new EDD\Stats(); return $stats->get_order_refund_amount( array( 'range' => $dates['range'], 'function' => 'SUM', 'exclude_taxes' => $exclude_taxes, 'currency' => $currency, 'relative' => true, 'output' => 'formatted', ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'overview_sales_earnings_chart', array( 'label' => __( 'Sales and Earnings', 'easy-digital-downloads' ) . ' — ' . $label . ' — ' . __( 'Net', 'easy-digital-downloads' ), 'views' => array( 'chart' => array( 'data_callback' => 'edd_overview_sales_earnings_chart', 'type' => 'line', 'options' => array( 'datasets' => array( 'earnings' => array( 'label' => __( 'Earnings', 'easy-digital-downloads' ), 'borderColor' => 'rgba(24,126,244,0.75)', 'backgroundColor' => 'rgba(24,126,244,0.1)', 'fill' => true, 'borderWidth' => 2, 'type' => 'currency', 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', 'yAxisID' => 'earnings-y', ), 'sales' => array( 'label' => __( 'Sales', 'easy-digital-downloads' ), 'borderColor' => 'rgba(252,108,18,0.75)', 'backgroundColor' => 'rgba(252,108,18,0.05)', 'fill' => true, 'borderWidth' => 2, 'borderCapStyle' => 'round', 'borderJoinStyle' => 'round', 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', 'yAxisID' => 'sales-y', ), ), 'scales' => array( 'yAxes' => array( array( 'id' => 'earnings-y', 'type' => 'linear', 'display' => true, 'position' => 'left', 'ticks' => array( 'formattingType' => 'format', 'beginAtZero' => true, 'precision' => 0, ), 'gridLines' => array( 'display' => true, ), ), array( 'id' => 'sales-y', 'type' => 'linear', 'position' => 'right', 'display' => true, 'ticks' => array( 'formattingType' => 'integer', 'beginAtZero' => true, 'hideNegativeTicks' => true, 'precision' => 0, ), 'gridLines' => array( 'display' => true, 'color' => 'rgba(0,0,0,0.03)', ), ), ), ), ), ), ), ) ); } catch ( \EDD_Exception $exception ) { edd_debug_log_exception( $exception ); } } add_action( 'edd_reports_init', 'edd_register_overview_report' ); /** * Register downloads report and endpoints. * * @since 3.0 * * @param \EDD\Reports\Data\Report_Registry $reports Report registry. */ function edd_register_downloads_report( $reports ) { try { $options = Reports\get_dates_filter_options(); $dates = Reports\get_filter_value( 'dates' ); $exclude_taxes = Reports\get_taxes_excluded_filter(); $currency = ''; $hbh = Reports\get_dates_filter_hour_by_hour(); $label = $options[ $dates['range'] ] . ( $hbh ? ' (' . edd_get_timezone_abbr() . ')' : '' ); $download_data = Reports\get_filter_value( 'products' ); $download_data = ! empty( $download_data ) && 'all' !== Reports\get_filter_value( 'products' ) ? edd_parse_product_dropdown_value( Reports\get_filter_value( 'products' ) ) : false; $endpoint_label = __( 'Sales / Earnings', 'easy-digital-downloads' ); // Mock downloads and prices in case they cannot be found later. $download = edd_get_download(); $prices = array(); $download_label = ''; if ( $download_data ) { $download = edd_get_download( $download_data['download_id'] ); $prices = $download->get_prices(); if ( isset( $download_data['price_id'] ) && is_numeric( $download_data['price_id'] ) ) { $args = array( 'price_id' => $download_data['price_id'] ); $price_name = edd_get_price_name( $download->ID, $args ); if ( $price_name ) { $download->post_title .= ': ' . $price_name; } } $download_label = esc_html( ' (' . $download->post_title . ')' ); } $tiles = array_filter( array( 'most_valuable_download', 'average_download_sales_earnings', 'download_sales_earnings', ), function( $endpoint ) use ( $download_data ) { switch( $endpoint ) { case 'download_sales_earnings': return false !== $download_data; break; default: return false === $download_data; } } ); $charts = array_filter( array( 'download_sales_by_variations', 'download_earnings_by_variations', 'download_sales_earnings_chart' ), function( $endpoint ) use ( $download_data, $download ) { switch( $endpoint ) { case 'download_sales_by_variations': case 'download_earnings_by_variations': return ( false !== $download_data && false === $download_data['price_id'] && true === $download->has_variable_prices() ); break; default: return false !== $download_data; } } ); $tables = array_filter( array( 'top_selling_downloads', 'earnings_by_taxonomy', ), function( $endpoint ) use ( $download_data ) { return false === $download_data; } ); $reports->add_report( 'downloads', array( 'label' => edd_get_label_plural(), 'priority' => 10, 'icon' => 'download', 'endpoints' => array( 'tiles' => $tiles, 'charts' => $charts, 'tables' => $tables, ), 'filters' => array( 'dates', 'products', 'taxes' ), ) ); $reports->register_endpoint( 'most_valuable_download', array( 'label' => sprintf( __( 'Most Valuable %s', 'easy-digital-downloads' ), edd_get_label_singular() ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $stats = new EDD\Stats(); $d = $stats->get_most_valuable_order_items( array( 'range' => $dates['range'], 'currency' => $currency, 'function' => 'SUM' ) ); if ( ! empty( $d ) && isset( $d[0] ) ) { $d = $d[0]; if ( $d->object instanceof EDD_Download ) { $title = $d->object->post_title; if ( $d->object->has_variable_prices() ) { $prices = array_values( wp_filter_object_list( $d->object->get_prices(), array( 'index' => absint( $d->price_id ) ) ) ); $title .= ( is_array( $prices ) && isset( $prices[0] ) ) ? ': ' . $prices[0]['name'] : ''; } return esc_html( $title ); } } }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'average_download_sales_earnings', array( 'label' => __( 'Average Sales / Earnings', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) { $stats = new EDD\Stats( array( 'function' => 'AVG', 'range' => $dates['range'], 'exclude_taxes' => $exclude_taxes, 'currency' => $currency, 'output' => 'formatted', ) ); return $stats->get_order_item_count() . ' / ' . $stats->get_order_item_earnings(); }, 'display_args' => array( 'comparison_label' => $label . ' — ' . __( 'Net ', 'easy-digital-downloads' ), ), ), ), ) ); $reports->register_endpoint( 'download_sales_earnings', array( 'label' => $endpoint_label, 'views' => array( 'tile' => array( 'data_callback' => function () use ( $download_data, $dates, $currency ) { $price_id = isset( $download_data['price_id'] ) && is_numeric( $download_data['price_id'] ) ? absint( $download_data['price_id'] ) : null; $stats = new EDD\Stats( array( 'product_id' => absint( $download_data['download_id'] ), 'price_id' => $price_id, 'currency' => $currency, 'range' => $dates['range'], 'output' => 'formatted', ) ); $earnings = $stats->get_order_item_earnings( array( 'function' => 'SUM' ) ); $sales = $stats->get_order_item_count( array( 'function' => 'COUNT' ) ); return esc_html( $sales . ' / ' . $earnings ); }, 'display_args' => array( 'comparison_label' => $label . $download_label, ), ), ), ) ); $reports->register_endpoint( 'earnings_by_taxonomy', array( 'label' => __( 'Earnings By Taxonomy', 'easy-digital-downloads' ) . ' — ' . $label, 'views' => array( 'table' => array( 'display_args' => array( 'class_name' => '\\EDD\\Reports\\Data\\Downloads\\Earnings_By_Taxonomy_List_Table', 'class_file' => EDD_PLUGIN_DIR . 'includes/reports/data/downloads/class-earnings-by-taxonomy-list-table.php', ), ), ), ) ); $reports->register_endpoint( 'top_selling_downloads', array( 'label' => sprintf( __( 'Top Selling %s', 'easy-digital-downloads' ), edd_get_label_plural() ) . ' — ' . $label, 'views' => array( 'table' => array( 'display_args' => array( 'class_name' => '\\EDD\\Reports\\Data\\Downloads\\Top_Selling_Downloads_List_Table', 'class_file' => EDD_PLUGIN_DIR . 'includes/reports/data/downloads/class-top-selling-downloads-list-table.php', ), ), ), ) ); $reports->register_endpoint( 'download_sales_by_variations', array( 'label' => __( 'Sales by Variation', 'easy-digital-downloads' ) . $download_label, 'views' => array( 'chart' => array( 'data_callback' => function() use ( $download_data, $download, $dates, $prices, $currency ) { $stats = new EDD\Stats(); $sales = $stats->get_order_item_count( array( 'product_id' => absint( $download_data['download_id'] ), 'range' => $dates['range'], 'grouped' => true, 'currency' => $currency ) ); // Set all values to 0. foreach ( $prices as $key => $price ) { $prices[ $key ]['sales'] = 0; } // Parse results from the database. foreach ( $sales as $data ) { $prices[ $data->price_id ]['sales'] = absint( $data->total ); } $sales = array_values( wp_list_pluck( $prices, 'sales' ) ); return array( 'sales' => $sales, ); }, 'type' => 'pie', 'options' => array( 'cutoutPercentage' => 0, 'datasets' => array( 'sales' => array( 'label' => __( 'Sales', 'easy-digital-downloads' ), 'backgroundColor' => array( 'rgb(133,175,91)', 'rgb(9,149,199)', 'rgb(8,189,231)', 'rgb(137,163,87)', 'rgb(27,98,122)', ), ), ), 'labels' => array_values( wp_list_pluck( $prices, 'name' ) ) ), ), ) ) ); $reports->register_endpoint( 'download_earnings_by_variations', array( 'label' => __( 'Earnings by Variation', 'easy-digital-downloads' ) . $download_label, 'views' => array( 'chart' => array( 'data_callback' => function() use ( $download_data, $prices, $dates, $currency ) { $stats = new EDD\Stats(); $earnings = $stats->get_order_item_earnings( array( 'product_id' => absint( $download_data['download_id'] ), 'range' => $dates['range'], 'grouped' => true, 'currency' => $currency ) ); // Set all values to 0. foreach ( $prices as $key => $price ) { $prices[ $key ]['earnings'] = floatval( 0 ); } // Parse results from the database. foreach ( $earnings as $data ) { $prices[ $data->price_id ]['earnings'] = floatval( $data->total ); } $earnings = array_values( wp_list_pluck( $prices, 'earnings' ) ); return array( 'earnings' => $earnings, ); }, 'type' => 'pie', 'options' => array( 'cutoutPercentage' => 0, 'datasets' => array( 'earnings' => array( 'label' => __( 'Earnings', 'easy-digital-downloads' ), 'type' => 'currency', 'backgroundColor' => array( 'rgb(133,175,91)', 'rgb(9,149,199)', 'rgb(8,189,231)', 'rgb(137,163,87)', 'rgb(27,98,122)', ), ), ), 'labels' => array_values( wp_list_pluck( $prices, 'name' ) ) ), ), ) ) ); $reports->register_endpoint( 'download_sales_earnings_chart', array( 'label' => __( 'Sales and Earnings', 'easy-digital-downloads' ) . esc_html( $download_label ), 'views' => array( 'chart' => array( 'data_callback' => function () use ( $download_data, $currency ) { global $wpdb; $dates = Reports\get_dates_filter( 'objects' ); $day_by_day = Reports\get_dates_filter_day_by_day(); $hour_by_hour = Reports\get_dates_filter_hour_by_hour(); $chart_dates = Reports\parse_dates_for_range( null, 'now', false ); $sql_clauses = array( 'select' => 'edd_oi.date_created AS date', 'where' => '', 'groupby' => '', ); $union_clauses = array( 'select' => 'date', 'where' => '', 'groupby' => '', ); // Default to 'monthly'. $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'MONTH', 'edd_oi.date_created' ); $sql_clauses['orderby'] = 'MONTH(edd_oi.date_created)'; $union_clauses['groupby'] = Reports\get_groupby_date_string( 'MONTH', 'date' ); $union_clauses['orderby'] = 'MONTH(date)'; // Now drill down to the smallest unit. if ( $hour_by_hour ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'edd_oi.date_created' ); $sql_clauses['orderby'] = 'HOUR(edd_oi.date_created)'; $union_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'date' ); $union_clauses['orderby'] = 'HOUR(date)'; } elseif ( $day_by_day ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'edd_oi.date_created' ); $sql_clauses['orderby'] = 'DATE(edd_oi.date_created)'; $union_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'date' ); $union_clauses['orderby'] = 'DATE(date)'; } $price_id = isset( $download_data['price_id'] ) && is_numeric( $download_data['price_id'] ) ? sprintf( 'AND price_id = %d', absint( $download_data['price_id'] ) ) : ''; $earnings_statuses = edd_get_gross_order_statuses(); $earnings_status_string = implode( ', ', array_fill( 0, count( $earnings_statuses ), '%s' ) ); $order_item_earnings = $wpdb->prepare( "SELECT SUM(edd_oi.total / edd_oi.rate) AS earnings, %1s FROM {$wpdb->edd_order_items} edd_oi INNER JOIN {$wpdb->edd_orders} edd_o ON edd_oi.order_id = edd_o.id WHERE edd_oi.product_id = %d %1s AND edd_oi.date_created >= %s AND edd_oi.date_created <= %s AND edd_o.status IN ({$earnings_status_string}) GROUP BY {$sql_clauses['groupby']}", $sql_clauses['select'], $download_data['download_id'], $price_id, $dates['start']->copy()->format( 'mysql' ), $dates['end']->copy()->format( 'mysql' ), ...$earnings_statuses ); /** * The adjustments query needs a different order status check than the order items. This is due to the fact that * adjustments refunded would end up being double counted, and therefore create an inaccurate revenue report. */ $adjustments_statuses = edd_get_net_order_statuses(); $adjustments_status_string = implode( ', ', array_fill( 0, count( $adjustments_statuses ), '%s' ) ); $order_adjustments = $wpdb->prepare( "SELECT SUM(edd_oa.total / edd_oa.rate) AS earnings, %1s FROM {$wpdb->edd_order_adjustments} edd_oa INNER JOIN {$wpdb->edd_order_items} edd_oi ON edd_oi.id = edd_oa.object_id AND edd_oi.product_id = %d %1s AND edd_oi.date_created >= %s AND edd_oi.date_created <= %s INNER JOIN {$wpdb->edd_orders} edd_o ON edd_oi.order_id = edd_o.id AND edd_o.type = 'sale' AND edd_o.status IN ({$adjustments_status_string}) WHERE edd_oa.object_type = 'order_item' AND edd_oa.type != 'discount' GROUP BY {$sql_clauses['groupby']}", $sql_clauses['select'], $download_data['download_id'], $price_id, $dates['start']->copy()->format( 'mysql' ), $dates['end']->copy()->format( 'mysql' ), ...$adjustments_statuses ); $earnings_sql = "SELECT SUM(earnings) as earnings, {$union_clauses['select']} FROM ({$order_item_earnings} UNION {$order_adjustments})a GROUP BY {$union_clauses['groupby']} ORDER BY {$union_clauses['orderby']}"; $earnings_results = $wpdb->get_results( $earnings_sql ); $statuses = edd_get_net_order_statuses(); $status_string = implode( ', ', array_fill( 0, count( $statuses ), '%s' ) ); $join = $wpdb->prepare( "INNER JOIN {$wpdb->edd_orders} edd_o ON (edd_oi.order_id = edd_o.id) AND edd_o.status IN({$status_string}) AND edd_o.type = 'sale' ", ...$statuses ); $sales_sql = $wpdb->prepare( "SELECT COUNT(edd_oi.total) AS sales, {$sql_clauses['select']} FROM {$wpdb->edd_order_items} edd_oi {$join} WHERE edd_oi.product_id = %d %1s AND edd_oi.date_created >= %s AND edd_oi.date_created <= %s AND edd_oi.status IN ({$status_string}) GROUP BY {$sql_clauses['groupby']} ORDER BY {$sql_clauses['orderby']} ASC", $download_data['download_id'], $price_id, $dates['start']->copy()->format( 'mysql' ), $dates['end']->copy()->format( 'mysql' ), ...$statuses ); $sales_results = $wpdb->get_results( $sales_sql ); $sales = array(); $earnings = array(); // Initialise all arrays with timestamps and set values to 0. while ( strtotime( $chart_dates['start']->copy()->format( 'mysql' ) ) <= strtotime( $chart_dates['end']->copy()->format( 'mysql' ) ) ) { $timestamp = $chart_dates['start']->copy()->format( 'U' ); $date_on_chart = $chart_dates['start']; $sales[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' ); $sales[ $timestamp ][1] = 0; $earnings[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' ); $earnings[ $timestamp ][1] = 0.00; // Loop through each date there were sales/earnings, which we queried from the database. foreach ( $earnings_results as $earnings_result ) { $date_of_db_value = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $earnings_result->date ) ); // Add any sales/earnings that happened during this hour. if ( $hour_by_hour ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d H' ) === $date_on_chart->format( 'Y-m-d H' ) ) { $earnings[ $timestamp ][1] += $earnings_result->earnings; } // Add any sales/earnings that happened during this day. } elseif ( $day_by_day ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d' ) === $date_on_chart->format( 'Y-m-d' ) ) { $earnings[ $timestamp ][1] += $earnings_result->earnings; } // Add any sales/earnings that happened during this month. } else { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m' ) === $date_on_chart->format( 'Y-m' ) ) { $earnings[ $timestamp ][1] += $earnings_result->earnings; } } } // Loop through each date there were sales/earnings, which we queried from the database. foreach ( $sales_results as $sales_result ) { $date_of_db_value = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $sales_result->date ) ); // Add any sales/earnings that happened during this hour. if ( $hour_by_hour ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d H' ) === $date_on_chart->format( 'Y-m-d H' ) ) { $sales[ $timestamp ][1] += $sales_result->sales; } // Add any sales/earnings that happened during this day. } elseif ( $day_by_day ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d' ) === $date_on_chart->format( 'Y-m-d' ) ) { $sales[ $timestamp ][1] += $sales_result->sales; } // Add any sales/earnings that happened during this month. } else { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m' ) === $date_on_chart->format( 'Y-m' ) ) { $sales[ $timestamp ][1] += $sales_result->sales; } } } // Move the chart along to the next hour/day/month to get ready for the next loop. if ( $hour_by_hour ) { $chart_dates['start']->addHour( 1 ); } elseif ( $day_by_day ) { $chart_dates['start']->addDays( 1 ); } else { $chart_dates['start']->addMonth( 1 ); } } return array( 'earnings' => array_values( $earnings ), 'sales' => array_values( $sales ), ); }, 'type' => 'line', 'options' => array( 'datasets' => array( 'earnings' => array( 'label' => __( 'Earnings', 'easy-digital-downloads' ), 'borderColor' => 'rgba(24,126,244,0.75)', 'backgroundColor' => 'rgba(24,126,244,0.1)', 'fill' => true, 'borderWidth' => 2, 'type' => 'currency', 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', 'yAxisID' => 'earnings-y', ), 'sales' => array( 'label' => __( 'Sales', 'easy-digital-downloads' ), 'borderColor' => 'rgba(252,108,18,0.75)', 'backgroundColor' => 'rgba(252,108,18,0.05)', 'fill' => true, 'borderWidth' => 2, 'borderCapStyle' => 'round', 'borderJoinStyle' => 'round', 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', 'yAxisID' => 'sales-y', ), ), 'scales' => array( 'yAxes' => array( array( 'id' => 'earnings-y', 'type' => 'linear', 'display' => true, 'position' => 'left', 'ticks' => array( 'maxTicksLimit' => 5, 'formattingType' => 'format', 'suggestedMin' => 0, 'beginAtZero' => true, ), 'gridLines' => array( 'display' => true, ), ), array( 'id' => 'sales-y', 'type' => 'linear', 'position' => 'right', 'display' => true, 'ticks' => array( 'maxTicksLimit' => 5, 'formattingType' => 'integer', 'suggestedMin' => 0, 'beginAtZero' => true, 'hideNegativeTicks' => true, ), 'gridLines' => array( 'display' => true, 'color' => 'rgba(0,0,0,0.03)', ), ), ), ), ), ), ), ) ); } catch ( \EDD_Exception $exception ) { edd_debug_log_exception( $exception ); } } add_action( 'edd_reports_init', 'edd_register_downloads_report' ); /** * Register refunds report and endpoints. * * @since 3.0 * * @param \EDD\Reports\Data\Report_Registry $reports Report registry. */ function edd_register_refunds_report( $reports ) { try { // Variables to hold date filter values. $options = Reports\get_dates_filter_options(); $dates = Reports\get_filter_value( 'dates' ); $exclude_taxes = Reports\get_taxes_excluded_filter(); $currency = Reports\get_filter_value( 'currencies' ); $hbh = Reports\get_dates_filter_hour_by_hour(); $label = $options[ $dates['range'] ] . ( $hbh ? ' (' . edd_get_timezone_abbr() . ')' : '' ); $reports->add_report( 'refunds', array( 'label' => __( 'Refunds', 'easy-digital-downloads' ), 'icon' => 'image-rotate', 'priority' => 15, 'endpoints' => array( 'tiles' => array( 'refund_count', 'fully_refunded_order_count', 'fully_refunded_order_item_count', 'refund_amount', 'average_refund_amount', 'average_time_to_refund', 'refund_rate', ), 'charts' => array( 'refunds_chart', ), ), 'filters' => array( 'dates', 'taxes', 'currencies' ) ) ); $reports->register_endpoint( 'refund_count', array( 'label' => __( 'Number of Refunds', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $stats = new EDD\Stats(); $number = $stats->get_order_refund_count( array( 'range' => $dates['range'], 'currency' => $currency ) ); return esc_html( $number ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'fully_refunded_order_count', array( 'label' => __( 'Number of Fully Refunded Orders', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $stats = new EDD\Stats(); $number = $stats->get_order_refund_count( array( 'range' => $dates['range'], 'status' => array( 'complete' ), 'currency' => $currency ) ); return esc_html( $number ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'fully_refunded_order_item_count', array( 'label' => __( 'Number of Fully Refunded Items', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $stats = new EDD\Stats(); $number = $stats->get_order_item_refund_count( array( 'range' => $dates['range'], 'status' => array( 'refunded' ), 'currency' => $currency ) ); return esc_html( $number ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'refund_amount', array( 'label' => __( 'Total Refund Amount', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) { $stats = new EDD\Stats(); return $stats->get_order_refund_amount( array( 'range' => $dates['range'], 'exclude_taxes' => $exclude_taxes, 'output' => 'formatted', 'currency' => $currency ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'average_refund_amount', array( 'label' => __( 'Average Refund Amount', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) { $stats = new EDD\Stats(); return $stats->get_order_refund_amount( array( 'function' => 'AVG', 'range' => $dates['range'], 'exclude_taxes' => $exclude_taxes, 'output' => 'formatted', 'currency' => $currency ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'average_time_to_refund', array( 'label' => __( 'Average Time to Refund', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $stats = new EDD\Stats(); return $stats->get_average_refund_time( array( 'range' => $dates['range'], 'currency' => $currency ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'refund_rate', array( 'label' => __( 'Refund Rate', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $stats = new EDD\Stats(); return $stats->get_refund_rate( array( 'range' => $dates['range'], 'output' => 'formatted', 'status' => edd_get_gross_order_statuses(), 'currency' => $currency ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'refunds_chart', array( 'label' => __( 'Refunds', 'easy-digital-downloads' ) . ' — ' . $label, 'views' => array( 'chart' => array( 'data_callback' => 'edd_overview_refunds_chart', 'type' => 'line', 'options' => array( 'datasets' => array( 'amount' => array( 'label' => __( 'Amount', 'easy-digital-downloads' ), 'borderColor' => 'rgba(24,126,244,0.75)', 'backgroundColor' => 'rgba(24,126,244,0.1)', 'fill' => true, 'borderWidth' => 2, 'type' => 'currency', 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', 'yAxisID' => 'amount-y', ), 'number' => array( 'label' => __( 'Number', 'easy-digital-downloads' ), 'borderColor' => 'rgba(252,108,18,0.75)', 'backgroundColor' => 'rgba(252,108,18,0.05)', 'fill' => true, 'borderWidth' => 2, 'borderCapStyle' => 'round', 'borderJoinStyle' => 'round', 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', 'yAxisID' => 'number-y', ), ), 'scales' => array( 'yAxes' => array( array( 'id' => 'amount-y', 'type' => 'linear', 'display' => true, 'position' => 'left', 'ticks' => array( 'maxTicksLimit' => 5, 'formattingType' => 'format', 'suggestedMin' => 0, 'beginAtZero' => true, 'precision' => 0, ), 'gridLines' => array( 'display' => true, ), ), array( 'id' => 'number-y', 'type' => 'linear', 'position' => 'right', 'display' => true, 'ticks' => array( 'maxTicksLimit' => 5, 'formattingType' => 'integer', 'suggestedMin' => 0, 'beginAtZero' => true, 'precision' => 0, ), 'gridLines' => array( 'display' => true, 'color' => 'rgba(0,0,0,0.03)', ), ), ), ), ), ), ), ) ); } catch ( \EDD_Exception $exception ) { edd_debug_log_exception( $exception ); } } add_action( 'edd_reports_init', 'edd_register_refunds_report' ); /** * Register payment gateways report and endpoints. * * @since 3.0 * * @param \EDD\Reports\Data\Report_Registry $reports Report registry. */ function edd_register_payment_gateways_report( $reports ) { try { // Variables to hold date filter values. $options = Reports\get_dates_filter_options(); $dates = Reports\get_filter_value( 'dates' ); $exclude_taxes = Reports\get_taxes_excluded_filter(); $currency = Reports\get_filter_value( 'currencies' ); $gateway = Reports\get_filter_value( 'gateways' ); $hbh = Reports\get_dates_filter_hour_by_hour(); $label = $options[ $dates['range'] ] . ( $hbh ? ' (' . edd_get_timezone_abbr() . ')' : '' ); $tiles = array( 'sales_per_gateway', 'earnings_per_gateway', 'refunds_per_gateway', 'average_value_per_gateway', ); $tables = array_filter( array( 'gateway_stats', ), function( $endpoint ) use ( $gateway ) { return empty( $gateway ) || 'all' === $gateway; } ); $charts = array_filter( array( 'gateway_sales_breakdown', 'gateway_earnings_breakdown', 'gateway_sales_earnings_chart', ), function( $endpoint ) use ( $gateway ) { switch( $endpoint ) { case 'gateway_sales_earnings_chart': return ! empty( $gateway ) && 'all' !== $gateway; break; default: return ( empty( $gateway ) || 'all' === $gateway ); } } ); $reports->add_report( 'gateways', array( 'label' => __( 'Payment Gateways', 'easy-digital-downloads' ), 'icon' => 'image-filter', 'priority' => 20, 'endpoints' => array( 'tiles' => $tiles, 'tables' => $tables, 'charts' => $charts, ), 'filters' => array( 'dates', 'gateways', 'taxes', 'currencies' ), ) ); $reports->register_endpoint( 'sales_per_gateway', array( 'label' => __( 'Sales', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $gateway = 'all' !== Reports\get_filter_value( 'gateways' ) ? Reports\get_filter_value( 'gateways' ) : ''; $stats = new EDD\Stats(); return $stats->get_gateway_sales( array( 'range' => $dates['range'], 'gateway' => $gateway, 'currency' => $currency, ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'earnings_per_gateway', array( 'label' => __( 'Earnings', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) { $gateway = 'all' !== Reports\get_filter_value( 'gateways' ) ? Reports\get_filter_value( 'gateways' ) : ''; $stats = new EDD\Stats(); return $stats->get_gateway_earnings( array( 'range' => $dates['range'], 'exclude_taxes' => $exclude_taxes, 'gateway' => $gateway, 'output' => 'formatted', 'currency' => $currency ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'refunds_per_gateway', array( 'label' => __( 'Refunds', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $gateway = 'all' !== Reports\get_filter_value( 'gateways' ) ? Reports\get_filter_value( 'gateways' ) : ''; $stats = new EDD\Stats(); return $stats->get_gateway_earnings( array( 'range' => $dates['range'], 'gateway' => $gateway, 'output' => 'formatted', 'type' => 'refund', 'status' => array( 'complete' ), 'currency' => $currency ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'average_value_per_gateway', array( 'label' => __( 'Average Order Value', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) { $gateway = 'all' !== Reports\get_filter_value( 'gateways' ) ? Reports\get_filter_value( 'gateways' ) : ''; $stats = new EDD\Stats(); if ( empty( $gateway ) ) { return $stats->get_order_earnings( array( 'range' => $dates['range'], 'exclude_taxes' => $exclude_taxes, 'function' => 'AVG', 'output' => 'formatted', 'currency' => $currency ) ); } else { return $stats->get_gateway_earnings( array( 'range' => $dates['range'], 'exclude_taxes' => $exclude_taxes, 'gateway' => $gateway, 'function' => 'AVG', 'output' => 'formatted', 'currency' => $currency ) ); } }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'gateway_stats', array( 'label' => __( 'Gateway Stats', 'easy-digital-downloads' ) . ' — ' . $options[ $dates['range'] ], 'views' => array( 'table' => array( 'display_args' => array( 'class_name' => '\\EDD\\Reports\\Data\\Payment_Gateways\\Gateway_Stats', 'class_file' => EDD_PLUGIN_DIR . 'includes/reports/data/payment-gateways/class-gateway-stats-list-table.php', ), ), ), ) ); $gateway_list = array_map( 'edd_get_gateway_admin_label', array_keys( edd_get_payment_gateways() ) ); $reports->register_endpoint( 'gateway_sales_breakdown', array( 'label' => __( 'Gateway Sales', 'easy-digital-downloads' ) . ' — ' . $options[ $dates['range'] ], 'views' => array( 'chart' => array( 'data_callback' => function() use ( $dates, $currency ) { $stats = new EDD\Stats(); $g = $stats->get_gateway_sales( array( 'range' => $dates['range'], 'grouped' => true, 'currency' => $currency ) ); $gateways = array_flip( array_keys( edd_get_payment_gateways() ) ); foreach ( $g as $data ) { $gateways[ $data->gateway ] = $data->total; } $gateways = array_map( function( $v ) { return null === $v ? 0 : $v; }, $gateways ); return array( 'sales' => array_values( $gateways ), ); }, 'type' => 'pie', 'options' => array( 'cutoutPercentage' => 0, 'datasets' => array( 'sales' => array( 'label' => __( 'Sales', 'easy-digital-downloads' ), 'backgroundColor' => array( 'rgb(133,175,91)', 'rgb(9,149,199)', 'rgb(8,189,231)', 'rgb(137,163,87)', 'rgb(27,98,122)', ), ), ), 'labels' => $gateway_list, ), ), ) ) ); $reports->register_endpoint( 'gateway_earnings_breakdown', array( 'label' => __( 'Gateway Earnings', 'easy-digital-downloads' ) . ' — ' . $options[ $dates['range'] ], 'views' => array( 'chart' => array( 'data_callback' => function() use ( $dates, $exclude_taxes, $currency ) { $stats = new EDD\Stats(); $g = $stats->get_gateway_earnings( array( 'grouped' => true, 'range' => $dates['range'], 'exclude_taxes' => $exclude_taxes, 'currency' => $currency ) ); $gateways = array_flip( array_keys( edd_get_payment_gateways() ) ); foreach ( $g as $data ) { $gateways[ $data->gateway ] = $data->earnings; } $gateways = array_values( array_map( function( $v ) { return null === $v ? 0.00 : $v; }, $gateways ) ); return array( 'earnings' => $gateways, ); }, 'type' => 'pie', 'options' => array( 'cutoutPercentage' => 0, 'datasets' => array( 'earnings' => array( 'label' => __( 'Earnings', 'easy-digital-downloads' ), 'backgroundColor' => array( 'rgb(133,175,91)', 'rgb(9,149,199)', 'rgb(8,189,231)', 'rgb(137,163,87)', 'rgb(27,98,122)', ), 'type' => 'currency', ), ), 'labels' => $gateway_list, ), ), ) ) ); $reports->register_endpoint( 'gateway_sales_earnings_chart', array( 'label' => __( 'Sales and Earnings', 'easy-digital-downloads' ) . ' — ' . $label, 'views' => array( 'chart' => array( 'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) { global $wpdb; $dates = Reports\get_dates_filter( 'objects' ); $day_by_day = Reports\get_dates_filter_day_by_day(); $hour_by_hour = Reports\get_dates_filter_hour_by_hour(); $chart_dates = Reports\parse_dates_for_range( null, 'now', false ); $sql_clauses = array( 'select' => 'date_created AS date', 'where' => '', ); // Default to 'monthly'. $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'MONTH', 'date_created' ); $sql_clauses['orderby'] = 'MONTH(date_created)'; // Now drill down to the smallest unit. if ( $hour_by_hour ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'date_created' ); $sql_clauses['orderby'] = 'HOUR(date_created)'; } elseif ( $day_by_day ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'date_created' ); $sql_clauses['orderby'] = 'DATE(date_created)'; } $gateway = Reports\get_filter_value( 'gateways' ); $column = $exclude_taxes ? '( total - tax ) / rate' : 'total / rate'; $currency_sql = ''; if ( ! empty( $currency ) && array_key_exists( strtoupper( $currency ), edd_get_currencies() ) ) { $currency_sql = $wpdb->prepare( " AND currency = %s ", strtoupper( $currency ) ); } $results = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT({$column}) AS sales, SUM({$column}) AS earnings, {$sql_clauses['select']} FROM {$wpdb->edd_orders} o WHERE gateway = %s AND status IN ('complete', 'revoked') {$currency_sql} AND date_created >= %s AND date_created <= %s GROUP BY {$sql_clauses['groupby']} ORDER BY {$sql_clauses['orderby']} ASC", esc_sql( $gateway ), $dates['start']->copy()->format( 'mysql' ), $dates['end']->copy()->format( 'mysql' ) ) ); $sales = array(); $earnings = array(); /** * Initialise all arrays with timestamps and set values to 0. * * We use the Chart based dates for this loop, so the graph shows in the proper date ranges while the actual DB queries are all UTC based. */ while ( strtotime( $chart_dates['start']->copy()->format( 'mysql' ) ) <= strtotime( $chart_dates['end']->copy()->format( 'mysql' ) ) ) { $timestamp = $chart_dates['start']->copy()->format( 'U' ); $date_on_chart = $chart_dates['start']; $sales[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' ); $sales[ $timestamp ][1] = 0; $earnings[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' ); $earnings[ $timestamp ][1] = 0.00; // Loop through each date there were sales/earnings, which we queried from the database. foreach ( $results as $result ) { $date_of_db_value = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $result->date ) ); // Add any sales/earnings that happened during this hour. if ( $hour_by_hour ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d H' ) === $date_on_chart->format( 'Y-m-d H' ) ) { $sales[ $timestamp ][1] += $result->sales; $earnings[ $timestamp ][1] += $result->earnings; } // Add any sales/earnings that happened during this day. } elseif ( $day_by_day ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d' ) === $date_on_chart->format( 'Y-m-d' ) ) { $sales[ $timestamp ][1] += $result->sales; $earnings[ $timestamp ][1] += $result->earnings; } // Add any sales/earnings that happened during this month. } else { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m' ) === $date_on_chart->format( 'Y-m' ) ) { $sales[ $timestamp ][1] += $result->sales; $earnings[ $timestamp ][1] += $result->earnings; } } } // Move the chart along to the next hour/day/month to get ready for the next loop. if ( $hour_by_hour ) { $chart_dates['start']->addHour( 1 ); } elseif ( $day_by_day ) { $chart_dates['start']->addDays( 1 ); } else { $chart_dates['start']->addMonth( 1 ); } } $sales = array_values( $sales ); $earnings = array_values( $earnings ); return array( 'earnings' => $earnings, 'sales' => $sales, ); }, 'type' => 'line', 'options' => array( 'datasets' => array( 'earnings' => array( 'label' => __( 'Earnings', 'easy-digital-downloads' ), 'borderColor' => 'rgba(24,126,244,0.75)', 'backgroundColor' => 'rgba(24,126,244,0.1)', 'fill' => true, 'borderWidth' => 2, 'type' => 'currency', 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', 'yAxisID' => 'earnings-y', ), 'sales' => array( 'label' => __( 'Sales', 'easy-digital-downloads' ), 'borderColor' => 'rgba(252,108,18,0.75)', 'backgroundColor' => 'rgba(252,108,18,0.05)', 'fill' => true, 'borderWidth' => 2, 'borderCapStyle' => 'round', 'borderJoinStyle' => 'round', 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', 'yAxisID' => 'sales-y', ), ), 'scales' => array( 'yAxes' => array( array( 'id' => 'earnings-y', 'type' => 'linear', 'display' => true, 'position' => 'left', 'ticks' => array( 'maxTicksLimit' => 5, 'formattingType' => 'format', 'suggestedMin' => 0, 'beginAtZero' => true, 'precision' => 0, ), 'gridLines' => array( 'display' => true, ), ), array( 'id' => 'sales-y', 'type' => 'linear', 'position' => 'right', 'display' => true, 'ticks' => array( 'maxTicksLimit' => 5, 'formattingType' => 'integer', 'suggestedMin' => 0, 'beginAtZero' => true, 'hideNegativeTicks' => true, 'precision' => 0, ), 'gridLines' => array( 'display' => true, 'color' => 'rgba(0,0,0,0.03)', ), ), ), ), ), ), ), ) ); } catch ( \EDD_Exception $exception ) { edd_debug_log_exception( $exception ); } } add_action( 'edd_reports_init', 'edd_register_payment_gateways_report' ); /** * Register taxes report and endpoints. * * @since 3.0 * * @param \EDD\Reports\Data\Report_Registry $reports Report registry. */ function edd_register_taxes_report( $reports ) { try { // Variables to hold date filter values. $options = Reports\get_dates_filter_options(); $dates = Reports\get_filter_value( 'dates' ); $currency = Reports\get_filter_value( 'currencies' ); $hbh = Reports\get_dates_filter_hour_by_hour(); $label = $options[ $dates['range'] ] . ( $hbh ? ' (' . edd_get_timezone_abbr() . ')' : '' ); $download_data = Reports\get_filter_value( 'products' ); $download_data = ! empty( $download_data ) && 'all' !== Reports\get_filter_value( 'products' ) ? edd_parse_product_dropdown_value( Reports\get_filter_value( 'products' ) ) : false; $download_label = ''; if ( $download_data ) { $download = edd_get_download( $download_data['download_id'] ); if ( isset( $download_data['price_id'] ) && is_numeric( $download_data['price_id'] ) ) { $args = array( 'price_id' => $download_data['price_id'] ); $price_name = edd_get_price_name( $download->ID, $args ); if ( $price_name ) { $download->post_title .= ': ' . $price_name; } } $download_label = esc_html( ' (' . $download->post_title . ')' ); } $country = Reports\get_filter_value( 'countries' ); $region = Reports\get_filter_value( 'regions' ); $tiles = array( 'total_tax_collected', 'total_tax_collected_for_location', ); $tables = array_filter( array( 'tax_collected_by_location', ), function( $table ) use ( $download_data ) { return false === $download_data; } ); $reports->add_report( 'taxes', array( 'label' => __( 'Taxes', 'easy-digital-downloads' ), 'priority' => 25, 'icon' => 'editor-paste-text', 'endpoints' => array( 'tiles' => $tiles, 'tables' => $tables, ), 'filters' => array( 'dates', 'products', 'countries', 'regions', 'currencies' ), ) ); $reports->register_endpoint( 'total_tax_collected', array( 'label' => __( 'Total Tax Collected', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $currency ) { $download = Reports\get_filter_value( 'products' ); $download = ! empty( $download ) && 'all' !== Reports\get_filter_value( 'products' ) ? edd_parse_product_dropdown_value( Reports\get_filter_value( 'products' ) ) : array( 'download_id' => '', 'price_id' => '' ); $stats = new EDD\Stats(); return $stats->get_tax( array( 'output' => 'formatted', 'range' => $dates['range'], 'download_id' => $download['download_id'], 'price_id' => (string) $download['price_id'], 'currency' => $currency ) ); }, 'display_args' => array( 'comparison_label' => $label . $download_label, ), ), ), ) ); if ( ! empty( $country ) && 'all' !== $country ) { $location = ''; if ( ! empty( $region ) && 'all' !== $region ) { $location = edd_get_state_name( $country, $region ) . ', '; } $location .= edd_get_country_name( $country ); $reports->register_endpoint( 'total_tax_collected_for_location', array( 'label' => __( 'Total Tax Collected for ', 'easy-digital-downloads' ) . $location, 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates, $country, $region, $currency ) { $download = Reports\get_filter_value( 'products' ); $download = ! empty( $download ) && 'all' !== Reports\get_filter_value( 'products' ) ? edd_parse_product_dropdown_value( Reports\get_filter_value( 'products' ) ) : array( 'download_id' => '', 'price_id' => '' ); $stats = new EDD\Stats(); return $stats->get_tax_by_location( array( 'output' => 'formatted', 'range' => $dates['range'], 'download_id' => $download['download_id'], 'price_id' => (string) $download['price_id'], 'country' => $country, 'region' => $region, 'currency' => $currency ) ); }, 'display_args' => array( 'comparison_label' => $label . $download_label, ), ), ), ) ); } $reports->register_endpoint( 'tax_collected_by_location', array( 'label' => __( 'Tax Collected by Location', 'easy-digital-downloads' ), 'views' => array( 'table' => array( 'display_args' => array( 'class_name' => '\\EDD\\Reports\\Data\\Taxes\\Tax_Collected_By_Location', 'class_file' => EDD_PLUGIN_DIR . 'includes/reports/data/taxes/class-tax-collected-by-location-list-table.php', ), ), ), ) ); } catch ( \EDD_Exception $exception ) { edd_debug_log_exception( $exception ); } } add_action( 'edd_reports_init', 'edd_register_taxes_report' ); /** * Register file downloads report and endpoints. * * @since 3.0 * * @param \EDD\Reports\Data\Report_Registry $reports Report registry. */ function edd_register_file_downloads_report( $reports ) { try { // Variables to hold date filter values. $options = Reports\get_dates_filter_options(); $filter = Reports\get_filter_value( 'dates' ); $hbh = Reports\get_dates_filter_hour_by_hour(); $label = $options[ $filter['range'] ] . ( $hbh ? ' (' . edd_get_timezone_abbr() . ')' : '' ); $download_data = Reports\get_filter_value( 'products' ); $download_data = ! empty( $download_data ) && 'all' !== Reports\get_filter_value( 'products' ) ? edd_parse_product_dropdown_value( Reports\get_filter_value( 'products' ) ) : false; $download_label = ''; if ( $download_data ) { $download = edd_get_download( $download_data['download_id'] ); if ( isset( $download_data['price_id'] ) && is_numeric( $download_data['price_id'] ) ) { $args = array( 'price_id' => $download_data['price_id'] ); $price_name = edd_get_price_name( $download->ID, $args ); if ( $price_name ) { $download->post_title .= ': ' . $price_name; } } $download_label = esc_html( ' (' . $download->post_title . ')' ); } $tiles = array_filter( array( 'number_of_file_downloads', 'average_file_downloads_per_customer', 'most_downloaded_product', 'average_file_downloads_per_order', ), function( $endpoint ) use ( $download_data ) { switch( $endpoint ) { case 'average_file_downloads_per_customer': case 'most_downloaded_product': case 'average_file_downloads_per_order': return false === $download_data; break; default: return true; } } ); $tables = array_filter( array( 'top_five_most_downloaded_products', ), function( $endpoint ) use ( $download_data ) { return false === $download_data; } ); $charts = array( 'file_downloads_chart', ); $reports->add_report( 'file_downloads', array( 'label' => __( 'File Downloads', 'easy-digital-downloads' ), 'icon' => 'download', 'priority' => 30, 'endpoints' => array( 'tiles' => $tiles, 'tables' => $tables, 'charts' => $charts, ), 'filters' => array( 'dates', 'products' ), ) ); $reports->register_endpoint( 'number_of_file_downloads', array( 'label' => __( 'Number of File Downloads', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter ) { $download = Reports\get_filter_value( 'products' ); $download = ! empty( $download ) && 'all' !== Reports\get_filter_value( 'products' ) ? edd_parse_product_dropdown_value( Reports\get_filter_value( 'products' ) ) : array( 'download_id' => '', 'price_id' => '' ); $stats = new EDD\Stats(); return $stats->get_file_download_count( array( 'range' => $filter['range'], 'download_id' => $download['download_id'], 'price_id' => (string) $download['price_id'], ) ); }, 'display_args' => array( 'comparison_label' => $label . $download_label, ), ), ), ) ); $reports->register_endpoint( 'average_file_downloads_per_customer', array( 'label' => __( 'Average per Customer', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter ) { $stats = new EDD\Stats(); return $stats->get_average_file_download_count( array( 'range' => $filter['range'], 'column' => 'customer_id', ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'average_file_downloads_per_order', array( 'label' => __( 'Average per Order', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter ) { $stats = new EDD\Stats(); return $stats->get_average_file_download_count( array( 'range' => $filter['range'], 'column' => 'order_id', ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'most_downloaded_product', array( 'label' => __( 'Most Downloaded Product', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter ) { $stats = new EDD\Stats(); $d = $stats->get_most_downloaded_products( array( 'range' => $filter['range'] ) ); if ( $d ) { return esc_html( $d[0]->object->post_title ); } }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'top_five_most_downloaded_products', array( 'label' => __( 'Top Five Most Downloaded Products', 'easy-digital-downloads' ) . ' – ' . $label, 'views' => array( 'table' => array( 'display_args' => array( 'class_name' => '\\EDD\\Reports\\Data\\File_Downloads\\Top_Five_Most_Downloaded_List_Table', 'class_file' => EDD_PLUGIN_DIR . 'includes/reports/data/file-downloads/class-top-five-most-downloaded-list-table.php', ), ), ), ) ); $reports->register_endpoint( 'file_downloads_chart', array( 'label' => __( 'Number of File Downloads', 'easy-digital-downloads' ) . $download_label, 'views' => array( 'chart' => array( 'data_callback' => function () use ( $filter, $download_data ) { global $wpdb; $dates = Reports\get_dates_filter( 'objects' ); $chart_dates = Reports\parse_dates_for_range( null, 'now', false ); $day_by_day = Reports\get_dates_filter_day_by_day(); $hour_by_hour = Reports\get_dates_filter_hour_by_hour(); $sql_clauses = array( 'select' => 'date_created AS date', 'where' => '', 'groupby' => '', ); // Default to 'monthly'. $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'MONTH', 'date_created' ); $sql_clauses['orderby'] = 'MONTH(date_created)'; // Now drill down to the smallest unit. if ( $hour_by_hour ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'date_created' ); $sql_clauses['orderby'] = 'HOUR(date_created)'; } elseif ( $day_by_day ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'date_created' ); $sql_clauses['orderby'] = 'DATE(date_created)'; } $product_id = ''; $price_id = ''; if ( is_array( $download_data ) ) { $product_id = $wpdb->prepare( 'AND product_id = %d', absint( $download_data['download_id'] ) ); $price_id = isset( $download_data['price_id'] ) && is_numeric( $download_data['price_id'] ) ? $wpdb->prepare( 'AND price_id = %d', absint( $download_data['price_id'] ) ) : ''; } $results = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(id) AS total, {$sql_clauses['select']} FROM {$wpdb->edd_logs_file_downloads} edd_lfd WHERE edd_lfd.date_created >= %s AND edd_lfd.date_created <= %s {$product_id} {$price_id} GROUP BY {$sql_clauses['groupby']} ORDER BY {$sql_clauses['orderby']} ASC", $dates['start']->copy()->format( 'mysql' ), $dates['end']->copy()->format( 'mysql' ) ) ); $file_downloads = array(); // Initialise all arrays with timestamps and set values to 0. while ( strtotime( $chart_dates['start']->copy()->format( 'mysql' ) ) <= strtotime( $chart_dates['end']->copy()->format( 'mysql' ) ) ) { $timestamp = $chart_dates['start']->copy()->format( 'U' ); $date_on_chart = $chart_dates['start']; $file_downloads[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' ); $file_downloads[ $timestamp ][1] = 0; foreach ( $results as $result ) { $date_of_db_value = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $result->date ) ); // Add any file downloads that happened during this hour. if ( $hour_by_hour ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d H' ) === $date_on_chart->format( 'Y-m-d H' ) ) { $file_downloads[ $timestamp ][1] += absint( $result->total ); } // Add any file downloads that happened during this day. } elseif ( $day_by_day ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d' ) === $date_on_chart->format( 'Y-m-d' ) ) { $file_downloads[ $timestamp ][1] += absint( $result->total ); } // Add any file downloads that happened during this month. } else { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m' ) === $date_on_chart->format( 'Y-m' ) ) { $file_downloads[ $timestamp ][1] += absint( $result->total ); } } } // Move the chart along to the next hour/day/month to get ready for the next loop. if ( $hour_by_hour ) { $chart_dates['start']->addHour( 1 ); } elseif ( $day_by_day ) { $chart_dates['start']->addDays( 1 ); } else { $chart_dates['start']->addMonth( 1 ); } } $file_downloads = array_values( $file_downloads ); return array( 'file_downloads' => $file_downloads ); }, 'type' => 'line', 'options' => array( 'datasets' => array( 'file_downloads' => array( 'label' => __( 'File Downloads', 'easy-digital-downloads' ), 'borderColor' => 'rgba(24,126,244,0.75)', 'backgroundColor' => 'rgba(24,126,244,0.1)', 'fill' => true, 'borderWidth' => 2, 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', 'yAxisID' => 'file-downloads-y', ), ), 'scales' => array( 'yAxes' => array( array( 'id' => 'file-downloads-y', 'type' => 'linear', 'display' => true, 'position' => 'left', 'ticks' => array( 'maxTicksLimit' => 5, 'formattingType' => 'integer', 'suggestedMin' => 0, 'beginAtZero' => true, 'precision' => 0, ), ), ), ), ), ), ), ) ); } catch ( \EDD_Exception $exception ) { edd_debug_log_exception( $exception ); } } add_action( 'edd_reports_init', 'edd_register_file_downloads_report' ); /** * Register discounts report and endpoints. * * @since 3.0 * * @param \EDD\Reports\Data\Report_Registry $reports Report registry. */ function edd_register_discounts_report( $reports ) { try { // Variables to hold date filter values. $options = Reports\get_dates_filter_options(); $filter = Reports\get_filter_value( 'dates' ); $currency = Reports\get_filter_value( 'currencies' ); $hbh = Reports\get_dates_filter_hour_by_hour(); $label = $options[ $filter['range'] ] . ( $hbh ? ' (' . edd_get_timezone_abbr() . ')' : '' ); $discount = Reports\get_filter_value( 'discounts' ); $discount = ! empty( $discount ) && 'all' !== $discount ? $discount : 0; $d = edd_get_discount( $discount ); $discount_label = false !== $d ? esc_html( ' (' . $d->name . ')' ) : ''; $tiles = array_filter( array( 'number_of_discounts_used', 'ratio_of_discounted_orders', 'customer_savings', 'average_discount_amount', 'most_popular_discount', 'discount_usage_count', ), function( $tile ) use ( $discount ) { switch ( $tile ) { case 'discount_usage_count': return 0 !== $discount; break; default: return 0 === $discount; } } ); $tables = array_filter( array( 'top_five_discounts', ), function( $table ) use ( $discount ) { return 0 === $discount; } ); $charts = array( 'discount_usage_chart', ); $reports->add_report( 'discounts', array( 'label' => __( 'Discounts', 'easy-digital-downloads' ), 'icon' => 'tickets-alt', 'priority' => 35, 'endpoints' => array( 'tiles' => $tiles, 'tables' => $tables, 'charts' => $charts, ), 'filters' => array( 'dates', 'discounts' ), ) ); $reports->register_endpoint( 'number_of_discounts_used', array( 'label' => __( 'Number of Discounts Used', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter ) { $stats = new EDD\Stats(); return $stats->get_discount_usage_count( array( 'range' => $filter['range'], ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'ratio_of_discounted_orders', array( 'label' => __( 'Discount Ratio', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter ) { $stats = new EDD\Stats(); return $stats->get_ratio_of_discounted_orders( array( 'range' => $filter['range'], ) ); }, 'display_args' => array( 'context' => 'secondary', 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'customer_savings', array( 'label' => __( 'Customer Savings', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter, $d ) { $stats = new EDD\Stats(); return $stats->get_discount_savings( array( 'range' => $filter['range'], 'output' => 'formatted', 'discount_code' => isset( $d->code ) ? $d->code : '', ) ); }, 'display_args' => array( 'comparison_label' => $label . $discount_label, ), ), ), ) ); $reports->register_endpoint( 'average_discount_amount', array( 'label' => __( 'Average Discount Amount', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter ) { $stats = new EDD\Stats(); return $stats->get_average_discount_amount( array( 'range' => $filter['range'], 'output' => 'formatted', ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'most_popular_discount', array( 'label' => __( 'Most Popular Discount', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter ) { $stats = new EDD\Stats(); $r = $stats->get_most_popular_discounts( array( 'range' => $filter['range'], 'number' => 1, ) ); if ( ! empty( $r ) ) { $r = $r[0]; return esc_html( $r->code . ' (' . $r->count . ')' ); } }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); if ( $d ) { $reports->register_endpoint( 'discount_usage_count', array( 'label' => __( 'Discount Usage Count', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $filter, $d ) { $stats = new EDD\Stats(); return $stats->get_discount_usage_count( array( 'range' => $filter['range'], 'discount_code' => $d->code, ) ); }, 'display_args' => array( 'comparison_label' => $label . $discount_label, ), ), ), ) ); } $reports->register_endpoint( 'top_five_discounts', array( 'label' => __( 'Top Five Discounts', 'easy-digital-downloads' ) . ' – ' . $label, 'views' => array( 'table' => array( 'display_args' => array( 'class_name' => '\\EDD\\Reports\\Data\\Discounts\\Top_Five_Discounts_List_Table', 'class_file' => EDD_PLUGIN_DIR . 'includes/reports/data/discounts/class-top-five-discounts-list-table.php', ), ), ), ) ); if ( $d ) { $reports->register_endpoint( 'discount_usage_chart', array( 'label' => __( 'Discount Usage', 'easy-digital-downloads' ), 'views' => array( 'chart' => array( 'data_callback' => function () use ( $filter, $d ) { global $wpdb; $dates = Reports\get_dates_filter( 'objects' ); $day_by_day = Reports\get_dates_filter_day_by_day(); $hour_by_hour = Reports\get_dates_filter_hour_by_hour(); $chart_dates = Reports\parse_dates_for_range( null, 'now', false ); $sql_clauses = array( 'select' => 'edd_oa.date_created AS date', 'where' => '', ); // Default to 'monthly'. $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'MONTH', 'edd_oa.date_created' ); $sql_clauses['orderby'] = 'MONTH(edd_oa.date_created)'; // Now drill down to the smallest unit. if ( $hour_by_hour ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'edd_oa.date_created' ); $sql_clauses['orderby'] = 'HOUR(edd_oa.date_created)'; } elseif ( $day_by_day ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'edd_oa.date_created' ); $sql_clauses['orderby'] = 'DATE(edd_oa.date_created)'; } $discount_code = ! empty( $d->code ) ? $wpdb->prepare( 'AND type = %s AND description = %s', 'discount', esc_sql( $d->code ) ) : $wpdb->prepare( 'AND type = %s', 'discount' ); $results = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(id) AS total, {$sql_clauses['select']} FROM {$wpdb->edd_order_adjustments} edd_oa WHERE 1=1 {$discount_code} AND edd_oa.date_created >= %s AND edd_oa.date_created <= %s GROUP BY {$sql_clauses['groupby']} ORDER BY {$sql_clauses['orderby']} ASC", $dates['start']->copy()->format( 'mysql' ), $dates['end']->copy()->format( 'mysql' ) ) ); $discount_usage = array(); // Initialise all arrays with timestamps and set values to 0. while ( strtotime( $chart_dates['start']->copy()->format( 'mysql' ) ) <= strtotime( $chart_dates['end']->copy()->format( 'mysql' ) ) ) { $timestamp = $chart_dates['start']->copy()->format( 'U' ); $date_on_chart = $chart_dates['start']; $discount_usage[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' ); $discount_usage[ $timestamp ][1] = 0; // Loop through each date in which there were discount codes used, which we queried from the database. foreach ( $results as $result ) { $date_of_db_value = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $result->date ) ); // Add any discount codes that were used during this hour. if ( $hour_by_hour ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d H' ) === $date_on_chart->format( 'Y-m-d H' ) ) { $discount_usage[ $timestamp ][1] += abs( $result->total ); } // Add any discount codes that were used during this day. } elseif ( $day_by_day ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d' ) === $date_on_chart->format( 'Y-m-d' ) ) { $discount_usage[ $timestamp ][1] += abs( $result->total ); } // Add any discount codes that were used during this month. } else { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m' ) === $date_on_chart->format( 'Y-m' ) ) { $discount_usage[ $timestamp ][1] += abs( $result->total ); } } } // Move the chart along to the next hour/day/month to get ready for the next loop. if ( $hour_by_hour ) { $chart_dates['start']->addHour( 1 ); } elseif ( $day_by_day ) { $chart_dates['start']->addDays( 1 ); } else { $chart_dates['start']->addMonth( 1 ); } } $discount_usage = array_values( $discount_usage ); return array( 'discount_usage' => $discount_usage ); }, 'type' => 'line', 'options' => array( 'datasets' => array( 'discount_usage' => array( 'label' => __( 'Discount Usage', 'easy-digital-downloads' ), 'borderColor' => 'rgba(24,126,244,0.75)', 'backgroundColor' => 'rgba(24,126,244,0.1)', 'fill' => true, 'borderWidth' => 2, 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', ), ), 'scales' => array( 'yAxes' => array( array( 'type' => 'linear', 'display' => true, 'position' => 'left', 'ticks' => array( 'maxTicksLimit' => 2, 'formattingType' => 'integer', 'suggestedMin' => 0, 'beginAtZero' => true, 'precision' => 0, ), ), ), ), ), ), ), ) ); } } catch ( \EDD_Exception $exception ) { edd_debug_log_exception( $exception ); } } add_action( 'edd_reports_init', 'edd_register_discounts_report' ); /** * Register customer report and endpoints. * * @since 3.0 * * @param \EDD\Reports\Data\Report_Registry $reports Report registry. */ function edd_register_customer_report( $reports ) { try { // Variables to hold date filter values. $options = Reports\get_dates_filter_options(); $dates = Reports\get_filter_value( 'dates' ); $exclude_taxes = Reports\get_taxes_excluded_filter(); $hbh = Reports\get_dates_filter_hour_by_hour(); $label = $options[ $dates['range'] ] . ( $hbh ? ' (' . edd_get_timezone_abbr() . ')' : '' ); $reports->add_report( 'customers', array( 'label' => __( 'Customers', 'easy-digital-downloads' ), 'icon' => 'groups', 'priority' => 40, 'endpoints' => array( 'tiles' => array( 'new_customer_growth', 'average_revenue_per_customer', 'average_number_of_orders_per_customer', ), 'tables' => array( 'top_five_customers', 'most_valuable_customers', ), 'charts' => array( 'new_customers', ), ), ) ); $reports->register_endpoint( 'new_customer_growth', array( 'label' => __( 'New Customers', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates ) { $stats = new EDD\Stats(); return $stats->get_customer_count( array( 'range' => $dates['range'], 'relative' => true, ) ); }, 'display_args' => array( 'comparison_label' => $label, ), ), ), ) ); $reports->register_endpoint( 'average_revenue_per_customer', array( 'label' => __( 'Average Revenue per Customer', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () { $stats = new EDD\Stats(); return $stats->get_customer_lifetime_value( array( 'function' => 'AVG', 'output' => 'formatted', ) ); }, ), ), ) ); $reports->register_endpoint( 'average_number_of_orders_per_customer', array( 'label' => __( 'Average Orders per Customer', 'easy-digital-downloads' ), 'views' => array( 'tile' => array( 'data_callback' => function () use ( $dates ) { $stats = new EDD\Stats(); return $stats->get_customer_order_count( array( 'range' => $dates['range'], 'function' => 'AVG', 'relative' => true, ) ); }, ), ), ) ); $reports->register_endpoint( 'top_five_customers', array( 'label' => __( 'Top Five Customers — All Time', 'easy-digital-downloads' ), 'views' => array( 'table' => array( 'display_args' => array( 'class_name' => '\\EDD\\Reports\\Data\\Customers\\Top_Five_Customers_List_Table', 'class_file' => EDD_PLUGIN_DIR . 'includes/reports/data/customers/class-top-five-customers-list-table.php', ), ), ), ) ); $reports->register_endpoint( 'most_valuable_customers', array( 'label' => __( 'Most Valuable Customers', 'easy-digital-downloads' ) . ' — '. $label, 'views' => array( 'table' => array( 'display_args' => array( 'class_name' => '\\EDD\\Reports\\Data\\Customers\\Most_Valuable_Customers_List_Table', 'class_file' => EDD_PLUGIN_DIR . 'includes/reports/data/customers/class-most-valuable-customers-list-table.php', ), ), ), ) ); $reports->register_endpoint( 'new_customers', array( 'label' => __( 'New Customers', 'easy-digital-downloads' ) . ' — ' . $label, 'views' => array( 'chart' => array( 'data_callback' => function () { global $wpdb; $dates = Reports\get_dates_filter( 'objects' ); $day_by_day = Reports\get_dates_filter_day_by_day(); $hour_by_hour = Reports\get_dates_filter_hour_by_hour(); $chart_dates = Reports\parse_dates_for_range( null, 'now', false ); $sql_clauses = array( 'select' => 'date_created AS date', 'groupby' => Reports\get_groupby_date_string( 'MONTH', 'date_created' ), 'orderby' => 'MONTH(date_created)', ); if ( $hour_by_hour ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'date_created' ); $sql_clauses['orderby'] = 'HOUR(date_created)'; } elseif ( $day_by_day ) { $sql_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'date_created' ); $sql_clauses['orderby'] = 'DATE(date_created)'; } $results = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(c.id) AS total, {$sql_clauses['select']} FROM {$wpdb->edd_customers} c WHERE c.date_created >= %s AND c.date_created <= %s GROUP BY {$sql_clauses['groupby']} ORDER BY {$sql_clauses['orderby']} ASC", $dates['start']->copy()->format( 'mysql' ), $dates['end']->copy()->format( 'mysql' ) ) ); $customers = array(); // Initialise all arrays with timestamps and set values to 0. while ( strtotime( $chart_dates['start']->copy()->format( 'mysql' ) ) <= strtotime( $chart_dates['end']->copy()->format( 'mysql' ) ) ) { $timestamp = $chart_dates['start']->copy()->format( 'U' ); $date_on_chart = $chart_dates['start']; $customers[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' ); $customers[ $timestamp ][1] = 0; foreach ( $results as $result ) { $date_of_db_value = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $result->date ) ); // Add any new customers that were created during this hour. if ( $hour_by_hour ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d H' ) === $date_on_chart->format( 'Y-m-d H' ) ) { $customers[ $timestamp ][1] += $result->total; } // Add any new customers that were created during this day. } elseif ( $day_by_day ) { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m-d' ) === $date_on_chart->format( 'Y-m-d' ) ) { $customers[ $timestamp ][1] += $result->total; } // Add any new customers that were created during this month. } else { // If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result. if ( $date_of_db_value->format( 'Y-m' ) === $date_on_chart->format( 'Y-m' ) ) { $customers[ $timestamp ][1] += $result->total; } } } // Move the chart along to the next hour/day/month to get ready for the next loop. if ( $hour_by_hour ) { $chart_dates['start']->addHour( 1 ); } elseif ( $day_by_day ) { $chart_dates['start']->addDays( 1 ); } else { $chart_dates['start']->addMonth( 1 ); } } return array( 'customers' => array_values( $customers ), ); }, 'type' => 'line', 'options' => array( 'datasets' => array( 'customers' => array( 'label' => __( 'New Customers', 'easy-digital-downloads' ), 'borderColor' => 'rgba(24,126,244,0.75)', 'backgroundColor' => 'rgba(24,126,244,0.1)', 'fill' => true, 'borderWidth' => 2, 'pointRadius' => 4, 'pointHoverRadius' => 6, 'pointBackgroundColor' => 'rgb(255,255,255)', ), ), 'scales' => array( 'yAxes' => array( array( 'type' => 'linear', 'display' => true, 'position' => 'left', 'ticks' => array( 'maxTicksLimit' => 5, 'formattingType' => 'integer', 'suggestedMin' => 0, 'beginAtZero' => true, 'precision' => 0, ), ), ), ), ), ), ), ) ); } catch ( \EDD_Exception $exception ) { edd_debug_log_exception( $exception ); } } add_action( 'edd_reports_init', 'edd_register_customer_report' ); /** * Register export report and endpoints. * * @since 3.0 * * @param \EDD\Reports\Data\Report_Registry $reports Report registry. */ function edd_register_export_report( $reports ) { try { $reports->add_report( 'export', array( 'label' => __( 'Export', 'easy-digital-downloads' ), 'icon' => 'migrate', 'priority' => 1000, 'capability' => 'export_shop_reports', 'display_callback' => 'display_export_report', 'filters' => false, ) ); } catch ( \EDD_Exception $exception ) { edd_debug_log_exception( $exception ); } } add_action( 'edd_reports_init', 'edd_register_export_report' ); /** * Render the `Export` report. * * @since 3.0 */ function display_export_report() { global $wpdb; wp_enqueue_script( 'edd-admin-tools-export' ); ?>

html->month_dropdown( 'start_month', 0, 'edd_export_earnings', true ); ?> html->year_dropdown( 'start_year', 0, 5, 0, 'edd_export_earnings' ); ?>
html->month_dropdown( 'end_month', 0, 'edd_export_earnings', true ); ?> html->year_dropdown( 'end_year', 0, 5, 0, 'edd_export_earnings' ); ?>

html->date_field( array( 'id' => 'edd-order-export-start', 'class' => 'edd-export-start', 'name' => 'order-export-start', 'placeholder' => _x( 'From', 'date filter', 'easy-digital-downloads' ), ) ); ?> html->date_field( array( 'id' => 'edd-order-export-end', 'class' => 'edd-export-end', 'name' => 'order-export-end', 'placeholder' => _x( 'To', 'date filter', 'easy-digital-downloads' ), ) ); ?>
html->product_dropdown( array( 'name' => 'download_id', 'id' => 'edd_orders_export_download', 'chosen' => true, /* translators: the plural post type label */ 'placeholder' => sprintf( __( 'All %s', 'easy-digital-downloads' ), edd_get_label_plural() ), ) ); ?> html->customer_dropdown( array( 'name' => 'customer_id', 'id' => 'edd_order_export_customer', 'chosen' => true, 'none_selected' => '', 'placeholder' => __( 'All Customers', 'easy-digital-downloads' ), ) ); wp_nonce_field( 'edd_ajax_export', 'edd_ajax_export' ); ?>

html->date_field( array( 'id' => 'edd-orders-export-start', 'class' => 'edd-export-start', 'name' => 'orders-export-start', 'placeholder' => _x( 'From', 'date filter', 'easy-digital-downloads' ), ) ); ?> html->date_field( array( 'id' => 'edd-orders-export-end', 'class' => 'edd-export-end', 'name' => 'orders-export-end', 'placeholder' => _x( 'To', 'date filter', 'easy-digital-downloads' ), ) ); ?>
html->select( array( 'id' => 'edd_orders_export_status', 'name' => 'status', 'show_option_all' => __( 'All Statuses', 'easy-digital-downloads' ), 'show_option_none' => false, 'selected' => false, 'options' => edd_get_payment_statuses(), ) ); wp_nonce_field( 'edd_ajax_export', 'edd_ajax_export' ); ?>

html->date_field( array( 'id' => 'edd-taxed-orders-export-start', 'class' => 'edd-export-start', 'name' => 'taxed-orders-export-start', 'placeholder' => _x( 'From', 'date filter', 'easy-digital-downloads' ) ) ); ?> html->date_field( array( 'id' => 'edd-taxed-orders-export-end', 'class' => 'edd-export-end', 'name' => 'taxed-orders-export-end', 'placeholder' => _x( 'To', 'date filter', 'easy-digital-downloads' ) ) ); ?>
html->select( array( 'id' => 'edd_taxed_orders_export_status', 'name' => 'status', 'show_option_all' => __( 'All Statuses', 'easy-digital-downloads' ), 'show_option_none' => false, 'selected' => false, 'options' => edd_get_payment_statuses(), ) ); ?> html->country_select( array( 'name' => 'country', 'id' => 'edd_reports_filter_taxed_countries', 'selected' => false, 'show_option_all' => false, ) ); ?> html->region_select( array( 'id' => 'edd_reports_filter_regions', 'placeholder' => __( 'All Regions', 'easy-digital-downloads' ), ) ); wp_nonce_field( 'edd_ajax_export', 'edd_ajax_export' ); ?>

prepare( "tt.taxonomy IN ({$placeholders})", $taxonomies ); $sql = "SELECT t.*, tt.*, tr.object_id FROM {$wpdb->terms} AS t INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id INNER JOIN {$wpdb->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE {$taxonomy__in}"; $results = $wpdb->get_results( $sql ); $taxonomies = array(); if ( $results ) { foreach ( $results as $r ) { $t = get_taxonomy( $r->taxonomy ); $taxonomies[ absint( $r->term_id ) ] = $t->labels->singular_name . ': ' . esc_html( $r->name ); } } ?> html->select( array( 'name' => 'taxonomy', 'id' => 'edd_export_taxonomy', 'options' => $taxonomies, 'selected' => false, 'show_option_none' => false, 'show_option_all' => __( 'All Taxonomies', 'easy-digital-downloads' ), ) ); ?> html->product_dropdown( array( 'name' => 'download', 'id' => 'edd_customer_export_download', 'chosen' => true, /* translators: the plural post type label */ 'placeholder' => sprintf( __( 'All %s', 'easy-digital-downloads' ), edd_get_label_plural() ), ) ); wp_nonce_field( 'edd_ajax_export', 'edd_ajax_export' ); ?>

html->date_field( array( 'id' => 'edd-taxed-customers-export-start', 'class' => 'edd-export-start', 'name' => 'taxed-customers-export-start', 'placeholder' => _x( 'From', 'date filter', 'easy-digital-downloads' ) ) ); ?> html->date_field( array( 'id' => 'edd-taxed-customers-export-end', 'class' => 'edd-export-end', 'name' => 'taxed-customers-export-end', 'placeholder' => _x( 'To', 'date filter', 'easy-digital-downloads' ) ) ); ?>

html->product_dropdown( array( 'name' => 'download_id', 'id' => 'edd_download_export_download', 'chosen' => true, /* translators: the plural post type label */ 'placeholder' => sprintf( __( 'All %s', 'easy-digital-downloads' ), edd_get_label_plural() ), ) ); ?>

html->date_field( array( 'id' => 'edd-api-requests-export-start', 'class' => 'edd-export-start', 'name' => 'api-requests-export-start', 'placeholder' => _x( 'From', 'date filter', 'easy-digital-downloads' ) ) ); ?> html->date_field( array( 'id' => 'edd-api-requests-export-end', 'class' => 'edd-export-end', 'name' => 'api-requests-export-end', 'placeholder' => _x( 'To', 'date filter', 'easy-digital-downloads' ) ) ); ?>

html->product_dropdown( array( 'name' => 'download_id', 'id' => 'edd_file_download_export_download', 'chosen' => true, /* translators: the plural post type label */ 'placeholder' => sprintf( __( 'All %s', 'easy-digital-downloads' ), edd_get_label_plural() ), ) ); ?>
html->date_field( array( 'id' => 'edd-file-download-export-start', 'class' => 'edd-export-start', 'name' => 'file-download-export-start', 'placeholder' => _x( 'From', 'date filter', 'easy-digital-downloads' ) ) ); ?> html->date_field( array( 'id' => 'edd-file-download-export-end', 'class' => 'edd-export-end', 'name' => 'file-download-export-end', 'placeholder' => _x( 'To', 'date filter', 'easy-digital-downloads' ) ) ); ?>
0, 'sales' => 0 ); $stats = new EDD_Payment_Stats; $to_date_earnings = $stats->get_earnings( 0, 'this_month', null, $include_taxes ); $to_date_sales = $stats->get_sales( 0, 'this_month' ); $current_day = date( 'd', current_time( 'timestamp' ) ); $current_month = date( 'n', current_time( 'timestamp' ) ); $current_year = date( 'Y', current_time( 'timestamp' ) ); $days_in_month = cal_days_in_month( CAL_GREGORIAN, $current_month, $current_year ); $estimated['earnings'] = ( $to_date_earnings / $current_day ) * $days_in_month; $estimated['sales'] = ( $to_date_sales / $current_day ) * $days_in_month; // Cache for one day set_transient( 'edd_estimated_monthly_stats' . $include_taxes, $estimated, 86400 ); } return maybe_unserialize( $estimated ); } /** * Adds postbox nonces, which are used to save the position of tile endpoint meta boxes. * * @since 3.0 */ function edd_add_screen_options_nonces() { wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); } add_action( 'admin_footer', 'edd_add_screen_options_nonces' ); /** * This function adds a notice to the bottom of the Tax reports screen if a default tax rate is detected, stating * that we cannot report on the default tax rate. * * @since 3.0 * @param \EDD\Reports\Data\Report|\WP_Error $report The current report object, or WP_Error if invalid. */ function edd_tax_report_notice( $report ) { if ( 'taxes' === $report->object_id && false !== edd_get_option( 'tax_rate' ) ) { ?>

: