laipower/wp-content/plugins/easy-digital-downloads/includes/admin/reporting/reports.php

2815 lines
91 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Reports functions.
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
* @since 3.0 Full refactor of Reports.
*/
use EDD\Reports;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Load a report early in the admin-area.
*
* This action-function loads the Report early, so that a redirect can occur in
* the event that the report is not valid, registered, or the user cannot view
* it.
*
* Note that pre-3.0 reports are shimmed via EDD\Reports::legacy_reports()
*
* @since 3.0
*/
function edd_admin_load_report() {
// Redirect URL (on error)
$redirect_url = edd_get_admin_url( array(
'page' => '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( '<h2 class="edd-reports__heading">%s</h2>', 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' );
?>
<div class="wrap">
<h1><?php esc_html_e( 'Reports', 'easy-digital-downloads' ); ?></h1>
<?php Reports\display_filters( EDD()->report ); ?>
<div id="edd-item-wrapper" class="full-width edd-clearfix">
<?php edd_reports_sections(); ?>
</div>
</div><!-- .wrap -->
<?php
}
/**
* Register overview report and endpoints.
*
* @since 3.0
*
* @param \EDD\Reports\Data\Report_Registry $reports Report registry.
*/
function edd_register_overview_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( '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 . ' &mdash; ' . __( '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 . ' &mdash; ' . __( '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,
'purchase_count' => 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' ) . ' &mdash; ' . $label . ' &mdash; ' . __( '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 . ' &mdash; ' . __( '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' ) . ' &mdash; ' . $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() ) . ' &mdash; ' . $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' );
$chart_dates = Reports\parse_dates_for_range( null, 'now', false );
$period = Reports\get_graph_period();
$sql_clauses = Reports\get_sql_clauses( $period, 'edd_oi.date_created' );
$union_clauses = array(
'select' => 'date',
'groupby' => 'date',
'orderby' => '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, {$sql_clauses['select']}
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 {$price_id} 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']}",
$download_data['download_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, {$sql_clauses['select']}
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
{$price_id}
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']}",
$download_data['download_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()->utils->date( $earnings_result->date );
// Add any sales/earnings that happened during this hour.
if ( 'hour' === $period ) {
// 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' === $period ) {
// 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()->utils->date( $sales_result->date );
// Add any sales/earnings that happened during this hour.
if ( 'hour' === $period ) {
// 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' === $period ) {
// 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' === $period ) {
$chart_dates['start']->addHour( 1 );
} elseif ( 'day' === $period ) {
$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' ) . ' &mdash; ' . $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' ) . ' &mdash; ' . $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' ) . ' &mdash; ' . $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' ) . ' &mdash; ' . $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' ) . ' &mdash; ' . $label,
'views' => array(
'chart' => array(
'data_callback' => function () use ( $dates, $exclude_taxes, $currency ) {
global $wpdb;
$dates = Reports\get_dates_filter( 'objects' );
$chart_dates = Reports\parse_dates_for_range( null, 'now', false );
$period = Reports\get_graph_period();
$sql_clauses = Reports\get_sql_clauses( $period, '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()->utils->date( $result->date );
// Add any sales/earnings that happened during this hour.
if ( 'hour' === $period ) {
// 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' === $period ) {
// 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' === $period ) {
$chart_dates['start']->addHour( 1 );
} elseif ( 'day' === $period ) {
$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 );
$period = Reports\get_graph_period();
$sql_clauses = Reports\get_sql_clauses( $period );
$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()->utils->date( $result->date );
// Add any file downloads that happened during this hour.
if ( 'hour' === $period ) {
// 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' === $period ) {
// 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' === $period ) {
$chart_dates['start']->addHour( 1 );
} elseif ( 'day' === $period ) {
$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' );
$chart_dates = Reports\parse_dates_for_range( null, 'now', false );
$period = Reports\get_graph_period();
$sql_clauses = Reports\get_sql_clauses( $period, '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()->utils->date( $result->date );
// Add any discount codes that were used during this hour.
if ( 'hour' === $period ) {
// 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' === $period ) {
// 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' === $period ) {
$chart_dates['start']->addHour( 1 );
} elseif ( 'day' === $period ) {
$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,
'purchase_count' => 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 &mdash; 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' ) . ' &mdash; '. $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' ) . ' &mdash; ' . $label,
'views' => array(
'chart' => array(
'data_callback' => function () {
global $wpdb;
$dates = Reports\get_dates_filter( 'objects' );
$chart_dates = Reports\parse_dates_for_range( null, 'now', false );
$period = Reports\get_graph_period();
$sql_clauses = Reports\get_sql_clauses( $period );
$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
AND c.purchase_count > 0
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()->utils->date( $result->date );
// Add any new customers that were created during this hour.
if ( 'hour' === $period ) {
// 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' === $period ) {
// 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' === $period ) {
$chart_dates['start']->addHour( 1 );
} elseif ( 'day' === $period ) {
$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() {
wp_enqueue_script( 'edd-admin-tools-export' );
?>
<div id="edd-dashboard-widgets-wrap">
<div class="metabox-holder">
<div id="post-body">
<div id="post-body-content" class="edd-reports-export edd-admin--has-grid">
<?php
do_action( 'edd_reports_tab_export_content_top' );
$views = array(
'earnings-report',
'sales-earnings',
'sales',
'orders',
'taxed-orders',
'customers',
'taxed-customers',
'downloads',
'api-requests',
'download-history',
);
foreach ( $views as $view ) {
include "views/export-{$view}.php";
}
do_action( 'edd_reports_tab_export_content_bottom' );
?>
</div>
</div>
</div>
</div>
<?php
}
/**
* Retrieves estimated monthly earnings and sales
*
* @since 1.5
*
* @param bool $include_taxes If the estimated earnings should include taxes
* @return array
*/
function edd_estimated_monthly_stats( $include_taxes = true ) {
$estimated = get_transient( 'edd_estimated_monthly_stats' . $include_taxes );
if ( false === $estimated ) {
$estimated = array(
'earnings' => 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' ) ) {
?>
<p class="description">
<strong><?php esc_html_e( 'Notice', 'easy-digital-downloads' ); ?>: </strong>
<?php esc_html_e( 'Tax reports are only generated for taxes associated with a location. The legacy default tax rate is unable to be reported on.', 'easy-digital-downloads' ); ?>
</p>
<?php
}
}
add_action( 'edd_reports_page_bottom', 'edd_tax_report_notice', 10, 1 );
/**
* Will return HTML for relative date ranges dropdown.
*
* @since 3.1
*/
function edd_reports_get_relative_date_ranges() {
require_once EDD_PLUGIN_DIR . 'includes/reports/reports-functions.php';
$range = isset( $_REQUEST['range'] )
? sanitize_text_field( $_REQUEST['range'] )
: '';
$relative_range = isset( $_REQUEST['relative_range'] )
? sanitize_text_field( $_REQUEST['relative_range'] )
: '';
if ( empty( $range ) || empty( $relative_range ) ) {
return;
}
echo Reports\display_relative_dates_dropdown_options( $range, $relative_range );
edd_die();
}
add_action( 'wp_ajax_edd_reports_get_relative_date_ranges', 'edd_reports_get_relative_date_ranges' );